To answer your three questions:
Fixing the properly escaped output:
Make your XSS patch the DOM so that the JavaScript you inject is removed from that particular tag. For example, you might prefix the injected XSS with a character such as a pipe (i.e. |
) to help distinguish the real name:
var nameHtml = document.getElementById('username').innerHTML;
var parts = nameHtml.split("\">|", 2);
document.getElementById('username').innerHTML = parts[0];
So, if your XSS looks like this:
">|<script>alert(1);</script>
The code above will strip out everything after and including the ">|
.
If you can't execute code on that page, can you do something else?
If you can still inject HTML into the page, but can't get the XSS due to a content security policy or something similar, then you could inject CSS to hide the username element entirely, using display: none !important;
so that it overrides everything. If it's a stored XSS and you can CSRF the submit, then that would save you the hassle too.
How would you prevent your payload from executing more than once?
There are many ways to do this, including:
- Check for the existence of an element with a known ID in the page - if it's found, don't run, otherwise create it.
- Check for the existence of a named attribute on an element that you know exists in the target page - if it's found, don't run, otherwise add it.
- Define a class or variable and check for its existence.
- If you're trying to prevent it running again on refreshes, set a cookie.