Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

pgAdmin 4 Multi?Target Vulnerability Scanner
pgAdmin 4 Multi?Target Vulnerability Scanner
pgAdmin 4 Multi?Target Vulnerability Scanner

=============================================================================================================================================
| # Title pgAdmin 4 Multi?Target Vulnerability Scanner

=============================================================================================================================================
| # Title : pgAdmin 4 Multi?Target Vulnerability Scanner |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : https://www.pgadmin.org |
=============================================================================================================================================

[+] References : https://packetstorm.news/files/id/213056/ & CVE-2025-13780

[+] Summary : This project is a PHP?based multi?target vulnerability scanner designed to identify potential exposure to CVE?2025?13780 affecting pgAdmin 4 versions ? 8.14.
The tool operates as a heuristic / threat?intelligence scanner, not an exploit. It evaluates targets by:

Detecting whether the service is pgAdmin 4

Extracting and analyzing the running version

Assessing version?based vulnerability conditions

Simulating known regex bypass conditions related to the CVE

Calculating a confidence score to estimate exposure likelihood

[+] Key features include:

Single and multi?target scanning

CLI?friendly output suitable for SOC, CI/CD, and automation

Safe, non?exploitative design (no command execution)

Clear exit codes for pipeline integration

Evidence?ready results for reporting and triage

The scanner is intended for defensive security testing, blue?team validation, and risk assessment on systems you own or are authorized to test.

[+] POC :

php scanner.php -u http://localhost:5050 -v

php scanner.php --url=http://target:5050 --email=This email address is being protected from spambots. You need JavaScript enabled to view it. --password=pass --json

<?php

class PgAdminScanner {
private $base_url;
private $email;
private $password;
private $timeout;
private $verbose;
private $csrf_token;
private $version;
private $session;
private $cookie_file;
private $scan_result = [];

public function __construct($base_url, $email = null, $password = null, $timeout = 10, $verbose = false) {
$this->base_url = rtrim($base_url, '/');
$this->email = $email;
$this->password = $password;
$this->timeout = $timeout;
$this->verbose = $verbose;
$this->cookie_file = tempnam(sys_get_temp_dir(), 'pgadmin_cookies_');
$this->session = $this->initCurlSession();
}

public function __destruct() {
if ($this->session) {
curl_close($this->session);
}
if (file_exists($this->cookie_file)) {
@unlink($this->cookie_file);
}
}

private function initCurlSession() {
$ch = curl_init();

curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => $this->timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_COOKIEJAR => $this->cookie_file,
CURLOPT_COOKIEFILE => $this->cookie_file,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
CURLOPT_HEADER => false,
]);

return $ch;
}

private function httpRequest($url, $method = 'GET', $data = null, $headers = []) {
$ch = $this->session;

// Reset session for this request
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, $method === 'POST');

if ($method === 'POST' && $data) {
if (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'application/json') !== false) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
} else {
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
}
} else {
curl_setopt($ch, CURLOPT_POSTFIELDS, null);
}

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);

if ($response === false) {
return [
'status' => 0,
'body' => null,
'error' => $error,
'curl_errno' => curl_errno($ch)
];
}

return [
'status' => $http_code,
'body' => $response,
'error' => $error,
'curl_errno' => 0
];
}

private function log($message, $type = 'info') {
$prefixes = [
'info' => '[*]',
'success' => '[+]',
'warning' => '[!]',
'error' => '[-]',
'vuln' => '[VULNERABLE]',
'safe' => '[NOT VULNERABLE]',
'debug' => '[DEBUG]'
];

$prefix = isset($prefixes[$type]) ? $prefixes[$type] : $prefixes['info'];

if ($type === 'debug' && !$this->verbose) {
return;
}

$output = $prefix . ' ' . $message . PHP_EOL;

if (php_sapi_name() === 'cli') {
echo $output;
} else {
// For web interface, we'll store logs
$this->scan_result['logs'][] = ['type' => $type, 'message' => $message];
}
}

