Unicorn Shop:Unicode Numeric 类型转换绕过

记录 Unicorn Shop 题目中利用 Unicode numeric value 绕过单字符价格限制的分析过程。

  • #CTF
  • #Unicode
  • #Python
  • #输入验证

一、核心考点

  • Python unicodedata 模块
  • Unicode 字符与 unicodedata.numeric()
  • 字符串长度限制
  • 类型转换与输入验证逻辑错误
  • Unicode 绕过

二、题目核心逻辑

商品 ID 为 4 的价格是 1337,但题目限制 price: Only one char allowed!,也就是要求 len(price) == 1。

普通输入 1337 的长度为 4,显然无法直接通过限制。

Unicorn Shop 商品页面

三、关键突破点

通过报错或源码发现,后端使用了 unicodedata.numeric(price),并且要求输入只有一个字符。这个函数接受的范围远远超过普通的 0-9:Unicode 中存在不同语言的数字、罗马数字和数字形式等特殊字符。

本题中找到的埃塞俄比亚数字字符“万”,长度为 1,但 numeric value 为 10000,因此可以参与价格判断。

四、用 Python 自动寻找特殊字符

可以使用下面的辅助脚本,查找 Unicode 中数值不小于 1337 的字符:

import unicodedata

for i in range(0x10000):
    char = chr(i)
    value = unicodedata.numeric(char, None)

    if value is not None and value >= 1337:
        print(
            "字符:", repr(char),
            "数值:", value,
            "Unicode:", hex(i)
        )

五、为什么这是漏洞,而不是 Python 的 Bug?

unicodedata.numeric() 本身没有问题。问题在于,开发者的预期是“用户输入一个普通数字”,但实际代码允许任意具有 numeric value 的 Unicode 字符参与价格判断。

因此,这属于输入验证或类型转换逻辑错误,而不是 unicodedata.numeric() 存在漏洞。

一句话记忆

本题通过单字符限制阻止 1337,但后端使用 unicodedata.numeric() 将具有 Unicode numeric 属性的字符转换为数值,因此可以利用 numeric value 为 10000 的埃塞俄比亚数字字符,在 len=1 的情况下绕过价格限制。

小结

遇到数字输入限制时,除了检查长度和字符集,还要确认后端实际采用的类型转换函数,以及它对 Unicode 字符的处理范围。