はじめに

なんか目次バグってるかも。

CTFとは

CTFとはパソコンでかちゃかちゃしてなんとかすることである特定の文字列を入手するゲームのことである。
そのCTFのひとつがpicoCTFであり、その練習ができるのがpicoGymである!

 前回、picoCTFのアーカイブであるpicoGymから12問解いてみました。

結果最初は簡単だったのに途中から急に問題が難しくなり、ヒントも雑になったりとあれれーという感じでした。

しかしいい勉強にはなるような気がしたので続きとしてここに書いていこうと思います。

解いてみた

書きながら解いたり、解いた後書いたりしてます。

13.Static ain't always noise (正答率88%/高評価87%)

ELFから文字列を探す問題でした。
staticというELFファイルとltdis.shというBASHスクリプトが配布されます。
とりあえず wget URL でダウンロードし、 chmod 777 ファイル名 で起動できるようにしてstaticの方を開いてみました。
するとどこかにあるよって英語で言われ、スクリプトを開くと使い方が示され、スクリプトでstaticを開くとディスアセンブルしたデータと文字列のデータがそれぞれテキストファイルでもらえました。
文字列の方をmore ファイル名 で開くとフラグがありました。

こう聞くと簡単なんですが、最初はテキストファイルの開き方がわからなくて、./ファイル名で開いてもフラグが表示されませんでした。
それで別の方法で解こうとしてELFファイルの中身が見れればいいだろって思い、readelfやobjdumpを試してみました。
それで結局objdump -S ファイル名 という-Sでファイルの中身をすべて表示するコマンドでフラグを見つけました。
このときclsと打ってコンソールをきれいにしようとしてもできないと思ったらLinuxではclearでした。
そこからあのスクリプトはこのコマンドをまとめてくれてあるだけで、自分のテキストファイルの開き方が悪かったんだなとわかりました。
ちなみにmoreでスクリプトを開くとechoで説明文を表示し、
objdump -Dj .text $1 > $1.ltdis.x86_64.txt
でディスアセンブリデータを、
strings -a -t x $1 > $1.ltdis.strings.txt
で文字列データを得ていることがわかります。

久しぶりに自力でクリアできてうれしかったです。

14.Tab, Tab, Attack (78%/81%)

普通にunzipしてchmod 777して開いたらフラグが出てきました。
ヒントにTabを11回押すとか書いてあったんですけどあれは何だったんですかね。
てか難易度が違いすぎません?
同じpicoCTF 2021の問題らしいですが20ポイントの問題とは思えません。
実際にリアルタイムでやったことがないからわからないんですけど、たぶん時間内に点を取ってもらうためにサービス問題とかもあるってことですかね。
でもクリアできたので嬉しいです。

15. keygenme-py (37%/90%)

解いている途中。
fernetというAESが用いられているライブラリが使われている?
AESはさすがに実装できないが、行列を回転して掛け算とxorを繰り返している感じ?
cryptgraphyのインストールにてこづったが、pyファイルの起動を間違えただけかもしれない?
一応cryptgraphyのバージョンに合わせてpythonのバージョンを下げた。
その際にanacondaで仮想環境を作り直した。切り替えはactivate 環境名。
てか正答率37%はむずすぎなんじゃね。