public function checkConnectivity() {
$this->log("Checking connectivity to " . $this->base_url);

$response = $this->httpRequest($this->base_url . '/login');

if ($response['curl_errno'] !== 0) {
$this->log("Connection failed: " . $response['error'], "error");
return false;
}

if ($response['status'] == 200 || $response['status'] == 302) {
$body_lower = strtolower($response['body']);
if (strpos($body_lower, 'pgadmin') !== false ||
strpos($response['body'], 'pgAdmin') !== false) {
$this->log("Target appears to be pgAdmin", "success");

// Extract version
$this->version = $this->extractVersion($response['body']);
if ($this->version) {
$this->log("Detected version: " . $this->version, "success");
} else {
$this->log("Could not detect version from login page", "warning");
}

// Try ping endpoint for version
if (!$this->version) {
$ping_response = $this->httpRequest($this->base_url . '/misc/ping');
if ($ping_response['status'] == 200) {
$this->version = $this->extractVersionFromJson($ping_response['body']);
if ($this->version) {
$this->log("Detected version from API: " . $this->version, "success");
}
}
}

// Extract CSRF token
$this->csrf_token = $this->extractCsrfToken($response['body']);
if ($this->csrf_token) {
$this->log("CSRF token obtained", "success");
$this->log("CSRF token (first 20 chars): " . substr($this->csrf_token, 0, 20) . "...", "debug");
}

return true;
}
}

$this->log("Target does not appear to be pgAdmin or is not accessible (HTTP {$response['status']})", "error");
return false;
}

private function extractVersion($html) {
// Try multiple patterns to extract version
$patterns = [
'/"version":\s*"([^"]+)"/i',
'/pgadmin\s*4?\s*[vV]?(\d+\.\d+(?:\.\d+)?)/i',
'/APP_VERSION\s*[=:]\s*["\']([^"\']+)["\']/i',
'/data-version=["\']([^"\']+)["\']/i',
'/<title>.*?pgadmin\s*4?\s*-?\s*[vV]?(\d+\.\d+(?:\.\d+)?).*?<\/title>/i',
'/"pgAdmin4_version":\s*"([^"]+)"/i',
'/app_version["\']?\s*[=:]\s*["\']([^"\']+)["\']/i',
];

foreach ($patterns as $pattern) {
if (preg_match($pattern, $html, $matches)) {
$version = $matches[1];
// Clean version string
$version = preg_replace('/[^0-9.]/', '', $version);
if (preg_match('/^\d+(\.\d+){0,2}$/', $version)) {
return $version;
}
}
}

return null;
}

private function extractVersionFromJson($json) {
try {
$data = json_decode($json, true);
if ($data) {
if (isset($data['version'])) {
return $data['version'];
}
if (isset($data['pgAdmin4_version'])) {
return $data['pgAdmin4_version'];
}
}

// Try regex on JSON string
$patterns = [
'/"version":\s*"([^"]+)"/',
'/"pgAdmin4_version":\s*"([^"]+)"/'
];

foreach ($patterns as $pattern) {
if (preg_match($pattern, $json, $matches)) {
return $matches[1];
}
}
} catch (Exception $e) {
// Silent fail
}

return null;
}

private function extractCsrfToken($html) {
$patterns = [
'/"csrfToken":\s*"([^"]+)"/',
'/name="csrf_token"\s+value="([^"]+)"/',
'/"csrf_token":"([^"]+)"/',
'/csrf_token\s*=\s*"([^"]+)"/'
];

foreach ($patterns as $pattern) {
if (preg_match($pattern, $html, $matches)) {
return $matches[1];
}
}

return null;
}

public function authenticate() {
if (!$this->email || !$this->password) {
$this->log("No credentials provided, skipping authentication", "warning");
return false;
}

$this->log("Attempting authentication as " . $this->email);

// First get login page to get CSRF token
$login_page = $this->httpRequest($this->base_url . '/login');
$csrf_token = $this->extractCsrfToken($login_page['body']);

if (!$csrf_token) {
$this->log("Could not obtain CSRF token for authentication", "error");
return false;
}

// Prepare login data
$login_data = [
'email' => $this->email,
'password' => $this->password,
'csrf_token' => $csrf_token
];

$headers = [
'X-pgA-CSRFToken: ' . $csrf_token,
'Referer: ' . $this->base_url . '/login',
'Origin: ' . $this->base_url,
'X-Requested-With: XMLHttpRequest',
'Content-Type: application/x-www-form-urlencoded'
];

$response = $this->httpRequest(
$this->base_url . '/authenticate/login',
'POST',
$login_data,
$headers
);

$this->log("Login response: HTTP " . $response['status'], "debug");

if ($response['status'] == 200 || $response['status'] == 302) {
// Check if we're redirected to browser page
$browser_check = $this->httpRequest($this->base_url . '/browser/');
if (strpos($browser_check['body'], 'login') === false &&
strpos($browser_check['body'], 'authenticate') === false) {
$this->log("Authentication successful", "success");

// Update CSRF token from browser page
$new_csrf = $this->extractCsrfToken($browser_check['body']);
if ($new_csrf) {
$this->csrf_token = $new_csrf;
$this->log("CSRF token updated after authentication", "debug");
}

return true;
}
}

$this->log("Authentication failed (check credentials)", "error");
return false;
}

