0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ctf4b 2022 21 Util Write Up

Posted at

SECCON ctf4b 2022

21 Util Write up

クソ雑魚ナメクジエンジニアによるUtil の解法を...

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

サーバーは 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)
	}
}

ここ

commnd := "ping -c 1 -W 1 " + param.Address + " 1>&2"

POSTリクエストのAddressに入れたものをサーバーサイドでそのまま実行しているみたい。
実際に普通にブラウザから攻撃すると Invalid IP address とでてきて攻撃できない

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

ということで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の送信ボタンの動作を見てみる。(抜粋)

<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

取れました
zap_OTHER_quickstartlaunch_other_startPage__apinonce=2b2e3fd7461a57af& - Google Chrome 2022_06_14 2_30_46.png

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?