Skip to content

LamentXU123/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://typhon.lamentxu.top/
博客: 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

提供 bypass* 系列接口。主要见 API 文档

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.

404星链计划

Typhon 现已加入 404星链计划

Star History Chart

About

pyjail (python jail) 绕过 一把梭 CTF 工具

Topics

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •  

Languages