Seccon_Bignners_2022  参加

# Seccon_Bignners_2022
今回初めて本コンペに参加した。
しかし、1問も解けずに時間切れになってしまった。（Welcomeは誰でもできる）
それで、easyレベルの問題についての復習をするために本記事を記載した。（完全に自分用）

# 参考にしたサイトや解説


# Web問題
### 1.Util
問題のサイトに行くと以下のようなサイトが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/34e15a87-7b6e-4c81-856c-e935f342dc69.png)
とりあえず、localhost(127.0.0.1)のアドレスを入力してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/f45f5eb3-99aa-9929-1344-750b5e44628e.png)
pingの結果を返してくれている模様。
入力フォームがあるので、うまくコマンドインジェクションができそう。。。

メインのソースコードを見てみる。
```golang:util_main.go
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という変数にparam.Addressという変数をつなげて、pingのコマンドとし実行している。
つまり、param.Addressにコマンドを埋め込めれば目的のflagがゲットできそう。
ここでのparam.AddressはWebページからのフォームから入力されたデータに該当している。(詳しくはWebページのHTMLとmain.goを参照。パラメータのaddressに該当していることが分かる。)

そしたら、入力フォームにそれっぽいコマンドを入れてみる。
「127.0.0.1;ls /」とか
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/fc93f19f-d732-9fc9-9499-3797f47a4c6c.png)
無効なIPアドレスだと怒られました。
表示されているWebページのHTMLを見てみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/1fc09de2-5889-dafd-64d9-be636358a4aa.png)
しっかりと入力制限がかけられているおり、正しく入力されていない場合は「Invalid IP address」を返す制御をしている。
しかし、該当の処理はフロントエンド側(Webページ)で完結している感じがする、、、
（通信をしている痕跡がなく、メインのプログラムコードにも上記のような制御は存在しない）
つまり、リクエストをWebページからではなく別の方法で行えば、コマンドインジェクションが成立しそう。

正常に入力するとpingというリクエストが投げられているので、このリクエストをcURLでコピーしてターミナルで実行してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/c5ca4926-52ad-fe49-3c0a-af55a833ce46.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/9b2be568-ff68-bc51-f42a-1d0d1c0cff9e.png)
ちゃんと、Webページと同じ結果が確認できた。
次に、入力フォームのデータに該当する「address:127.0.0.1」の部分に先ほどのコマンドを入力してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/0e41d7ed-f688-a859-a8d8-bdac94eca3e4.png)
そしたら、ファイル一覧の中に「flag_A74FIBkN9sELAjOc.txt」というflagっぽいtxtファイルを発見。
中身をcatで見てみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/ffb3c22f-ecdd-e456-3ee7-8ee05c38dfe3.png)
flagをゲット

### 3.Gallery
問題のサイトに行くと以下のようなサイトが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/54807550-128b-719d-87a0-af381ac81932.png)
Plese Select!の部分がドロップダウンリストになっている。
pngを選択すると、それに該当する.pngファイルが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/4c26dff8-2f8c-1dd8-193f-06a2be995500.png)
ファイルをクリックすると、画像が表示される。
おそらく、ドロップダウンリストの選択肢にはないが、flagに関連する画像ファイルが用意されていると思われる。

とりあえず、メインのプログラムを見てみる。
```golang:main_1.go
func main() {
	r := mux.NewRouter()
	r.PathPrefix("/images/").Methods("GET").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir(DIR))))

	r.HandleFunc("/", IndexHandler)

	http.ListenAndServe(":"+PORT, middleware()(r))
}
```
どうやら、Httpリクエストをもらって、IndexHandlerとmiddlewareって関数で処理をしてから出力を返してる模様。

IndexHandler関数の中身を見てみる。
```golang:handlers_1.go
func IndexHandler(w http.ResponseWriter, r *http.Request) {
	t, err := template.New("index.html").ParseFiles("./static/index.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
		return
	}

	// replace suspicious chracters
	fileExtension := strings.ReplaceAll(r.URL.Query().Get("file_extension"), ".", "")
	fileExtension = strings.ReplaceAll(fileExtension, "flag", "")
	if fileExtension == "" {
		fileExtension = "jpeg"
	}
	log.Println(fileExtension)

	data := Embed{}
	data.ImageList, err = getImageList(fileExtension)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
		return
	}

	if err := t.Execute(w, data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
		return
	}
}
```
クエリのfile_extensionがflagだった場合は.jpegを返すような制御がされている。

さらに、次の処理でgetImageListという関数を呼び出して、file_extensionの内容に応じたfileを出力するように制御している。

```golang:handlers_2.go
func getImageList(fileExtension string) ([]string, error) {
	files, err := os.ReadDir("static")
	if err != nil {
		return nil, err
	}

	res := make([]string, 0, len(files))
	for _, file := range files {
		if !strings.Contains(file.Name(), fileExtension) {
			continue
		}
		res = append(res, file.Name())
	}

	return res, nil
}
```
getImageListでは、file_extensionの内容の中にファイル名に含めれているかを検証し、含まれていればそれに該当するファイル名を返している模様。

つまり、リクエストでfile_extensionにflagを入力しても.jpegしか表示されないが、f,l,a,gのどれかを入力すればflagに該当するファイル名のものを表示してくれるかもしれない。
とりあえず、file_extension=fでURLを入力してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/4af2458d-605c-aed4-71f1-519fe0df5f39.png)
flagぽいpdfが表示された。該当のファイルを開いてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/712f4e28-53a6-54e8-868a-b42049a6cffa.png)
全て「？」になってしまっている、、、

なんか暗号化されてるのかと思ったが、メインプログラムでIndexHandler関数とは別にmiddleware関数が呼ばれてたはず。
次に、middleware関数を見てみる。
```golang:middleware_1.go
func middleware() func(http.Handler) http.Handler {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
			h.ServeHTTP(&MyResponseWriter{
				ResponseWriter: rw,
				lengthLimit:    10240, // SUPER SECURE THRESHOLD
			}, r)
		})
	}
}
```
lengthLimitって変数が10240に限定されている。
変数の名前的に何かしらの長さの限界値を示しているよう。
他にlengthLimitが使われているところがないか探してみる。
以下の関数で使用されている。
```golang:middleware_2.go
func (w *MyResponseWriter) Write(data []byte) (int, error) {
	filledVal := []byte("?")

	length := len(data)
	if length > w.lengthLimit {
		w.ResponseWriter.Write(bytes.Repeat(filledVal, length))
		return length, nil
	}

	w.ResponseWriter.Write(data[:length])
	return length, nil
}
```
データの長さがlengthLimit以上であったら全て「？」に書き換える処理を行なっている。
つまり、一定以上のデータの長さよりデータを受け取れないようにしている。
したがって、データを分割してもらってから復元すれば良さそう。（やり方はわからんが、、、）

どうやらHTTPには範囲指定してデータをもらえる方法があるっぽい。
https://developer.mozilla.org/ja/docs/Web/HTTP/Range_requests
こんな感じで指定できるらしい。
```
curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023"
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/73ce2cf9-962a-ca7c-d115-7af66c6f5113.png)
いけたっぽい、これで0〜1023[bytes]まで抜き取れた。
ちまちま抜き取るのはめんどいから1回で10000[bytes]まで抜き取る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/e9c17395-342f-e524-803c-5735c65a5f9a.png)
2回で全て抜き取れた、データファイルは16084[bytes]だった。
（lengthLimitの10240より6000ぐらいオーバーしていた、、、）
分割した2つのファイルをcatでくっつける。
```
cat out1 out2 > out3
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/d0faef56-ec4b-dc63-b798-57d88cdb8e46.png)
くっつけたら、しっかりとpdfとして認識された。
開いてみると、flagが記載されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447413/cbab2aa2-b110-fed4-65c6-b371774b3fee.png)

## 最後
その他の問題の解説気が向いたら更新します、、、、