public function testRegexBypass() {
$this->log("Testing regex bypass patterns (heuristic simulation)", "warning");
$this->log("Note: This is local regex simulation, not actual pgAdmin validation", "info");

$vulnerable_regex = '/(^|\n)[ \t]*\\\/';

$test_cases = [
[
'name' => 'Normal payload',
'payload' => "SELECT 1;\n\\! echo test",
'should_bypass' => false
],
[
'name' => 'UTF-8 BOM bypass (hex EF BB BF)',
'payload' => "\xEF\xBB\xBF\\! echo test",
'should_bypass' => true,
'note' => 'BOM prefix prevents regex match'
],
[
'name' => 'CRLF injection bypass',
'payload' => "SELECT 1;\n\r\\! echo test",
'should_bypass' => true,
'note' => 'CR breaks the pattern matching'
],
[
'name' => 'Tab before backslash',
'payload' => "SELECT 1;\n\t\\! echo test",
'should_bypass' => false,
'note' => 'Should be caught by regex'
]
];

$results = [];

foreach ($test_cases as $test) {
$match = preg_match($vulnerable_regex, $test['payload']);
$bypassed = ($match === 0 || $match === false);

$results[$test['name']] = [
'bypassed' => $bypassed,
'expected' => $test['should_bypass'],
'success' => ($bypassed === $test['should_bypass']),
'note' => $test['note'] ?? ''
];

if ($bypassed && $test['should_bypass']) {
$this->log(" ? " . $test['name'] . ": BYPASSED (as expected)", "success");
} elseif (!$bypassed && !$test['should_bypass']) {
$this->log(" ? " . $test['name'] . ": BLOCKED (as expected)", "info");
} else {
$this->log(" ? " . $test['name'] . ": UNEXPECTED - " .
($bypassed ? "BYPASSED" : "BLOCKED"), "warning");
}
}

return $results;
}

public function checkVersionVulnerable() {
if (!$this->version) {
$this->log("Could not determine version, assuming potentially vulnerable", "warning");
return true; // Assume vulnerable if unknown
}

// Parse version safely
$parts = array_map('intval', explode('.', $this->version . '.0.0'));
$major = $parts[0];
$minor = $parts[1];
$patch = $parts[2];

$this->log("Parsed version: {$major}.{$minor}.{$patch}", "debug");

// CVE-2025-13780 affects versions <= 8.14
if ($major < 8) {
$this->log("Version {$this->version} is in vulnerable range (major < 8)", "warning");
return true;
} elseif ($major == 8 && $minor <= 14) {
$this->log("Version {$this->version} is in vulnerable range (8.x <= 8.14)", "warning");
return true;
} else {
$this->log("Version {$this->version} appears to be patched (> 8.14)", "success");
return false;
}
}

public function checkRestoreEndpoint() {
$this->log("Checking restore endpoint accessibility", "info");

$endpoints = [
'/restore/job/' => 'Restore Job API',
'/tools/restore/' => 'Tools Restore',
'/misc/bgprocess/' => 'Background Process'
];

$accessible = [];

foreach ($endpoints as $endpoint => $name) {
$response = $this->httpRequest($this->base_url . $endpoint);

if ($response['status'] != 404 && $response['status'] != 0) {
$accessible[] = [
'endpoint' => $endpoint,
'name' => $name,
'status' => $response['status'],
'requires_auth' => ($response['status'] == 401 || $response['status'] == 403)
];

$this->log(" {$name} ({$endpoint}): HTTP {$response['status']}" .
($response['status'] == 401 ? ' (requires auth)' : ''), "debug");
}
}

return $accessible;
}

