一道ctf题目走进vsprintf漏洞利用技巧
字数 1121 2025-08-10 13:48:27

PHP vsprintf 漏洞利用技巧详解

1. 漏洞背景

vsprintf 是 PHP 中的一个格式化字符串函数,与 sprintf 类似,但接受数组作为参数而非可变参数列表。当开发者不当使用此函数时,可能导致严重的安全漏洞。

2. vsprintf 基础语法

string vsprintf ( string $format , array $args )

格式说明符完整结构:

%[argnum$][flags][width][.precision]specifier
  • % - 起始符号
  • argnum$ - 参数位置(可选)
  • flags - 标志(可选)
  • width - 宽度(可选)
  • .precision - 精度(可选)
  • specifier - 类型说明符(必须)

3. 漏洞利用关键点

3.1 UTF-8 截断绕过

在题目中,使用 admin%ff 可以绕过用户名检查:

  • UTF-8 编码会截断无效字节
  • admin%ff 会被视为 admin
  • === 严格比较时,admin%ff 不等于 admin

3.2 格式化字符串注入

利用 %1$c 获取特定字符:

  • %1$c 表示将第一个参数格式化为字符
  • 通过构造 SHA1 密码使其首字符为双引号(ASCII 34)
  • 从而闭合 SQL 语句中的引号

3.3 特殊格式构造

利用 %1$'> 实现格式字符串逃逸:

  • %1$'>' 是填充标志,> 是填充字符
  • 由于缺少宽度和类型说明符,整个格式被忽略
  • 后续的 %2$s 得以保留,用于输出 flag

4. 完整利用流程

  1. 绕过用户名检查

    • 使用 admin%ff 绕过第一个 SQL 查询
    • UTF-8 截断使查询认为用户存在
  2. SQL 注入构造

    • 用户名:admin%1$c||1#%1$'>%2$s
    • 密码:构造 SHA1 使其首字符为双引号(如 668 的 SHA1 以 34 开头)
  3. 最终 SQL 语句

    SELECT * FROM users WHERE username = "admin"||1#%2$s" AND password = "34..."
    
  4. 格式化字符串利用

    • %1$c 被替换为双引号
    • %1$'> 被忽略
    • %2$s 在最后的 vsprintf 中被替换为 flag

5. 防御措施

  1. 参数化查询

    • 始终使用预处理语句而非字符串拼接
  2. 输入验证

    • 对用户输入进行严格过滤
    • 使用白名单验证
  3. 输出编码

    • 根据上下文使用适当的编码函数(如 htmlspecialchars)
  4. 最小权限原则

    • 数据库连接使用最小必要权限

6. 扩展利用技巧

6.1 单引号逃逸

构造 %1$\'

  • 反斜杠被当作类型说明符吃掉
  • 单引号得以逃逸,可用于 SQL 注入

6.2 多上下文利用

  • Web 应用中的 vsprintf 漏洞
  • Pwn 题目中的格式化字符串漏洞
  • 两者技巧可互相借鉴

7. 参考资源

PHP vsprintf 漏洞利用技巧详解 1. 漏洞背景 vsprintf 是 PHP 中的一个格式化字符串函数,与 sprintf 类似,但接受数组作为参数而非可变参数列表。当开发者不当使用此函数时,可能导致严重的安全漏洞。 2. vsprintf 基础语法 格式说明符完整结构: % - 起始符号 argnum$ - 参数位置(可选) flags - 标志(可选) width - 宽度(可选) .precision - 精度(可选) specifier - 类型说明符(必须) 3. 漏洞利用关键点 3.1 UTF-8 截断绕过 在题目中,使用 admin%ff 可以绕过用户名检查: UTF-8 编码会截断无效字节 admin%ff 会被视为 admin 但 === 严格比较时, admin%ff 不等于 admin 3.2 格式化字符串注入 利用 %1$c 获取特定字符: %1$c 表示将第一个参数格式化为字符 通过构造 SHA1 密码使其首字符为双引号(ASCII 34) 从而闭合 SQL 语句中的引号 3.3 特殊格式构造 利用 %1$'> 实现格式字符串逃逸: %1$'> 中 ' 是填充标志, > 是填充字符 由于缺少宽度和类型说明符,整个格式被忽略 后续的 %2$s 得以保留,用于输出 flag 4. 完整利用流程 绕过用户名检查 : 使用 admin%ff 绕过第一个 SQL 查询 UTF-8 截断使查询认为用户存在 SQL 注入构造 : 用户名: admin%1$c||1#%1$'>%2$s 密码:构造 SHA1 使其首字符为双引号(如 668 的 SHA1 以 34 开头) 最终 SQL 语句 : 格式化字符串利用 : %1$c 被替换为双引号 %1$'> 被忽略 %2$s 在最后的 vsprintf 中被替换为 flag 5. 防御措施 参数化查询 : 始终使用预处理语句而非字符串拼接 输入验证 : 对用户输入进行严格过滤 使用白名单验证 输出编码 : 根据上下文使用适当的编码函数(如 htmlspecialchars) 最小权限原则 : 数据库连接使用最小必要权限 6. 扩展利用技巧 6.1 单引号逃逸 构造 %1$\' : 反斜杠被当作类型说明符吃掉 单引号得以逃逸,可用于 SQL 注入 6.2 多上下文利用 Web 应用中的 vsprintf 漏洞 Pwn 题目中的格式化字符串漏洞 两者技巧可互相借鉴 7. 参考资源 PHP 官方文档 - vsprintf OWASP 格式化字符串攻击 SQL 注入防御指南