python 沙盒逃逸
python 绕过沙盒中常见的函数、属性、模块解释(备忘)
1.func_globals
返回包含函数全局变量的字典的引用————————定义函数的模块的全局命名空间。
function.func_globals
1 | >>> def foo(): pass |
2.__getattribute__
被调用无条件地实现类的实例的属性访问。
object. getattribute(self, name)
1)self 必需的。类的实例,在调用时自动传递。
2)name 必需的。属性的名称。
‘’.class.mro[2].subclasses()[59].init.getattribute(‘func_globals’)
3.__dict__
模块对象有一个由dictionary对象实现的名称空间(这是由模块中定义的函数的func_globals属性引用的字典)。属性引用在本词典中被翻译为查找,例如,
m.x相当于m.dict [“x”]。
1 | >>> ''.__class__.__dict__['upper'] |
4.dir()
将显示对象的属性的名称,__dict__是dir()的子集
dir ([object])
1 | >>> dir(''.__class__) |
5.__base__
每个类都有一个__base__属性能列出器基类
注意:base 和 __bases__的区别
他们都是返回当前类的基类,只不过__bases__返回的是一个元祖
1 | >>> ''.__class__.__base__ |
6.__mro__
递归地显示父类一直到 object
1 | >>> ''.__class__.__mro__ |
7.__subclasses__()[]
获取子类
1 | >>> ''.__class__.__mro__[2].__subclasses__()[40] |
8.__import__
import 一个模块
import (name)
1 | >>> __import__('os') |
9.__bulitin__
Python的内建模块,该内建模块中的功能可以直接使用,不用在其前添加内建模块前缀
在Python2.X版本中,内建模块被命名为__builtin__,而到了Python3.X版本中,却更名为builtins。
10.__builtins__
是对内建模块的一个引用
这个和__builtin__有一些区别
1)无论任何地方要想使用内建模块,都必须在该位置所处的作用域中导入__builtin__内建模块;而对于__builtins__却不用导入,它在任何模块都直接可见,可以把它当作内建模块直接使用
2)__builtins__虽是对内建模块的引用,但这个引用要看是使用__builtins__的模块是哪个模块
1 | ① 在主模块__main__中: |
11.reload
重新加载之前导入的模块
reload (module)
1 | >>> import sys |
12.getattr
返回对象的命名属性的值。
getattr (object, name)
相当于 object.name
name 必须是一个字符串
1 | >>> class A(): |
13.__getattr__
当属性查找没有在通常的位置找到属性时调用(例如,它不是实例属性,也不是在类树中找到self)
14.__name__
这个值获得的只是一个字符串,不是模块的引用
要使用sys.modules[name]才获得的是模块的引用
1 | >>> sys.modules['__main__'] |
15.func_code
返回表示已编译函数体的代码对象。
1 | function.func_code |
注意:这个代码对象必须存在几个参数
co_argcount 这个参数是返回该函数的参数
1 | >>> foo.func_code.co_argcount |
co_code 返回函数的字节码(可用dis.dis(字节码)将其转换为汇编格式
1 | >>> foo.func_code.co_code |
16.timeit 模块
这个模块是用来测试代码的执行时间的,能执行代码自然能执行命令
使用前需要导入timeit
使用:
timeit(命令,number=1)
1 | >>> import timeit |
其中命令是字符串的形式
17.platform 模块
由名字可以知道这个模块和平台有关,里面的函数主要是为了返回和平台的一些信息,但是我们还是可以调用
popen 这个函数执行命令
1 | print platform.popen('命令',mode='r',bufsize= -1).read() |
18.__globals__
function.__globals__
等同于globals(),dir()
的结果是上面两个的键值
在fuzz 中常常和 __init__
配合使用,__init__
一般跟在类的后面,相当于实例化这个类
1 | [].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls') |
19.__call__
使实例能够像函数一样被调用
x.call 等同于 x()
1 | >>> func.__call__ |
20.pickle
这个是python 的一个序列化的方法,用于将对象存储在字符串对象中,实现对象的持久化
基本的语法:
序列化:
1 | import pickle |
反序列化:
1 | n=pickle.loads(p) |
我们可以通过 pickle 的方式加载命令
1 | pickle.loads(b"cos\nsystem\n(S'ls'\ntR.") |
21.os/subprocess/commands
1 | os.system('ifconfig') |
这里重点说一下subprocess 吧
- 1.subprocess.run()
Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。 - 2.subprocess.call()
执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。 - 3.subprocess.check_call()
Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。 - 4.subprocess.check_output()
Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。 - 5.subprocess.getoutput(cmd)
接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。 - 6.subprocess.getstatusoutput(cmd)
执行cmd命令,返回一个元组(命令执行状态,命令执行结果输出),其功能类似于commands.getstatusoutput()。
22.eval/exec/execfile
- 1.eval(expression):
返回python 表达式执行的结果 - 2.exec(source)
动态执行python代码。也就是说exec可以执行复杂的python代码,而不像eval函数那样只能计算一个表达式的值。exec函数的返回值永远为None。 - 3.execfile(filename)
执行一个文件的内容
文件是将被解析为python序列的类似于模块的文件
23.importlib模块
1 | import importlib |
他可以代替import 非常好
python沙箱逃逸概述
沙箱逃逸,就是在给我们的一个代码执行环境下(Oj或使用socat生成的交互式终端),脱离种种过滤和限制(waf),最终成功拿到shell权限的过程
python 能执行命令或者存在执行命令功能的函数是一定的,但是他的存在形式是多样的,他过滤了这种形式我们就换一种形式表示。
对于python的沙箱逃逸而言,我们来实现目的的最终想法有以下几个
- 使用os包中的popen,system两个函数来直接执行shell
- 使用commands模块中的方法
- 使用subprocess
- 使用写文件到指定位置,再使用其他辅助手段,总体来说,我们使用以下几个函数,就可以直接愉快的拿到shell啦!
1 | import os |
攻击思路
1.直接引入执行命令的模块os等
遭遇过滤:
re.compile('import\s+(os|commands|subprocess|sys)')
不直接使用import用
__import__()
取而代之
遭遇过滤:__import__(module)
不直接用
__import__(module)
转换编码__import__("pbzznaqf".decode('rot_13'))
遭遇过滤__import__
2.不用__import__
直接调用内建函数__bulitin__/__bulitins__
常见的一些危险的函数都是__builtin__里面的,我们可以直接用 eval() exec() execfile()
遭遇过滤:把__builtin__
中的危险函数都del掉,看你怎么绕
3.reload()
函数重新加载完整的没有阉割的__builtin__reload(__builtin__)
遭遇过滤:reload()也是一个内建函数,如果我们在__builtin__中把reload()也del掉呢?
4.imp模块也是一个可以引入东西的一个模块
1 | import imp |
再次成功引入
遭遇过滤:看来还是没有从源头del干净,我们知道python 的模块其实都存放在sys.modules中,不要啥就删啥。sys.modules['os']=None
这样就ok了
5.这回问题有点棘手,要知道如何应对还要仔细分析一下import的步骤
import 的步骤:
1)如果是 import A,检查 sys.modules 中是否已经有 A,如果有则不加载,如果没有则为 A 创建 module 对象,并加载 A
2)如果是 from A import B,先为 A 创建 module 对象,再解析A,从中寻找B并填充到 A 的 dict 中
那我们可以向更源头追溯
我们都知道任何的模块归根道理都是文件,只要文件还在,我们就一定有办法!
比如类unix 的系统中,os 模块的路径一般都是/usr/lib/python2.7/os.py,那我们就直接写这个
1 | import sys |
遭遇过滤:我把你sys也一并del,让你用!哼
6.和上面一样的思路,文件还在我们就直接用文件,import 的本质就是把对应的模块文件执行一遍
1 | execfile('/usr/lib/python2.7/os.py') |
遭遇过滤:execfile() 别用了
7.直接用文件读取函数读入文件,然后再exec() 也能实现一样的效果
一些题目
XMAN2018 minbash //SUSCTF-2018
题解:
ssh连接后发现命令均显示:-rbash: clear: command not found
我们可以尝试python里面的库去执行,用的是os库里面的listdir函数
1 | import os |