public function scan() {
$this->log("Starting vulnerability scan for " . $this->base_url, "info");
$this->log("This is a HEURISTIC scanner - manual verification required", "warning");

$start_time = microtime(true);

$this->scan_result = [
'target' => $this->base_url,
'scan_time' => date('Y-m-d H:i:s'),
'is_pgadmin' => false,
'version' => null,
'version_vulnerable' => null,
'bypass_tests' => [],
'endpoints' => [],
'authenticated' => false,
'vulnerable' => false,
'confidence_score' => 0,
'confidence_level' => 'unknown',
'error' => null,
'logs' => []
];

try {
// Step 1: Check connectivity
if (!$this->checkConnectivity()) {
$this->scan_result['error'] = 'Target not reachable or not pgAdmin';
$this->scan_result['is_pgadmin'] = false;
return $this->scan_result;
}

$this->scan_result['is_pgadmin'] = true;
$this->scan_result['version'] = $this->version;

// Step 2: Check version vulnerability
$version_vulnerable = $this->checkVersionVulnerable();
$this->scan_result['version_vulnerable'] = $version_vulnerable;

// Step 3: Test regex bypass (heuristic)
$bypass_results = $this->testRegexBypass();
$this->scan_result['bypass_tests'] = $bypass_results;

// Step 4: Check restore endpoints
$endpoints = $this->checkRestoreEndpoint();
$this->scan_result['endpoints'] = $endpoints;

// Step 5: Authenticate if credentials provided
if ($this->email && $this->password) {
$authenticated = $this->authenticate();
$this->scan_result['authenticated'] = $authenticated;
}

// Calculate confidence score
$confidence_score = $this->calculateConfidence($version_vulnerable, $bypass_results, $endpoints);
$this->scan_result['confidence_score'] = $confidence_score;
$this->scan_result['confidence_level'] = $this->getConfidenceLevel($confidence_score);

// Determine vulnerability status
$this->scan_result['vulnerable'] = $this->determineVulnerability($version_vulnerable, $confidence_score);

$end_time = microtime(true);
$this->scan_result['scan_duration'] = round($end_time - $start_time, 2);

if ($this->scan_result['vulnerable']) {
$this->log("HEURISTIC RESULT: Target appears VULNERABLE to CVE-2025-13780", "vuln");
$this->log("Confidence: {$confidence_score}/100 ({$this->scan_result['confidence_level']})", "warning");
} else {
$this->log("HEURISTIC RESULT: Target does not appear vulnerable", "safe");
}

} catch (Exception $e) {
$this->scan_result['error'] = $e->getMessage();
$this->log("Scan error: " . $e->getMessage(), "error");
}

return $this->scan_result;
}

private function calculateConfidence($version_vulnerable, $bypass_results, $endpoints) {
$score = 0;

// Version factor (40%)
if ($version_vulnerable === true) {
$score += 40;
} elseif ($version_vulnerable === null) {
$score += 15; // Unknown version
}

// Regex bypass factor (30%)
$bom_bypass = isset($bypass_results['UTF-8 BOM bypass (hex EF BB BF)']) ?
$bypass_results['UTF-8 BOM bypass (hex EF BB BF)']['bypassed'] : false;
$crlf_bypass = isset($bypass_results['CRLF injection bypass']) ?
$bypass_results['CRLF injection bypass']['bypassed'] : false;

if ($bom_bypass && $crlf_bypass) {
$score += 30;
} elseif ($bom_bypass || $crlf_bypass) {
$score += 20;
}

// Endpoint accessibility factor (20%)
if (count($endpoints) > 0) {
$score += 20;
} elseif ($this->scan_result['authenticated']) {
$score += 10; // Authenticated but no endpoints found
}

// Authentication factor (10%)
if ($this->scan_result['authenticated']) {
$score += 10;
}

return min(100, $score);
}

private function getConfidenceLevel($score) {
if ($score >= 80) return 'high';
if ($score >= 60) return 'medium';
if ($score >= 40) return 'low';
return 'very_low';
}

private function determineVulnerability($version_vulnerable, $confidence_score) {
if (!$version_vulnerable) {
return false;
}

if ($confidence_score >= 70) {
return true;
}

if ($confidence_score >= 50 && $version_vulnerable) {
return true; // Likely vulnerable
}

return false;
}

