When learning how to find, exploit, or prevent security vulnerabilities, it’s important to understand the root causes of the vulnerability and what actually makes an application vulnerable.
Today, let’s talk about an extremely common vulnerability, XSS, it’s mechanisms, and how you can spot it in source code.
How XSS happens
Cross-site scripting vulnerabilities, or XSS happens whenever an attacker can execute malicious scripts on a victim’s browser.
Applications often use user input to construct web pages. For example, a site might have a search functionality where the user can input a search term, and the search results page will include the term at the top of the results page. If a user searches abc
, the source code for that page might look like this:
<h2>You searched for abc; here are the results!</h2>
But what if that application cannot tell the difference between user input and the legitimate code that makes up the original web page?
Attackers might be able to submit executable scripts and get that script embedded on victim’s webpage. These malicious scripts can be used to steal cookies, leak personal information, change site contents, or redirect the user to a malicious site.
For example, if the application also allows users to search via URLs:
https://example.com/search?q=abc
If an attacker can trick victims into visiting this URL:
https://example.com/search?q=<script>alert('XSS by Vickie’);</script>
The script in the URL will become embedded in page the victim is visiting, making the victim’s browser run whatever code the attacker wants. This is called a “reflected XSS” attack.
Dataflow analysis
Before we go on to looking for reflected XSS in an application, there are a few code review concepts that you should understand: “sources
”, “sinks
”, and “data flow
”. In code analysis, a “source” is the code that allows a vulnerability to happen. Whereas a “sink” is where the vulnerability actually happens.
Take command injection vulnerabilities, for example. A “source” in this case could be a function that takes in user input. Whereas the “sink” would be functions that execute system commands. If the untrusted user input can get from “source” to “sink” without proper sanitization or validation, there is a command injection vulnerability.
Many common vulnerabilities can be identified by tracking this “data flow” from appropriate sources to corresponding sinks.
Signatures of XSS
The vulnerability we will be looking at today is XSS.
What are the sources and sinks of XSS? With all XSS vulnerabilities, we are essentially looking for user input being used in the server’s output that will be displayed back to the user.
Let’s start looking for reflected XSS candidates in an example application! We’re gonna fire up the analysis tool we are using today named Ocular
, and import the project that we are analyzing. We are analyzing a vulnerable Java application called Tarpit Java.
After I import the project, I like to run a command to make sure that I have the project properly loaded. I typically run cpg.method.name.l
for this purpose. This command will look for all the methods defined in the project, extract their names, and list them.
Our code was imported correctly! Let’s start hunting for a reflected XSS by looking for the sources
of an XSS vulnerability. The typical sources of an XSS vulnerability is palces where the application takes in user input. A good way to identify these locations is to look for identifiers (local variables, globals, and class members) that are of the type HttpServletRequest
. So we’ll filter the identifier list by it’s type name. With Ocular, you can do string searches with regex:
Here, we are essentially looking for identifiers whose type name contain the string HttpServletRequest
. This gives us a list of identifiers that contain input from HTTP requests.
We’ll define this as our source
.
Next up, next look for sink functions for XSS. This is typically anywhere the application displays output to a user, such as HTTP responses, prints to log files, and so on. To keep things simple, we can look for all calls to print
functions in the application. Specifically, we’re looking for the data getting passed in as arguments to print
functions:
Finally, we can tell Ocular to show all the places where a source
can reach a sink
in terms of dataflow, and pretty print the results:
Let’s look at one of these dataflows to verify the potential XSS! What the dataflow tells us here is that the request identifier on line 83 of insider.java
will eventually end up getting printed on line 87 of insider.java
as a parameter named x
:
When we go into the source code of Tarpit Java, you can see that the code is indeed leading to XSS.
String x = request.getParameter("x");
BufferedReader r = new BufferedReader(new FileReader(x));
while ((x = r.readLine()) != null) {
response.getWriter().println(x);
}
Static analysis is the most efficient way of uncovering most vulnerabilities in your applications. If you’re interested in learning more about ShiftLeft’s static analysis tools ShiftLeft CORE or Ocular, visit us here. And if you are interested in learning more about common vulnerabilities in web applications, check out our free course on the OWASP top ten.
By the way, you can watch the video version of this demo here.