大量实践证明构建恶意的pickle非常容易,而对恶意pickle进行unpickle操作将可能会产生一个shell,甚至是一个远程shell。所以,本文中将向大家介绍如何利用Python pickle进行任意代码执行操作。
0×00 Python pickle禁忌
0×01 pickle举例
首先,我们以Python pickle shellcode的canonical开始,我将其保存为canonical.pickle。
接着,我们试着解包这个pickle,看看会产生什么结果。
0×02 pickle简介
Pickle是一种堆栈语言,这意味着pickle指令将数据压入堆栈或者将数据弹出堆栈,并以某种方式操作它。为了理解canonical pickle是如何工作的,我们只需要理解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返回值)压入堆栈中。
0×04 获取shell示例
对于我们的任意计算,我们先慢慢地计算10阶斐波那契数,并将其打印出来,然后得到一个shell。
注意,因为Python允许我们导入模块,以及在函数内部定义函数,所以我们可以在我们的foo函数中编写想要的任何代码。
运行此代码生成以下内容:
0×05 创建通用pickle
我们想要创建一个通用的pickle,这样我们可以向其中插入任意base64编码的函数并运行它们,比如上面编写的函数。从本质上讲,我们希望产生一个可以执行下面的Python代码的pickle,其中的code_enc就是我们编码后的函数。
为了让其具有更好的可读性,调整其格式如下:
接着,让我们一点一点构建这部分。为了调用base64.b64decode(code_enc),我们模仿前面用os.system做的操作。
我们可以以相同的方式将调用对象添加到marshal.loads中:
通过使用__builtin__模块,可以以相同的方式调用函数globals:
为了构建函数,我们可以将这些结合到一起,然后得到:
最后,我们需要通过附加“(tR.”(其中句号结束了pickle)来调用堆栈顶部的函数。
将这些片段组合在一起,我们就得到了一个通用的pickle。
0×06 模板代码
另外,改变可执行代码仅仅需要改变foo函数,运行打印出marshal处理过和编码后的函数的Python程序,然后在替换generic.pickle中base64编码的字符串。
下面是一个很方便的模板。
本文转自fatshi51CTO博客,原文链接:http://blog.51cto.com/duallay/1858300 ,如需转载请自行联系原作者