Detecting Cross Site Scripting vulnerabilities in web applications

Cross Site Scripting vulnerabilities allow attackers to spoof content, steal user cookies, and even execute malicious code on the user’s browsers. There are even advanced exploitation frameworks such as Beef that allow attackers to perform complex attacks through JavaScript hooks. Web pentesters can use Nmap to discover these vulnerabilities in web servers in an automated manner.

This recipe shows how to find Cross Site Scripting vulnerabilities in web applications with Nmap NSE.

How to do it…

To scan a web server looking for files vulnerable to Cross Site Scripting (XSS), we use the following command:

$ nmap -p80 --script http-unsafe-output-escaping  <target>

All of the files suspected to be vulnerable will be listed:

80/tcp open  http    syn-ack
| http-unsafe-output-escaping: 
|_  Characters [> " '] reflected in parameter id at http://target/1.php?id=1

The script output will also include the vulnerable parameter and which characters were returned without being filtered or encoded.

If you are working with a PHP server, run the following Nmap command instead:

$nmap -p80 --script http-phpself-xss,http-unsafe-output-escaping <target>

Against a web server with vulnerable files, you will see a similar output to the one shown below:

80/tcp open  http    syn-ack
| http-phpself-xss: 
|   Unsafe use of $_SERVER["PHP_SELF"] in PHP files
|     State: VULNERABLE (Exploitable)
|     Description:
|       PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities.
|     Extra information:
|   Vulnerable files with proof of concept:
|     http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
|     http://calder0n.com/sillyapp/secret/2.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
|     http://calder0n.com/sillyapp/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
|     http://calder0n.com/sillyapp/secret/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
|   Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=calder0n.com
|     References:
|       http://php.net/manual/en/reserved.variables.server.php
|_      https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
| http-unsafe-output-escaping: 
|_  Characters [> " '] reflected in parameter hola at http://calder0n.com/sillyapp/secret/1.php?hola=1

How it works…

The script http-unsafe-output-escaping was written by Martin Holst Swende, and it spiders a web server to detect the possible problems with the way web applications return output based on user input. Thescript inserts the following payload into all the parameters it finds:


The payload shown above is designed to detect the characters > " ', which could cause Cross Site Scriptingvulnerabilities.

I wrote the script http-phpself-xss to detect the Cross Site Scripting vulnerabilities caused by the lack of sanitation of the $_SERVER["PHP_SELF"'] script. The script will crawl a web server to find all of the files with a .php extension, and append the following payload to each URI:


If the same pattern is reflected on the website, it means that a page is using the variable$_SERVER["PHP_SELF"] unsafely.

The official documentation of the scripts http-unsafe-output-escaping and http-phpself-xss can be found at the following URLs:

There’s more…

The scripts http-unsafe-output-escaping and http-phpself-xss depend on the libraryhttpspider. This library can be configured to increase its coverage and overall behavior.

For example, the library will only crawl 20 pages by default, but we can set the argumenthttpspider.maxpagecount accordingly for bigger sites:

$nmap -p80 --script http-phpself-xss --script-args httpspider.maxpagecount=200 <target>

Another interesting argument is httpspider.withinhost, which limits the web crawler to a given host. This is turned on by default, but if you need to test a collection of web applications linked to each other, you could use the following command:

$nmap -p80 --script http-phpself-xss --script-args httpspider.withinhost=false <target>

We can also set the maximum depth of directories we want to cover. By default this value is only 3, so if you notice that the web server has deeply nested files, especially when “pretty urls” such as /blog/5/news/comment/are implemented, I recommend that you update this library argument by using the following command:

$nmap -p80 --script http-phpself-xss --script-args httpspider.maxdepth=10 <target>

The official documentation for the library can be found at http://nmap.org/nsedoc/lib/httpspider.html.

HTTP User Agent

There are some packet filtering products that block requests made using Nmap’s default HTTP User Agent. You can use a different User Agent value by setting the argument http.useragent:

$ nmap -p80 --script http-sql-injection --script-args http.useragent="Mozilla 42" <target>

HTTP pipelining

Some web servers allow the encapsulation of more than one HTTP request in a single packet. This may speed up the execution of an NSE HTTP script, and it is recommended that it is used if the web server supports it. The HTTP library, by default, tries to pipeline 40 requests, and automatically adjusts that number according to the traffic conditions, based on the Keep-Alive header.

$ nmap -p80 --script http-sql-injection --script-args http.pipeline=25 <target>

Additionally, you can use the argument http.max-pipeline to set the maximum number of HTTP requests to be added to the pipeline. If the script parameter http.pipeline is set, this argument will be ignored:

$.nmap -p80 --script http-methods --script-args http.max-pipeline=10 <target>

See also

  • The Detecting possible XST vulnerabilities recipe
  • The Detecting web application firewalls recipe
  • The Detecting SQL injection vulnerabilities in web applications recipe
  • The Detecting web servers vulnerable to slowloris denial of service attacks recipe

2 thoughts on “XSS

  1. ‘PHP_SELF’
    The filename of the currently executing script, relative to the document root. For instance, $_SERVER[‘PHP_SELF’] in a script at the address http://example.com/test.php/foo.bar would be /test.php/foo.bar. The __FILE__ constant contains the full path and filename of the current (i.e. included) file. If PHP is running as a command-line processor this variable contains the script name since PHP 4.3.0. Previously it was not available.

  2. http://coding-talk.com/f19/why-is-$_server%5Bphp_self%5D-unsafe-for-use-13998/
    PHP_SELF can be "category/simple.php/extra/bad/params/", instead of just "category/simple.php"
    Example: Consider:
    PHP Code:
      <form action=""> 
    The output suddenly becomes very alarming:
    The solution should also be obvious. Convert the user-supplied data to entities. The code becomes:
    PHP Code:
      <form action=""> 
    And an attack, as above, would be rendered:
    PHP Code:
    This still violates the assumption that the script name and path are the only data in $_SERVER['PHP_SELF'], but the payload has been neutralized.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s