因爲受賞金廠商保密協議所限,我沒法公開原始漏洞詳情,但我更清楚 「talk is cheap, show me the code」,耗時費神,找到一個 wargame,相較賞金漏洞,不但體現了相同精髓,這個 wargame 還多了些限制條件,因此,讓它變得更加有趣、更有挑戰。php
我把 wargame 源碼寫入git
CMDi_lab/escaping_quotation/index.php,核心以下:github
快速過下源碼。首先,用 GET 方法獲取 f一、f2 兩個參數;而後,用相同的正則過濾 f一、f2,包括過濾引號防止利用環境寫 webshell,過濾常見命令分隔符(;)、命令替換符($())防止注入新命令,過濾常見命令(ls、cat)禁止基礎操做;接着,用引號再建一道防護工事,讓全部輸入均在引號內,讓命令行元字符失效;最後,以 f一、f2 爲命令參數執行系統命令 file。web
怎麼樣!看上去是很完善的防護體系。先正常訪問試試:正則表達式
服務端執行 file 命令,正確識別出目錄和文件的類型。嘗試提交命令分隔符(;)和命令替換符(「):shell
因爲服務端正則表達式匹配上 ; 和 「,致使提交的文件名被置爲空,因此,file 提示沒法找到相關文件。api
經驗主義,我將從三個層面尋找突破口:攻擊正則、攻擊引號、攻擊命令行。那麼,我就準備開動了,各位。運維
在命令注入場景下審查正則表達式,我習慣關注四個方面:是否使用多行模式修飾符(/foo/m)、是否遺漏匹配對象末尾的換行符(/^\d+$/)、是否容許空白字符(\s)、是否誤寫反斜槓匹配模式(/\/)。測試
使用多行模式修飾符。把多行模式用於匹配但願容許的字符時,就會存在邏輯問題。好比,以下代碼:編碼
本來但願只容許 xx.xx.xx.xx 格式的 IP 地址,因爲使用多行模式,只要輸入中某行知足條件便可,那麼,我能夠用換行符輸入多行,第一行知足要求 127.0.0.一、第二行任意內容,這樣輕鬆繞過正則限制:
本 wargame 未使用多行模式,因此不存在這個問題。
遺漏匹配對象末尾的換行符。某些模式在匹配時會忽略字符串末的換行符,而換行符自身又是一個有效的目錄分隔符,將致使注入新命令。以下代碼:
原意是過濾掉輸入中非字母、數字外的其餘全部字符,輸入換行符試試:
果真被過濾了。但,若把換行符放至字符串末尾,正則反而沒法匹配上:
這可有趣了,又能愉快地注入新命令了:
本 wargame 未能成功過濾掉換行符,但不是由於上面的緣由。
容許空白字符。空白字符包括空格、換行符、水平製表符、垂直製表符等四個,命令注入的好朋友換行符也在其中。代碼:
本意只容許字母、數字、空格等字符,但遺漏了換行符,致使命令注入漏洞:
本 wargame 未能成功過濾掉換行符,但不是由於上面的緣由。
誤寫反斜槓匹配模式。正則表達式自身是個字符串,並不是直接傳遞給正則引擎,而是先由語言對字符串進行處理後再傳遞給正則引擎。我但願匹配上反斜槓(),逆向思考下這個過程,因爲反斜槓在正則引擎是個特殊字符,因此 \ 才能讓正則引擎正確識別到反斜槓 ;正則引擎以前,\ 通過語言的的字符串處理,因爲反斜槓在字符串中也是特殊字符,因此,一個 就得用 \ 表示、兩個 就得用 \\ 表示。那麼,但凡用正則表達式匹配斜槓,必須得用 \\。這是不具有原生字符串特性(r)的腳本語言的通病,是有一點繞。好比:
訪問看看:
記得 wargame 也過濾了反斜槓,回過頭看看,哇喔,的確誤用了:
OK,在正則部分,因爲誤寫匹配模式,我找到了漏網之魚,反斜槓。如何利用?不知道,走一步看一步。
接着我來琢磨下 2五、26 行。這兩行目的很清晰,用引號包裹輸入字符串,預防可能因正則過濾不嚴傳遞一些個特殊字符到命令行環境,思路是對的,但效果就差強人意了。
載荷一旦進入引號內,都將退化成普通字符串,好無殺傷力,惟一例外,命令替換符(「 或 $()),遺憾的是,命令替換符被正則嚴防死守,沒法到達 2五、26 行。因此,下意識地想到,引號逃逸。
引號逃逸,目的是讓輸入跳脫至引號外,恢復特殊字符的身份,而再也不被引號所束縛,僅僅是個普通字符。我經常使用兩種手法,一是閉合、二是轉義。
閉合手法逃逸引號。在輸入中添加一個引號,讓其與左引號結對,天然閉合,接着輸入中就能出現惡意字符,最後輸入中再添加一個引號,與右引號結對,或者,輸入註釋符以忽略右引號。好比:
個人全部輸入都只能留在引號內,致使命令分隔符沒法被命令行正確識別:
我在輸入中增長兩個引號(②、③),這樣恰好與代碼中的引號閉合(① 和 ②、③ 和 ④),因此,個人其餘輸入字符(;id;)就能出如今引號以外,成功逃逸引號:
轉義手法逃逸引號。引號自身也是個特殊字符,若是有辦法讓它變成普通字符,那麼輸入的其餘特殊字符就能讓命令行正確識別。反斜槓能夠辦到!以下代碼:
含有惡意字符的輸入被限定在引號內:
假設服務端生成的命令模型爲 file 「foo」 「;date」,這時,我利用反斜槓將 ② 號引號轉義爲普通字符,那麼 ① 和 ③ 號引號將天然結合,接着利用註釋符將 ④ 號引號註釋掉,;date 對於命令行直接可見,邏輯上我能用 file 「foo」 「;id #bar」 注入任意命令,再次逃逸到引號外:
注,註釋符 # 須要 URL 編碼爲 %23。
好了,還記得前面我找到正則漏洞沒法過濾反斜槓麼,wargame 中的引號已經沒法束縛我,雖然當下沒法直接利用,但至少又讓我向前邁出一步。
繼續看 2九、30 兩行的命令執行代碼。顯然這與命令注入漏洞多少有些關係。命令注入常見三種手法:利用命令分隔符注入命令、利用命令替換符注入命令、利用命令選項注入命令。
命令分隔符注入命令。命令分隔符包括換行符(\n)、分號(;)、邏輯與(&&、&)、邏輯或(||、|),若在 win 批處理腳本中還能用 %1A。好比:
命令替換符注入命令。shell 優先執行命令替換符內的命令,目的是便於運維人員將前個命令的輸出做爲後個命令的輸入。命令替換符包括 $(…)、反引號 …
。好比:
命令選項注入命令。命令選項(option)和命令參數(argument)是兩個概念,國內外不少文獻都將他倆混淆。好比:
其中,-d 是命令選項、/tmp/ 是命令參數。不少時候,藍隊過濾掉全部命令分隔符、命令替換符,雖然我沒法直接注入命令,但我能夠注入其餘命令選項,這就給我很大想像空間,某些選項能夠讀取文件、有些又能寫入文件、甚至執行其餘命令。好比,有個頁面,能夠將 web 目錄打包爲你指定的歸檔文件,輸入爲歸檔文件名 $archive 參數,服務端過濾全部命令注入相關字符,調用 system(「tar -cf」 . $archive . 「*」) 執行命令:
但我經過注入 tar 命令的 –checkpoint、–checkpoint-action=exec 兩個選項,成功執行命令 id:
wargame 中執行的是 file 命令,查看下它有哪些用得上的選項,好比,是否有選項能夠讀文件,man 中搜索 read,找到 -f 選項:
仔細看下,該參數並不能讀取顯示文件內容,只是從該文件中獲取文件列表,沒意思(。・_・。)。等等,報錯信息中有啥提示:
哇噢,歷害啦,經過注入命令選項 -f,讓我能夠讀取 wargame 的文件內容。
0×04 全面瓦解
管它金城湯池仍是銅牆鐵壁,一顆鬆滑螺釘,它將全面瓦解。
將多個獨立漏洞組合成漏洞鏈,完成目標攻擊,絕對是個人 G 點。回顧前面的成果,因爲誤寫正則表達式,致使沒法過濾反斜槓;經過反斜槓,能夠逃逸引號;經過引號逃逸,創造出命令選項注入的條件;經過注入 -f 選項,實現 flag 文件讀取。過濾反斜槓的正則,就是那顆鬆滑的螺釘。
如今,攻擊目標前,還剩一個問題,我並不清楚 flag 文件路徑及文件名。首先想到的是暴破。土!的確很土氣,用常見的 flag、FLAG、f14g 等等常見 flag 名暴了一遍,毫無收穫。換個手法,通配符模式匹配。這下洋氣了吧。
通配符模式匹配(globbing patterns),也叫路徑名擴展(pathname expansion),簡單來講,在表示文件名/目錄名或路徑時,你能夠用 ? 表明任一可見字符、用 * 表明零或多個可見字符、用 [a-z] 表明字符範圍,惟一例外,以 . 開頭的文件或目錄、以 / 分隔的路徑必須顯式寫明,不然沒法被模式匹配。
好比,我並不還知道 /tmp/ 目錄下有個名爲 FindMe 的文件,但,藉助通配符屢次測試,不但刺探出該文件的存在,還成功查看到文件內容:
好,如今一切就緒,攻擊 CMDi_lab/escaping_quotation。有了前面的分析,我構造了載荷 f1=foo&f2=-f ? bar #,將 file 「foo」 「bar」 轉換爲 file 「foo」 「-f ? bar #」,猜解文件名只有一個字符的文件:
顯然,沒找到這樣的文件,相同思路,藉助 burp 自動查找文件名長度在 [1, 16] 的全部文件:
跑完仍是沒有找到任何文件。這就奇怪了,前面說過,通配符沒法匹配 .,莫非是隱藏文件,調整下載荷, f1=foo&f2=-f .? bar #,再次暴破:
找到名爲 .f1a9_ 的目錄,繼續調整載荷 f1=foo&f2=-f .f1a9_/.? bar #,暴破:
找到名爲 .f1a9_/.flag_15_here.txt 的文件,帶上準確路徑訪問:
WTF!不該該啊,邏輯上說不通。別急,捋一捋,莫非載荷中新增部分有被過濾的字符?回到前面的正則源碼處,的確過濾了 flag 關鍵字,我用通配符替換,載荷變成 f1=foo&f2=-f .f1a9_/.fl?g_15_here.txt bar #,另外,命令選項 -f 前應該得加個空格,最終載荷爲 f1=foo&f2= -f .f1a9_/.fl?g_15_here.txt bar #,來一發:
多麼愉悅的攻擊體驗!
絲滑般的思緒,真實而天然!思緒天然?!正則未正確過濾反斜槓、利用反斜槓逃逸引號、通配符模式猜解路徑、注入命令選項讀取文件,作做、彆扭!以上是我爲了湊字數、增篇幅寫的,真實的攻擊手法並不是如此。
仔細審計正則過濾的代碼。用 \ 而非 \\ 表述反斜槓,不只沒法正確過濾反斜槓,還會引起連鎖反應。你看,緊隨 \ 其後的是 |\n:
前面提過,\ 結果字符串轉義後到達正則引擎變成 ,它與 |\n 結合變成 |\n,正則引擎誤解成匹配豎線與換行符的組合。當我輸入豎線與換行符的組合,確認被過濾:
換言之,服務端只過濾 |\n 而放行 \n。有換行符,我能夠直接注入新命令,好比,執行命令 id:
既然能注入命令了,查看 flag 易如反掌!命令 grep -r . . 能夠查看當前目錄下全部文件內容,服務端過濾了 grep,我用內部空變量輕鬆繞過(g$1rep -r . .),或者,無效轉義繞過(g\rep -r . .),或者,通配符繞過(/bin/gr?p -r . . 或 /bin/gr[d-f]p -r . .),我有 1024 種方式吊打目標。
OK,清晰,爭取一次搞定,構造載荷 ?f1=foo&f2=%0a/bin/gr[d-f]p+-r+.+.+%23,頁面顯示:
最後聊聊你關心的賞金漏洞。大體業務場景,服務端執行打包命令壓縮幾個固定目錄,容許用戶輸入歸檔文件名,屢次刺探確認使用的 zip 命令,相似:
其中,歸檔文件名 archive.tar 可控。服務端正則過濾全部命令分隔符、命令替換符、其餘元字符,同時,禁止出口流量,顯然沒法直接注入命令。
一番嘗試,發現容許橫線(-),這就告訴我能夠注入命令選項。我開始分析環境 zip 自身有哪些選項能夠爲我所用。先查找關鍵字 execute,一無所得;接着搜索關鍵字 command,找選項 –unzip-command(簡寫 -TT) 和 –test(簡寫 -T),容許用戶指定第三方程序來校驗歸檔文件的完整性:
換言之,選項 -T 和 –unzip-command 能夠注入新命令 id:
成功拿到賞金。
命令注入攻擊,除了常規的命令分隔符、命令替換符以外,利用環境自身也能實現。
注一,wargame 的原型來自 Kaibro 所寫的 wargame,見 http://final.kaibro.tw:10002/; 注二,escaping_quotation 源碼,以及更多命令注入相關 wargame 見 https://github.com/yangyangwithgnu/CMDi_lab