ブログの名前なんて適当で良いのでは

説明を求めるな、記事を読め

angstrom ctf 2018のwriteupと感想

Twitter上でとあるプロがWeb全完しているツイートをしていたので、僕もWebだけチャレンジしてみた。

Source Me1

HTMLを見るとパスワードが書いてあるので、普通にログインするとフラグ
actf{source_aint_secure}

get me

パスにauthパラメータがあるので、これをtrueにすればフラグ
actf{why_did_you_get_me}

Source Me2

HTMLを見るとJavaScriptでなんかのmd5値の比較があったので、そのハッシュ値をググったら、password1234がでてきた。これを使って普通にログインするとフラグ
actf{md5_hash_browns_and_pasta_sauce}

SEQUEL

適当にシングルクォーテーションを入れると以下のエラーがでる。

Warning: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given in /root/problems/web/sequel/deploy/source/public/index.php on line 5

よって、 ' UNION SELECT 1, 2 #-- とかやればログインできてフラグ
actf{sql_injection_more_like_prequel_injection}

MADLIBS

http://web2.angstromctf.com:7777/get-source からソースが見れる。 app.secret_keyflag.txtの内容を読み込んでいることがわある。 あとまぁ自明にSSTIだとわかるので、試しにAuthorNameに適当な入力を {{ 1 + 2}} とすると、3がHTMLに描画される。app.secret_keyとかは、app.configに辞書型として保存されているので、{{config}} を投げてみると一気にリークするので、その中にフラグ

(でもなんでapp.configじゃなくてconfigでいいんだろう・・・グローバル定義されているのかな・・・) actf{wow_ur_a_jinja_ninja}

md5 (writreupを見て理解)

ソースを見ると、入力は違うけど、salt付きmd5が等しい場合にFLAGを出力する問題

<?php  
  include 'secret.php';  
  if($_GET\["str1"\] and $_GET\["str2"\]) {  
    if ($_GET\["str1"\] !== $_GET\["str2"\] and  
        hash("md5", $salt . $_GET\["str1"\]) === hash("md5", $salt . $_GET\["str2"\])) {  
      echo $flag;  
    } else {  
      echo "Sorry, you're wrong.";  
    }  
    exit();  
  }  
?>

よくあるタイプのmd5比較かと思いきや、===で比較している。うーんと考えていて結局わからなかった。誰か教えて。 他の人writeupを見たら、PHPだと以下のような結合が可能なみたい。

php > echo "secret" . array();
secretArray
php > echo "secret" . array("x", "y", "z");
secretArray

なので、配列としてパラメータを投げてあげれば良い。

http://web.angstromctf.com:3003/?str1[]=a&str2[]=b

actf{but_md5_has_charm}

FILE STORER

アカウント作ってログインしてみると、URLを指定してファイルをuploadできる。色々見ても特にそれっぽい手がかりがないが、files以下で.gitとかにアクセスすると、\[Errno 21\] Is a directory: u'.git' とか出るので、存在が確定。 dvcs-ripperを使ってしゅっと取ってくる。

$ dvcs-ripper/rip-git.pl -u http://web2.angstromctf.com:8899/files/.git

取得してきたファイルの中ににindex.pyがあるので見てみると、最初のほうでadminユーザの作成を行い、そこでadminのパスワードがフラグだとわかる。

class user:
    def __init__(self, username, password):
        self.username = username
        self.__password = password
        self.files = []
    def getPass(self):
        return self.__password

users = {}

users["admin"] = user("admin", os.environ["FLAG"])

また、後ろの方に/user/usernameのルーティングがあることがわかる。

@app.route("/user/<username>", methods=['POST'])
def getInfo(username):
    val = getattr(users[username], request.form['field'], None)
    if val != None: return val
    else: return "error"

よって、/user/adminに対してPOSTで、fieldパラメータに_user__passwordを設定して投げればフラグ

$ curl -X POST -d "field=_user__password" http://web2.angstromctf.com:8899/user/admin

actf{2_und3rsc0res_h1des_n0th1ng}

the best website (時間内には解けなかった)

アクセスするととりわけ、いわゆる普通なページが表示される。ソースコードを見ると、コメント文でlog.txtとあるので、アクセスしてみると以下の様に出る。

Sat Aug 10 2017 10:23:17 GMT-0400 (EDT) - Initial website
Sat Aug 10 2017 14:54:07 GMT-0400 (EDT) - Database integration
Sat Aug 11 2017 14:08:54 GMT-0400 (EDT) - Make some changes to the text
Sat Mar 17 2018 16:24:17 GMT+0000 (UTC) - Add super secret flag to database

何をやったかとその時間が表示されている。一番最新のコメントからデータベースにフラグがあるとわかる。 そこで、データベースとの通信している箇所を探してみると、怪しい感じの通信がある。

http://web.angstromctf.com:7667/boxes?ids=5aad412be07e1e001cfce6d2,5aad412be07e1e001cfce6d3,5aad412be07e1e001cfce6d4

これのレスポンスが以下である。

{"boxes":[{"_id":"5aad412be07e1e001cfce6d2","data":"Go away.^This website has literally nothing of interest. You might as well leave.","__v":0},{"_id":"5aad412be07e1e001cfce6d3","data":"You will be very bored.^Seriously, there's nothing interesting.","__v":0},{"_id":"5aad412be07e1e001cfce6d4","data":"Please just leave.^Scrolling more will only give you more boring content.","__v":0}]}

要は、idを3つ指定してその要素がレスポンスされている。あまりデータベースに詳しいわけじゃないので、_idを検索してみると、Mongodbの記事がいっぱいヒットするので、Mongodbだとわかる。 次に、どうやってここからフラグを引っこ抜くかだが、指定したidの情報を返してくれるという機能があるので、フラグが格納されているデータのid情報をおくれば良いと考え、mongodbのid生成方法についてぐぐると以下の記事を見つけた。

http://toshitanian.hatenablog.com/entry/2014/02/14/015314

これを見ると、mongodbのidは以下のフォーマットになっているらしい。

4バイトの,[Unix](http://d.hatena.ne.jp/keyword/Unix)エポックからの経過秒数([Unix](http://d.hatena.ne.jp/keyword/Unix)時間)
3バイトのマシンID
2バイトのプロセスID
3バイトのカウンタ(開始番号はランダム)

最初のlog.txt情報からフラグをデータベースに追加した時間はわかり、5aad4131となる。次にマシンIDとプロセスIDはきっと同じだろうと予想して、カウンタの末尾1byteだけをブルートフォースするスクリプトを書いて回していたら、5aad4131e07e1e001cfce6d5のidで、フラグが降ってきた。

actf{0bj3ct_ids_ar3nt_s3cr3ts}