public function printReport($format = 'text') {
if ($format === 'json') {
header('Content-Type: application/json');
echo json_encode($this->scan_result, JSON_PRETTY_PRINT);
return;
}

if (php_sapi_name() !== 'cli') {
echo '<!DOCTYPE html><html><head><title>Scan Report</title><style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: 0 auto; }
.vuln { background: #ffebee; border: 2px solid #f44336; padding: 15px; }
.safe { background: #e8f5e9; border: 2px solid #4caf50; padding: 15px; }
.info { background: #e3f2fd; border: 2px solid #2196f3; padding: 15px; }
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style></head><body><div class="container">';
}

$result = $this->scan_result;

echo PHP_EOL . str_repeat("=", 70) . PHP_EOL;
echo "CVE-2025-13780 SCAN REPORT" . PHP_EOL;
echo str_repeat("=", 70) . PHP_EOL . PHP_EOL;

echo "Target: " . $result['target'] . PHP_EOL;
echo "Scan Time: " . $result['scan_time'] . PHP_EOL;
echo "Duration: " . ($result['scan_duration'] ?? 'N/A') . " seconds" . PHP_EOL . PHP_EOL;

echo "Status: ";
if ($result['vulnerable']) {
echo " [VULNERABLE]" . PHP_EOL;
} else {
echo " [NOT VULNERABLE]" . PHP_EOL;
}

echo "Confidence: " . $result['confidence_score'] . "/100 (" .
($result['confidence_level'] ?? 'unknown') . ")" . PHP_EOL . PHP_EOL;

echo str_repeat("-", 70) . PHP_EOL;
echo "DETAILS:" . PHP_EOL;
echo str_repeat("-", 70) . PHP_EOL . PHP_EOL;

echo "Is pgAdmin: " . ($result['is_pgadmin'] ? 'Yes' : 'No') . PHP_EOL;
echo "Version: " . ($result['version'] ?: 'Unknown') . PHP_EOL;

if ($result['version']) {
echo "Version Vulnerable (<= 8.14): " .
($result['version_vulnerable'] ? 'Yes' : 'No') . PHP_EOL;
}

echo "Authenticated: " . ($result['authenticated'] ? 'Yes' : 'No') . PHP_EOL . PHP_EOL;

// Endpoints
if (!empty($result['endpoints'])) {
echo "Accessible Endpoints:" . PHP_EOL;
foreach ($result['endpoints'] as $ep) {
echo " - {$ep['name']} ({$ep['endpoint']}): HTTP {$ep['status']}" .
($ep['requires_auth'] ? ' (requires auth)' : '') . PHP_EOL;
}
echo PHP_EOL;
}

// Bypass tests
if (!empty($result['bypass_tests'])) {
echo "Regex Bypass Tests (Heuristic):" . PHP_EOL;
foreach ($result['bypass_tests'] as $name => $test) {
$status = $test['bypassed'] ? 'BYPASSED' : 'BLOCKED';
$expected = $test['expected'] ? 'should bypass' : 'should block';
$icon = ($test['bypassed'] == $test['expected']) ? '?' : '?';
echo " {$icon} {$name}: {$status} ({$expected})" . PHP_EOL;
}
echo PHP_EOL;
}

if ($result['vulnerable']) {
echo str_repeat("", 70) . PHP_EOL;
echo "VULNERABILITY DETECTED - CVE-2025-13780" . PHP_EOL;
echo str_repeat("", 70) . PHP_EOL . PHP_EOL;

echo "Impact: Remote Code Execution via regex bypass in restore functionality" . PHP_EOL . PHP_EOL;

echo "Affected Versions: pgAdmin 4 <= 8.14" . PHP_EOL;
echo "Patched Versions: pgAdmin 4 > 8.14" . PHP_EOL . PHP_EOL;

echo "Recommended Actions:" . PHP_EOL;
echo "1. IMMEDIATE: Upgrade to pgAdmin 4 version > 8.14" . PHP_EOL;
echo "2. Restrict network access to pgAdmin interface" . PHP_EOL;
echo "3. Monitor for suspicious SQL file uploads" . PHP_EOL;
echo "4. ? Implement WAF rules to block BOM/CRLF patterns" . PHP_EOL . PHP_EOL;

echo "Manual Verification Required:" . PHP_EOL;
echo "1. Attempt to upload SQL file with BOM prefix (\\xEF\\xBB\\xBF\\! whoami)" . PHP_EOL;
echo "2. Test restore functionality" . PHP_EOL;
echo "3. Check for command execution" . PHP_EOL;
} elseif ($result['error']) {
echo " Scan Error: " . $result['error'] . PHP_EOL;
} else {
echo " Target appears secure against CVE-2025-13780" . PHP_EOL;
echo " (Based on heuristic analysis)" . PHP_EOL;
}

echo PHP_EOL . str_repeat("=", 70) . PHP_EOL;
echo "DISCLAIMER: This is a heuristic scanner. Manual verification is required." . PHP_EOL;
echo "Use only on authorized systems. The authors are not responsible for misuse." . PHP_EOL;
echo str_repeat("=", 70) . PHP_EOL;

if (php_sapi_name() !== 'cli') {
echo '</div></body></html>';
}
}
}

// CLI Interface with proper input sanitization
if (php_sapi_name() === 'cli') {
$longopts = [
"url:",
"email:",
"password:",
"verbose",
"json",
"output:",
"help"
];

$options = getopt("u:e:p:vh", $longopts);

if (isset($options['h']) || isset($options['help'])) {
echo "CVE-2025-13780 pgAdmin 4 Vulnerability Scanner (PHP)" . PHP_EOL;
echo "Heuristic scanner for regex bypass RCE vulnerability" . PHP_EOL . PHP_EOL;
echo "Usage: php scanner.php --url=http://target:5050 [options]" . PHP_EOL . PHP_EOL;
echo "Options:" . PHP_EOL;
echo " -u, --url=TARGET Target URL (required)" . PHP_EOL;
echo " -e, --email=EMAIL pgAdmin email (optional)" . PHP_EOL;
echo " -p, --password=PASS pgAdmin password (optional)" . PHP_EOL;
echo " -v, --verbose Enable verbose output" . PHP_EOL;
echo " --json Output results in JSON format" . PHP_EOL;
echo " --output=FILE Save results to file" . PHP_EOL;
echo " -h, --help Show this help" . PHP_EOL . PHP_EOL;
echo "Examples:" . PHP_EOL;
echo " php scanner.php -u http://localhost:5050" . PHP_EOL;
echo " php scanner.php --url=http://target:5050 -e This email address is being protected from spambots. You need JavaScript enabled to view it. -p pass -v" . PHP_EOL;
echo " php scanner.php -u http://target:5050 --json --output=results.json" . PHP_EOL . PHP_EOL;
exit(0);
}

$url = $options['u'] ?? $options['url'] ?? null;
$email = $options['e'] ?? $options['email'] ?? null;
$password = $options['p'] ?? $options['password'] ?? null;
$verbose = isset($options['v']) || isset($options['verbose']);
$json_output = isset($options['json']);
$output_file = $options['output'] ?? null;

if (!$url) {
echo " Error: Target URL is required. Use --help for usage information." . PHP_EOL;
exit(1);
}

// Validate URL
if (!filter_var($url, FILTER_VALIDATE_URL)) {
echo " Error: Invalid URL format: " . htmlspecialchars($url) . PHP_EOL;
exit(1);
}

// Display banner
echo " ??????? ?????????? ??????? ??? ?????????????? ?????? ??? ?????? " . PHP_EOL;
echo " ???????? ??????????????????????? ?????????????? ?????? ????????????" . PHP_EOL;
echo " ????????? ????? ?????? ?????? ?????????????????????????? ????????" . PHP_EOL;
echo " ???????????????????????? ?????? ?????????????????????????? ????????" . PHP_EOL;
echo " ?????? ??????????????????????????????????????????? ?????? ?????? ???" . PHP_EOL;
echo " ?????? ???????????? ??????? ??????? ??????????? ?????? ?????? ???" . PHP_EOL;
echo " pgAdmin 4 RCE Scanner - By indoushka " . PHP_EOL;
echo " CVE-2025-13780 Heuristic Vulnerability Scanner" . PHP_EOL;
echo " [!] Use only on authorized systems [!]" . PHP_EOL . PHP_EOL;

// Create scanner instance
$scanner = new PgAdminScanner($url, $email, $password, 10, $verbose);

// Run scan
$result = $scanner->scan();

// Output results
if ($json_output) {
$output = json_encode($result, JSON_PRETTY_PRINT);
if ($output_file) {
file_put_contents($output_file, $output);
echo "Results saved to: " . $output_file . PHP_EOL;
} else {
echo $output;
}
} else {
$scanner->printReport();
}

// Exit with appropriate code
if ($result['vulnerable']) {
exit(1); // Vulnerable
} elseif ($result['error']) {
exit(2); // Error
} else {
exit(0); // Not vulnerable
}
}

// Web interface version with input sanitization
if (isset($_SERVER['REQUEST_METHOD']) && php_sapi_name() !== 'cli') {
// Handle POST request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json');

$url = filter_input(INPUT_POST, 'url', FILTER_SANITIZE_URL);
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);

if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) {
echo json_encode(['error' => 'Invalid URL']);
exit;
}

try {
$scanner = new PgAdminScanner($url, $email, $password);
$result = $scanner->scan();
echo json_encode($result);
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
}
exit;
}

