Email Me Form

// TL;DR


The target

EmailMeForm bills itself as one of the web’s leading form and survey builders — you create forms, publish them, collect responses. Anywhere a product lets users author content that’s later rendered back (to themselves or to others), there’s a surface for stored XSS, and EMF turned out to have several.

XSS #1 — form title

Creating a form, I set the form title to an XSS payload rather than normal text. When the form rendered, the title field was output without encoding and the script executed. A direct stored XSS in the most basic field of the form.

XSS #2 — success message

Next I edited the form’s success message — the text shown after someone submits — and placed a script in it. When a visitor completed the form and reached the confirmation, the success message was rendered unencoded and the script ran in their browser. This one is worth noting because it executes against visitors, not just the form owner.

XSS #3 — uploaded file field

The third was the most interesting. I added a file upload field to my form, then filled the form out as a visitor and uploaded .xml and .html files containing script.

Back in the form’s manage tab, the completed submission appeared along with a clickable link to the uploaded file. Opening that link served the file inline — so the browser rendered my .html / .xml content in EMF’s context and the script executed.

The core mistake here is serving user-uploaded files inline instead of forcing a download. An uploaded .html file that the browser renders in your origin is effectively a stored XSS delivery mechanism.

Fix for this one: files should be served as downloads (with Content-Disposition: attachment and X-Content-Type-Options: nosniff), never rendered inline from the trusted origin.

The common thread

All three bugs are the same vulnerability wearing different clothes: user-authored content rendered back without context-aware output encoding. The title, the success message, and the uploaded file are all “input the user controls” that the application later “displays to someone” — and in each case the encoding step was missing.

This is the lesson that keeps repeating across XSS findings: it’s not one field you have to get right, it’s every place user input is ever displayed.

The fix

  • Encode all user-authored content for its output context before rendering — titles, messages, and every other field.
  • Never serve uploaded files inline. Force a download, set Content-Disposition: attachment, and send X-Content-Type-Options: nosniff so the browser won’t sniff and render them.
  • Serve user uploads from a sandboxed, cookieless domain so that even if something renders, it isn’t in the application’s trusted origin.

I reported these to EmailMeForm. They fixed most of them and thanked me; at the time of writing, one remained unfixed.

Thanks for reading — happy hacking!

Anas Mahmood

Web & API penetration tester and bug bounty hunter. I write up the bugs I find and the methods that found them.

Leave a Reply

Your email address will not be published. Required fields are marked *