ctf4b 2022 21 Util Write Up

# SECCON ctf4b 2022
## 21 Util Write up
クソ雑魚ナメクジエンジニアによるUtil の解法を...

フラグの場所
`Dockerfile` を見ると、`/` 以下に`flag_XXXXXXX.txt`という名前で保存されていることが分かる。

サーバーは `go` 言語で書かれている
```go
package main

import (
	"os/exec"

	"github.com/gin-gonic/gin"
)

type IP struct {
	Address string `json:"address"`
}

func main() {
	r := gin.Default()

	r.LoadHTMLGlob("pages/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "index.html", nil)
	})

	r.POST("/util/ping", func(c *gin.Context) {
		var param IP
		if err := c.Bind(&param); err != nil {
			c.JSON(400, gin.H{"message": "Invalid parameter"})
			return
		}

		commnd := "ping -c 1 -W 1 " + param.Address + " 1>&2"
		result, _ := exec.Command("sh", "-c", commnd).CombinedOutput()

		c.JSON(200, gin.H{
			"result": string(result),
		})
	})

	if err := r.Run(); err != nil {
		panic(err)
	}
}

```
ここ
```go
commnd := "ping -c 1 -W 1 " + param.Address + " 1>&2"
```
POSTリクエストの`Address`に入れたものをサーバーサイドでそのまま実行しているみたい。
実際に普通にブラウザから攻撃すると `Invalid IP address` とでてきて攻撃できない

フロントエンド部分ではじかれている気がする。

ということでHTMLはこう
```html
<!DOCTYPE html>
<html lang="ja">

<head>
  <title>Ping check</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>

<body>
  <div class="container">
    <section class="hero">
      <div class="hero-body">
        <nav class="breadcrumb" aria-label="breadcrumbs">
          <ul>
            <li><a href="#">Util</a></li>
            <li class="is-active"><a href="#" aria-current="page">Ping check</a></li>
          </ul>
        </nav>
        <p class="title">Ping check</p>
      </div>
    </section>
    <section class="px-5">
      <div class="field">
        <label class="label">IP address</label>
        <div class="control">
          <input id="addressTextField" class="input" type="text" name="address" placeholder="127.0.0.1">
        </div>
      </div>
      <div class="control">
        <button id="submit" type="submit" class="button is-link">check</button>
      </div>
      <div id="notify" class="mt-3"></div>
    </section>
  </div>

  <script>
    function send() {
      var address = document.getElementById("addressTextField").value;

      if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) {
        var json = {};
        json.address = address

        var xhr = new XMLHttpRequest();
        xhr.open("POST", "/util/ping");
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.send(JSON.stringify(json));

        document.getElementById("notify").innerHTML = "<p>sending...</p>";

        xhr.onload = function () {
          if (xhr.status != 200) {
            document.getElementById("notify").innerHTML =
              "<p>Request Error : " + xhr.response + "</p>";
          } else {
            document.getElementById("notify").innerHTML =
              "<pre><code>" + JSON.parse(xhr.response).result.replaceAll("\n", "<br />") + "</code></pre>";
          }
        };
      } else {
        document.getElementById("notify").innerHTML = "<p>Invalid IP address</p>";
      }
    }

    var init = function () {
      var btn = document.getElementById("submit");
      var popup = function () {
        send();
      };
      btn.addEventListener("click", popup, false);
    };

    window.addEventListener("load", init, false);

  </script>
</body>

</html>
```

特にJSの送信ボタンの動作を見てみる。(抜粋)
```js
<script>
  function send() {
    var address = document.getElementById("addressTextField").value;

    if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) {
      var json = {};
      json.address = address

      var xhr = new XMLHttpRequest();
      xhr.open("POST", "/util/ping");
      xhr.setRequestHeader("Content-Type", "application/json");
      xhr.send(JSON.stringify(json));

      document.getElementById("notify").innerHTML = "<p>sending...</p>";

      xhr.onload = function () {
        if (xhr.status != 200) {
          document.getElementById("notify").innerHTML =
            "<p>Request Error : " + xhr.response + "</p>";
        } else {
          document.getElementById("notify").innerHTML =
            "<pre><code>" + JSON.parse(xhr.response).result.replaceAll("\n", "<br />") + "</code></pre>";
        }
      };
    } else {
      document.getElementById("notify").innerHTML = "<p>Invalid IP address</p>";
    }
  }

  var init = function () {
    var btn = document.getElementById("submit");
    var popup = function () {
      send();
    };
    btn.addEventListener("click", popup, false);
  };

  window.addEventListener("load", init, false);

</script>
```

ふ～ん。エッチじゃん。特にフロントエンド側でバリデーションしているのがエッチ。(バックエンドでもバリデーションを行いましょう)

競技者としてはサーバーサイドではバリデーションしているのにバックエンドのリクエストはバリデーションはさんでいない事に注目。

## 攻撃
Burp suite とか OWASP ZAP とかで串はさんでリクエスト改ざんしてみましょう

攻撃コードは `127.0.0.1 && cat /flag*.txt` と予測
![Ping check - Google Chrome 2022_06_14 2_28_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465042/9aadfd59-59a5-560d-9429-7d67fe9206e1.png)

取れました
![zap_OTHER_quickstartlaunch_other_startPage__apinonce=2b2e3fd7461a57af& - Google Chrome 2022_06_14 2_30_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465042/c0d8118c-4e5c-002c-3e90-0ccc9f9d38e4.png)

プロキシはさんで解きましたが、curl で解く方法や、Postman で解く方法もあるみたいです。