Overview
Mirai is a retired Easy-rated Linux machine that teaches a lesson most
penetration testers learn the hard way: enumerate the operating system, not just
the application. The box presents a Pi-hole v3.1.4 admin panel with a login
form, API endpoints, and a queryads.php script that shells out to the
blocklist. I spent time testing each of these surfaces and found nothing
exploitable. The actual entry point is the host itself: default Raspberry Pi
credentials (pi:raspberry) that were never changed after Pi-hole installation.
Privilege escalation is immediate. The pi user has NOPASSWD: ALL in
sudoers, the Raspbian default. Root access takes one command. The root flag,
however, introduces a forensic recovery step. It was deleted from a USB stick
mounted at /media/usbstick/, and recovering it requires reading raw block
device data. On a 10 MB ext4 filesystem with no subsequent writes, strings
against the raw device is sufficient.
Three services, one real vulnerability, zero software exploits. The box rewards operators who check defaults before chasing web application bugs.
Reconnaissance
The target drops ICMP echo requests, so host discovery requires -Pn to skip
the ping probe and treat the host as up:
nmap -Pn -sV -sC -p- --min-rate 5000 10.129.14.196
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 6.7p1 Debian | Raspbian-era OpenSSH |
| 53 | DNS | dnsmasq 2.76 | DNS forwarder for Pi-hole |
| 80 | HTTP | lighttpd 1.4.35 | Pi-hole admin interface |
Three services. The SSH banner string contains Debian rather than Ubuntu,
and the lighttpd web server combined with Pi-hole strongly indicates a Raspberry
Pi running Raspbian. OpenSSH 6.7p1 maps to Debian 8 (Jessie), which reached
end-of-life in June 2020. The DNS service is dnsmasq, the lightweight forwarder
that Pi-hole wraps with its blocklist and web interface.
I chose a full port scan (-p-) with --min-rate 5000 because the three
visible services are too few for a machine rated Medium. There could be a
high-port service (a Docker API, a management console, a secondary web
application). The full scan confirms: there is nothing else. The attack surface
is genuinely limited to SSH, DNS, and HTTP.
Attack Surface Analysis
Pi-hole admin panel
lighttpd serves a Pi-hole v3.1.4 installation at /admin/. The login form
accepts a single password field with no username. I tested 14 common defaults:
pihole, raspberry, admin, password, 1234, changeme, and variations
with capitalisation and common substitutions. All returned HTTP 401.
Pi-hole’s authentication model is worth understanding. The admin password is a
SHA-256 double-hash stored in /etc/pihole/setupVars.conf. The login API
(?login) compares the submitted password’s double-hash against the stored
value and returns a session token on match. There is no username; the password
alone authenticates. This design means brute-force attacks need only one
dimension, but the double-hash (SHA-256 of SHA-256) slows offline cracking
negligibly compared to bcrypt or argon2.
The unauthenticated API at /admin/api.php returns operational statistics:
queries today, blocked percentage, top queried domains, top blocked domains. I
reviewed each endpoint (?summary, ?topItems, ?getQueryTypes,
?getAllQueries) for information disclosure. The data is interesting from a
network intelligence perspective (it reveals which domains the network’s clients
are resolving), but none of it contains credentials or exploitable paths.
queryads.php command injection surface
queryads.php accepts a domain parameter and passes it to pihole -q to query
the blocklist. This is a classic command injection candidate: user input flows
into a shell command. I tested standard injection payloads:
domain=google.com;id
domain=google.com|id
domain=$(id)
domain=google.com%0aid
domain=`id`
All were rejected. The domain input is validated against a strict FQDN regex
(/^[a-zA-Z0-9.-]+$/ or equivalent) before execution. The filter rejects
special characters, whitespace, null bytes, and URL-encoded variants. I also
tested double-URL encoding and Unicode normalisation bypasses; none succeeded.
The input validation is adequate for this context.
Pi-hole v3.1.4 CVE research
I searched for known vulnerabilities in Pi-hole v3.1.4. The closest match is CVE-2020-8816, an authenticated RCE in the DHCP static lease functionality, but it affects Pi-hole v4.3.2 and requires valid admin credentials. No unauthenticated RCE exists for v3.1.4. The web surface is a dead end.
SSH default credentials
With the web application exhausted, I turned to the operating system. The
Raspbian identification from the reconnaissance phase is the key observation.
Raspberry Pi OS ships with a well-known default user pi with password
raspberry. This is documented on the Raspberry Pi Foundation’s own website
and has been the default since the original Model B in 2012. The Foundation
added a first-boot password prompt in April 2022, but this box predates that
change.
Vulnerability Analysis
| Attribute | Value |
|---|---|
| CWE | CWE-1393 (Use of Default Credentials) |
| CVSS 3.1 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Root cause | Raspberry Pi default password unchanged |
| Impact | Full system access via SSH |
| MITRE ATT&CK | T1078.001 (Valid Accounts: Default Accounts) |
This is not a software vulnerability in the traditional sense. No buffer overflow, no logic flaw, no missing authorisation check. It is a deployment misconfiguration that is endemic in IoT and home-lab environments. Operators install Pi-hole for its DNS filtering capability and never harden the underlying operating system. The Raspberry Pi Foundation’s own telemetry (prior to the 2022 change) suggested that a significant fraction of installations retained the default password.
The CVSS score is 9.8 because the attack requires no privileges, no user interaction, and no special conditions. Anyone who can reach port 22 and knows the default credentials has full access. The only mitigating factor is network reachability, which is not modelled in the CVSS base score.
Exploitation
Initial access
ssh [email protected]
# Password: raspberry
Login succeeds. The Pi-hole application, its admin panel, and the
queryads.php injection surface were all irrelevant. I spent roughly 30
minutes on web enumeration before trying SSH defaults. The box is designed to
punish this ordering.
id
# uid=1000(pi) gid=1000(pi) groups=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),105(netdev),999(input),1001(spi),1002(i2c),1003(gpio)
The pi user is a member of the sudo group, which on Debian-based systems
grants sudo access by default. The adm group provides read access to
/var/log/, useful for post-exploitation intelligence. The gpio, spi, and
i2c groups confirm this is a Raspberry Pi (or emulating one).
cat /home/pi/Desktop/user.txt
User flag captured.
Privilege escalation
sudo -l
User pi may run the following commands on localhost:
(ALL : ALL) NOPASSWD: ALL
The pi user has unrestricted passwordless sudo. This is the Raspbian default
sudoers entry, configured during OS image creation. It exists because the
Raspberry Pi was designed as an educational platform where administrative
friction was considered counterproductive. In any deployment beyond a hobbyist
workbench, this is a direct path from user compromise to full root.
sudo su -
Root shell obtained.
Root flag recovery
The root flag is not at /root/root.txt. Instead, /media/usbstick/ is
mounted and contains a single file:
cat /media/usbstick/damnit.txt
Damnit! Sorry man I accidentally deleted your files off the USB stick.
Do you know if there is any way to get them back?
-James
I identified the underlying device and filesystem:
mount | grep usbstick
# /dev/sdb on /media/usbstick type ext4 (ro,nosuid,nodev,noexec,relatime,data=ordered)
fdisk -l /dev/sdb
# Disk /dev/sdb: 10 MB, 10485760 bytes
The device is a 10 MB ext4 filesystem mounted read-only. When a file is deleted on ext4, the filesystem zeroes the inode’s block pointers and marks the data blocks as free, but it does not overwrite the actual data. On a small, idle device with no subsequent writes, the flag’s data blocks are almost certainly intact on disk.
Full forensic recovery tools (extundelete, photorec, foremost) would work, but
on a device this small, strings against the raw block device is faster and
sufficient:
sudo strings /dev/sdb | grep -E "^[a-f0-9]{32}$"
This pipes the entire raw block device through strings, which extracts
sequences of four or more printable ASCII characters. The grep filter matches
exactly 32 lowercase hexadecimal characters on a line by themselves: the MD5
hash format that HTB uses for flags. On a 10 MB device, the false positive rate
for this pattern is effectively zero.
Root flag captured.
Why strings works (and when it does not)
The strings | grep approach succeeds here because of three conditions:
- Known target format. The flag is 32 hex characters. This is a highly specific pattern that almost never appears in random data or filesystem metadata.
- Small device. At 10 MB, the entire device fits in memory and processes in
under a second. On a 500 GB disk,
stringswould produce gigabytes of output and the grep would likely return false positives. - No overwrites. The filesystem is mounted read-only and no writes occurred after deletion. If the blocks had been reallocated and partially overwritten, the flag would be fragmented or destroyed.
Alternative recovery methods
extundelete: recovers files by scanning the filesystem journal for recently deleted inodes. On ext4, the journal retains metadata about deleted files for a period after deletion. This is the correct forensic approach when the target format is unknown or when recovering binary files.
sudo extundelete /dev/sdb --restore-all
photorec: a signature-based file carver that scans raw blocks for known file headers (JPEG SOI markers, PDF headers, ZIP local file headers). It ignores the filesystem entirely and works across filesystem types. Overkill for a 32-character text string, but essential for recovering structured files where the content is not printable ASCII.
debugfs: the ext2/3/4 filesystem debugger can list deleted inodes directly and dump their data blocks if the block pointers have not been zeroed.
sudo debugfs /dev/sdb -R 'ls -d'
Each tool trades precision for generality. strings is the least precise but
fastest. extundelete is filesystem-aware but ext-specific. photorec is
filesystem-agnostic but slowest.
Post-Exploitation
A compromised Pi-hole is a network-level threat, not just a host-level one. Pi-hole controls DNS resolution for every client configured to use it (often the entire LAN via DHCP). The post-exploitation impact extends far beyond the Pi itself:
- DNS poisoning: modify
/etc/pihole/custom.listor the upstream resolver configuration to redirect arbitrary domains to attacker-controlled IPs. Clients trust the DNS response implicitly. - Credential harvesting: DNS query logs (
/var/log/pihole.log) reveal every domain resolution on the network. This provides intelligence for targeted phishing: which banking sites, which email providers, which internal services the network’s users access. - Lateral movement: the Pi sits on the same subnet as all client devices.
SSH keys in
/home/pi/.ssh/, Wi-Fi credentials in/etc/wpa_supplicant/wpa_supplicant.conf, and any scripts or configuration files on the device may provide access to other hosts. - Persistent interception: unlike a compromised endpoint, a compromised DNS server affects all clients without touching their devices. The attacker maintains a man-in-the-middle position as long as clients use the Pi for resolution.
Defensive Analysis
Detection opportunities
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1078.001 | SSH login with default credentials (failed login audit trail) |
| Privilege escalation | T1548.003 | sudo to root from pi user (auth.log) |
| Collection | T1005 | Reads of raw block devices by non-backup processes |
| Persistence | T1565.001 | DNS configuration modifications (file integrity monitoring) |
SSH monitoring: fail2ban or a similar rate-limiter would not prevent this
attack (the credentials are correct on the first attempt), but logging
successful SSH authentications with the pi username post-deployment is a
strong signal. If the default password has been changed, the pi user should
ideally be renamed or disabled entirely.
Sudo auditing: the auth.log entry for sudo su - from the pi user is
distinctive. In a hardened deployment, pi would have restricted sudo (specific
commands only, with password required), and any sudo su attempt would trigger
an alert.
Block device access: any process running strings, dd, cat, or
hexdump against a raw block device (/dev/sd*) outside of scheduled backup
or forensic windows is anomalous. auditd rules on open() syscalls to
/dev/sd* would catch this.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Change the default pi password | Low | Critical |
| P0 | Remove NOPASSWD ALL from sudoers for pi | Low | Critical |
| P1 | Set a Pi-hole admin password | Low | High |
| P1 | Disable SSH password authentication (keys only) | Low | High |
| P1 | Rename or disable the pi user account | Low | High |
| P2 | Deploy fail2ban for SSH rate limiting | Low | Medium |
| P2 | Use full-disk encryption on removable media | Medium | Medium |
| P3 | Monitor for raw block device access (auditd) | Low | Low |
| P3 | Upgrade from Debian 8 to a supported release | High | High |
The root cause is cultural rather than technical. Raspberry Pi was designed as an educational tool where ease of use trumped security. The default credentials and passwordless sudo reflect that design priority. When operators repurpose the Pi as network infrastructure (DNS server, VPN endpoint, home automation controller), they inherit a security posture designed for a classroom. The remediation is not just changing a password; it is treating the Pi as production infrastructure with the same hardening standards applied to any server.
For the deleted file recovery vector, the defence is encryption at rest. If the
USB stick used LUKS or dm-crypt, strings against the raw device would return
only ciphertext. Full-disk encryption prevents data recovery after deletion
regardless of the recovery tool used.
Key Takeaways
-
Enumerate the operating system, not just the application. The Pi-hole admin panel dominates the enumeration phase and consumes time with its login form, API, and
queryads.phpinjection surface. The entry point is the host beneath it. When a scan reveals SSH alongside a web application, test default and common credentials before investing in web exploitation. -
Default credentials are the most reliable exploit class. No buffer overflow, no CVE, no complex chain.
pi:raspberryprovides root access (via passwordless sudo) on thousands of Raspberry Pi deployments worldwide. CISA’s Known Exploited Vulnerabilities catalogue does not track default credentials, but they account for a disproportionate share of real-world compromises. -
stringson raw block devices recovers deleted data. On small, idle filesystems where the target format is known,strings | grepis faster than full forensic suites. The technique fails on large disks (false positives), overwritten blocks (data destruction), and non-printable targets (binary files). Know the tool’s limitations before relying on it. -
Passwordless sudo eliminates the privilege boundary. The Raspbian default of
NOPASSWD: ALLfor thepiuser collapses user compromise and root compromise into a single step. Any hardening effort that leaves sudoers unchanged is incomplete. -
A compromised DNS server is a network-wide threat. Pi-hole controls name resolution for every client that uses it. The blast radius of this compromise extends to every device on the network, not just the Pi. DNS infrastructure should receive the same security attention as domain controllers and authentication servers.