root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat popcorn.md
// writeups

Popcorn

15 Mar 2017 · 36 min read · root access
Popcorn - maquina de Hack The Box

Executive summary — Popcorn is a 2009 box (Ubuntu 9.10) where reconnaissance is half the challenge: fuzzing against the IP produces dozens of false positives because Apache redirects any path to the popcorn.htb vhost with an unconditional 301. Repeating the fuzz against the correct hostname surfaces four real paths, among them Torrent Hoster, a 2007 application with open registration. Its "screenshot" upload function decides whether a file is an image by reading the Content-Type the client itself declares — lying in that header is enough to upload a PHP webshell and get code execution as www-data. With credential paths exhausted, the escalation to root comes from a kernel exploit (full-nelson, CVE-2010-4258 + CVE-2010-3849 + CVE-2010-3850) compiled directly on the target.

PlatformHack The Box
Operating systemLinux
DifficultyMedium
StatusRetired
Target IP10.129.59.215

Attack map

[Recon] popcorn.htb — Apache 2.2.12 redirects EVERYTHING to its default vhost
   │  ffuf by IP -> 30+ "findings", all fake (unconditional 301)
   │  ffuf against popcorn.htb -> 4 real paths: /test /rename /torrent /index
   ▼
[/test] unauthenticated phpinfo()
   │  kernel 2.6.31-14-generic-pae, PHP 5.2.10, i686 architecture
   ▼
[/torrent] Torrent Hoster (© 2007) — open registration, trivially OCR-able captcha
   │  hand-crafted bencode .torrent -> the infohash predicts the final path
   │  "Submit Screenshot" (upload_file.php) trusts the Content-Type the CLIENT
   │  DECLARES to decide whether a file is an image
   ▼
[RCE]  www-data  (PHP webshell at /torrent/upload/<infohash>.php)
   │  user.txt world-readable (0644); config.php and DB hashes lead nowhere
   ▼
[Kernel]  Ubuntu 9.10, 2.6.31-14-generic-pae i686 — unpatched since 2009
   │  full-nelson.c (CVE-2010-4258 + CVE-2010-3849 + CVE-2010-3850)
   │  compiled ON the target itself (local gcc), launched in the background (no TTY)
   ▼
[ROOT]  uid=0(root) -> root.txt

1. Reconnaissance

sudo nmap -p- --min-rate 3000 -T4 -Pn -oN scans/allports.txt 10.129.59.215
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
sudo nmap -p22,80 -sCV -oN scans/services.txt 10.129.59.215
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 5.1p1 Debian 6ubuntu2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 3e:c8:1b:15:21:15:50:ec:6e:63:bc:c5:6b:80:7b:38 (DSA)
|_  2048 aa:1f:79:21:b8:42:f4:8a:38:bd:b8:05:ef:1a:07:4d (RSA)
80/tcp open  http    Apache httpd 2.2.12
|_http-server-header: Apache/2.2.12 (Ubuntu)
|_http-title: Did not follow redirect to http://popcorn.htb/
Service Info: Host: popcorn.hackthebox.gr; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Dating the box from the versions. OpenSSH 5.1p1 Debian 6ubuntu2 + Apache 2.2.12 maps to Ubuntu 9.10 (Karmic Koala), from October 2009. The 1024-bit DSA host key confirms it: DSA was disabled by default in OpenSSH over a decade ago. A system this old turns a kernel exploit escalation from a last resort into a leading hypothesis — worth keeping in mind from the start, without skipping enumeration.

Two more notes from this output: "Did not follow redirect to http://popcorn.htb/" — work against the hostname, not the IP; this turns out to be the single most important detail of the whole recon phase. And "Host: popcorn.hackthebox.gr" — the box's original name, a historical leftover with no offensive use here.

sudo bash -c 'echo "10.129.59.215 popcorn.htb" >> /etc/hosts'
curl -s http://popcorn.htb/
<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
</body></html>

The default Apache page: all real content lives in subdirectories.

The fuzzing mistake: false positives from redirection. Worth documenting because the diagnosis is more instructive than the result.

