---
# System prepended metadata

title: ping CTF 2023
tags: [ctf]

---

# ping CTF 2023

[toc]

## path-traversal-101

FLAGはいくつかのtaskをクリアすれば手に入りそうです。

`index.js`
```javascript=
        } else if (!session.task3) {
            const result = task3(solution);
            console.log("task3: " + result);
            if (result) {
                session.task3 = true;
                res.render("exam", {
                    task: FLAG,
                });
            } else {
                res.render("exam", {
                    task: tasks[2],
                    error: "Try again!",
                });
            }
        }
```


**ソースコードを眺めると、全てのリクエストの`User-Agent`　に`robot`と必要なことがわかります。**

`robot.js`
```javascript=
export default (req, res, next) => {
    const userAgent = req.get("User-Agent");
    if (userAgent == "robot") {
        next();
    } else {
        res.render("robot", { error: "🤖🤖🤖🤖🤖" });
    }
};

```

そして、フラグを取得するには、TASKとやらをこなしていく必要がある。

```javascript=
        if (!session.task1) {
            const result = task1(solution);
            console.log("task1: ");
            console.log(result);
            if (result) {
                session.task1 = true;
                res.render("exam", {
                    task: tasks[1],
                });
            } else {
                res.render("exam", {
                    task: tasks[0],
                    error: "Try again!",
                });
            }
        } else if (!session.task2) {
            const result = task2(solution);
            console.log("task2: " + result);
            if (result) {
                session.task2 = true;
                res.render("exam", {
                    task: tasks[2],
                });
            } else {
                res.render("exam", {
                    task: tasks[1],
                    error: "Try again!",
                });
            }
        } else if (!session.task3) {
            const result = task3(solution);
            console.log("task3: " + result);
            if (result) {
                session.task3 = true;
                res.render("exam", {
                    task: FLAG,
                });
            } else {
                res.render("exam", {
                    task: tasks[2],
                    error: "Try again!",
                });
            }
        }
```

条件自体はそこまで厳しくないのでやっていくだけ。

まず最初に、以下のURLにアクセスを行い、User-Agentをrobotに変更する。

```text=
https://path-traversal-101.knping.pl/%F0%9F%A4%96
```

![image](https://hackmd.io/_uploads/BkCBkQM8a.png)



Task1は、以下の条件。

```javascript=
export const task1 = (solution) => {
    preTask(solution);
    if (!solution.startsWith("/robot") || solution.endsWith("/flag")) {
        throw new Error(
            "You cannot access the flag!!! You are task1 UNAUTHORIZED!!! 🤖🤖🤖🤖🤖"
        );
    }

    const solutionPath = path.join("/", solution);
    return solutionPath === "/flag";
};
```


最初が`/robot`で終わる必要があり、`/flag`で終わってはいけなさそうなのでそれに従う。

```
/robot/../flag/.
```

![image](https://hackmd.io/_uploads/r11RJXGUp.png)

次はTask2

```javascript=
export const task2 = (solution) => {
    preTask(solution);
    solution = solution.replaceAll("../", "");
    if (solution === "/flag") {
        throw new Error(
            "You cannot ACCESS the flag!!! You are UNAUTHORIZED!!! 🤖🤖🤖🤖🤖"
        );
    }

    const solutionPath = path.join("/", solution);
    return solutionPath === "/flag";
};
```

これは、`/flag`ならだめというものなので、`./flag`でよし。と思わせておいて、実は`preTask`関数に阻まれるためうまくいかない。

```javascript
const preTask = (solution) => {
    if (typeof solution !== "string") {
        throw new Error("Solution must be a string");
    }
    if (solution.length > 512) {
        throw new Error("Solution must be less than 512 characters");
    }
    if (solution === "flag") {
        throw new Error("Your solution can't be 'flag'");
    }
    if (solution === "./flag") {
        throw new Error("Your solution can't be './flag'");
    }
};
```

がこれもそこまできつい条件ではないので、

`././flag`にすれば良いことがわかる。

最後のTask3


```javascript
export const task3 = (solution) => {
    preTask(solution);
    if (solution.includes("../") || solution === "/flag") {
        throw new Error(
            "You CANNOT ACCESS the flag!!! You are UNAUTHORIZED!!! 🤖🤖🤖🤖🤖"
        );
    }

    const solutionPath = path.join("/", solution);
    return solutionPath === "/flag";
};
```

../が含まれていてはならず、`/flag`でもダメ。

先ほどと同じ値でバイパスができる。

これで`FAKE`フラグを取得可能なので、本番環境に打ち込めばﾖｼ

![image](https://hackmd.io/_uploads/SkaNZmGIT.png)


`flag`
![image](https://hackmd.io/_uploads/rkCa6ff8a.png)

## i-see-no-vulnerability

コードを読むと、画像にある文字を、出力するようなアプリケーションであることがわかります。


`visioedDict[uuid]`には、画像から読み取った文字が入ります。

```javascript
		const unsafe_text = visionedDict[uuid];
		if (unsafe_text === undefined) {
			return res.redirect("/");
		}
		const text = DOMPurify.sanitize(unsafe_text);
		const page = readFileSync("./templates/result.html", "utf8")
			.replaceAll("{{VISION_TEXT}}", text)
			.replaceAll("{{IMAGE}}", uuid);
		res.send(page);
```


textを、`{{VISION_TEXT}}`と置き換えますが、rsult.htmlをしっかり読まなかったせいで、当初、`Dompurify`のバイパスをしなければならないと考えていました。

`result.html`
```html
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Image {{IMAGE}}</title>
    <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" />
</head>
<body>
    <section class="hero">
        <div class="hero-body">
            <p class="title">I'm a visionary!!!</p>
            <p class="subtitle">I see...</p>
            <div id="vision">{{VISION_TEXT}}</div>
        </div>
    </section>
    <footer class="footer">
        <div class="content has-text-centered">
            <p><a href="/">Go back</a></p>
            <p>
                NSFW? <form method="post" action="/report/{{IMAGE}}"><input type="submit" value="Click here to report" class="button" /></form>
            </p>
        </div>
    </footer>
    <script>
        const text = "{{VISION_TEXT}}";
        if (text.length === 0) {
            vision.innerHTML = "<img src='/i-see-nothing.gif' />";
        }
    </script>
</body
```

ですが、`DOMpurify`のバイパスなどはせずとも、以下の箇所ですぐにXSSを発火させることができることに気づきます。

```htmlembedded
<script>
        const text = "{{VISION_TEXT}}";
        if (text.length === 0) {
            vision.innerHTML = "<img src='/i-see-nothing.gif' />";
        }
</script>
```

ここまで気づけばもうあとはやるだけです。

以下の画像を、アップロードあと、botに対して送れば終わりです。

![text_image](https://hackmd.io/_uploads/r1j-xB7I6.png)


## 復習

## 　pocket-app

**解けなかったので復習。**


## dont-be-alarmed

**解けなかったので復習。**
