一种用于在Python字节码中嵌入Payload的隐写工具–Stegosaurus

Stegosaurus

本文将给大家介绍这款名叫Stegosaurus的隐写工具,它允许我们在Python字节码文件(pyc或pyo)中嵌入任意Payload。由于编码密度较低,因此我们嵌入Payload的过程既不会改变源代码的运行行为,也不会改变源文件的文件大小。Payload代码会被分散嵌入到字节码之中,所以类似strings这样的代码工具无法查找到实际的Payload。Python的dis模块会返回源文件的字节码,然后我们就可以使用Stegosaurus来嵌入Payload了。在本文发稿时,还没有任何针对这种Paylaod嵌入技术的有效检测方法。 Stegosaurus下载地址:https://bitbucket.org/jherron/stegosaurus/src

注:Stegosaurus仅支持Python3.6及其以下版本。

工具使用

bigsec

使用样例

假设我们需要将Payload嵌入至下面这个Python脚本的字节码之中,脚本文件名为example.py: bigsec 第一步就是使用Stegosaurus来查看在不改变源文件(Carrier)大小的情况下,我们的Payload能携带多少字节的数据。 bigsec 现在,我们可以安全地嵌入最多20个字节的Payload了。如果你不想覆盖源文件的话,你可以使用-s参数来单独生成一个嵌入了Payload的py文件: bigsec 现在我们可以用ls命令查看磁盘目录,嵌入了Payload的文件(carrier文件)和原始的字节码文件两者大小是完全相同的: bigsec 注意:如果你没有使用-s参数,那么原始的字节码文件将会被覆盖。 我们可以通过向Stegosaurus传递-x参数来提取出Payload: bigsec 我们的Payload不一定要是一个ASCII字符串,shellcode也是可以的: bigsec 查看嵌入Payload之前和之后的Python代码运行情况: bigsec 通过strings查看Stegosaurus嵌入了Payload之后的文件输出情况(payload并没有显示出来): bigsec 接下来使用Python的dis模块来查看Stegosaurus嵌入Payload之前和之后的文件字节码变化情况:、

嵌入之前:

bigsec

嵌入之后:

bigsec

Stegosaurus使用注意事项

Payload的发送和接受方法完全取决于用户个人喜好,Stegosaurus只给大家提供了一种向Python字节码文件嵌入或提取Payload的方法。但是为了保证嵌入之后的代码文件大小不会发生变化,因此Stegosaurus所支持嵌入的Payload字节长度十分有限。因此 ,如果你需要嵌入一个很大的Payload,那么你可能要将其分散存储于多个字节码文件中了。虽然Stegosaurus有这样的一个缺点,但它的优点还是很多的:

1.以“代码碎片”的形式传递Payload; 2.当需要的时候,我们可以从不同的来源发送并组合Payload; 3.部分Payload代码的泄漏并不会泄漏完整的Payload; 4.通过将Payload代码分散嵌入至多个看似无关的文件中来绕过安全检测;

(注:目前本工具还不支持跨Python字节码文件嵌入Payload。)

Stegosaurus的工作机制

为了在不改变源文件大小的情况下向其嵌入Payload,我们需要识别出字节码中的无效空间(Dead Zone)。这里所谓的无效空间指的是那些即使被修改也不会改变原Python脚本正常行为的那些字节数据。 需要注意的是,我们可以轻而易举地找出Python3.6代码中的无效空间。Python的引用解释器CPython有两种类型的操作码:即无参数的和有参数的。在版本号低于3.5的Python版本中,根据操作码是否带参,字节码中的操作指令将需要占用1个字节或3个字节。在Python3.6中就不一样了,Python3.6中所有的指令都占用2个字节,并会将无参数指令的第二个字节设置为0,这个字节在其运行过程中将会被解释器忽略。这也就意味着,对于字节码中每一个不带参数的操作指令,Stegosaurus都可以安全地嵌入长度为1个字节的Payload代码。 下面给出的是一些常见的不带参数的操作码:

BINARY_SUBTRACT

INPLACE_ADD

RETURN_VALUE

GET_ITER

YIELD_VALUE

IMPORT_STAR

END_FINALLY

NOP

...

如果你想知道字节码所发生的变化,可以看看下面这个Python代码段:

def test(n):

      return n + 5 + n – 3

我们首先使用3.6以下版本Python的dis模块来查看:

0  LOAD_FAST        0 (n)

3  LOAD_CONST       1 (5)    <-- opcodes with an arg take 3bytes

6  BINARY_ADD       <-- opcodes withoutan arg take1 byte

7  LOAD_FAST        0 (n)

10 BINARY_ADD

11 LOAD_CONST              2 (3)

14 BINARY_SUBTRACT

15 RETURN_VALUE

:( no easy bytes to embed a payload

现在使用Python3.6的dis模块来查看:

我们可以通过Stegosaurus的-vv选项来查看Payload是如何嵌入到这些无效空间之中的:

目前,Stegosaurus只能利用这种无效空间,我们会在将来引入更多可支持利用的无效空间。

成长空间

1.添加“自毁”选项-d,该参数将会在我们从carrier文件中提取出Payload之后清除源文件中的Payload代码; 2.添加跨文件嵌入Payload的功能; 3.添加-t功能,测试Payload是否会在carrier文件中显示; 4.查找字节码中更多的无效空间,增加可嵌入的Payload数据; 5.添加-g选项,通过增加文件大小来支持大型Payload的嵌入;

  • 参考来源:bitbucket, FB小编Alpha_h4ck编译,来源FreeBuf.COM