ffuf -u http://10.129.59.215/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -t 25 -mc 200,301,302,401,403 -s
plugins
search
wp-content
installation
wp-includes
tmp
scripts
includes
bin
templates
user
libraries
js
media
themes
language
cgi-bin
images
admin
modules
xmlrpc
forum
contact
stats
... (thirty-odd results)

Thirty-odd results: wp-content, wp-includes, plugins, templates, libraries… Looked like a full WordPress or Joomla install. Before chasing any of it, an invented path was checked:

for p in plugins wp-content torrent test rutaquenoexiste12345 admin; do
  printf "%-22s " "$p"
  curl -s -o /dev/null -w "%{http_code} %{size_download} -> %{redirect_url}\n" "http://10.129.59.215/$p"
done
plugins                301 313 -> http://popcorn.htb/plugins
wp-content             301 316 -> http://popcorn.htb/wp-content
torrent                301 313 -> http://popcorn.htb/torrent
test                   301 310 -> http://popcorn.htb/test
rutaquenoexiste12345   301 326 -> http://popcorn.htb/rutaquenoexiste12345   <----
admin                  301 311 -> http://popcorn.htb/admin

"rutaquenoexiste12345" also returns 301. Every result was fake.

The mechanism. Apache is configured to redirect any request that arrives by IP to the canonical vhost popcorn.htb, before checking whether the path exists. The redirect is unconditional, so the server answers 301 to absolutely everything. Since 301 was in the fuzzer's accepted status codes, every dictionary word looked like a hit. The rule this yields: always include an invented control path in any fuzzing run. If it answers the same as the "findings", the whole result set is noise — a ten-second check that here saved chasing a WordPress install that didn't exist. Note also that response size varied (313, 316, 326…) precisely because the 301 body includes the destination URL, whose length depends on the path name, which rules out filtering by a fixed size.

ffuf -u http://popcorn.htb/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -t 25 -mc 200,301,302,401,403 -s
test
index
torrent
rename

Four results, all real. From 30+ fake hits to 4 genuine ones just by changing the request's Host header.

Triaging the findings.

for p in test rename torrent index; do
  echo "=== /$p ==="
  curl -s -o /dev/null -w "  HTTP %{http_code} size %{size_download}\n" "http://popcorn.htb/$p/"
done
=== /test ===     HTTP 200 size 47729
=== /rename ===   HTTP 200 size 95
=== /torrent ===  HTTP 200 size 11406
=== /index ===    HTTP 404 size 286

/test — a phpinfo():

curl -s http://popcorn.htb/test | grep -oiE "<title>[^<]*|PHP Version [0-9.]+|System </td><td class=\"v\">[^<]*"
<title>phpinfo()
PHP Version 5.2.10
System </td><td class="v">Linux popcorn 2.6.31-14-generic-pae #48-Ubuntu SMP Fri Oct 16 15:22:42 UTC 2009 i686

An exposed phpinfo() is a gift to an attacker (CWE-200). In one line it hands over: the exact kernel version (2.6.31-14-generic-pae), the architecture (i686 — meaning the exploit must be built for 32-bit), the PHP version, absolute filesystem paths and loaded extensions. The entire kernel-exploit selection later relies on this data, obtained before any code execution.

/rename — a rename API:

curl -s http://popcorn.htb/rename/
Renamer API Syntax: index.php?filename=old_file_path_an_name&newfilename=new_file_path_and_name

An unauthenticated API that moves arbitrary files sounds like a top-tier vector, but it runs as www-data, the same user we'll obtain by another route. It can't write into /home/george or any root-owned path, so it grants no extra privilege. Noted as a plan B in case the upload failed, and dropped once RCE landed via the other path: flashy, functional, and useless for escalation.

/torrent — the real application:

<title>Torrent Hoster</title>
<a href="http://popcorn.htb/torrent/login.php">...login.png</a>
<a href="http://popcorn.htb/torrent/users/index.php?mode=register">...register.png</a>
<a href="http://popcorn.htb/torrent/torrents.php?mode=upload">...link-upload.png</a>

Torrent Hoster (© 2007): login, open registration and file upload. That's the way in.

🔒 Clearance required

This content is Root Access only. Everything else on the site — the free tier, the whole public library — stays open.

See Root Access plans

No account? Create one free then upgrade from your console.