WaniCTF 2021-autumn writeup


WaniCTF 2021-autummに参加しました。
結果は88位、2216ポイントでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/ff7bb247-2972-da70-6c65-4608682fc46e.png)

WaniCTF 2021-springは271位で470ポイントだったので、結構解けるようになったなと思いました。特にhard問が解けたのは嬉しかったです。

Web全完出来なかったのだけが悔いですが....
これをバネに精進しようと思います。
来年は全完してやるんだから！

それでは、面白かった問題のWriteUpを書いていきます。


## Misc
### nearest
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/52a2d7bc-7b51-bc7d-665a-cd9b418fba91.png)
配布ファイル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/b92ae8ab-ac0a-0442-5bf8-334314fc3e9a.png)

みんな大好き、OSINT問です。
画像の中で気になるのは、「黄色い電車、駅前、商店街、さくら（というお店？）の看板、踏切」あたりでしょうか。

上記ワードで検索してみます。
画像タブを見てみると、それっぽいサイトがヒットしてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/80e3c0e9-c918-a2f2-00da-81065265c090.png)

どうやら尾道駅のようです。
https://www.ononavi.jp/onomichiya/search/detail.php?id=4126
ストリートビューで確認したところビンゴでした。

Flag{onomichieki}


## Web

### POST Challenge
問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/292bffea-d4d3-cf16-1079-4497c8cd23f4.png)
問題ページ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/8ce13394-1d7e-0dcb-3dd3-f7a5105ffa74.png)
配布コード（一部）

``` app.js
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var logger = require("morgan");
var expressLayouts = require("express-ejs-layouts");
var fileUpload = require("express-fileupload");

const crypto = require("crypto");
const fs = require("fs");

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(expressLayouts);
app.set("layout extractScripts", true);

app.use(
  fileUpload({
    limits: { fileSize: 1024 * 1024 },
  })
);

app.use(express.static("public"));

app.use(function (req, res, next) {
  if (res.locals.message == null) {
    res.locals.message = "";
  }
  if (req.method === "POST") {
    const debug = {
      requestHeader: req.headers,
      requestBody: req.body,
    };
    res.locals.debug = JSON.stringify(debug);
  } else {
    res.locals.debug = "";
  }
  next();
});

app.get("/", function (req, res, next) {
  res.render("index");
});

app.post("/chal/1", function (req, res) {
  let FLAG = null;
  if (req.body.data === "hoge") {
    FLAG = process.env.FLAG_PART1;
  }
  res.render("chal", { FLAG, chal: 1 });
});

app.post("/chal/2", function (req, res) {
  // リクエストヘッダのUser-AgentにどのブラウザでもついているMozilla/5.0がある場合のみFLAGを送信
  let FLAG = null;
  if (
    req.headers["user-agent"].includes("Mozilla/5.0") &&
    req.body.data === "hoge"
  ) {
    FLAG = process.env.FLAG_PART2;
  }
  res.render("chal", { FLAG, chal: 2 });
});

app.post("/chal/3", function (req, res) {
  let FLAG = null;
  if (req.body.data?.hoge === "fuga") {
    FLAG = process.env.FLAG_PART3;
  }
  res.render("chal", { FLAG, chal: 3 });
});

app.post("/chal/4", function (req, res) {
  let FLAG = null;
  if (req.body.hoge === 1 && req.body.fuga === null) {
    FLAG = process.env.FLAG_PART4;
  }
  res.render("chal", { FLAG, chal: 4 });
});

function md5file(filePath) {
  const target = fs.readFileSync(filePath);
  const md5hash = crypto.createHash("md5");
  md5hash.update(target);
  return md5hash.digest("hex");
}

app.post("/chal/5", function (req, res) {
  let FLAG = null;
  if (req.files?.data?.md5 === md5file("public/images/wani.png")) {
    FLAG = process.env.FLAG_PART5;
  }
  res.render("chal", { FLAG, chal: 5 });
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;
```
#### Challenge1

解法

```
$ curl -X POST -d "data=hoge" https://post.web.wanictf.org/chal/1
```

-X HTTPメソッドの指定
-d,--data POSTリクエストとしてフォームを送信する

#### Challenge2

解法

```
curl -X POST --header "user-agent: Mozilla/5.0"  -d "data=hoge"  https://post.web.wanictf.org/chal/2

```
-H,--header HTTPヘッダにヘッダーを追加、もしくは変更する

#### challenge3

解法

