This post documents the complete walkthrough of Gemini Inc: 1, a boot2root VM created by 9emin1, and hosted at VulnHub. If you are uncomfortable with spoilers, please stop reading now.

On this post


Gemini Inc contacted you to perform a penetration testing on one of their internal servers. The server has a web application for employees to export their profile to a PDF. Identify any possible vulnerabilities with the goal of complete server compromise with root privilege. Provide the content of flag.txt located in the root directory as proof.

Information Gathering

Let’s kick this off with a nmap scan to establish the available services in the host.

nmap -n -v -Pn -p- -A --reason -oN nmap.txt
22/tcp open  ssh     syn-ack ttl 64 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)
80/tcp open  http    syn-ack ttl 64 Apache httpd 2.4.25
| http-ls: Volume /
| SIZE  TIME              FILENAME
| -     2018-01-07 08:35  test2/
| http-methods:
|_  Supported Methods: POST OPTIONS HEAD GET
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Index of /

nmap finds 22/tcp and 80/tcp open. The document root lists one directory test2 and this is how the site looks like in my browser.


There’s no need to fuzz the site for directories and/or files because the landing page has offered an important piece of information about the web application—it’s built on Master Login System.

Master Login System

If you’d watched the walkthrough video at the project page, you’d have gotten the default login credential without resorting to any brute-force attack; and it’s valid too.


The drop-down action is available after logging in as admin with credential (admin:1234).


Here’s the admin’s profile page.


Here’s the admin’s profile page in PDF.


I discover that I can access both the profile and export page without having to log in. This means that the export page (export.php) probably hardcoded the profile page (profile.php?u=1) for PDF conversion. Another interesting fact—export.php uses wkhtmltopdf for PDF conversion.


I also discover that Display name and Email are not validated in the user’s profile edit page (user.php). You can verify this from the source code of user.php.

$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))

This opens up the web application to vulnerabilities, such as cross-site scripting (XSS), iframe injection and server side request forgery (SSRF). And you know what’s the best part? It’s reflected on the profile page (profile.php?u=1) and by extension, the export page (export.php).

Simple XSS test.




Issue #3570

SSRF refers to an attack where an attacker is able to send a crafted request to trick a vulnerable web application to perform an unanticipated action.

In this case, we’d like to trick the web application to read local files such as /etc/passwd that we weren’t able to, expose through PDF using wkhtmltopdf.

After scouring through the issues in the wkhtmltopdf GitHub project, I found issue #3570SSRF and file read with wkhtmltoimage. In another stroke of luck, I found this page (by googling for “wkhtmltoimage ssrf”) that shows you how to exploit issue #3570. Although parts of the page were in Indonesian, the idea was so clear, it doesn’t require translation. :wink:

It goes like this—wkhtmltopdf follows 302 redirection, captures the HTML, and turns it to PDF.

All we’ve to do is to host the following code as 1.php in our attacking machine.

     $file = $_GET['f'];

And put this in one of the injectable fields, e.g. Display name.

<iframe src="" width="100%" height=1220></iframe>

Simple? Let’s give it a shot.


Sweet. But how do we proceed from here? We can try brute-force attack on gemini1’s password. A more efficient way is to read SSH related files off the victim, such as authorized_keys and id_rsa.


There you have it—/home/gemini1/.ssh/authorized_keys. This is gemini1’s public key. I bet the private key (id_rsa) is in there as well.


Awesome. We can now copy and paste the private key to our attacking machine and log in to gemini1’s SSH account.

Low-Privilege Shell


Not too shabby.

Privilege Escalation

One of my favorite privilege escalation techniques is to target files setuid to root. If there’s a way to exploit such a file, we can become root.

Let’s look for such files.


Notice how the modification date/time of listinfo stands out from the rest?

Let’s run listinfo and see what’s the output.


From what I can make of it, it appears to be the output of ifconfig, netstat and current date.

Let’s look for strings in listinfo.


It’s evident the output of listinfo is the result of running the commands highlighted above.

Now, notice that date has no full path? If we change the search path $PATH and upload a malicious date, one that spawns a shell, then running listinfo escalates our privileges to root.

The following C code date.c allows us to setuid and setgid as root, and spawn a shell.

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main() {

First, we use scp to upload the malicious date.c to gemini1’s home directory from our machine.

# scp -i /root/keys/gemini1 date.c [email protected]:/home/gemini1

Next, we compile it.

$ gcc -o date date.c


Lastly, we alter the search path $PATH in gemini1’s shell such that invoking date will run the malicious date instead.

$ export PATH=/home/gemini1:$PATH


Running listinfo gives us this.


The pesky output from listinfo is still there. Let’s do what I always do: generate the SSH key pair I control, upload the public key to /root/.ssh/authorized_keys, and log in with the private key.




I learned a great deal about SSRF from this VM.