解き終わった。
ライセンスキーを解析する問題。
コードを見るとフラグのような文字列が発見できる。
その文字列と比較している部分は最初のメニューの'c'の選択肢からたどり着く。
たどり着いたcheck_key関数の中を見てみる。
まず文字列の長さ、最初のxの前のpicoCTF{~の文字列の部分が比較される。
その後フラグのxの部分が一文字ずつ"FREEMAN"という文字列の文字からハッシュ値を出して比較されている。
最初の比較に対応するように文字列を入れるかその比較分を消すかしたあと、xが比較されている値をprintで表示すればxの中身がわかりクリアできた。
正しいライセンスキーを入力するとfull_versionという変数にfernetによって暗号化された製品版に対応するコードが展開されて実行される。
最初はxの値がわかった後、まだ何かあるのかと思い復号後のコードも見てみたが普通のコードだった。
てか何のソフトだったのかわからない。
Arcaneってなに?
LOLのアニメのことだったのだろうか?


16.Matryoshka doll (62%/90%)

マトリョーシカのような問題。
マトリョーシカの画像が配布される。
ということはこのマトリョーシカの中に何か入っているはずだ。
jpgファイルだがバイナリで見てみると何のファイルかを表す目印であるシグネチャはPNGだった。
どっちでも開けるっぽい。
PNGファイルのフォーマットは最初に8バイトのシグネチャがあり、その後はチャンクというデータのまとまりが続く。
チャンクはチャンクの長さ4バイト、チャンク名4バイト、データがチャンクの長さ分、CRC4バイトの形式である。
最初はIHDRというチャンク、最後はIENDというチャンクとなる。
最初は全てのチャンクの中身を見てみようかなーとか思っていたが、チャンク内のデータがzlibで圧縮されており、解凍するにはzlibかlibpngというライブラリを導入しなければならなかった。
けれどライブラリとかめっちゃ苦手で、その導入とかが何やっているかわからなくてできないからあきらめた。
ほんっとになんか外部のライブラリとか導入がめんどくさいうえにその使い方も調べなくちゃいけなくてすげーよくわからん。
でも結局チャンク内を解凍する必要はなかった。
画像ファイルの割にはなんか大きすぎるよなーと思ったため、最後のIENDチャンクをStirlingというバイナリエディタで検索してみると、IENDの後にやたら長いデータがまだありました。

ここでStirlingの設定>環境設定>キーアサインまたはツールバーから選択系の中のデータ先頭まで選択を追加して使えるようにしておきます。
そしてIENDのチャンクの最後から先頭までのバイナリを選択し、すべて消して保存します。
するとPKというシグネチャが先頭に来るファイルが出来上がります。
これはZIPファイルのシグネチャです。
そのため拡張子をzipにして解凍すると中にまた画像ファイルがあります。
これを5回繰り返すとフラグが入っているテキストファイルが手に入ってクリアです。

他の人はどんな方法でクリアしてるのかなーと調べてみるとファイルの構造がわかるなんか便利な関数があるっぽいですね。
pngとかzipはdeflateというアルゴリズムで圧縮されているらしいので、やっぱり自分で実装したいなーと思いました。
上記のサイトを見て実装しようと思いましたが、結構むずそう。

17.crackme-py (80%/81%)

なんか暗号化されてるっぽい文字列があり、デコードする関数があるのでそれに渡すだけ。
crackmeだからpythonをデバッガで解くのかと思ったら全然違かった。
難易度が違いすぎて逆に焦った。
picoGymは難易度順に並べられないのだろうか。

18.Magikarp Ground Mission (59%/88%)

bashの使い方を学べる問題。
まずsshでbashに接続する。
次にlsで何のファイルがあるか確認する。
そして
more ファイル名
でinstructionのテキストファイルを開いてやることを読み、flagのテキストファイルからフラグの一部を集めていく。
cdでディレクトリを移動していく。
cd / でルートへ
cdまたはcd ~ でホームへ
pwdで現在のディレクトリを確認できる。

19. tunn3l v1s10n (54%/45%)

さっぱりわからん。
ビットマップらしいけど開かぬ。
16のマトリョーシカの問題みたいにbmpの構造を理解してバイナリから解析してやろうとか考えて仕様を調べてたら複雑でなんか疲れてやる気が出なくなってずっと放置してしまった。
何時間も考えて頑張って解こうとしてやる気を失うくらいなら、5分考えてわからなかったらすぐwriteup見てやるぞ精神でやった方がいいんじゃないかと思ってきた。
writeupを調べてみた。

ちらっと解き方を見てまだ全部ちゃんとwriteupを読んだわけじゃないですよ感を出しながら自分で解いてみる。
やっぱファイル構造の問題だったかー。
なんか前見たときはあってたような気がしたんだけどなー。
windowsのプログラマ電卓でなんか計算した気がするけど気のせいだったかー。
よしやるぞ。
ビットマップのwikiを見ながら解く。
ビットマップはファイルヘッダ、情報ヘッダ、カラーマスク、カラーパレット、ビットマップデータ、カラープロファイルというデータの塊で構成されるそう。

最初はファイルヘッダです。
まず、ファイルの種類を決めるバイナリの一番最初にあるシグネチャ(マジックナンバー)を見る。
するとBM(42 4D)という2バイトの文字があるからビットマップだ。
次はファイルサイズが4バイトで表記されており、8E 26 2C 00であった。
バイナリエディタで一番最後まで見てみると

2C2680の行の2C268Dまであるから、さっきのファイルサイズは逆から読むリトルエンディアンであり、00から始まっているから1を足した2C268Eであっている。
次は2バイトの予約領域が2つあり、0で埋まっていればいいためおk。
次は4バイトでファイルの先頭からビットマップデータまでのオフセット(アドレスの差)。
ビットマップデータがどこかわからんから置いておく。
以上、14バイトのファイルヘッダでした。

次は情報ヘッダです。
確かwiki読んでるとOS/2とか1.1, 2.xとか、windows 3.0以降、95以降、98以降とかいろいろバージョンが書かれていてややこしくなって読むのやめたんだっけ。

windowsの情報ヘッダ(識別するため特にinfoヘッダと呼ばれる)ではまず4バイトのヘッダサイズがあり、情報ヘッダのサイズは40(0x28)で固定。
しかしあるのはBA D0 00 00のため異なる。
これを28 00 00 00に直してみると、開けるようになった。
しかし表示されたのはnotaflag{sorry}であったため、まだ直すところがあるらしい。
次は4バイトでピットマップの横幅:0x46E、4バイトで縦幅:0x132が続くがわからんから置いておく。
次に2バイトでプレーン数というよくわかんないやつで1のためおk。
次の2バイトは1ピクセルあたりのビット数で0,1,4,8,16,24,32のどれからしい。
18 00より0x18は24のためおk。
次は4バイトで圧縮形式で0~5のどれかであり、0のためおけ。
続いて4バイトで画像のデータサイズ:0x2C2658、水平方向の解像度:0x1625、垂直方向の解像度:0x1625、使用する色数:0、重要な色数:0。
いやこれ全部確かめるの面倒すぎる。

もう一回writeupを見てみる。
すると後回しにしたビットマップの横幅縦幅がおかしかったみたいだ。
先ほど表示された画像を見直してみると
なんか色が部分的におかしくなっていた。
現在の横幅が6E 04 00 00で0x46E=1134、縦幅が32 01 00 00 で0x132=306。
適当にいじると昔のテレビの砂嵐の画面みたいになった。
幅のいじり方がわからないからとりあえず全体を見てみる。

次はカラーマスク。
12バイトらしい。
色を取り出すときに使われるデータらしいけどよくわからん。

次はカラーパレット。
画像に用いられる色を青、緑、赤、予約領域それぞれ1バイトの計4バイトずつで定義されている。
定義された色には番号が振られ、後のビットマップデータで用いられる。

次はビットマップデータ。
先ほどのカラーパレッドのパレッド番号を用いたり、直接RGBを書いてピクセルを表すらしい。
1行ずつ表され、1行は4バイト境界にそろえる、すなわち4の倍数のバイト出ないといけないらしい。
今回は1ピクセルあたりのビット数が24ビット(3バイト)のため、1行が4の倍数でなかったらその分だけ0で埋める。

最後にカラープロファイル。
よくわからん。

全体的に軽く見てみたけどよくわからぬ。
そのため他のwriteupも見てみた。
するともっと最初に直すべきところがあった。
後回しにしたファイルヘッダのビットマップデータへのオフセットである。
ファイルヘッダが14バイト、情報ヘッダが40バイト、カラーマスクが12バイト、カラーパレットはサイズがわからない。
と思ったら、このファイルにはカラーマスクとカラーパレットは存在しないようです。
まずカラーマスクが存在する条件はinfoヘッダかつ圧縮形式がビットフィールドである場合らしいです。
今回はinfoヘッダですが圧縮形式が0の無圧縮のためカラーマスクはありません。
そしてカラーパレットは1ピクセルあたりのビット数が1,4,8の場合にあるらしいが、今回は24のため存在しない。
よってファイルヘッダ14バイト、情報ヘッダ40バイトに続いてビットマップデータが存在するため14+40=54バイト(0x36)となります。
よってファイルヘッダ内のオフセットの4バイト、BA D0 00 00を36 00 00 00に変えると

先ほどまで変な色だったものがちゃんとした色になりました。

あとはこの画像がなんか横長だなーと感じて画像の大きさを修正するという流れでしょうか。
別に横長の画像って普通な気がするなー。
まあとにかく頑張って間違っているところを探しているところ、なんかやっぱ横長だよなと感じてサイズに注目するというのが正しいというか想定の解き方ってことにしましょう。

また情報ヘッダに戻ります。
画像の適切なサイズを求めるのに必要そうなデータをまとめました。
ピットマップの横幅:0x46E (1134) ピクセル
ビットマップの縦幅:0x132 (306) ピクセル
画像のデータサイズ:0x2C2658 (2893400) バイト
水平方向の解像度:0x1625 (5669) ピクセル/m
垂直方向の解像度:0x1625 (5669) ピクセル/m

ビットマップの横と縦をただ掛けると1134*306=347004 ピクセル。
しかしこの値では正しくなく、実際はデータサイズは色や1行を4の倍数にそろえたりするため少しややこしくなる。
まず今回は1ピクセルあたり24ビット、すなわち3バイトで表記される。
よって横幅1134ピクセルは、×3して1134*3=3402バイト。
そしてこの横幅の一行は4バイトの倍数にそろえないといけないため、4で割った余りを4から引いた数を足して4の倍数にする。
4-(3402 mod 4)=2
よって3402+2=3404
ちゃんと3404/4=851と割り切れる。
この3404バイトが一行あたりのデータとなるため、2893400バイトから割れば
2893400/3404=850 ピクセルと、縦幅が求められる。
850は16進数で0x352より、ビットマップの縦幅を0x132から0x352に変えると

解くことができました。
一応フラグは隠しました。
解像度は使いませんでしたね。
1メートルあたりのピクセルのため、1インチあたりに直せばdpiにできます。

まとめると、今回はビットマップの中身をバイナリで見て、
1.圧縮形式や1ピクセルあたりのビット数を確認してカラーマスク、カラーパレットがないことを把握し、ファイルヘッダと情報ヘッダのサイズからビットマップデータへのオフセットを直す
2.情報ヘッダのサイズを40に直す
3.なんか画像が横長だなという違和感からビットマップのサイズをデータサイズと横幅、1行あたりのバイトを1ピクセル24ビットであること、4バイトの倍数にそろえることという条件から求めて縦幅を正しい値に直す
この3点ができれば解ける問題でした。
いや解けねぇよ。むりむりのむり。

20. Easy Peasy (19%/56%)

え、どゆこと。
19%しか解けてない。
ってことは激ムズ問題じゃん。
これから解きますが、19の問題より正答率が低いのかー。
とりあえずやってみます。

とりあえずサーバーにつないでみると、暗号化されたフラグが表示されました。
This is the encrypted flag!
51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57
pythonのファイルを開くと、サーバーにあるプログラムが書かれており、暗号化の関数とかが載っています。
読んでみると、鍵とフラグを読み取り、フラグを鍵で1文字ずつxorして2桁の16進数にしたものをつなげて表示するようです。
そしてその鍵は50000文字あり、暗号化に使った長さ分だけずらしているみたいです。
50000文字使い切ったらまた最初から使うみたいです。
このプログラムでは暗号化されたフラグが表示された後、好きな文字を暗号化することができます。
ということは、50000文字入力して鍵を最初に戻し、フラグに使った鍵を入力した文字とxorして求めればフラグがわかるということか!!
でも5万文字もどうやって入力するのだろう。
pythonとかで自動でできそうだけどやり方がわからない。
無理だ。恐るべきワンタイムパッド。。
一応"picoCTF{"で分かるとこだけ鍵を求めてみる。

#include<stdio.h>
#include<stdlib.h>
int ch16(char m)
{
int a;
if('0'<=m&&m<='9')a=m-'0';
if('a'<=m&&m<='f')a=m-'a'+10;
if('A'<=m&&m<='F')a=m-'A'+10;
return a;
}

int main()
{
char s[]="51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57";
char a[]="picoCTF{";
char *s2=malloc(sizeof(s)/2);
for(int i=0;i<sizeof(a);i+=2){
s2[i/2]=ch16(s[i])*16+ch16(s[i+1]);
}
for(int i=0;i<sizeof(a);i++){
printf("%d ",a[i]^s2[i]);
}
 return 0; 
}

33 123 44 34 90 84 70 123 80 (10進数)
21 7b 2c 22 5a 54 46 7b 50 (16進数)
! { , " Z T F { P  (文字)
部分的に鍵は求められましたが、肝心の{}の中身がわかりませんでした。

writeupを読みました!
やっぱり方針としては間違っていなかったようです。
winsock使えばc言語でもできるかもしれませんが、やっぱりここは簡便なコマンドラインのpythonの使い方を学ぼうと思います。
コマンドラインでは -c  というオプションをつけることで -c "pythonのコード" のようにpythonのコードを実行できるそうです。
さらに | でつなぐことによってnetcatコマンドも使えるそうです。

鍵を5万文字分使い切って最初に戻したい。
フラグは
51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57
と64文字のため、2桁の16進数で表されていることから32文字のカギが既に使われている。
よって(50000-32)文字だけ鍵を使えば最初に戻る。
pythonではprintで文字を表示(入力)できる。
しかもめっちゃ便利なことに "文字"*n (nは整数)  で文字を繰り返して表示することもできるらしい。
0とxorすればそのまま鍵が得られる。
しかし0*50000-32だと数式になってしまうため16進数として"\x00"*(50000-32)とすればいい。
このとき\x0ではなく、ちゃんと0を2桁書く。まあこの時のカギは使わないため0でなくてもよい。
また表示するだけでは入力されないため、+"\n"でEnterを押したことにする。
その後フラグに用いられた鍵32文字分を表示するため+"\x00"*32 を加える。
このときpythonを先に書いて|の後にncを書き、
python -c "print("\x00"*(50000-32)+"\n"+"\x00"*32)" | nc mercury.picoctf.net 58913
これでいいかと思えば、ダブルクォーテーションが入れ子になり途切れた扱いになる。
そのため中身をシングルクォーテーションにして
python -c "print('\x00'*(50000-32)+'\n'+'\x00'*32)" | nc mercury.picoctf.net 58913

これで
62272a2e7b7d5b505c7830365c783063595c7866325c7864625c7865655c7865
と鍵が得られた。
これと暗号化されたフラグをxorすると

#include<stdio.h>
#include<stdlib.h>
int ch16(char m)
{
int a;
if('0'<=m&&m<='9')a=m-'0';
if('a'<=m&&m<='f')a=m-'a'+10;
if('A'<=m&&m<='F')a=m-'A'+10;
return a;
}

int main()
{
char enc[]="51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57";
char key[]="62272a2e7b7d5b505c7830365c783063595c7866325c7864625c7865655c7865";
char *enc2=malloc(32);
char *key2=malloc(32);
for(int i=0;i<32*2;i+=2){
enc2[i/2]=ch16(enc[i])*16+ch16(enc[i+1]);
}
for(int i=0;i<32*2;i+=2){
key2[i/2]=ch16(key[i])*16+ch16(key[i+1]);
}
for(int i=0;i<32;i++){
printf("%c",enc2[i]^key2[i]);
}
 return 0; 
}

35ecb423b3b43472c35cc2f41011c6d2

あれ?picoCTF{が出てこない。
これでずっと何か間違っているんじゃないかって確認してたんですが、ちゃんと問題文を読んでいませんでした。
(Wrap with picoCTF{}) ←これ!
すなわち{}の中身しか得られないということです。
そのためwriteupを見る前に復号した鍵は間違いです。
やっと解けました。

21. ARMssembly 0 (31%/57%)

アセンブリの問題。
今回はarmのアセンブリみたいです。
今までx86/x64しか見たことがなかったのでarmは初めてです。
VisualStudioでコンパイルして、それをc言語に逆コンパイルすれば解けるかもしれませんが、せっかくなのでアセンブリを読んでみようと思います。

chall.Sをテキストファイルとして開き、とりあえずmain関数から調べながら読んでみます。

main:
1 stp x29, x30, [sp, -48]!
2 add x29, sp, 0
3 str x19, [sp, 16]
4 str w0, [x29, 44]
5 str x1, [x29, 32]
6 ldr x0, [x29, 32]
7 add x0, x0, 8
8 ldr x0, [x0]
9 bl atoi
10 mov w19, w0
11 ldr x0, [x29, 32]
12 add x0, x0, 16
13 ldr x0, [x0]
14 bl atoi
15 mov w1, w0
16 mov w0, w19
17 bl func1
18 mov w1, w0
19 adrp x0, .LC0
20 add x0, x0, :lo12:.LC0
21 bl printf
22 mov w0, 0
23 ldr x19, [sp, 16]
24 ldp x29, x30, [sp], 48
25 ret

まずx29やw0とかはレジスタという変数みたいなやつです。
0~30の31個あり、wが32ビット、xが64ビットのサイズです。
一行目のstpはx29とx30の中身をスタックというメモリに移動させる命令です。
大体左側に右側のものを移す感じです。(と思っていたらarmでは逆もあるっぽい?) 
2行目のaddは足し算の命令で、x29=sp+0です。spはスタックポインタと言い、スタックというメモリの中のアドレスを示します。
3行目のstrはレジスタからメモリにデータを移す(なんで左から右に移すんだ!)命令で、x19の中身を[sp,16]というメモリ、すなわちスタックポインタから16だけずれたところに移しています。
6行目のldrはメモリからレジスタにデータを移す命令です。
x0に[x29,32]、すなわちx29に32だけ足した一のメモリの中身を移しています。
9行目のblはラベルにジャンプする命令です。
ラベルとはメモリアドレスにつけられた名前です。
atoiは文字列を数字に変える関数であり、それが呼び出されていることがわかります。
10行目のmovはレジスタからレジスタにデータを移動させる命令です。
17行目でfunc1という関数が呼び出されています。

func1:
1 sub sp, sp, #16
2 str w0, [sp, 12]
3 str w1, [sp, 8]
4 ldr w1, [sp, 12]
5 ldr w0, [sp, 8]
6 cmp w1, w0
7 bls .L2
8 ldr w0, [sp, 12]
9 b .L3

#がついた数字はただの数字です。
6行目のcmpは比較する命令で、これによりステータスレジスタというレジスタに比較結果が入ります。
その次の7行目のblsは比較された結果をもとにジャンプする命令です。
bはジャンプする意味、lsは<=を意味するため、blsでw1<=w0のとき.L2というラベルにジャンプするという意味です。
最後に.L3にジャンプしてその中でreturnするためmainに戻ります。

main関数の19行目のadrpはラベルのアドレスをレジスタに移動させる命令です。
21行目ではprintfを呼んでなんか表示させてます。
:lo12:.LC0というのはLC0のアドレスの下位12ビットという意味です。
LC0には"Result: %ld\n"という文字列がありました。
24行目のldpはメモリからレジスタにデータを移動させる命令です。

なんとなくmain関数の流れをつかむことができました。
この問題は4134207980と950176538という2つの整数が与えられた時に表示される整数を16進数に直してpicoCTF{}の中に入れろというものです。
そのためprintfの引数を辿っていこうと思います。

printfの引数はprintfを呼ぶ直前にあるはずです。
直前を見てみると

17 bl func1
18 mov w1, w0
19 adrp x0, .LC0
20 add x0, x0, :lo12:.LC0
21 bl printf
19、20行目は文字列のあるアドレスを求めてるっぽいです。
ということは18行目のw0をw1に代入する操作が怪しい。
w0を追ってみると、16行目でw19が代入されているが、その後func1が呼ばれているためその中のw0を追う必要がある。

.L2:
ldr w0, [sp, 8]
.L3:
add sp, sp, 16
ret
.size func1, .-func1
.section .rodata
.align 3

func1:
1 sub sp, sp, #16
2 str w0, [sp, 12]
3 str w1, [sp, 8]
4 ldr w1, [sp, 12]
5 ldr w0, [sp, 8]
6 cmp w1, w0
7 bls .L2
8 ldr w0, [sp, 12]
9 b .L3

8行目でw0に[sp,12]が入る。
その前の7行目で.L2が呼ばれた場合はw0に[sp,8]が入り、retがないためそのまま.L3に進みadd sp,sp,16
ret
でたぶんmainに戻っている。

よくわからなくなったため.L2が呼ばれなかったときをまず考える。
このときw1<=w0が偽であるからw1>w0である。
w1は[sp,12]、w0は[sp,8]である。
これらはfunc1に与えられた引数であるから、func1が呼ばれる直前を見る。

14 bl atoi
15 mov w1, w0
16 mov w0, w19
17 bl func1

w1にw0が代入された後、w0にw19が代入されている。
さらにその前の14行目にはatoi関数がある。
atoi関数は文字列を数字に直す関数であり、main関数内で2回呼ばれている。
そのためこのプログラムに与えられた2つの整数は最初文字列として与えられ、atoi関数で数字に変換されたと考えられる。
atoi関数の後ろを見てみると

6 ldr x0, [x29, 32]
7 add x0, x0, 8
8 ldr x0, [x0]
9 bl atoi
10 mov w19, w0
11 ldr x0, [x29, 32]
12 add x0, x0, 16
13 ldr x0, [x0]
14 bl atoi
15 mov w1, w0

10行目ではw19にw0が、15行目ではw1にw0が代入されている。
そのためatoi関数の戻り値はw0に入っていると思われる。
よってfunc1の直前では、2つのatoi関数の戻り値がw1,w0に入っていることが分かった。

問題文では4134207980と950176538が与えられるため、順番的にw0=4134207980,
w1=950176538であると思われる。
func1に戻ると、strとldrでなんかデータを動かしている。

func1:
1 sub sp, sp, #16
2 str w0, [sp, 12]
3 str w1, [sp, 8]
4 ldr w1, [sp, 12]
5 ldr w0, [sp, 8]
6 cmp w1, w0
7 bls .L2
8 ldr w0, [sp, 12]
9 b .L3

strはレジスタからメモリにデータを移動するためw0の中身が[sp,12]へ、w1が[sp,8
]へ。
ldrはメモリからレジスタに移す命令で、[sp,12]がw1に、[sp,8]がw0に移動されてる。
ってことはw0とw1の値が交換されてるってことか!
するとw0=950176538, w1=4134207980となる。
w1>w0のため、cmpのw1<=w0が偽となり、.L2は呼び出されない。
じゃあ.L2にジャンプする分岐はフェイクってこと?いやただ.L2に飛ばなかっただけか。
その後の8行目のldrでw0にw1と同じ値をコピーしている。
そして.L3に飛んでretでmainに戻る。
現在w0=w1=4134207980。

17 bl func1
18 mov w1, w0
19 adrp x0, .LC0
20 add x0, x0, :lo12:.LC0
21 bl printf

その後w1にw0が代入されてprintfが呼び出されている。
ということはw1がprintfの引数と思われ、w1=4134207980のため、
printf("Result: %ld\n",w1);
という感じに4134207980が16進数として表示されたのだと思われる。
4134207980は16進数でF66B01ECのため、32ビット(4バイト)であるからそのままpicoCTF{}の中に入れてクリア!!

参考:ARMの命令
         ARM条件分岐
        ARMのlo12の意味 (69ページ。PDFの83/593)

writeupを読んでみるとこれはただのif文で大小比較するコードみたいですね。
アセンブリで読むとややこしく感じます。

22. Cookies (60%/58%)

クッキーに関する問題だと思われる。
とりあえずchromeでクッキーを調べる方法をググった。
するとデベロッパーツールのネットワークからクッキーの中身が見れることが分かった。
しかしクッキーの中身である値は-1かeyJfZmxhc2hlcyI6W3siIHQiOlsiZGFuZ2VyIiwiVGhhdCBkb2Vzbid0IGFwcGVhciB0byBiZSBhIHZhbGlkIGNvb2tpZS4iXX1dfQ.YZxUOg.c8zgsXneS7EHYnj3AbWgvKyQiRY
という意味の分からない文字列しか得られなかった。
お手上げです。
くっ、正答率が60%と高いのにわからないなんて。

writeupを読みました。
やっぱり私はネット関係がさっぱりですね。
ほんとにまったくわかりません。
まずは検索欄に薄い文字で書いてあったsnickerdoodleを入力すると私も大好き的なことが表示され、クッキーの中のnameという値が0になりました。
このことからクッキーのnameの値が変わると、サイトの表示が変化することがわかります。
ということはnameの値を変化させることができればサイトの表示も変化させられ、フラグが表示されるのではないかと予想がつきます。
ではcookieをどうやって変化させるのか。
私はクッキーはなんかでーたほぞんしてるやつってイメージしかなかったのでわかりませんでした。

まず1つ目!
curlを使う。
curlとはコマンドからデータ送受信ができるやつで、主にUNIX系で使われるそうだがwindows10でも使えるやつ。
-bというオプションでcookieを送れる。
そのため
curl -b "name=0" http://mercury.picoctf.net:21485/check
とすれば、I love snickerdoodle cookies!と表示される時のデータが送られてくる。
しかしこれでは値を1個ずつ打たなければなりません。

それで2つ目。
pythonとかでプログラムを書き、for文とかで回す。
pythonにはネット関係を扱うライブラリはいろいろあるみたいですが、有名なrequestsを使ってみます。
使ったことがほとんどないので0から書いてみます。
まずrequestsをimportする。
requests.get(url)でサイトの中身が得られ、なんかの変数に入れて.textをつけると文字列として扱えるようになる。
今回はcookieを送りたく、requests.get(url,cookies=クッキーの中身)とすれば送れるぽい。
しかしこのクッキーはdictという辞書オブジェクトでないといけないぽい。
そこで
c=dict(name="0")
としてクッキーに代入するか、そのまま
cookies={"name":"0"}
とすればよい。
これで
import requests
url="http://mercury.picoctf.net:21485/"
c=dict(name="0")
r=requests.get(url,cookies=c)
print(r.text)

curlと同じことができるようになった。
次はfor文で100回くらい回せるようにする。
for i in range(100)
と書き、0だったものをiに変え、for文の中に処理を移す。
しかしこれではサイトが100回も表示され、その中からフラグを探さなければならない。
そこでr.textに"picoCTF"という文字があるか調べられれば良い。
なんとpythonではin演算子で調べられるらしい。
めちゃ便利じゃん。すげぇ。
if文でin演算子を使って調べるようにして

import requests
url="http://mercury.picoctf.net:21485/"
for i in range(100):
    print(i)
    c=dict(name=str(i))
    r=requests.get(url,cookies=c)
    if "picoCTF" in r.text:
        print(r.text)
        break

無事フラグが得られました。
このサイト丸パクリです。
一番シンプルなコードで分かりやすかったです。
ちなみに最初に得られた
eyJfZmxhc2hlcyI6W3siIHQiOlsiZGFuZ2VyIiwiVGhhdCBkb2Vzbid0IGFwcGVhciB0byBiZSBhIHZhbGlkIGNvb2tpZS4iXX1dfQ.YZxUOg.c8zgsXneS7EHYnj3AbWgvKyQiRY
は、一応base64は試していたんですけど、ピリオドも含めて変換しようとしていたためうまく変換できていなかったっぽいです。
最初のピリオドまでの
eyJfZmxhc2hlcyI6W3siIHQiOlsiZGFuZ2VyIiwiVGhhdCBkb2Vzbid0IGFwcGVhciB0byBiZSBhIHZhbGlkIGNvb2tpZS4iXX1dfQ
を変換すると、
{"_flashes":[{" t":["danger","That doesn't appear to be a valid cookie."]}]}
となりました。

よし、pythonでできたんだからwinsockでもできるだろうからネットを理解するためにやろう。
とか思ったんですがめんどくさくなったので止めました。
やっぱそうですよね。
printfを自分で実装しようなんて思わないですよね。
でもpythonはc言語で作られたわけで、requestsライブラリもc言語で作れるはずなんですがほんとどうやって作っているんですかね。
誰か知っていたらぜひ教えてください。

23. vault-door-training (37%/74%)

なんかまた正答率が低い!?
今回はjavaの問題です。
javaは結構前にゲーム作ろうとしてなんかよくわかんなかった記憶があります。
とりあえずソースコードを読んでみます。
読むと、入力を受け付け、それが正しいか比較しているようです。
その比較部分を見てみます。

String input = userInput.substring("picoCTF{".length(),userInput.length()-1);
if (vaultDoor.checkPassword(input)) {

moji.substring(2,6)でmojiの2番目から6番目の文字を取り出します。
moji.length()はmojiの長さ。
すると比較されているこのinputはpicoCTF{}の中身だけを比較したいということがわかります。
そのため下にあるパスワードを中に入れて終わりです。

今回はwarmupらしいので簡単でした。
いずれはDr. Evilの設計図を取り戻してみせます!
なんで正答率が低かったのでしょうかね。

24.Insp3ct0r (37%/86%)

サイトを点検してほしいという問題。
とりあえずhtmlのソースコードを見るとフラグの1/3があった。
サイトにはhtml,css,jsの3つが使われているらしいため、それぞれ確認するとフラグがありクリア。

25. Glory of the Garden (52%/82%)

わお、また画像の問題かよ。
え、今度はjpg?
うわー、今度はjpgの構造調べなくちゃいけねーのかよー。
めんどいなー。
そう思ってとりあえずバイナリエディタで開き、テキトーにスクロールして全体をざっと見ていると、、
一番最後にフラグあるじゃん。
完。

絶対解く順番間違えてるだろー。

26. Warmed Up (55%/86%)

0x3dは十進数でなんて言うでしょーかっ?

27.The Numbers (21%/52%)

フラグが数字に置き換えられていると思われる数列が与えられる。
pが16、iが9、いったいこれは・・・
とりあえずasciiと比べると
p:112->16
i:105->9
rot的にずらされている?
いやでも文字の範囲の数字ではない。
じゃあxorとか?
112 xor 16=96
105 xor 9=96
それだ。
#include<stdio.h>
int main()
{
int a[]={20,8,5,14,21,13,2,5,18,19,13,1,19,15,14};
for(int i=0;i<sizeof(a)/sizeof(int);i++){
printf("%c",a[i]^96);
}
 return 0; 
}
これでよし。

28.2Warm (43%/81%)

42って2しんすうでなんてあらわすのだべさ。

29.Wireshark doo dooo do doo... (62%/81%)

解けませんでした。
大量のデータがあり、何を見ればいいのかわかりませんでした。
で、writeupを見ました。

解き方はまず ファイル>オブジェクトをエクスポート>HTTP をみる。
するとほとんどがmultipart/encryptedであるのに対して、2つだけtext/htmlとtext/plainというデータが入ってそうな怪しいものがある。
それをみるとフラグらしきものが見つかる。
Gur synt vf cvpbPGS{c33xno00_1_f33_h_qrnqorrs}
アルファベットが変な感じになっているが、文字の範囲内のためxorではなく、=がなくてスペースを含んでいるためbase64でもなさそう。(スペースを含んでいてもbase64のこともあった。)
そのためrot13が怪しく、それで復号するとフラグが得られる。
これは中身のあるデータを送る前にTCPでデータを送る準備をするため、それ以外のデータの中でhtmlがデータの本体だと考え、htmlに絞り込んでその中から探すというやり方だと思います。
2つ目の解き方は同様にhtmlが怪しいと考え、"http"でhttpだけに絞り込む。
しかしそれでも量が多く、データを受け取るときにはGETメソッドを使っているのではないかと考え、”http.request.method==GET”でGETメソッドだけに絞り込む。
するとさっきと同様に2つ絞り込める。
そこで右クリックより 追跡>HTTPストリーム からそのGETメソッドでどのようなデータのやり取りをしたのか一連の流れを見て、フラグのような文字列を見つける。
このとき検索欄は tcp.stream eq 5 となります。

最後の解き方として、.htmlファイルが怪しいと考え、.htmlファイルはMIMEタイプというファイルの種類の表し方でtext/htmlであるため、「html contains "text/html"」と検索すると出ます。
ちなみに.txtはtext/plain、pngはimage/png、exeはapplication/octet-streamとなります。

まだwiresharkの問題はほとんど解いておらず、html以外に着目する場合はわからないのでその時また学んでいこうと思います。

30.speeds and feeds (40%/80%)

なんかncで接続すると
G0X173.1034Y3.8621
G1Z0.1
こんな感じの文字列が羅列されて送られてくる。
ヒントを見ると”CNCマシーンで使われているのは何言語でしょう?”と書かれている。
まずCNCがわからないのでググると、コンピュータ数値制御というもので、旋盤などをコンピュータで操作することらしい。
この旋盤などを操作するときに使われている言語を調べると、NC言語というものが用いられているらしい。
その書式をこのサイトで見てみると
G0X173.1034Y3.8621

Gは準備機能で、Gコードと呼ばれる。
Gコードはそのあとの数値によっていろいろ意味が変わってくるらしく、ここにその意味がまとまっている。
それによるとG0は主軸を移動させる指令で、現在位置からG0に続く座標に移動させます。
座標を見てみるとX=173.1034, Y=3.8621のためそこに移動するのだと思います。

G1Z0.1
これはG1であり、G0と同様に座標まで移動させます。
G1とG0の違いは、G0では複数の軸を同時に指令すると直線で移動しないことがある。
G1は直線で移動する。らしいです。ここに書いてあります。
よって今回はZ=0.1に直線で移動します。

今回はG0とG1しかなさそうです。
送られてきたこのコードに沿って描画すれば文字が浮かび上がってきそうです。
どうやって描画しようかなーと考え、c言語でwinapiはめんどい、kuinで描画はできるかもしれないけどデータの数値を整理しなくちゃいけない。(あとkuinはバージョンが変化すると古いコードが使えなくなってめんどくさい。まあ一時的に解ければいいんだけれども。)
じゃあexcelでデータを整理して散布図で描画しようという結論に至りました。
さっそくexcelにコピーしましたが、これどうやって座標を取り出そうか。。
ということでVBAを使って数値を取り出すことにしました。
コードを見ると、X,Y,Zの座標がありますが、XとYだけ、Zだけ、Xだけの3パターンがあることがわかりました。
またZは0.1の値しかとらないみたいです。
空間的に考えると、Zはただ文字の区切りをつけるための存在に思われます。
そのためXとYを取り出します。

久しぶりにVBAを使ったので、IF文やFOR文の使い方、Cellsとか全て0から学びなおしました。
文字の切り出しはMidという関数で行いました。
コードはこんな感じです。

Sub myget()
init_row = ActiveCell.Row
init_column = ActiveCell.Column

Do While 1
    X = ""
    Set a = Cells(init_row + i, init_column)
    If a = "" Then
        Exit Do
    End If
    '最初の座標を得る
    zahyo1 = Mid(a, 3, 1)
    If zahyo1 = "Z" Then
        Z = Mid(a, 4, 3)
        Cells(init_row + i, init_column + 3) = Z
    ElseIf zahyo1 = "X" Then
        For j = 1 To Len(a) - 3
            moji = Mid(a, 3 + j, 1)
            If moji = "Y" Then
                zahyo_y = j + 3
                Exit For
            End If
            X = X & moji
        Next
        moji = Mid(a, zahyo_y, 1)
        If moji = "Y" Then
            Y = Mid(a, zahyo_y + 1, Len(a) - (zahyo_y + 1))
        End If
        
        Cells(init_row + i, init_column + 1) = X
        Cells(init_row + i, init_column + 2) = Y
    End If

    i = i + 1
Loop

End Sub

これでXとY座標を隣のセルに取り出せました。
そして散布図を描画してみると、なんか足りませんでした。
picoCTFのはずがcoCTFになっていたり、文字が所々なくなっていました。
picoCTFのWebshellからncコマンドでデータを得ましたが、これだとデータが長すぎて途切れてしまうそうです。
じゃあファイルに保存すればいいやと思ったら、webshellで保存したファイルをmoreで開くと3%ずつ表示する感じでくそめんどいです。
netcatをwindowsにインストールしようかと思いましたがめんどいので却下。
てかcurlでもできるんじゃねとか思ったらできました。

C:\Users\ユーザー名\Desktop>curl mercury.picoctf.net:16524 >a.txt
このとき16524をスペースじゃなくて:でつなぐのが違いです。

で保存したテキストファイルからexcelにデータを移して描画っ!
なんか足りない?
しかしこれは描画方法を変更すれば直りました。
右クリックで データの選択>非表示および空白のセル>データ要素を線で結ぶ
これでつながっていなかった部分の線もつながりも字が表示されました。
それがこちらです。

よっしゃ解けたわ。
えーと、picoCTF{num3r1cal_c0ntr0l_13  ん?
なにこれ?
13の後にスペース要るのか?
そのあとは 5? j?
そのあとは ff? ii?
最後は adだよな。
13のあとちょっと間空いてるのはis ~で単語として分けてるだけか?
5ffad? fffad?
何だこれ?意味わからん。
うそ…だろ・・・
ここまで来てわからないのか・・・・

writeupを読みました。
なんとGコードを描画してくれるサイトがあるそうです。
そこへファイルをドロップすると…
あれ?
なんかたりない。。どゆこと?
ファイルが欠損している?
curlの画面をもう一度見てみると
curl: (56) Recv failure: Connection was reset
なんか失敗してた。
よくわからないのでpicoCTFのwebshellから得た最後の方のGコードをcurlで途中まで得たやつにつなげて描画すると

zで分離していないため繋がってぐちゃぐちゃになっているが、何とかちゃんと表示することができた。
さっきのサイトでやると
ちゃんと表示できた。
なんでちゃんとダウンロードできなかったのかよくわからんけど疲れたからこれでクリアとする。
ファイルの欠損に気が付かないで無駄に時間を使ってしまった。。。。

このwriteupによるとpythonでもPILというライブラリを使うことで描画できるみたいなので、今度描画系の問題が来たらpythonで解いてみようと思います。
あとこのサイトによるとC言語でもwinapiを使わないで、bitmapを作ることで描画できるみたいなのでやる気が出たらやろうと思います。


31.Shop (72%/84%) 

ELFファイルの問題。
フルーティフルなフラグが100コインで売っているのに40コインしか持ってないため買えない。
そのためELFファイルを見てみる。
wgetでELFファイルをダウンロードする。
そして
objdump -S source 
でELFファイルの中身を見る。
するとget_flagというフラグを買うときの関数が見つかった。
このフラグを得る関数を見る


080d4440 <main.get_flag>:
 80d4440:       65 8b 0d 00 00 00 00    mov    %gs:0x0,%ecx
 80d4447:       8b 89 fc ff ff ff       mov    -0x4(%ecx),%ecx
 80d444d:       3b 61 08                cmp    0x8(%ecx),%esp
 80d4450:       0f 86 e3 00 00 00       jbe    80d4539 <main.get_flag+0xf9>
 80d4456:       83 ec 44                sub    $0x44,%esp
 80d4459:       8d 05 83 d6 0f 08       lea    0x80fd683,%eax
 80d445f:       89 04 24                mov    %eax,(%esp)
 80d4462:       c7 44 24 04 08 00 00    movl   $0x8,0x4(%esp)
 80d4469:       00 
 80d446a:       e8 d1 eb ff ff          call   80d3040 <io/ioutil.ReadFile>
 80d446f:       8b 44 24 14             mov    0x14(%esp),%eax
 80d4473:       8b 4c 24 18             mov    0x18(%esp),%ecx
 80d4477:       8b 54 24 08             mov    0x8(%esp),%edx
 80d447b:       89 54 24 24             mov    %edx,0x24(%esp)
 80d447f:       8b 5c 24 0c             mov    0xc(%esp),%ebx
 80d4483:       89 5c 24 1c             mov    %ebx,0x1c(%esp)
 80d4487:       8b 6c 24 10             mov    0x10(%esp),%ebp
 80d448b:       89 6c 24 20             mov    %ebp,0x20(%esp)
 80d448f:       89 04 24                mov    %eax,(%esp)
 80d4492:       89 4c 24 04             mov    %ecx,0x4(%esp)
 80d4496:       e8 b5 00 00 00          call   80d4550 <main.check>
 80d449b:       8b 44 24 24             mov    0x24(%esp),%eax
 80d449f:       89 44 24 28             mov    %eax,0x28(%esp)
 80d44a3:       8b 44 24 1c             mov    0x1c(%esp),%eax
 80d44a7:       89 44 24 2c             mov    %eax,0x2c(%esp)
 80d44ab:       8b 44 24 20             mov    0x20(%esp),%eax
 80d44af:       89 44 24 30             mov    %eax,0x30(%esp)
 80d44b3:       c7 44 24 34 00 00 00    movl   $0x0,0x34(%esp)
 80d44ba:       00 
 80d44bb:       c7 44 24 38 00 00 00    movl   $0x0,0x38(%esp)
 80d44c2:       00 
 80d44c3:       c7 44 24 3c 00 00 00    movl   $0x0,0x3c(%esp)
 80d44ca:       00 
 80d44cb:       c7 44 24 40 00 00 00    movl   $0x0,0x40(%esp)
 80d44d2:       00 
 80d44d3:       8d 05 c0 87 0e 08       lea    0x80e87c0,%eax
 80d44d9:       89 44 24 34             mov    %eax,0x34(%esp)
 80d44dd:       8d 05 b0 f4 10 08       lea    0x810f4b0,%eax
 80d44e3:       89 44 24 38             mov    %eax,0x38(%esp)
 80d44e7:       8d 05 20 2e 0e 08       lea    0x80e2e20,%eax
 80d44ed:       89 04 24                mov    %eax,(%esp)
 80d44f0:       8d 44 24 28             lea    0x28(%esp),%eax
 80d44f4:       89 44 24 04             mov    %eax,0x4(%esp)
 80d44f8:       e8 f3 e0 f7 ff          call   80525f0 <runtime.convT2Eslice>
 80d44fd:       8b 44 24 08             mov    0x8(%esp),%eax
 80d4501:       8b 4c 24 0c             mov    0xc(%esp),%ecx
 80d4505:       89 44 24 3c             mov    %eax,0x3c(%esp)
 80d4509:       89 4c 24 40             mov    %ecx,0x40(%esp)
 80d450d:       8d 44 24 34             lea    0x34(%esp),%eax
 80d4511:       89 04 24                mov    %eax,(%esp)
 80d4514:       c7 44 24 04 02 00 00    movl   $0x2,0x4(%esp)
 80d451b:       00 
 80d451c:       c7 44 24 08 02 00 00    movl   $0x2,0x8(%esp)
 80d4523:       00 
 80d4524:       e8 17 22 ff ff          call   80c6740 <fmt.Println>
 80d4529:       c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 80d4530:       e8 1b 87 fd ff          call   80acc50 <os.Exit>
 80d4535:       83 c4 44                add    $0x44,%esp
 80d4538:       c3                      ret    
 80d4539:       e8 b2 ab fb ff          call   808f0f0 <runtime.morestack_noctxt>
 80d453e:       e9 fd fe ff ff          jmp    80d4440 <main.get_flag>

フラグが表示されるとしたらprintlnのところだと思うため、そのあたりを見てみる。

objdump -s --start-address=0x80e2e00 --stop-address=0x80e2ef0 source
とかでアセンブリで参照されているメモリアドレスとかを見てみる。


なんかprintlnの前あたりにあるメモリを参照してみると Flag is:Hex_Digit とかいう文字列が見つかった。
16進数らしいがそのフラグ自体が見つからない。
ReadFileの関数前には flag.txtという文字列が参照されているため、そのテキストファイルにフラグが書いてありそう。
じゃあそのテキストファイルを見ればいいのかと思ったけれど、ELFのデバッグ方法がわからぬ。
しかもサーバーを介してデバッグできるのかもよくわからぬ。
ここでwriteupを読もうと思ったけれどそうえばまだヒントを見てなかった。
ヒントを読むとエッジケースを試せと書いてあった。
edge caseってなんだ?と思いググると極端な値を入れろって意味っぽい。
一応その前にscanfのところを見てみる。

入力はメニューの0-4の選択、買う時と売るときの個数の選択がある。
それぞれ見てみる。
だがあまりよく分からなかった。
それで結局、99999999999とか99999999999999999999999999999999999とか膨大な値を入れてみたが何も起こらなかった。
そのため諦めようとしたが、そーえば小さい値を入れていないなと思い、売る個数をマイナスにしたところお金が増えた。
それでフラグを買うと10進数のアスキーコードが得られ、変換してクリア。

一応その部分を見てみる。


スタックを追えなかったためおそらくだが、水色の部分で売った分をマイナスしていると思われる。
しかしこのScanfの処理では3個以上売った時以外はGo言語のエラーハンドリングの分岐しかないように思われる。
そのためマイナスの値の処理がなく、マイナスの値を引いて増えてしまっているのだと思われる。
しかもこの3個以上売った時の処理が嫌味を言ってくるというやつで、普通に買ったquiet quiches (静かなキッシュ?)を3個売ると”どんな詐欺したんだ?”とか言ってくる。

この問題はアセンブリをじっくり読むのではなくて、ヒントから思いついて解く感じだと思われる。

てか結局フラグ10進数だったじゃん。
なんだよ16進数って。嘘じゃんか。

32. Scavenger Hunt (27%/59%)

Webの問題。
最初は24.Insp3ct0rと同じじゃんとか思ったのですが、3つ目から違いました。
まずhtmlとcssからフラグの一部を2つ見つけます。
その後jsを見るとGoogleにインデックスされないようにするには?というヒントが与えられます。
ググるとRobots.txtにDisallowと書けばいいとわかり、サイトのURLに/robots.txt とつければ3つ目が手に入ります。
そこには
I think this is an apache server... can you Access the next flag?
と書かれており、サーバー関係でなんかすれば最後のフラグが手に入りそうです。
とりあえずcurlで-Iのオプションでヘッダ情報を得てみました。
しかし特に何も得られませんでした。
もう何も方法が思いつかないので諦めてwriteup読みます。。

Apacheが使われているサイトでは.htaccessというWebサーバー設定のファイルがあるそうです。
robots.txtと同じように/をつけて後ろに書けば開けます。
しかし調べると基本的には使わない方がいいようで、httpd.confに書いた方がいいらしいです。
サーバーシステムのrootアクセス権限を持っていないときに使うべきものらしいです。

アクセスするとまだヒントがありました。
I love making websites on my Mac, I can Store a lot of information there.
今度はMac関係らしいです。

.DS_Storeというファイルでした。
Macシステムが書くディレクトリに勝手に保存するフォルダの設定ファイルらしいです。
確かにUSBに勝手に入っていましたが何だこれと思い消した記憶があります。
Macによるものだったんですね。

今回の問題はそれぞれ関連するものを知っていないと解けない問題でした。
ググってもあまり出てきにくい内容で、使ったことのある人があれなんじゃねって思い浮かぶような感じかなーと個人的に思いました。


あと、Bloggerで今書いているのですが、書きすぎて結構動作が重くなってきたのでまた新しい記事として続きを書いていこうと思います。
最初の方はpicoCTFの不満点をぐちぐち書いていましたが、今はなんだかんだハマっています。
これを読んでる人は既にpicoCTFをやっている人だと思いますが、ぜひ皆さんもpicoCTFを一緒に楽しんでいきましょう!
私も今度2022年にpicoCTFが開催されたら、今度はリアルタイムでやってみたいです。
そのときはテキストファイルに実況して、終了したらまたBloggerに載せたいと思います。
まあその時にやる気があるかはわからないですがね。。