這個注入與bind注入差不多
環境搭建
直接在IndexController.class.php
中創建一個demo
public function index(){
$map=array();
$map['id']=$_GET['id'];
$data=M('users')->where($map)->find();
dump($data);
}
數據庫配置:
<?php
return array(
//'配置項'=>'配置值'
'DB_TYPE' => 'mysql',
'DB_HOST' => 'localhost',
'DB_NAME' => 'thinkphp',
'DB_USER' => 'root',
'DB_PWD' => 'root',
'DB_PORT' => '3306',
'DB_FIELDS_CACHE' => true,
'SHOW_PAGE_TRACE' => true,
);
漏洞分析
payload:
http://127.0.0.1/thinkphp32/index.php?id[0]=exp&id[1]==1%20and%20updatexml(1,concat(0x7,user(),0x7e),1)
indexController.class.php
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$map=array();
$map['id']=$_GET['id'];
$data=M('users')->where($map)->find();
dump($data);
}
}
可以看到這里並沒有使用I函數
(使用I函數
則不會產生exp注入),原因我們在之后分析,我們先來分析這個注入。直接跟入where函數:
可以看到跟以前一樣,由於傳入的是數組,直接將id數組賦值給了$this->options['where']
,然后返回。
繼續跟入find,還是跟以前一樣,需要關注的兩個位置是:
$options = $this->_parseOptions($options);
$resultSet = $this->db->select($options);
先來跟入$this->_parseOptions($options);
/**
* 分析表達式
* @access protected
* @param array $options 表達式參數
* @return array
*/
protected function _parseOptions($options=array()) {
if(is_array($options))
$options = array_merge($this->options,$options);
if(!isset($options['table'])){
// 自動獲取表名
$options['table'] = $this->getTableName();
$fields = $this->fields;
}else{
// 指定數據表 則重新獲取字段列表 但不支持類型檢測
$fields = $this->getDbFields();
}
// 數據表別名
if(!empty($options['alias'])) {
$options['table'] .= ' '.$options['alias'];
}
// 記錄操作的模型名稱
$options['model'] = $this->name;
// 字段類型驗證
if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {
// 對數組查詢條件進行字段類型檢查
foreach ($options['where'] as $key=>$val){
$key = trim($key);
if(in_array($key,$fields,true)){
if(is_scalar($val)) {
$this->_parseType($options['where'],$key);
}
}elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){
if(!empty($this->options['strict'])){
E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');
}
unset($options['where'][$key]);
}
}
}
// 查詢過后清空sql表達式組裝 避免影響下次查詢
$this->options = array();
// 表達式過濾
$this->_options_filter($options);
return $options;
}
可以看到由於傳入數組所以在if(is_scalar($val))處並沒有進入_parseType進行類型轉換,最后返回,接下分析$this->db->select($options)
:
繼續進入$this->buildSelectSql($options);
:
然后是$this->parseSql($this->selectSql,$options);
:
之后重點在這個parseWhere
中的parseWhereItem
函數,和bind基本一樣。
可以看到在parseWhereItem
直接進行了拼接,拼接結果為:
`id` =1 and updatexml(1,concat(0x7,user(),0x7e),1)
最后返回最終拼接結果為:
造成了sql注入。
最后回到一開始的I函數
問題,在I函數
內部的think_filter
中,對exp進行了正則匹配,如果匹配到了會在其后面加空格,從而使得后面的將會對其進行處理無法正確拼接sql注入語句。
修復
在取出傳入參數時使用I函數
即可避免產生exp注入。