自作CTFを作ってみた
ある日 twitterをやっていたら、以下のような良記事が目に飛び込み
「ちょうど後輩もLinux使い始めるし、新しい講義で初めてHTML CSS JSを触るし、WEBサーバーとかやってみたいし、なんか作った実績欲しいし、一石四鳥だから自分もCTF作るか」となったので、ここに作るまでの手順と難しかったこと詰まったことを自分が忘れないように書こうと思った。
*間違えはできるだけなくすようにしていますが、素人なので間違っている可能性があるので間違ってたら指摘してもらえると嬉しいです。
やりたいこと
webサーバーの用意
本当は最近流行っているらしいDockerなるものを使ってみたかったけど、どうやら難しいらしいので辞めて、なんでもサーバーサイドでも使えるNode.jsというので実装しようと思った。
偉い人から「先ずはExpressというフレームワークでやってみたら?」とアドバイスを貰ったので「Node.js Express ログイン機能」と検索したら、とても参考になる資料があり、これでほとんどの機能を実装しました。
とても参考になりました、作った人どうもありがとうございます。
Node.jsで会員登録システムを導入しよう · osamu38/node-express-curriculum Wiki · GitHub
詰まったこと
- TypeError:Router.use() requires middleware function but got a Objectというエラーが出てきた
- MySQLのクエリがうまく動かなかった
まずroutes以下に新しいプログラムを置いて実行したら上のような謎の英語エラーが起きた、そこでgoogle先生にそのまま貼り付けて質問をしたら、stackoverflowがヒットし解決案の一つに
「module.exports = router;が足りてないのでは?」と書いてあり、試して見たら解決した。
よく見たら、参考元にもちゃんと書いてあったのに何故か自分は書いてなかったらしく、ただの凡ミスでした。
次のはMySQLを
let query = "SELECT * FROM hoge WHERE id = "+ user_id;
みたいに書くやり方が上手くいかなかった。
(たぶん自分の写経が悪かった可能性が高い、でも、このやり方だとSQLインジェクションの可能性があるかもしれないみたい?)
なので
let query = "SELECT * FROM hoge WHERE id = ?";
という疑問符プレースホルダを使ったら上手くいった、これだと値をエスケープしてくれるので安心。sqlmapで攻撃を試してみたけど大丈夫だった。
あと出来上がったサイトはこんな感じ流石に全部作るのは無理なので無料のホームページテンプレートを使わさせてもらった(欲深いので50000兆円になっています)
動的にグラフを作りたい
全員の現在のポイントを表示するグラフを作りたかったのでChart.jsというものを
使ってみようと思った。
公式サイト Chart.js · GitBook
CDNでローカルにダウンロードしなくても良いってのが嬉しいね、あと更新しなくていいのもうれしい。
目指す機能としては
- グラフの表示がデータによって変化するのでどうにかしたい。
- マウスが重なるとラベル名とデータ内容がでるので、それを少し変えたい
- サーバ側のjsのデータをクライアント側のjsに渡したい(この表現で正しいのだろうか・・)つまりMySQLのデータをroutes下のjsで持ってきて、それをpublic/javascripts下のjsに点数と名前を渡したい
上の1、2はoptionsを以下のようにして解決した。
options: { legent:{ }, tooltips: { callbacks: { //tooltipItemはマウスが上に乗った時の番地などを教えてくれる。 label: function (tooltipItem, data) { console.log(tooltipItem.datasetIndex); return data.datasets[tooltipItem.datasetIndex].label + ": " + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + " 兆円"; } } } , scales: { yAxes: [{ ticks:{ beginAtZero:true, max:5000, } }] }, title:{ display: true, text: "億万長者リスト" } }
3は最初にクライアント側のjsからmysqlに繋いでデータを取得して加工しようと考えていたが結局できなかった。(そもそも、クライアント側でmysql繋いだらクエリが丸見えだし色々危ないじゃないの?)
なので
を参考にroutes側で以下のような感じでデータを送ることで解決した。(命名がごっちゃになってるし、コードごちゃごちゃしてるのは雑魚初心者プログラマーなのでゆるして欲しいです(;^ω^)あと非同期処理あるのでpromise使いたかった)
let user_data=[]; router.get('/', function(req, res, next) { connection.query(query2,function(err, rows) { if (!err) { for(let x in rows){ let datum = {}; datum["label"]=rows[x].name; datum["data"] = [rows[x].point]; datum["backgroundColor"] = pattern(rows[x].point); user_data[x]=datum; } } let userId = req.session.user_id; if (userId) { let query = 'SELECT user_id, name FROM users WHERE user_id = ? LIMIT 1'; connection.query(query,[userId], function(err, rows) { if (!err) { res.render("point",{users:rows[0].name,chart:user_data}); } }); }else{ res.render('point',{chart:user_data}); } }); });
書いた人間にしかわからない部分があるので軽く説明させていただきますと
user_dataはChart.jsのdatasetsに渡す配列のデータ
patternは点数によって返す色を変える関数
datasetsは[{label:hoge,data:[100]},{...},]みたいな感じの形をとるので、最初のconnection.queryで全てのユーザーの名前と点数を持ってきてdatasetsに合うようにデータを作って
res.render('point',{chart:user_data});
res.render("point",{users:rows[0].name,chart:user_data});
のように"point.ejs"(グラフを表示するページ)にデータをchartという名前でおくることにした。
詰まったところ
非同期処理が分からなくてとにかく大変だった。最初は
let user_data=[]; let datum = {}; for(let x in rows){ datum["label"]=rows[x].name; datum["data"] = [rows[x].point]; datum["backgroundColor"] = pattern(rows[x].point); user_data[x]=datum; }
とdatumを外に置いていたら、最終的に生成されるuser_dataが変なデータで埋まっていた。たぶん非同期処理が関係しているのだろうと思い、datumをfor文内に置くことで何故か上手くいった。
結果できたのがこんな感じです。
SSHの環境設定
OverTheWireみたいにsshでつなげて問題を解かせたいと思った。
とりあえず仮想マシン(ubuntu)にapt-get install sshdをやって接続できるようにしたが、繋いだ人間には自分のホームディレクトリ以外のファイルを見られてくないと思った。そこでsshで繋いだ人間は制限するような方法がないか探したら、とても参考になるサイトを見つけた
http://tech.godpress.net/?p=361
この人の説明どおりにchrootの設定を行い、あとは/etc/ssh/sshd_configを編集しrootログインを禁止した。
参考になりました、ありがとうございます。
詰まったこと
1でsshなどでログインしたときに出てくるメッセージはmotdとか言うものらしく、それを弄れば良い /etc/update-motd.dに表示したいメッセージを書けばできる。
できた結果はこんな感じ、このロゴみたいなのはfigletというのを使った。
2はユーザーのホームディレクトリを"usermod -d /home/最初に作ったユーザー/次のユーザー/ 次のユーザー"のように 最初に作ったユーザーのディレクトリ内に置けばパスが同じ(?)なので共有できる。
3はタブキーを押すと補間してくれる機能が、ディレクトリの書き込み権限がないとかで無効になっていたので権限を777にしたら、できるようになったがルートディレクトリでも権限を777にしたらsshがbroken pipeとでてきて接続できなくなったので仕方なく権限を755などにした。
しかし、これだとファイルが消せてしまうので所有者しか消せないようにするスティッキービットを追加することで解決した。
感想とその他
肝心な問題の内容は後輩にbashとかちょっとしたものを教えたかったので
- rot13
- ls -a で隠しファイルを探す
- cat stringsなどで画像ファイルに埋め込んだ文字列を探す
- ちょっとしたBOF攻撃
- historyコマンドを使ったフォレンジック
- Exifで場所の特定
- POSTデータの改ざん
- pythonのlambda式を使って難読化
などの問題を考えた。ちなみにこのCTFは色々セキュリティがダメなので公開してないです。
感想としては、色々なサイトを参考にして作ったけど調べたら案外なんでも揃ってるなと思った、ただ自分の欲しい情報にたどり着くまでの道のりが長かった(Chart.jsのマウスがグラフの上に乗った時の表示を変える方法なんかは何て探せば良いのか分からず地味に探すのが大変だった)
あと自分の汚いコードを人が見られる場所に置くのは恥ずかしいと思った、今度からは綺麗で簡潔で命名規則も一貫したコードになるように頑張る。