This post documents the complete walkthrough of Pinky’s Palace: 1, a boot2root VM created by Pink_Panther, and hosted at VulnHub. If you are uncomfortable with spoilers, please stop reading now.

On this post


Pinky is creating his own website! He has began setting up services and some simple web applications.

Information Gathering

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

# nmap -n -v -Pn -p- -A --reason -oN nmap.txt
8080/tcp  open  http       syn-ack ttl 64 nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: 403 Forbidden
31337/tcp open  http-proxy syn-ack ttl 64 Squid http proxy 3.5.23
|_http-server-header: squid/3.5.23
|_http-title: ERROR: The requested URL could not be retrieved
64666/tcp open  ssh        syn-ack ttl 64 OpenSSH 7.4p1 Debian 10+deb9u2 (protocol 2.0)
| ssh-hostkey:
|   2048 df:02:12:4f:4c:6d:50:27:6a:84:e9:0e:5b:65:bf:a0 (RSA)
|_  256 0a:ad:aa:c7:16:f7:15:07:f0:a8:50:23:17:f3:1c:2e (ECDSA)

The web server always returns 403 Forbidden, no matter what I do, which is frustrating. Even when I pass the HTTP request through the proxy (squid), I still get the same response. An idea struck me when I went to the proxy at—I should be using the hostname instead of the IP address!


Now, in full pink glory.


Directory/File Enumeration

Now that I’ve gotten over the first hurdle, let’s use dirbuster to fuzz the directories/files. But first, we need to set up the proxy in dirbuster.


Next, use a bigger wordlist to maximize the chances of getting a hit.


After dirbuster has completed doing its thing, this is what I get.


Pinky’s Admin Files Login

This is the attack surface I see at http://pinkys-palace:8080/littlesecrets-main/.


The form on this page points to login.php and logs.php logs any failed login attempts. Here’s an example when I use the credential (admin:admin) to log in.



Notice logs.php shows three parameters (user, pass and User-Agent)? This calls for sqlmap, which can test these parameters for SQLi far better and faster .

SQL Injection

According to sqlmap usage wiki,

The HTTP User-Agent header is tested against SQL injection if the --level is set to 3 or above.

Similarly, we need to set up proxy for sqlmap to reach pinkys-palace. Armed with all the information that we’ve gathered so far, it’s time to construct the sqlmap command.

# sqlmap --level=3 --proxy= --data="user=admin&pass=admin" --url=http://pinkys-palace:8080/littlesecrets-main/login.php

Here’s the test result from sqlmap.



We have an injection point. Time-based blind SQLi as the name suggests, is time-consuming for enumeration because the technique is a lot like fishing—sqlmap throws out a bait and waits for a fish to bite to confirm its existence.

Moving on, we can now determine the tables in the database.


Let’s dump the users table from pinky_sec_db.


Let’s crack these hashes with John the Ripper and “rockyou”.

# john --format=raw-md5 --show hashes.txt

1 password hash cracked, 1 left

Low-Privilege Shell

I’m able to log in to pinkymanage’s account with the cracked password.


Ultra Secret Admin Files

I see ultrasecretadminf1l35 in littlesecrets-main during enumeration of pinkymanage’s account.


The file .ultrasecret turns out to be the base64 encoded version of the RSA private key as hinted by note.txt.

Hmm just in case I get locked out of my server I put this rsa key here.. Nobody will find it heh..

I place the decoded RSA private key in /tmp and change its permissions, but I still can’t determine who is the key owner because the information is not stored in the key.


Looking at /etc/passwd confirms the existence of pinky. Thank goodness!


Perhaps I can use the RSA private key to log in to pinky’s account, assuming /home/pinky/.ssh/authorized_keys has the corresponding public key? Well, let’s find out.



Privilege Escalation

I see adminhelper at the home directory and it has been setuid to root during enumeration of pinky’s account.


There’s an accompanying note as well.


I’m certain that we are looking at a classic stack buffer overflow as the following supports that suspicion.

Image shows ASLR disabled.


Image shows the stack is executable.


It’s fortunate that adminhelper is small and simple. This is how the disassembly of the main function looks like.


This certainly brought back fond memories of 32-bit Linux exploit development. I’m pretty excited to try my hands on 64-bit Linux exploit development. Notice the 64-bit registers (e.g. rax) and how arguments pass through registers instead of the stack?

I use scp to download a copy of adminhelper to my Kali VM where gdb and PEDA are available. PEDA will greatly assist in the exploit development such as finding the correct offset as well as presenting the disassembly context in color.

Here, I create a random pattern of 80 bytes and save it in buf. Why 80 bytes? Even though it’s optional, notice the 80 (0x50) bytes of space allocated in the stack? This is to make way for the destination buffer in strcpy().


Next, I run adminhelper with the supplied argument.


This triggers a segmentation fault.


Next, examine the string (“IAAEAA4A”) at the stack to determine the offset.


Verify that the offset is able to control the RIP register.


Even though the stack aligns along the 8-byte boundary, the return address in the stack is 6 bytes.


Now that we can control RIP with the offset at 72 bytes, we can place our shellcode in an environment variable and use the following code to determine the memory address of the environment variable, where the shellcode is. This will be our return address in the exploit.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char *ptr;

    if(argc < 3) {
        printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
    ptr = getenv(argv[1]); /* get env var location */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
    printf("%s will be at %p\n", argv[1], ptr);

Since we are using the environment variable to store our payload, the size of the payload shouldn’t be an issue. Having said that, I still prefer a minimalist approach and decide to use the shortest possible 64-bit shellcode (27 bytes) to execute /bin/sh.


Once we copy getenvaddr.c over with scp and compile it, it’s time to get the party going.


A perfectionist may argue that euid=0 is not a real root shell. Well, that’s almost trivial to fix.


Eyes on the Prize