```
curl -X POST --header "Content-Type: application/json" -d'{"data":{"hoge":"fuga"}}'  https://post.web.wanictf.org/chal/3

```
JSONをPOSTするのでheaderにContent-Type: application/jsonを指定しています。

#### challenge4 

解法

```
curl -X POST --header "Content-Type: application/json" -d'{"hoge":1,"fuga":null}'  https://post.web.wanictf.org/chal/4
```
JSON形式であれば数字やnullも扱えるみたいです。
下記の記事を参考にしました。
https://www.tohoho-web.com/ex/json.html

#### challenge5

解法

```
curl -X POST -F data=@wani.png  https://post.web.wanictf.org/chal/5
```

Curlの使い方を復習することが出来ました。
-FオプションをつけるとMIME typeはmultipart/form-data、
-dオプションでは、application/x-www-form-urlencodedになるそうです。意識してなかったので勉強になりました。

### NoSQL
問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/2872059f-2d04-70e9-ebfc-313324dbbe0c.png)
問題ページ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/0acf60c8-da24-1487-d932-00b112629e8c.png)


配布ファイルを確認すると、MongoDBを利用していることがわかります。
また、タイトルからもNoSQL injectionでログインBypassするのだろうと推測しました。

MongoDBを使ったことがなかったので「MongoDBとは」から調べます。
こちらの記事が参考になりました。

https://qiita.com/saba1024/items/f2ad56f2a3ba7aaf8521

https://yoshinorin.net/2016/05/17/mongodb-basic/

上記を参考にすると、配布コードの下記部分でusernameとpasswordの検索を行っているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/24717f3b-bcd9-049c-14a9-4488a581ed6d.png)

mongoDBでは比較演算子が使えるので、今回は$neという演算子を使ってみます。
これは指定された値と等しくない値を選択します。
※neはnot equalの略称みたいです。

https://docs.mongodb.com/manual/reference/operator/query/ne/

下図のようにJSON形式でリクエストを送ることでFlagをゲットすることが出来ました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/8566241b-412d-c6d2-ae12-1f186dc3ef40.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/bb200187-dd1d-7b2c-d402-d5056b12967b.png)

NoSQL自体触ったことないのにインジェクションなんてできないよ。と思いましたがなんとかなりました。しかしNoSQLについてはガバガバなので、勉強しようと思います。

### traversal
問題 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/9b2c11f5-fae8-c1f1-1359-bf16d4ba7b23.png)
回答率 約10％

このタイミングでtraversalとくれば[あれ](https://www.jpcert.or.jp/at/2021/at210043.html)だろうなと考えました。

配布ファイルにはapacheとnginxのconfファイルとDockerfileがありましたので、確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/560aa9ed-b75a-f757-b11c-2416ac296b0c.png)

はい、例のバージョンですね。タイトルもtraversalですし、ほぼ間違いなくパストラバーサルの脆弱性をついてflagを獲得する問題だと考えました。

それっぽい単語でググってみると、みんな大好き[piyokangoさんの記事](https://piyolog.hatenadiary.jp/entry/2021/10/10/031834)を見つけました。

これによるとrequire all deniedが無効になっていると脆弱性による影響があるようです。

配布ファイルのapache.confを確認すると、当設定が無効になっているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/e7c15099-b12c-4f17-1380-fdb2944e0d40.png)

>AllowOverRide
>AllowOverRide は .htaccess で使用できるディレクティブを指定する為のディレクティブ

>Require
>ユーザーが指定の条件に従っているかをテストする
https://httpd.apache.org/docs/2.4/ja/mod/mod_authz_core.html#require


上記より、脆弱性をついてFlagが獲得できそうです。

```
/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd
```
というパスでリクエストを投げてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/fa686d2b-dfcc-916e-eef4-ac3e2ef1812f.png)

Badリクエストになってしまいました。
よく見るとnginxでエラーが出ているみたいです。

nginxをうまくバイパスしてくれる方法がないか探していたところ、いかにもという記事が見つかりました。
https://dev.classmethod.jp/articles/apache-cve-alb-nginx/

この記事によるとnginxは、
>/../の入力を見つけるとポインタを４つ分戻し、さらに次の/が見つかる>までロールバックする処理が入ります。
> 引用：https://dev.classmethod.jp/articles/apache-cve-alb-nginx/

という仕様になっているみたいです。
私はこのような仕様を知りませんでしたので、非常に勉強になりました。

上記を踏まえた上でリクエストを送付します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183260/4f788458-54b7-88ea-385b-33009807a3fe.png)

Flagを獲得できました！