SQL Injection exists in the ThinkPHP framework architecture
Many people believe that SQL injection can be perfectly avoided without splicing SQL statements by using the database query method provided by the framework. Then you are wrong. Sometimes the framework becomes the person who brings you into the trap.
We open the latest thinkphp framework documentation, where "expression query" Chapter: http://document.thinkphp.cn/manual_3_2.html#express_query
WTF: If the where statement is an array and the first value of the array is 'exp ', then the second value can be directly written into the SQL statement?
WTF, isn't it a perfect SQL injection?
Some may not understand. To be more specific, many webmasters write query statements like this:
$data = array();$data['user'] = $_POST['username'];$data['pass'] = md5($_POST['password']);M('user')->where($data)->find();
This should be a basic method for ThinkPHP to query databases. If the input parameters are as follows:
Username [0] = exp & username [1] = aa 'or 1 = 1% 23 & password = 1, is it a perfect universal password?
This feature exists in thinkphp3.1 and 3.2, and is widely used and harmful.
Some may find some limitations, because the thinkphp I function has the following code:
} Elseif (isset ($ input [$ name]) {// value operation $ data = $ input [$ name]; is_array ($ data) & array_walk_recursive ($ data, 'filter _ exp '); $ filters = isset ($ filter )? $ Filter: C ('default _ filter'); if ($ filters) {if (is_string ($ filters) {$ filters = explode (',', $ filters);} elseif (is_int ($ filters) {$ filters = array ($ filters);} foreach ($ filters as $ filter) {if (function_exists ($ filter) {$ data = is_array ($ data )? Array_map_recursive ($ filter, $ data): $ filter ($ data); // parameter filtering} else {$ data = filter_var ($ data, is_int ($ filter )? $ Filter: filter_id ($ filter); if (false ===$ data) {return isset ($ default )? $ Default: NULL; }}}} else {// default value of the variable $ data = isset ($ default )? $ Default: NULL ;}
Is_array ($ data) & array_assist_recursive ($ data, 'filter _ exp '); There is a simple filter. Let's look at the filter_exp function:
function filter_exp(&$value){ if (in_array(strtolower($value),array('exp','or'))){ $value .= ' '; }}
A space is added after exp. However, there are several serious problems:
1. filter_exp is before the fiter of the I function, so if the developer writes I ('get. school ', '', 'trim'), the space after exp is cleared directly, leading to invalid filtering. This writing method is quite common, including I often write it like this.
II. In the MVC Architecture of thinkphp, the Controller function variables are also used as the method for passing parameters through GET/POST, such as http: // serverName/index. php/Home/Blog/archive/year/2013/month/11 we can access public function archive ($ year = '20160901', $ month = '01 '). This URL can also be written as http: // serverName/index. php? C = Blog & a = archive & year = 2013 & month = 11, you can also enter http: // serverName/index. php? C = Blog & a = archive & year = 2013 & month [0] = exp & month [1] = sqli
This is an example in the document: Example. The OneThink in the vulnerability proof is injected for this reason.
III. The old thinkphp version does not use the I function to obtain variables, but the exp feature does exist all the time. In thinksns, the variable value obtained by using $ _ POST [xxx] directly, so this security risk will always exist. I also found instance verification in Thinksns.
OneThink is a self-developed Content Management System of ThinkPHP, which facilitates developers to perform secondary development.
After installation, you can directly POST the following data packets to the background logon location to find an error (for convenience, I commented on the verification code check section. In actual operations, I can send a data packet with the verification code ):
Database username exposed:
The reason is that we can see the code in login,/Application/Admin/Controller/PublicController. class. php:
Public function login ($ username = null, $ password = null, $ verify = null) {if (IS_POST) {/* Check the verification code TODO: */if (! Check_verify ($ verify) {// $ this-> error ('verification code input error! ');}/* Call the UC logon interface to log on */$ User = new UserApi; $ uid = $ User-> login ($ username, $ password ); if (0 <$ uid) {// UC logon succeeded/* logon user */$ Member = D ('member '); if ($ Member-> login ($ uid) {// logon user // TODO: Jump to the pre-Logon page $ this-> success ('logon successful! ', U ('index/Index');} else {$ this-> error ($ Member-> getError ());}} else {// logon Failure switch ($ uid) {case-1: $ error = 'the user does not exist or is disabled! '; Break; // disable case-2: $ error = 'incorrect password! '; Break; default: $ error =' Unknown error! '; Break; // 0-interface parameter error (used during debugging)} $ this-> error ($ error) ;}} else {if (is_login ()) {$ this-> redirect ('index/Index');} else {/* read Database Configuration */$ config = S ('db _ CONFIG_DATA '); if (! $ Config) {$ config = D ('config')-> lists (); S ('db _ CONFIG_DATA ', $ Config);} C ($ config ); // Add configuration $ this-> display ();}}}
After obtaining $ username and $ password, input the login function and follow up:
public function login($username, $password, $type = 1){ return $this->model->login($username, $password, $type); }
Input the $ this-> model-> login function to follow up:
Public function login ($ username, $ password, $ type = 1) {$ map = array (); switch ($ type) {case 1: $ map ['username'] = $ username; break; case 2: $ map ['email '] = $ username; break; case 3: $ map ['mobile'] = $ username; break; case 4: $ map ['id'] = $ username; break; default: return 0; // parameter error}/* Get user data */$ user = $ this-> where ($ map)-> find (); if (is_array ($ user) & $ user ['status']) {/* Verify the user password */if (think_ucenter_md5 ($ password, UC_AUTH_KEY) ===$ user ['Password']) {$ this-> updateLogin ($ user ['id']); // update user logon information return $ user ['id']; // login successful, returned user ID} else {return-2; // incorrect password} else {return-1; // the user does not exist or is disabled }}
As I mentioned earlier, the where statement is directly included. So we only need to make username an array, the first value is exp, and the second value is the injection statement.
This injection of thinkphp is similar to mongodb injection. Although it is in the Framework, injection is generated due to unreasonable framework design.
Solution:
Enhanced Filtering