其實url編碼就是一個字符ascii碼的十六進制。不過稍微有些變動,需要在前面加上“%”。比如“\”,它的ascii碼是92,92的十六進制是5c,所以“\”的url編碼就是%5c。
當url中傳入參數id時,瀏覽器在後面會對非ASCII碼的字符自動進行一次urlencode,且瀏覽器對ascii內的url編碼自動進行一次解碼。
先看一道題:
攻防世界——PHP2
根據題目,沒有給出任何信息,直接是一個場景,點擊打開場景頁面我們發現是一串文字:Can you anthenticate to this website?這裏是phps源碼泄露。
首先想到嘗試index.php,未果。再嘗試index.phps,成功發現了代碼泄露。
分析代碼可知:
第一步,要使得"admin"===$_GET[id]
不成立
第二步,經過$_GET[id] = urldecode($_GET[id]);
,使得$_GET[id] == "admin"
成立。
故用二次url編碼繞過
?id=%2561dmin
這樣,url中自動進行一次url解碼:
urldecode(%2561)=%61
代碼中再進行一次url解碼:
urldecode(%61)=a
這樣就繞過了代碼中的限制
即,當第一次比較時,實際是(%25是“%”的url編碼)
if("admin"==="%61dmin")
而經過
$_GET[id] = urldecode($_GET[id]);
第二次比較是:
if("admin" == "admin");
成立了。
下面貼上URL編碼表:
url編碼是按照ascii碼進行編碼的,即百分號%+ascii碼的十六進制,所以如果我們傳遞一個%80,80是十六進制,轉爲十進制爲128,超過了ascii碼的範圍0~127,所以這個時候會報錯。
攻防世界-web-Cat
打開網頁,有一個雲端測試功能,提示我們輸入域名,輸入幾個進行測試
輸入127.0.0.1,反饋如下
輸入的ip被執行了ping命令並返回,看來這是一個命令執行的功能。
嘗試使用管道進行命令執行,輸入127.0.0.1 | ls,然而得到的回顯是Invalid URL,之後各種命令執行的繞過都不行,看來命令執行是行不通了。
網站用的是php寫的,但是這個Cloud端就不一定是php的了。
在URL的傳參處?url=這裏,我們傳遞個%79發現傳遞之後變成了?url=w,看來是可以傳遞url編碼,系統會接受並進行解析和解碼,於是我們傳遞%80會出現報錯,url編碼使用的是16進制,80也就是128,ASCII碼是從0-127,所以這個時候會報錯。
報錯信息中可以看到:
出現報錯信息,是一段html代碼,將這些代碼複製出來保存爲html並打開。
看到了熟悉的django報錯頁面,看來是將輸入的參數傳到了後端的django服務中進行解析,而django設置了編碼爲gbk導致錯誤編碼了寬字符**(超過了ascii碼範圍)**。
在比賽的時候有個提示:
RTFM of PHP CURL===>>read the fuck manul of PHP CURL???
是關於php curl的,讓我們看一下CURL的官方文檔manul
意思是可以用@讀取文件內容,經測試@未被過濾。
所以思路來了。根據Django的目錄,我們使用@進行文件傳遞,對文件進行讀取之後還會把內容傳給url參數,如果像上面一樣有超出解析範圍的編碼的時候就會得到錯誤信息。我們的目標首先是數據庫文件,看從錯誤信息中能不能拿到flag,可以從配置文件settings.py的報錯中看看有沒有database的相關信息。
結合django的報錯得知了項目的絕對路徑爲/opt/api
這裏還需要懂一些django開發的基本知識,我感覺這道題涉及的面有點廣了,django項目下一般有個settings.py文件是設置網站數據庫路徑(django默認使用的的是sqlites數據庫),如果使用的是其它數據庫的話settings.py則設置用戶名和密碼。除此外settings.py還會對項目整體的設置進行定義。
讀取settings.py文件,獲取數據庫名。這裏需要注意django項目生成時settings.py會存放在以項目目錄下,再以項目名稱命名的文件夾下面。
payload:?url=@/opt/api/api/settings.py
同樣將報錯信息用html文件打開,可看到一些敏感信息:
或者報錯內容搜索database可以得到:
同樣在使用@讀取數據庫信息
payload:?url=@/opt/api/database.sqlite3
在報錯信息中搜索CTF得到flag。