If the 1st command line argument ($1) is null (-z) then show the usage instructions using the $0 as the name of the script and terminates it. If not, just assign that argument to the “f” variable, needed for the main function.
Next comes the declaration of our sources and sinks, with some incomplete strings because they will be used just for a match. Sinks use sources, so if one meets another (without taking sanitizing functions into consideration), we will have a dangerous reflection of user controlled input somewhere into the generated HTML.
The GET, POST and REQUEST global variables are an obvious source of trouble if used by these sinks. But the SERVER ones which are presented below, has some quirks. Let’s assume as input the URL below (with injection crafted to break out of HTML attributes):
Although there are three SERVER sources in the list, four SERVER variables will be found by our script, returning the following:
$_SERVER[‘PHP_SELF’] – returns the current URL decodedhttp://domain/page.php/"><svg onload=alert(1)>
$_SERVER[‘PATH_TRANSLATED’] – returns file path on the system/var/www/html/page.php/"><svg onload=alert(1)>
$_SERVER[‘PATH_INFO’] – returns info between page name and query string (?)/"><svg onload=alert(1)>
$_SERVER[‘REQUEST_URI’] – returns the current URLhttp://domain/page.php/"><svg onload=alert(1)>
This last one, REQUEST_URI, doesn’t decode the URL special chars like double quotes (“) and less than sign (>), which is a problem since browsers make the request with these encoded. So, this variable will be only exploitable in a stored XSS scenario, intercepting and editing the browser request (or using another HTTP client) or if it’s decoded anywhere else in the PHP code before landing on a sink.
The xssam() function is responsible for combining sources and sinks to find the direct occurrences in the first and second “for” loops. The “a” variable is used to get the name of a var that is receiving one of the dangerous global ones (sources), providing one level of variable tracking to the script by means of the third “for” loop.
In the main code the script decides between a single file calling the xssam function or the recursive mode. This is done by using the folder provided in command line ($2) with the “-r” option (which takes the place of the file name) to feed the same function in another “for” loop.
./xssaminer FILE (for single file)
./xssaminer -r FOLDER (recursive, all .php files within folder)
XSSaminer is aimed at low-hanging fruits and very prone to FPs (False Positives) but with so few LoC (Lines of Code) it is “cost effective” if you consider the LoC/results rate.
As an example of the tool, screenshots below show the results of it fired against an WordPress theme, Rational Lite:
It finds 3 potential XSS in 2 different files, where in the 2nd one they are just FPs. However, the 1st one is confirmed:
After tracking the vulnerable code snippet across the theme files, with the great help of my teammate @MarcS0h from Sucuri Security, we were able to pop an alert in an unauthenticated ajax action callback with the dismiss_id GET parameter:
A 0-day was found.
P.S.: Added a “?” to the list of sinks to check for things like “<?= $_GET[‘p’]; ?>” which is also a valid reflection.