环境搭建:
源码下载地址:https://github.com/Audi-1/sqli-labs
需要搭建以下环境:
apache的环境在windows下可以直接安装wamp、phpstudy、apmserv等,我安装的是wamp,安装过程中可能会遇到一些问题:像是之前安装过mysql,防火墙,端口被占用了之类的,导致启用不了或者logo出现红色和橙色,可能需要改一些配置文件,这个百度一下很容易解决
tomcat的环境比较容易搭建,百度照着做不会出错
安装sql-libs:
将之前下载的源码解压到web目录下,windows下的wamp解压在www目录下。
打开sql-connections/db-creds.inc文件
将数据库的username和password修改为你自己的mysql账号和密码
然后再访问127.0.0.1的页面,点击链接Setup/reset Database for labs创建数据库
然后就可以开始玩游戏了~
实验测试:
Less-1:基于错误的_get_单引号_字符型注入
输一个id值,返回了name和password,接着测试是否能注入,url后加上?id=',发现报错了,直接报的数据库的错,对web浏览器用户透明,那么可以从报错中得到很多信息,比如这是个MySQL的数据库,还可以猜想到后台的sql语句,应该是“SELECT * FROM table_name WHERE id='$_get['id']' LIMIT 0,1”这种,说明他没有过滤单引号,并且id是char型的输入,之所以报错是因为用了单引号,导致后面的部分“'LIMT 0,1;”多余出来了
于是构造sql语句-1‘OR’1'='1'--+屏蔽掉后面的,也可以用#屏蔽,但这里#没有被url编码,故需自己将他转成url编码%23
注入成功~接着来猜字段,有3个字段存在,再试试4个
没有第4个字段了,即没有第4列
接着用union 语句爆字段,但始终只显示一条记录,看了下源码,发现他并没有将结果循环输出,而是只返回符合查询结果的第一条记录
于是利用联合查询的特点,使原查询左边为空,那么我们定义的查询结果便可以返回出来
于是我们可以通过这里使用数据库的函数来爆出数据库的信息,构造如下语句
这里用到的数据库函数有cancat_ws(),char(),user(),database(),cancat_ws()是连接函数,第一个参数是分隔符,同样作用的函数还有cancat(),不同的是cancat()没有连接符,char()函数是将十进制参数转换成对应的acsii码,user()和database()都是内置的函数,分别返回用户名和数据库名,类似的函数还有version(),返回数据库的版本信息,但是没有直接返回表名的函数,所以需要通过其他方式获取表名
这里有一个很经典的方法,我们可以通过系统数据库information_schema来获取表名,information_schema数据库中含有很重要的三张表:SCHEMATA,TABLES和COLUMNS
SCHEMATA表中存储了MySQL中所有数据库的信息,包括数据库名,编码类型路径等,show databases的结果取之此表
TABLES表中存储了MySQL中所有数据库的表的信息(当然,索引是根据数据库名的),包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等,show tables from schemaname的结果取之此表
COLUMNS表中存储了MySQL中所有表的字段信息,show columns from schemaname.tablename的结果取之此表
于是,我们可以构造?id=-1' union select 1,2,table_name from information_schema where table_schema='security'#
这样就爆出了第一张表名,但要获取所有表名还需要用到'limit',limit是用来指定范围,他有两个参数(limit a,b)a是从第几行开始取,b是取多少行,但需要注意的是实际取出来的开始行下标比a大1,即limit 5,10是表示取6到15行数据,接下来我们就可以用它取指定范围的表了
这里取的是第4张表,如果超出能取的范围,他会报错
于是通过修改limit的范围我们获取到了所有的表名,且与数据库中的表名一致,其中users表用来存储用户信息的可能性最大,于是,我们可以用同样的方法爆他的字段名
获取到的字段名有三个id,username,password,于是我们就可以构造语句了
同样的,改变limit的范围以获取所有用户信息
还有一种方法是通过group分组代替limit将所有信息列出来,查找表名可以构造如下payload:
查找字段也是同样的
最后的payload可以合并不同的列,上下对应输出
Less-2:基于错误的_get_整型注入
尝试单引号:
这里的报错与less-1不同了,从报错可以看出,此处的id是当做数值来处理的,因为sql语句对于数字型的数据可以不加单引号,而less-1是作为字符串来处理的,猜想后台sql语句应该是select * from table_name where id = $_get['id'] limit 0,1于是构造-1 or 1=1
可以注入,接着用和之前相同的方法先报数据库名,然后是表名,接着是字段,最后的payload如下
Less-3:基于报错的_get_单引号_变形_字符型注入
看报错,所谓变形就是用)代替了空格,猜想后台sql是select * from table_name where id =('$_get['id']')limit 0,1于是构造-1')or'1'='1'--+
最后的payload:
Less-4:基于错误的_get_双引号_字符型注入
尝试单引号正常无报错信息,因为在php中双引号可以包含单引号,输入单引号后台就变成了id=("$_GET['id']'")
于是尝试双引号,查看报错信息,猜想后台sql语句为select * from table_name where id =("$_get['id']")limit 0,1;构造如下sql注入:
payload:
less-5:双注入_get_单引号_字符型注入
这个题很有意思,一开始我还是放原来的payload,已经准备好爆表名了,结果他给我的反应是这个
什么都没有,然后注意到提示是“双注入”,于是百度了一下,总结如下:
双查询注入顾名思义形式上是两个嵌套的查询,即select ...(select ...),里面的那个select被称为子查询,他的执行顺序也是先执行子查询,然后再执行外面的select,双注入主要涉及到了几个sql函数:
rand()随机函数,返回0~1之间的某个值
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by cluase分组语句,按照cluase对查询结果分组
双注入的原理总的来说就是,当一个聚合函数后面出现group分组语句时,会将查询的一部分结果以报错的形式返回,他有一个固定的公式,于是payload构造如下:
获取到数据库名后再用同样的方法获取表名
然后是用户信息,这里只能查询一行,所以不能用group_concat,可以修改limit的范围来遍历用户信息
Less-6:双注入_get_双引号_字符型注入
换汤不换药,按照Less-5的方法,只是把单引号改成了双引号,直接上payload:
Less-7:导出文件_get_字符型注入
尝试之前的方法行不通了,他把报错做了处理统一返回“You have an error in your SQL syntax”,明显的,他也给出了提示use outfile,outfile的固定结构是:select A into outfile B,这里的B通常是一个文件路径,A可以是文本内容(小马),也可以是数据库信息,于是这里就有两种思路:
第一种,构造select * from users into outfile "数据库导入导出数据的目录"
这真是一件很悲伤的事情...我的语句构造没问题,但是F盘下就是没有test.txt文档出现,于是查了一波资料,有一种说法是权限问题,需要root权限才能进行数据库的读写操作,可以用这个语句来判断: and (select count(*) from mysql.user)>0 ,如果回显正常,就是表示最高权限
我是最高权限呀,难道是路径转义的问题,又试一下
还是没有...好吧,放Mysql里面白盒测一下
报了一个错,查了下资料,报错原因是:Mysql数据库需要在指定的目录下进行数据的导出,secure_file_priv这个参数用来限制数据导入和导出操作的效果,例如执行LOAD DATA、SELECT ... INTO OUTFILE语句和LOAD_FILE()函数。这些操作需要用户具有FILE权限。
如果这个参数为空,这个变量没有效果;
如果这个参数设为一个目录名,MySQL服务只允许在这个目录中执行文件的导入和导出操作。这个目录必须存在,MySQL服务不会创建它;
如果这个参数为NULL,MySQL服务会禁止导入和导出操作。这个参数在MySQL 5.7.6版本引入。
于是查看了下secure_file_priv,然后换成他指定的目录,还是有问题...然后想到路径的转义,OK,成功了,路径下面也出现了1.txt
但是,我们这个是通过白盒测试拿到的数据,在正常情况下,我们是不知道数据库名和表名的,不过,原理也类似,可以结合前面用到的经典方法
然后是表名
字段
注意,这里我为方便重复使用的2.txt,但每次都要删除后才可以生成新的
最后的payload:
这里除了用以前的order by判断字段数,还有一个方法,像下面这样直接试就好了,只有字段数刚好正确才会生成2.txt,多了少了都不行
第二种,将一句话木马写入到文件,用菜刀拿下网站
这里文件后缀要改成.php(我懒得换图了),然后用菜刀连接,由于我这台机没装菜刀,所以就不截图了,大致意思就是这样,下面来扩展一下导入导出数据还会涉及到哪些函数:
@@datadir:数据库存储路径
@@basedir:MySQL安装路径
dumpfile:导出文件,类似outfile,不同的是,dumpfile一次导出一行,会和limit结合使用
load_file():将文件导入mysql,用法 select load_file("文件路径");
Less-8:bool型_单引号_盲注
首先尝试less-7的方法,没问题
接着探索新姿势,正常输入返回“You are in......”,尝试单引号却什么都没返回,看了下源码就是这样处理的,点题盲注
盲注主要分为bool型和时间性,通常涉及到这几个函数:
length(str):返回字符串str的长度
substr(str,pos,len):将str从pos位置开始截取len长度的字符返回,需要注意的是这里pos的是从1开始的
mid(str,pos,len):和substr()类似
ascii(str):返回字符串str最左边的acsii码(即首字母的acsii码)
ord():同上,返回acsii码
left(str,len):对字符串str左截取len长度
right(str,len):对字符串str右截取len长度
if(a,b,c):条件判断,如果a为true,返回b,否则返回c
盲注有个固定式:and ascii(substr(A,1,1))>B,或者and if(ascii(substr(A,1,1))>B,1,0),这里的A通常是一个select语句,B则是字符或数字的ascii码,他们的中心思想都是通过substr等截取函数以二分法的形式查询逐个匹配想要的信息,这个过程通常都很耗时,所以建议直接写个盲注脚本来跑
下面是盲注匹配的一个例子,我们来匹配数据库名,在之前的实验中已知数据库名是security,下面的sql语句是用来匹配数据库名的第一个字母
字母s的ascii码是115,所以他大于114,结果为true,页面显示正常
这里大于了115,结果为false,页面显示异常,好,接下来直接写个盲注脚本,跑出来结果如下:
Less-9:时间型_单引号_盲注
时间型盲注和bool型盲注应用场景不同之处在报错的返回上,从less-8我们知道,输入合法时他会返回正常页面“You are in......”,而非法输入时他没有返回任何东西,于是,我们可以根据这个特点跑盲注,通过他不同的返回页面来判断我们匹配的字符是否正确,而在less-9中合法输入他返回如下
非法输入也是返回该页面
这样,我们就不能根据他页面的返回内容来判断匹配结果了,因此我们需要用延时函数sleep()对两种输入进行区分,可以构造如下语句:
?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5))%23
这里的意思是,如果数据库名首字母的ascii码大于115,那么执行sleep(5),延时5秒,此时标签栏会变成缓冲,于是,我们就可以判断匹配的结果了,盲注脚本与less-8类似,只需要加入sleep函数即可
Less-10:时间型_双引号_盲注
把上面payload中的单引号改成双引号就可以了,如:
?id=1" and if(ascii(substr(database(),1,1))>115, 0, sleep(5))%23
参考文献:
http://www.freebuf.com/articles/web/34619.html
http://blog.csdn.net/u012763794/article/details/51207833
http://blog.itpub.net/26506993/viewspace-2121850/