// Display web form
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CVE-2025-13780 Scanner - Web Interface</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #0f172a; color: #f8fafc; line-height: 1.6; }
.container { max-width: 1000px; margin: 0 auto; padding: 20px; }
header { text-align: center; margin-bottom: 30px; padding: 20px; background: linear-gradient(135deg, #1e293b, #334155); border-radius: 10px; }
h1 { color: #60a5fa; margin-bottom: 10px; font-size: 2.5rem; }
.subtitle { color: #94a3b8; font-size: 1.1rem; }
.card { background: #1e293b; border-radius: 10px; padding: 25px; margin-bottom: 20px; border: 1px solid #334155; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; color: #cbd5e1; font-weight: 600; }
input[type="text"], input[type="password"] { width: 100%; padding: 12px; background: #0f172a; border: 2px solid #475569; border-radius: 6px; color: #f8fafc; font-size: 16px; }
input:focus { outline: none; border-color: #60a5fa; }
button { background: linear-gradient(135deg, #3b82f6, #1d4ed8); color: white; border: none; padding: 14px 28px; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s; width: 100%; }
button:hover { background: linear-gradient(135deg, #2563eb, #1e40af); transform: translateY(-2px); }
button:disabled { background: #475569; cursor: not-allowed; }
.loading { display: none; text-align: center; margin: 20px 0; }
.spinner { border: 4px solid #334155; border-top: 4px solid #60a5fa; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto 10px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#results { display: none; }
.result-card { background: #1e293b; border-radius: 10px; padding: 25px; margin-top: 20px; }
.vulnerable { border-left: 6px solid #ef4444; background: linear-gradient(135deg, #1e293b, #7f1d1d); }
.safe { border-left: 6px solid #10b981; background: linear-gradient(135deg, #1e293b, #064e3b); }
.error { border-left: 6px solid #f59e0b; background: linear-gradient(135deg, #1e293b, #78350f); }
.result-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.status-badge { padding: 6px 12px; border-radius: 20px; font-weight: bold; font-size: 0.9rem; }
.status-vuln { background: #ef4444; color: white; }
.status-safe { background: #10b981; color: white; }
.details { background: #0f172a; padding: 15px; border-radius: 6px; margin-top: 15px; }
pre { background: #0f172a; padding: 15px; border-radius: 6px; overflow-x: auto; color: #cbd5e1; font-family: 'Courier New', monospace; }
.confidence-meter { height: 10px; background: #334155; border-radius: 5px; margin: 10px 0; overflow: hidden; }
.confidence-fill { height: 100%; background: linear-gradient(90deg, #10b981, #f59e0b, #ef4444); }
.footer { text-align: center; margin-top: 30px; color: #64748b; font-size: 0.9rem; }
.disclaimer { background: #7f1d1d; border-left: 4px solid #ef4444; padding: 15px; margin: 20px 0; border-radius: 0 6px 6px 0; }
.disclaimer h4 { color: #fecaca; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="container">
<header>
<h1> pgAdmin 4 Vulnerability Scanner</h1>
<p class="subtitle">CVE-2025-13780 - Heuristic Detection of Regex Bypass RCE</p>
</header>

<div class="disclaimer">
<h4> IMPORTANT DISCLAIMER</h4>
<p>This is a HEURISTIC vulnerability scanner for CVE-2025-13780. It does not execute exploits.</p>
<p>Use ONLY on systems you own or have explicit permission to test. Manual verification required.</p>
</div>

<div class="card">
<h2>Scan Configuration</h2>
<form id="scanForm">
<div class="form-group">
<label for="url">Target URL:</label>
<input type="text" id="url" name="url" placeholder="http://localhost:5050" required>
</div>

<div class="form-group">
<label for="email">Email (optional, for authentication testing):</label>
<input type="text" id="email" name="email" placeholder="This email address is being protected from spambots. You need JavaScript enabled to view it.">
</div>

<div class="form-group">
<label for="password">Password (optional):</label>
<input type="password" id="password" name="password" placeholder="password">
</div>

<button type="submit" id="scanBtn">Start Scan</button>
</form>

<div class="loading" id="loading">
<div class="spinner"></div>
<p>Scanning target... This may take a few seconds.</p>
</div>
</div>

<div id="results">
<!-- Results will be inserted here by JavaScript -->
</div>

<div class="footer">
<p>pgAdmin 4 CVE-2025-13780 Scanner | PHP Version | Educational Use Only</p>
<p>This tool provides heuristic analysis only. Always verify findings manually.</p>
</div>
</div>

<script>
document.getElementById('scanForm').addEventListener('submit', async function(e) {
e.preventDefault();

const btn = document.getElementById('scanBtn');
const loading = document.getElementById('loading');
const resultsDiv = document.getElementById('results');

btn.disabled = true;
loading.style.display = 'block';
resultsDiv.style.display = 'none';

const formData = new FormData(this);

try {
const response = await fetch('', {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json'
}
});

const data = await response.json();

// Display results
resultsDiv.innerHTML = generateResultsHTML(data);
resultsDiv.style.display = 'block';

} catch (error) {
resultsDiv.innerHTML = `
<div class="card error">
<h3> Scan Error</h3>
<p>${error.message}</p>
</div>
`;
resultsDiv.style.display = 'block';
} finally {
btn.disabled = false;
loading.style.display = 'none';
}
});

function generateResultsHTML(data) {
if (data.error) {
return `
<div class="card error">
<div class="result-header">
<h3> Scan Failed</h3>
</div>
<p>Error: ${data.error}</p>
</div>
`;
}

const isVulnerable = data.vulnerable;
const confidence = data.confidence_score || 0;
const confidencePercent = Math.min(100, confidence);

let html = `
<div class="card ${isVulnerable ? 'vulnerable' : 'safe'}">
<div class="result-header">
<h3>${isVulnerable ? ' VULNERABILITY DETECTED' : ' SECURE'}</h3>
<span class="status-badge ${isVulnerable ? 'status-vuln' : 'status-safe'}">
${isVulnerable ? 'VULNERABLE' : 'NOT VULNERABLE'}
</span>
</div>

<p><strong>Target:</strong> ${escapeHtml(data.target)}</p>
<p><strong>Version:</strong> ${escapeHtml(data.version || 'Unknown')}</p>
<p><strong>Confidence Score:</strong> ${confidence}/100</p>

<div class="confidence-meter">
<div class="confidence-fill" style="width: ${confidencePercent}%"></div>
</div>

<div class="details">
<h4>Details:</h4>
<ul>
<li>Is pgAdmin: ${data.is_pgadmin ? ' Yes' : ' No'}</li>
<li>Version Vulnerable: ${data.version_vulnerable ? ' Yes' : ' No'}</li>
<li>Authenticated: ${data.authenticated ? ' Yes' : ' No'}</li>
</ul>
`;

if (data.endpoints && data.endpoints.length > 0) {
html += `<h4>Accessible Endpoints:</h4><ul>`;
data.endpoints.forEach(ep => {
html += `<li>${ep.name}: HTTP ${ep.status}</li>`;
});
html += `</ul>`;
}

if (isVulnerable) {
html += `
<div class="disclaimer" style="margin-top: 15px;">
<h4> IMMEDIATE ACTION REQUIRED</h4>
<p><strong>Affected:</strong> pgAdmin 4 ? 8.14</p>
<p><strong>Remediation:</strong></p>
<ol>
<li>Upgrade to pgAdmin 4 > 8.14 immediately</li>
<li>Restrict network access to pgAdmin interface</li>
<li>Monitor for suspicious SQL file uploads</li>
<li>Verify manually before taking action</li>
</ol>
</div>
`;
}

html += `
</div>
</div>

<div class="card">
<h3> Raw Scan Data</h3>
<pre>${escapeHtml(JSON.stringify(data, null, 2))}</pre>
</div>
`;

return html;
}

function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
</body>
</html>
<?php
}
?>
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================

Social Media Share