DiceCTF 2021 - Missing Flavortext
問題概要
Hmm, it looks like there's no flavortext here. Can you try and find it?
missing-flavortext.dicec.tf
Downloads
index.js
開くとログイン画面になっており、SQL Injectionの問題であることは容易に想像がつきます。使用しているのはSQLite3。username=admin、password=(ランダムな文字列)でログインすることが目的です。
解説
制約
index.js
を見ると分かるのですが、以下の制約があります。
-
username
、password
は空ではいけない
-
username
、password
に'
が含まれてはいけない - クエリは複数行使用しているので、コメントアウトするなら
/*
を使う
このことから、想定出来る解き方は大きく2通りです。
- 制約の2つめを頑張って突破し、
username=' or 1=1 /*
等として解く - マルチバイトを利用してusernameの後ろにある
'
を別の文字として認識させる
2つ目はUTF-8とのことなので難しそうです。ということで1つ目の手法で、どこかに穴があるはず...!
body-parserにおける罠
さりげなくindex.js
に書かれているこの1文が今回の鍵です。
app.use(bodyParser.urlencoded({ extended: true }));
ここのextended: true
なんですが、公式ドキュメントを参照すると以下のような仕様が分かります。
A new body object containing the parsed data is populated on the request object after the middleware (i.e. req.body). This object will contain key-value pairs, where the value can be a string or array (when extended is false), or any type (when extended is true).
つまり、extended: true
によりusername
とpassword
は配列としても送ることが出来ます。例えば、username=["admin?' or 1=1 /*]
として送信すると、以下のような挙動をします。
- 普通に制約2つ目の
'
禁止は通過可能。usernameは配列なので、その1要素として"'"
が含まれないため。 - クエリに関しては、1要素の配列なので普通の文字列と同等の扱いを受けて
username = 'admin?' or 1=1 /*' and password...
といったクエリが作られる - 結果、SQL Injectionが成功して唯一のユーザーであるadminとしてのログインに成功
Chrome拡張である「Advanced REST Client」を用いて以下のようにクエリを送るとレスポンスが来ます。
- POST: https://missing-flavortext.dicec.tf/login
- username[] =
' or 1=1 /*
- password =
pass
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div>
<p>Looks like there was no flavortext here either :(</p>
<p>Here's your flag?</p>
<p>dice{sq1i_d03sn7_3v3n_3x1s7_4nym0r3}</p>
</div>
</body>
</html>
Discussion