NeverLAN CTF 2018 write-up (18/02/28更新)

23日から26日にかけて行われたNeverLAN CTFというオンラインCTFに参加しました。

neverlanctf.com

僕は去年12月にCTFの存在と、その楽しさを知り、CpawCTF、ksnctfといった常設のCTFに挑戦してきましたが、オンラインCTFとしてはこのNeverLAN CTFが初めての経験です。情報を学び始めたのは去年からで、主に基本情報処理技術者試験の勉強をしていたので得点は約10000pt満点中2841pt、順位はNon-Studentの参加者中で80位、というまだまだこれからな結果でした。しかしながらたくさん知ったことがありとても熱中して楽しくできたので、備忘録的にwrite-upを載せておきます。

高得点問題はほぼ解けていないので期待はしないでください(´;ω;`)

ヒントが解放されているので、でき次第更新していきます。

---Cryptography---

・I have a message for you

問題文には二進数が書かれている。これをASCIIに変換すると、

 Tmh5b2IgdmZzIGEgY2N5IGR2IHZqaGV6bXYgYnltdyBkZWNyIHhmZ3YgaCBkZmhwcmdjIHZvZXF0aiBkb21tIGdjIGhrcnNkZHcgcnIgemJ1IGdoa3FiIHB6IG5jbHhxZHd0cHVxcg==
が得られる。さらにこれをbase64デコードすると、
Nhyob vfs a ccy dv vjhezmv bymw decr xfgv h dfhprgc voeqtj domm gc hkrsddw rr zbu ghkqb pz nclxqdwtpuqr
となる。スキュタレー暗号かなと思いpythonでこの文字列を任意の太さの棒に巻き付けたときに読める文字列を出力するプログラムを書いてみたが、特に知っている単語はみられなかった。。
 
・Picture Words
換字式暗号。与えられる画像にはいろんな記号が記されているが、同じ記号に特定のアルファベットを割り当てて、できた文字列をhttps://quipqiup.com/に入力すると解読してくれる。
 
---Scripting---

・basic math

・more basic math

・even more basic math with some junk

下のpythonでなんとかなる。numbers.txtは数が並んでいるページの内容を写したもの。ただし三問目は、テキストエディタであらかじめ空白やコンマを取り外す。途中に数字ではない偽の"101"があるので注意。

sum=0
for line in open("numbers.txt","r"):
    sum+=int(line)
print(sum)

 

 ---Reversing---

※まだアセンブリ言語を目で追う程度しかできないので、この分野の更新はないです。

・commitment issues

ダウンロードしたものをバイナリエディタで開くともろにflagがある。stringsコマンドやcatコマンドでも見れるはず。CryptoでいうROT13みたいな典型的な入門問題?

 

---Interweb---

Ajax_not_sorp

Ajaxについては全く知らなかったが、「ソースを表示」させると、おそらくuser欄に入力された場合の処理を書いてある部分、26行目に

$.ajax('webhooks/get_username.php',{
とある。どうやらここからユーザ名をとって照合しているようなので、URLに/webhooks/get_username.phpを加えると、移動先のページにはMrCleanと書かれている。
そしておそらくuser欄に入力された場合の処理を書いてある部分、43行目に
$.ajax('webhooks/get_pass.php?username='+$('#name').val(),{
とある。先に得た"MrClean"も使いURLに/webhooks/get_pass.php?username=MrCleanを加えると、移動先のページにフラグが書かれている。
 
・the_red_or_blue_pill
移動先のページのURLにパラメータredまたはblueを付加したそれぞれの場合にのみページ上部に文章が表示される。よくわからなかったがページ内の文章の文意から「赤と青を一緒にやるな!!」というのが読み取れたので、パラメータをred && blueにすると、blueの場合の文章が表示された。そこでいろいろやっていたらflagが出てきた。今やってみるとなぜか出てこないのでもう少し研究します…
 
ajax_not_borax
MD5がなんとかと。先ほどのAjax_not_sorpと似ているが、ユーザとパスワードの照合部分がそれぞれソース上では

// For element with id='name', when a key is pressed run this function
      $('#name').on('keypress',function(){
        // get the value that is in element with id='name'
        var that = $('#name');
        $.ajax('webhooks/get_username.php?username='+that.val(),{
        }).done(function(data){ // once the request has been completed, run this function
            data = data.replace(/(\r\n|\n|\r)/gm,""); // remove newlines from returned data
            if(data==MD5(that.val())){ // see if the data matches what the user typed in
              that.css('border', '1px solid green'); // if it matches turn the border green
              $('#output').html('Username is correct'); // state that the user was correct
            }else{ // if the user typed in something incorrect
              that.css('border', ''); // set input box border to default color
              $('#output').html('Username is incorrect'); // say the user was incorrect
            }
          }
        );
      });
      // dito ^ but for the password input now
      $('#pass').on('keypress', function(){
        var that = $('#pass');
        $.ajax('webhooks/get_pass.php?username='+$('#name').val(),{
        }).done(function(data){
            data = data.replace(/(\r\n|\n|\r)/gm,""); // remove newlines from data
            if(MD5(data)==MD5(that.val())){
              that.css('border', '1px solid green');
              $('#output').html(data);
            }else{
              that.css('border', '');
              $('#output').html('Password is incorrect');
            }
          }
        );
      });

詳しく見ると、

if(data==MD5(that.val())){ // see if the data matches what the user typed in
if(MD5(data)==MD5(that.val())){
の行で照合している。そこで、まずuserを調べるために「検証」(ChromeではDev Tools)を開き、上の囲いの行にブレークポイントをおく。すると停止時点でdataの値にはc5644ca91d1307779ed493c4dedfdcb7という値が入っている。これはMD5ハッシュ値なのでMD5変換(MD5),MD5逆変換(MD5Reverse)にて逆変換を試みるとtideadeという結果が返ってくる。これがuserフォームに入力すべきusernameである。こんどはパスワードについて調べる。同様に下の囲いの行にブレークポイントを置いてpassword欄に文字を打つと、ブレークポイントで停止した時点でdataの値にはZmxhZ3tzZDkwSjBkbkxLSjFsczlISmVkfQ==が入っている。base64エンコードされているようなのでデコードしてみるとflagが出てくる。
 
・Das_blog
 ログインページのソース上部に次のコメントがある。このようにテストアカウントのメモをうっかり残してしまうことはときどきあるらしい。
<!-- Development test account: user: JohnsTestUser, pass: AT3stAccountForT3sting -->
 これでログインをすると、You are now logged in as JohnsTestUser with permissions userと表示される。
ここで、いまURLに/login.phpとあるのでそれを消去してログインページからトップページに戻ると、当初とは表示が異なっている。しかしながらJohnsTestUserは"user"権限であり、「特殊な権限」がないと投稿された内容を見ることができないらしい。そこで、cookieを確認すると、最後のほうにpermissions=userとある。それをBurpSuiteなどでpermissions=adminに書き換えてやって更新すると、admin権限のユーザとみなされ、flagが表示される。
 
 ・tik-tik-boom
Purvestaさんの地域の時間が表示されている。文章によると、23:59に何かが起こるらしい。23分59秒きっかりに何かするのは無謀なので、23時59分だろう。ソースを見ると、
<span hidden>username and password did not match: admin hahahaN0one1s3verGett1ngTh1sp@ssw0rd</span>
というspanがある。そしてusernameとpasswordという2つのcookieがある。おそらく23時59分にそのcookieをusername=admin,password=hahahaN0one1s3verGett1ngTh1sp@ssw0rdに書き換えてやって更新すると何かが起こる(未検証)。正解者が解いた時間もその23時59分に偏っていたのであっているはず。
(追記)何も起こりませんでした。。
だが、どうやら時間を書き換えて正解した強者がいるらしい。それができるのがプロだよなあ。
 
---Network---
・Fuzzy Packets
与えられたpcapngファイルをWiresharkで開くと、ICMPがずら~っと並んでいる。まずすべてのパケットのペイロード部分にはThis is not the flag you're looking forとあるので探索対象から外す。次に、これは2つのipアドレス間の通信なのでたとえばsourceを172.26.13.112、destinationを192.241.233.138でフィルタリングし方向を固定してみる。ここでいろんなパケットを見ると、値が変化しているのがチェックサム、そしてcodeの部分だけである。そしてそのcodeの部分は0,1,...と不規則に変化している。「この0と1を並べていくとasciiコードが出てくるのでは」と推理する。Linuxを起動し、以下のようにScapyで解析を試みる。使ったのはkali Linux 2018.1。

f:id:verliezer93764:20180228101451p:plain

ASCIIコードならば8bitのうち先頭bitが0なのであっていそう。このASCIIコードを文字に変換するとflagが手に入る。

 

---Passwords---

・Encoding != Hashing

与えられたpcapファイルをWiresharkで開くとたくさんのパケットが並んでおりやりづらいので、とりあえずプロトコル階層を見る。そこで極めて少ない数の通信しかない種類を確認すると、IPv6でのUDPIPv4でのSSDPDNS、HTTPなどがみられるが、まずはHTTPが怪しいとみる。HTTPでフィルタリングして少し探すと10464番のHTTPヘッダで認証している様子が見られる。詳しく見るとこれはBASIC認証で、認証の際入力したidやパスワードを暗号化せずに平文のままネットワークを通過させてしまう危険な認証である。そこでflagが得られる。

 

・Zip Attack

パスワードで暗号化されたZipファイルと、それに含まれているという1つのjpegファイルが渡される。

既知平文攻撃というものがある。これはある暗号化Zipファイルに入っているあるファイルAについて、それと同一の暗号化されていないファイルA(インターネット上に公開されているものも含む:以下「既知ファイル」)が存在すればその暗号化Zipファイルを開くことができるという攻撃である。

pkcrackは、それを行うツールである。使用方法は以下の通り。

./pkcrack -C [暗号化されたZipファイル] -c [暗号化されたZipファイル内にある既知ファイル] -P [(後述)] -p [既知ファイル] -d [出力zipファイル]
なお、ディレクトリ階層に注意。

f:id:verliezer93764:20180228110542p:plain

さて、上記の公式に則ってやってみると、エラーが出てきてしまった。サイズは同じなのに…?

調べると、「問題のzipファイルを上げた出題者のOSの圧縮方式と自分のOSの圧縮方式が同じでないといけない」らしい。ためしに問題Zipファイルと、自分で画像を圧縮して作ったZipファイルについて、zipinfoしてみた。

f:id:verliezer93764:20180228111210p:plain

defXとdefNで異なっている。これらが何なのかはもう少し調べたいが、これが原因だろう。

また、-Pオプションで、既知ファイルをZip化した自作のZipファイルを指定すると、これが解決できる、というような記事をみた。そして、Zipファイルを作る際に「圧縮レベル」を指定できるようだ。0が圧縮しないでただzip化するだけ、9が最高圧縮率だという。

 その圧縮レベルを調整すれば、問題ファイルと同じ圧縮方式ができるかもしれない、と思ってレベル別に10個のzipファイルを作った。すると、レベル9で作った自作zipファイルについて、上記の公式に-Pオプション指定したとき、うまくいった。

f:id:verliezer93764:20180228110652p:plain

 成功すると、パスワードなしの状態で、-dで指定したdecrypted.zipが生成され、そこにencrypted.zipの内容がコピーされている。flag.txtを見て終了。個人的には一番きつかった。

 

・The WIFI Network

与えられたpcapファイルは、WPA2-PSKの4way-handshakeの様子らしい。

ヒントを見てしまったが、hashcatというツールを使うと、このハンドシェイクの通信と辞書ファイルによってパスワードを割り出してしまうという。

まず、https://hashcat.net/cap2hccapx/でこのpcapファイルをhccapxという独自の形式に変換してもらう。

つぎに、kali linuxにはデフォルトでhashcatが入っていたので、次のようなコマンドを打つ。

hashcat -a 0 -m 2500 neverlan.hccapx [辞書ファイル]

 ただこの「辞書ファイル」が厄介で、/var/share/dictにあったデフォルトの辞書ファイルやJohnTheRipperで使ったそこそこ大きいはずのOpenwallのフリー版辞書ファイルでも結果は出ず、いろんなデモの動画で使われていたrockyou.txtという辞書ファイルでやっとパスワードが出た。時間がかかるので、解析中はほかの問題を解くとよいかも。

 

---Trivia---

大文字にする、略称などいろんな答えが期待できたのでおもったよりきつかった。How far can you go?というどこかでみたような問題がわからなかった。

 

---Blast from the Past---

唯一全部解けた問題。

Cookie_monster

移動先のページにはHe's my favorite Red guyと書かれており、Red_Guy's_nameというcookieにNameGoesHereという値が入っている。そこをElmoに書き換えるとフラグをゲット。

 

・Siths use Ubuntu (Part 1 of 3)

Ubuntuに侵入されたらしい。問題文には"You've got to figure out how they keep getting in even though we've changed the password."とある。まずは与えられたovaファイルをダウンロードしてVirtualBoxなどから開く。Things-I-should-doというテキストファイルがあるが、関係ないファイル(出題者がスターウォーズ好きなのが次の問題の答えと合わせてわかる)。

part2でもpart3でもログファイルを使うが、実際侵入されるときには改竄対象にされるらしいので注意。

 lastコマンドを使うと、最近のログイン履歴を見ることができる。

f:id:verliezer93764:20180228122551p:plain

このうち

kyrolen  pts/18  172.16.164.128 Sta Feb 25 12:52 - 17:24 (04:31)
が外部(172.16.164.128)からの不正侵入。
この時間に何があったかを/var/log配下のauth.log.1で確認すると、確かに12時52分に"Accepted password for kyrolen from 172.16.164.128"とある。その後、12:52:45にcatコマンドで/etc/crontabを覗いているのが気になる。cronは定められたコマンドを定期的に実行し、どの日時にどのcron(ファイル)を実行するかを記入するのがcrontabである。定期的に特定のプログラムを実行するように侵入者が設定しようとしているかもしれない。

f:id:verliezer93764:20180228125433p:plain

 crontabには、以下の内容が書いてあった。一番下のファイルは、5分毎に実行するようになっており、最も怪しい。

f:id:verliezer93764:20180228125803p:plain

そこで、/etc/init.d/rebelsを覗くとflagが書かれている。 

f:id:verliezer93764:20180228125816p:plain

シェルスクリプトについては知らないので、ここの内容はもう少し調べてみます。

nc.traditionalと-p 443から、何かを送信するのかなとは感じる。

 

・Siths use Ubuntu (Part 2 of 3)

かつてのパスワードを求めろ、ということでJohnTheRipperを使う。shadowファイルには提示されている/etc/shadow.backupを使う。

参考:/etc/passwdのクラックツール『John The Ripper』を使ってみた | 俺的備忘録 〜なんかいろいろ〜

 

・Siths use Ubuntu (Part 3 of 3)

問題文には"You've got to figure out how they broke in."つまりはどうやって侵入したかを問うている。認証関係のログファイルauth.log.1を再び見てみると、先ほどの12時52分の"Accepted password for kyrolen from 172.16.164.128"の上には大量のログイン失敗のログが並んでいる。そのなかにflagが紛れ込んでいた。ひょっとしたら1問目を解いているときに見つかるかもしれない。

f:id:verliezer93764:20180228141420p:plain