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

purplebear 发布于4天前 阅读7次
0 条评论

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

Stegosaurus

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

Stegosaurus下载地址:【 点我下载

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

工具使用

$ python3 -m stegosaurus -h

usage: stegosaurus.py [-h] [-p PAYLOAD] [-r] [-s] [-v] [-x]carrier

positional arguments:

carrier               Carrier py, pyc or pyo file

optional arguments:

-h, --help            show this help message and exit

-p PAYLOAD,--payload PAYLOAD

Embed payload in carrier file

-r, --report          Report max available payload sizecarrier supports

-s,--side-by-side    Do not overwritecarrier file, install side by side

instead.

-v, --verbose         Increase verbosity once per use

-x, --extract         Extract payload from carrier file

使用样例

假设我们需要将Payload嵌入至下面这个Python脚本的字节码之中,脚本文件名为example.py:

" " "Example carrier file to embed our payloadin.

" " "

import math

def fibV1(n):

if n == 0 or n ==1:

return n

return fibV1(n -1) + fibV1(n - 2)

def fibV2(n):

if n == 0 or n ==1:

return n

return int(((1 +math.sqrt(5))**n - (1 - math.sqrt(5))**n) / (2**n * math.sqrt(5)))

def main():

result1 =fibV1(12)

result2 =fibV2(12)

print(result1)

print(result2)

if __name__ == "__main__":

main()

第一步就是使用Stegosaurus来查看在不改变源文件(Carrier)大小的情况下,我们的Payload能携带多少字节的数据。

$ python3 -m stegosaurus example.py -r
Carrier can support a payload of 20 bytes

现在,我们可以安全地嵌入最多20个字节的Payload了。如果你不想覆盖源文件的话,你可以使用-s参数来单独生成一个嵌入了Payload的py文件:

$ python3 -m stegosaurus example.py -s --payload "rootpwd: 5+3g05aW"
Payload embedded in carrier

现在我们可以用ls命令查看磁盘目录,嵌入了Payload的文件(carrier文件)和原始的字节码文件两者大小是完全相同的:

$ ls -l __pycache__/example.cpython-36*
-rw-r--r--  1jherron  staff  743 Mar 10 00:58__pycache__/example.cpython-36-stegosaurus.pyc
-rw-r--r--  1jherron  staff  743 Mar 10 00:58__pycache__/example.cpython-36.pyc

注意:如果你没有使用-s参数,那么原始的字节码文件将会被覆盖。

我们可以通过向Stegosaurus传递-x参数来提取出Payload:

$ python3 -m stegosaurus__pycache__/example.cpython-36-stegosaurus.pyc -x
Extracted payload: root pwd: 5+3g05aW

我们的Payload不一定要是一个ASCII字符串,shellcode也是可以的:

$ python3 -m stegosaurus example.py -s --payload"\xeb\x2a\x5e\x89\x76"
Payload embedded in carrier
$ python3 -m stegosaurus__pycache__/example.cpython-36-stegosaurus.pyc -x
Extracted payload: \xeb\x2a\x5e\x89\x76

查看嵌入Payload之前和之后的Python代码运行情况:

$ python3 example.py
144
144
$ python3 __pycache__/example.cpython-36.pyc
144
144
$ python3 __pycache__/example.cpython-36-stegosaurus.pyc
144
144

通过strings查看Stegosaurus嵌入了Payload之后的文件输出情况(payload并没有显示出来):

$ python3 -m stegosaurus example.py -s --payload"PAYLOAD_IS_HERE"
Payload embedded in carrier
$ strings __pycache__/example.cpython-36-stegosaurus.pyc
.Example carrier file to embed our payload in.
fibV1)
example.pyr
math
sqrt)
fibV2
print)
result1
result2r
main
__main__)
__doc__r
__name__r
<module>
 
$ python3 -m stegosaurus__pycache__/example.cpython-36-stegosaurus.pyc -x
Extracted payload: PAYLOAD_IS_HERE

接下来使用Python的dis模块来查看Stegosaurus嵌入Payload之前和之后的文件字节码变化情况:

嵌入之前:

20 LOAD_GLOBAL             0 (int)
22 LOAD_CONST              2 (1)
24 LOAD_GLOBAL             1 (math)
26 LOAD_ATTR               2 (sqrt)
28 LOAD_CONST              3 (5)
30 CALL_FUNCTION           1
32 BINARY_ADD
34 LOAD_FAST               0 (n)
36 BINARY_POWER
38 LOAD_CONST               2 (1)
40 LOAD_GLOBAL             1 (math)
42 LOAD_ATTR               2 (sqrt)
44 LOAD_CONST              3 (5)
46 CALL_FUNCTION           1
48 BINARY_SUBTRACT
50 LOAD_FAST               0 (n)
52 BINARY_POWER
54 BINARY_SUBTRACT
56 LOAD_CONST              4 (2)

嵌入之后:

20 LOAD_GLOBAL             0 (int)
22 LOAD_CONST              2 (1)
24 LOAD_GLOBAL             1 (math)
26 LOAD_ATTR               2 (sqrt)
28 LOAD_CONST              3 (5)
30 CALL_FUNCTION           1
32 BINARY_ADD
34 LOAD_FAST               0 (n)
36 BINARY_POWER
38 LOAD_CONST              2 (1)
40 LOAD_GLOBAL             1 (math)
42 LOAD_ATTR               2 (sqrt)
44 LOAD_CONST              3 (5)
46 CALL_FUNCTION           1
48 BINARY_SUBTRACT
50 LOAD_FAST                0 (n)
52 BINARY_POWER
54 BINARY_SUBTRACT
56 LOAD_CONST              4 (2)

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 3 bytes

6  BINARY_ADD                        <-- opcodes withoutan arg take 1 byte

7  LOAD_FAST                0 (n)

10 BINARY_ADD

11 LOAD_CONST              2 (3)

14 BINARY_SUBTRACT

15 RETURN_VALUE

# 一种用于在Python字节码中嵌入Payload的隐写工具 – Stegosaurus no easy bytes to embed a payload

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

0  LOAD_FAST                0 (n)

2  LOAD_CONST               1 (5)    <-- all opcodes now occupy two bytes

4  BINARY_ADD                        <-- opcodes withoutan arg leave 1 byte for the payload

6  LOAD_FAST                0 (n)

8  BINARY_ADD

10 LOAD_CONST              2 (3)

12 BINARY_SUBTRACT

14 RETURN_VALUE

# 一种用于在Python字节码中嵌入Payload的隐写工具 – Stegosaurus easy bytes to embed a payload

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

$ python3 -m stegosaurus ../python_tests/loop.py -s -p"ABCDE" -vv
Read header and bytecode from carrier
BINARY_ADD (0)
BINARY_ADD (0)
BINARY_SUBTRACT (0)
RETURN_VALUE (0)
RETURN_VALUE (0)
Found 5 bytes available for payload
Payload embedded in carrier
BINARY_ADD (65)     <-- A
BINARY_ADD (66)     <-- B
BINARY_SUBTRACT (67) <-- C
RETURN_VALUE (68)   <-- D
RETURN_VALUE (69)   <-- E

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

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

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

共收到0条回复

需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。