| 4 min read
In several past articles, we have briefly touched on the concept of taint analysis. In this article, we will fill in the knowledge gaps regarding taint analysis which may have resulted from our previous references. On one hand, this concept is intimately linked with code representations used by some of the ML
-powered vulnerability detectors we have presented before, and on the other hand, it is well-complemented by symbolic execution, so we deemed it necessary to clarify this concept a bit.
Most of the OWASP
top 10 web application vulnerabilities arise because an attacker can inject some code into the application’s inputs which are then used to perform some action in the server. The classic example for this is the SQL
injection.
For example, this page from bWAPP
has an input where the user is supposed to write a movie name, which should contain only alphanumeric characters:
bWAPP movie search.
Some movies will occasionally have punctuation marks such as a dash or a question mark. Here is what this page does with the user input:
Adapted from bWAPP code.
$title = $_POST["title"];
$sql = "SELECT * FROM movies WHERE title LIKE '%" . $title . "%'";
$recordset = mysql_query($sql, $link);
The input is taken from the POST
request and pasted right into a SQL
query which is immediately performed and shown to the user in a table. If instead of an actual movie name, an attacker writes this in the box:
%' UNION SELECT id, login, password, email, secret,
activated, admin FROM users;#
Then the SQL
query becomes this:
SELECT * FROM movies WHERE title LIKE '%%'
UNION SELECT id, login, password, email, secret,
activated, admin FROM users;
And all the movies' information is retrieved, along with the users' login information:
Tainted!
The user input has become tainted, and hence the SQL
query is now taintedas well. In the context of taint checking the $title
input above is called a source, which is where the bad input and the possible injection is coming from.
What is the problem with tainted inputs? it depends on what is done with them, i.e., at the sink, where the input is consumed. As we have stated in past articles illustrating other injection- or taint-style, this can be avoided with input validation and sanitization. So basically, simply check that the input is valid and fix it if it’s not by removing dangerous characters or only allowing known good ones.
Taint analysis or taint checking consists of identifying all sources of potentially dangerous user input, all security-critical sinks such as system calls, process interactions, invoking shells, altering files, etc, and figure out if there is any sanitizers between each source-sink pair:
Taint analysis diagram via Coseinc.
Then, depending on whether this taint analysis is static (code) or _dynamic (runtime), the taint-checking tool should either report to the developer so they can fix the issues or avoid the execution of security-critical operations at the sink level based on data that has been tainted, respectively.
Dynamic taint analysis: the Perl approach
The Perl
programming language is well-known for having used taint analysis as early as 1989. It is one of its main built-in security features, as can be seen by browsing perlsec.
The Perl
approach to taint checking is simple:
- Treat every input as tainted.
my $name = $cgi->param("name"); # Get the $name from user input, tainted!
- Any line of code that contains a tainted variable implies that any assigned variables in that line are also tainted.
my $full = $name."@fluidattacks.com"; # Now $full is also tainted
- A tainted variable can only be untainted by _laundering it via regular expressions:
if ($full =~ /^([-\@\w.]+)$/) {
$full = $1; # $full now untainted
}
- No tainted variable can be used in any risky command, such as invoking a sub-shell, opening files, interacting with system processes, etc. That’s the real run-time protection. Thus the following SQL
query
would fail:
$dbh->execute("SELECT * FROM users WHERE email = '$full';");
All a user needs to do to enable taint mode in Perl
is add the -T
switch when running from the command line or in the case of executable scripts, such as CGI
scripts (a common use case for Perl
), add that switch to the shebang
:
#!/usr/bin/perl -T
It is worth noting that, since Perl
is an interpreted scripting language, this taint mode is only a run-time protection which might not be bulletproof and also might block legitimate requests.
Static analysis: the PyT approach
PyT
is a static taint-checking tool for detecting security vulnerabilities in Python
web applications. More specifically, it was designed with Flask
applications in mind. It was developed as a Master's thesis project by Stefan Micheelsen and Bruno Thalman at Aalborg University.
We chose this as an example of static taint checking not for its results, but rather for the very well-written and easy to understand thesis that explains PyT inner workings and hence, the theory behind static taint analysis. Regarding the actual results component, I got 0 vulnerabilities in our own projects when using this tool and, curiously in the tiny but bug-ridden Damn Small Vulnerable Web application as well.
As you might now expect, taint analysis is linked to the flow of information inside the program, which can be more accurately represented by the program’s Control Flow Graph. They use as basis for a mathematical model known as a lattice, which has an interesting property, all monotone (steadily increasing or decreasing) functions defined on them have a fixed point, they eventually stand still. As it happens, code reachability and data flow can be represented in terms of equations on this lattice. These are guaranteed to have a solution by the fixed point property above. Here is a more friendly depiction of the process, in the author’s own drawings:
Overview of PyT’s process from [1].
The final step, of course, is reporting, so that the developer might take the appropriate measures to fix the taint vulnerabilities.
The idea in both incarnations of taint analysis is simple but powerful. Figure out the attack surface and make sure the tainted input can never reach what you are trying to protect. Following this simple idea will surely lead to more secure code. But if you are not sure, you can always give a taint-checking tool a try.
References
- Stefan Micheelsen, Bruno Thalman. PyT: A Static Analysis Tool for Detecting Security Vulnerabilities in Python Web Applications. MSc thesis
Share
Recommended blog posts
You might be interested in the following related posts.
Be more secure by increasing trust in your software
Users put their trust in you; they must be protected
Is your financial service as secure as you think?
We need you, but we can't give you any money
A digital infrastructure issue that many still ignore
How can we justify the investment in cybersecurity?
It's about time you relied on code-assisted pentesting