Home



Blog


Burp suite


Burp intruder


Burp proxy


Burp spider


Burp sequencer


Burp repeater


Books


Misc



RSS




Search site




Blog

Monday, 5 May 2008

Null byte attacks are alive and well

Null byte attacks against web and other applications are nothing new. Later in this post, I will describe two cases that may nonetheless be unfamiliar to some readers.

Before that, a well-known example to illustrate the issue. Consider a Java-based application which displays an image file specified by the user:

String filename = request.getParameter("filename");

if (filename.endsWith(".jpg"))
{
File f = new File(filename);
...

Forget about path traversal attacks for the moment. The application seeks to ensure that the function can only be used to access JPEG files by checking that the user-supplied filename ends in ".jpg". If so, the filename is passed to the constructor for java.io.File, which opens the specified file.

The file extension check can of course be defeated using input of the form:

secret.txt%00.jpg

where %00 is a URL-encoded null byte. The reason the attack works is because of the different ways in which null bytes are typically handled in native and managed code. In native code, the length of a string is determined by the position of the first null byte from the start of the string - the null byte effectively terminates the string. In managed code, on the other hand, string objects comprise a character array, which may contain null bytes, and a separate record of the string's length.

When the above Java code excutes String.endsWith() on our input, it knows that the filename string is 15 characters long, and it checks whether the last four characters (after the null byte) match ".jpg", which they do. However, when the filename is processed by java.io.File, this operation is ultimately implemented within native operating system APIs in which a string's length is determined by the first null byte after the start of the string. So the filename which is ultimately processed is equivalent to:

secret.txt

and so the application's defence is defeated.

Interestingly, the corresponding code in ASP.NET:

string Filename = Request.Params["filename"];

if (Filename.EndsWith(".jpg"))
{
FileStream fs = File.Open(Filename, FileMode.Open);
...

is not vulnerable to the same attack. Our malicious input results in the following exception:

System.ArgumentException:
Illegal characters in path.

Before our input is passed to the native filesystem APIs, ASP.NET checks whether the managed string contains any invalid characters, including null bytes, and rejects the input if it does. So ASP.NET helps to protect developers against null byte attacks in this instance.

Null byte attacks against LDAP

LDAP is a protocol for querying directory services, and in the context of web applications is most commonly encountered in functionality for searching personnel directories etc. Suppose an application lets us search by name for employees in the Marketing department only. When we supply the input:

John

the application performs an LDAP query with the following filter:

(&(displayName=John)(department=Marketing))

When an LDAP filter combines multiple logical conditions, as here, the boolean operator comes at the start of the list of conditions, and applies to all of the conditions in the list. Hence, there is nothing directly analogous to the "or 1=1" attack in SQL injection. In a conjunctive filter (one employing the boolean AND operator, &) any additional conditions we inject are only able to further restrict the results that will be returned, and cannot undo the restrictions imposed by existing conditions.

On some LDAP platforms, it is possible to supply two filters back-to-back, the second of which is ignored. In this situation, we can use the following crafted input to circumvent the department restriction and view all entries in the directory:

(&(displayName=*))(&(1=1)(department=Marketing))

(See this paper for examples of this kind of attack in action.)

However, some LDAP services, notably Microsoft ADAM (Active Directory Application Mode) do not tolerate queries with two filters. Hence, you may hear it said that LDAP injection into conjunctive filters cannot be usefully exploited against ADAM directories.

This overlooks the possibility of null byte attacks, however. If our query is being passed to ADAM, we can use the following input to circumvent the department restriction and view all entries:

(&(displayName=*))%00)(department=Marketing))

This input does not result in a well-formed LDAP filter, when considered as a managed string. However, because the query is ultimately processed by ADAM in native code, the filter gets truncated to our injected null byte, and so the subsequent conditions in the filter are not processed.

Further, the ASP.NET APIs for querying ADAM, in the System.DirectoryServices namespace, do not block input containing null bytes, in the way that System.IO.File does.

Null byte attacks against XPath

The Web Application Hacker's Handbook includes an example of XPath injection against an application function which retrieves users' credit card numbers based on their username and password (see p316 onwards). If the XML datastore contains entries of the form

<addressBook>
<address>
<name>James Hunter</name>
<password>letmein</password>
<email>james.hunter@pookmail.com</email>
<ccard>8113 5320 8014 3313</creditcard>
</address>
</addressBook>


then a legitimate user's request for their credit card number will result in this XPath query:

/addressBook/address[name='James Hunter' and
password='letmein']/ccard

In the book, we described how an attacker could retrieve a list of credit card numbers for all users with input like:

/addressBook/address[name='James Hunter' and
password='' or 'a'='a']/ccard

and could perform various blind attacks to extract other information from the datastore one bit at a time, in the manner of Absinthe for blind SQL injection.

However, none of these attacks involved subverting the /ccard attribute in the application's query. Hence, the ccard data field was the only item we could retrieve directly, and we could infer other data, such as names and passwords, only by manipulating the query filter to return either a credit card number or otherwise, based on a controllable condition.

Again, what was overlooked here was the possibility of a null byte attack. In fact, we can supply the following input to effectively replace the ccard attribute with one of our own:

/addressBook/address[name='James Hunter' and
password=''+or+1=1]/password%00']/ccard

This technique enables us to fully subvert both the filter and attributes of the query, and directly retrieve data such as names and passwords which we could otherwise infer only using cumbersome blind techniques.

As previously, common managed APIs for performing XPath queries, such as those in the .NET System.Xml.XPath namespace, do not block input containing null bytes, and yet are ultimately implemented in native code in which our input gets terminated at the null byte.

Wednesday, 30 April 2008

Can you hit a moving target?

Despite slanderous reports to the contrary, I remained sober at Infosec last week long enough to hear a number of skilled sales professionals peddling their wares. In amongst the vulnerability scanners that can find all known and unknown bugs, and the identity management solutions that will put hackers out of business, was an unbreakable authentication mechanism - "unbreakable" because it employs a device that changes the user's password every 60 seconds.

Can you guess a password that changes every 60 seconds?

Some would suppose that you cannot, or at least that you would be highly unlikely to do so. The intuition here is that each time a password is generated, you will only have a few chances to guess it before it changes. If you don't guess it in that time (which is very unlikely), you are back to square one. The situation, it seems, stands in stark contrast to that of a static password, where you can continue guessing indefinitely until you are successful.

This intuition, however, is mistaken, and employing a rapidly changing password in itself does not add much to the security of an authentication mechanism. To see why, let's compare two mechanisms that are equivalent in all other respects.

Suppose that a "changing password" mechanism employs a device that generates six-digit decimal numbers for passwords, which is fairly typical. To keep things simple, let's also suppose that an attacker only has time to make a single guess at each password before it changes.

In the equivalent "static password" mechanism, let's suppose that each user has a six-digit decimal number as a password, and that this never changes.

First, our attacker targets the static password mechanism. There are 1,000,000 possible passwords, so his first guess has a one in 1,000,000 chance of success - very unlikely. Assuming this guess is wrong, he has eliminated one possible password, so his next guess has a one in 999,999 chance of success. And so on. After 500,000 unsuccessful guesses, the attacker has eliminated half of the possible passwords, and so his next guess has a one in 500,000 chance of success - still very unlikely. But, significantly, at the outset of the exercise, the attacker may expect that, on average, he will have guessed the correct password by this point. If there are 1,000,000 possible passwords, and you try half of them, you have a 50% chance of success. Half the time, you will have guessed the password by this point - the other half, you will need to continue guessing.

Next, our attacker targets the changing password mechanism. Again, there are 1,000,000 possible passwords, so his first guess has a one in 1,000,000 chance of success. Assuming this guess is wrong, he tries again. But because the password is regenerated, he has not eliminated any outcomes for the second guess, so his next guess still has a one in 1,000,000 chance of success, and this remains the same no matter how many unsuccessful guesses he makes. He appears to have very little chance of guessing the password - hence the intuition.

However, as we saw in the case of the static password, after 500,000 unsuccessful guesses the attacker still has only a one in 500,000 chance of success in his next guess; nevertheless, at the outset of the exercise he may expect to have guessed the correct password by this point. So we may ask: what is the corresponding point at which the attacker targeting the changing password mechanism may expect to succeed?

Each time the attacker tries to guess a changing password, he has a 999,999 out of 1,000,000 chance of guessing incorrectly. So, the probability that he will fail to have guessed the password after one attempt is 0.999999. The probability that he will have failed after two attempts is 0.999999 * 0.999999. And so on. At the outset of the exercise, the probability that the attacker will have failed after N attempts is 0.999999 ^ N (where ^ means "to the power of").

So in a head-to-head challenge, what is the probability that the attacker targeting the changing password mechanism will have failed after 500,000 attempts? It is 0.999999 ^ 500,000, which is 0.606. That's right, there is nearly a 40% chance that the password will have been guessed by this point. With a bit of maths, we can work out that at the outset the attacker may expect that, on average, he will have guessed the correct password after 693,147 guesses - this is the point at which 0.999999 ^ N falls below 0.5.

Clearly, the changing password mechanism fared better, on average, than the static password mechanism - but by how much? In our scenario, the dynamic password takes on average 39% more attempts to guess than the static password - a relatively modest difference, of the same order of magnitude, and nothing like enough to justify the "unbreakable" intuition, or the likely expense of the password-generating device.

Now, in the real world there would of course be more factors in play than in my simplified scenario. Users may choose alphanumeric passwords with a larger range of possible values; but they may choose them non-randomly. They may write down their passwords; equally, they may leave their device lying around. Either mechanism may implement defences to frustrate brute force attacks. I don't want to suggest that password-generating devices are pointless - in many situations they can play a beneficial role in conjunction with other controls like conventional passwords, biometrics and account lockout. But if you hear the claim "It changes so it can't be broken", think "snake oil" and head for the bar.

Thursday, 20 March 2008

XSRF and threat ratings

Readers who are relatively long in the tooth will remember the sweet, carefree days before the web was blighted by cross-site request forgery (XSRF). Like or loathe these vulnerabilities, they are here to stay, and as penetration testers we need to look for and report them.

One often overlooked aspect of the arrival of XSRF is that it obliges us to reassess the threat ratings associated with some other types of vulnerability. Here is one example.

Consider an application which contains a function for administrators to view a user's details:

https://myapp/admin/ViewUser.asp?UID=123

The UID parameter is inserted into a dynamic SQL query, which is passed to MS-SQL Server, and so the application is vulnerable to SQL injection. However, the ViewUser function is protected by robust access controls which prevent lower privileged users from accessing it. Only administrators, who are fully trusted in any case, can exploit the bug. In days gone by, we would have called this a low risk issue, probably worth fixing from a defence-in-depth perspective, but nothing to get excited about.

Enter XSRF. Consider a different application which implements a function for administrators to issue SQL queries directly to the database. This is done using URLs like the following:

https://otherapp/admin/doSQL.php?query=
SELECT+*+FROM+ORDERS

This function is wide open to XSRF. An attacker can create a malicious web page which, if viewed by a logged-in administrator, will perform arbitrary queries on the database. For example:

<img src="https://otherapp/admin/doSQL.php?query=
INSERT+INTO+USERS+(username,password,isAdmin)+
VALUES+('jlo','secrets',true)">

We might classify this XSRF vulnerability as a medium or (if we are feeling excited) high risk issue.

Now, each of the functions described enables a crafted request to perform arbitrary actions on the database - the only difference is that this is the intended purpose of the second function, and is the consequence of bad coding in the previous example. But from an attacker's perspective, it doesn't matter whether the application's behaviour was intended or not - a vulnerability is simply a condition that can be exploited for some illicit purpose. And the first vulnerability can be exploited via XSRF just as easily as the second:

<img src="https://myapp/admin/ViewUser.asp?UID=
123;+INSERT+INTO+USERS+(username,password,isAdmin)+
VALUES+('wade','congrats',true)">

Hence, regardless of the access controls protecting direct exploitation of the SQL injection vulnerability, the threat rating we assign to this issue should be at least as serious as that which we assign to the second vulnerability. The arrival of XSRF as a recognised attack vector obliges us to identify ways of exploiting some other bugs that we may previously have overlooked.

Friday, 14 March 2008

Book review: Ajax Security

Ajax Security, by Billy Hoffman and Bryan Sullivan, has several positive points to recommend it, and also some important caveats of which the reader should be aware.

The book is well written throughout, and clearly explains the technical subject matter with which it deals. The authors have a gift for imaginative and entertaining narrative, which is most evident in the accounts of hypothetical attacks against web applications that are described at various points. In the main, the book is well edited and contains a minimum of typos and other editorial glitches that are often the bane of technical computing books.

Several sections within the book stand out as being useful and informative. Chapter 8 discusses client-side storage mechanisms: notably Flash's Local Shared Objects, Mozilla's sessionStorage, and Internet Explorer's userData. Chapter 9 provides a useful overview of the Google Gears framework for offline Ajax applications, and Chapter 11 covers mashup applications. These sections will be of benefit even to many readers who already have a good grounding in web application security.

I have two broad complaints about the book. The first, and less significant, concerns the authors' determination to argue that Ajax represents a "fundamental shift" in web application architecture (p24), and that Ajax applications are "susceptible to far more dangerous security vulnerabilities than conventional" applications (back cover). Any author can be forgiven for stating that their subject is terribly important. But this polemical thrust appears in every chapter of the book, and frequently distracts from the interesting content which it punctuates. The argument is also ultimately unsuccessful in supporting its claims.

The authors' case boils down to two points. Firstly, Ajax applications contain more functionality than their pre-Ajax counterparts, other things being equal; more client-side wizardry requires more server-side code and interfaces to support it; and creating more functionality presents more opportunities to introduce vulnerabilities. Secondly, having more client-side code provides more information and clues about server-side functionality that can be attacked.

The observations about functional complexity are perfectly valid, but the differences identified between Ajax and conventional web applications are ones of degree, not of kind. For the most part, an Ajax application is inherently more vulnerable than a corresponding conventional application in the same way that any large and complex application is inherently more vulnerable than a simple one - it has more attack surface. This reading is borne out by the specific attacks discussed throughout the book. With the notable exception of JSON hijacking (which is unique to Ajax applications), all of the vulnerabilities described involve "old school" bugs like SQL injection and broken access controls, which can affect any kind of web application. What the authors' argument really demonstrates is that Ajax has led to insecurity because it provides developers with more opportunities to make familiar mistakes.

The argument concerning the greater transparency of Ajax applications is less convincing. The authors assert that "much of traditional web application security" relies on users being unable to see server-side code (p174) and that increased transparency is "probably the most significant and most dangerous" feature of Ajax applications (p391). It is no doubt true that if an application's functionality is insecure, then greater knowledge of that functionality can help an attacker. But, as the authors themselves acknowledge, applications cannot rely for their security on the obscurity of how their functionality works (p172). I would argue that developers should actively assume that an attacker fully understands how their application works, and create functionality that is robust against fully-informed attacks. All the authors' argument really shows is that an insecure Ajax application may leave more clues about where the holes are than a corresponding conventional application. Again, the conclusion that Ajax applications face "far more dangerous security vulnerabilities" is not supported.

My second broad complaint about Ajax Security, which is more significant, concerns the surprising number of technical errors in the advice that is offered to developers to avoid security vulnerabilities in their applications. Here are the most serious instances of defective advice within the book:

  • SQL injection. The main discussion of this topic (p102 onwards) begins with a demonstration that database stored procedures are not a panacea, with an example of a SQL injection vulnerability inside a stored procedure. The proposed alternative is input validation, which is described as a "general purpose strategy that solves all of these issues" (p105). After a few pages explaining how to do whitelist-based validation using regular expressions, the elephant in the corner speaks up: what about apostrophes? Of course, applications must often accept input which contains apostrophes and other syntax that can be used to perform SQL injection. The authors' answer is as follows: "The solution is to continue to refine the whitelist pattern. Consider limiting the number of words (the number of groups of alphanumeric characters delimited by whitespace characters). Consider limiting the number of apostrophes allowed". This is dangerous advice and would leave an application which followed it open to attack. In reality, arbitrarily complex SQL injection attacks can be performed using only a single apostrophe and no whitespace whatsoever. If you don't know how, consult Chapter 9 of WAHH for the full explanation.

It is undeniable that input validation is a key defence which applications should implement at every opportunity, but it has its limitations. My favourite example of this is close to home: a blog about web application security. The blogging application must allow the author and commenters to submit free-form input containing all kinds of potentially malicious syntax, precisely because they are discussing the subject of attacks against web applications. The core functionality of the blogging application is to store this input in a database and display it back to readers of the blog. The obvious attacks that must be defended against here are SQL injection and cross-site scripting. But any solution based on whitelist input validation is liable to break the application, preventing users from doing what they need to. The better solution is to handle user input in ways that are inherently safe: arbitrary input can be safely passed to the database using parameterised queries, and safely rendered back to readers using HTML-encoding. In the vast majority of cases, these defences are completely robust against SQL injection and XSS, even without any input validation being performed. (By the way, I make no claims about the security of this particular blogging application, because I didn't write it.)

Curiously, much later in the book (p261) parameterised queries are mentioned briefly as an effective remedy, in a discussion about client-side SQL injection into the Google Gears database.

  • Cross-site request forgery. To block XSRF attacks, developers are advised "to create a unique token and store that token in both server-side session state and in a client-side state mechanism like a cookie" (p76). But this recommendation achieves nothing - the primary cause of XSRF bugs is of course that browsers automatically submit tokens stored in cookies regardless of whether a request originates on-site or off-site. Again, an aside to a different discussion later in the book gets it right: "Placing the token in a cookie would be useless and completely defeat the purpose of sending it" (p430).

  • Cookie scope. It is wrongly asserted that cookies are not by default submitted to subdomains (p208), when in fact Internet Explorer always submits cookies to subdomains of their in-scope domain (regardless of the spec). Chapter 11 recommends that aggregator sites should place third-party widgets into IFrame jails residing in subdomains of the aggregator site's domain. The problem is raised of how to prevent a malicious widget stealing the user's session token for the aggregator site. It is wrongly advised that default cookie handling takes care of the problem: "by default, cookies set by site.com cannot be accessed by foo.site.com" (p323). This is mistaken: the mechanism proposed is indeed open to session hijacking by a malicious widget.

It is also wrongly asserted that the path scope of cookies defaults to the web root (p206), when in fact in all browsers (and indeed the spec) the scope defaults to the directory of the script which issued the cookie. It is also advised that an application can leverage path scope to prevent untrusted applications residing on the same domain from accessing its cookies (p242), without mentioning attacks that exist against this defence.

(If you don't believe me about the way browsers handle cookies with default scope, then download Burp, inject some cookies into responses from various paths and domains, and see for yourself.)

There is a certain amount of filler in the book. Chapter 10 ("Request Origin Issues") does not discuss same origin issues, but rather makes the simple points that web servers can't tell whether a request comes from a human clicking a link or from XmlHttpRequest, and that XHR is faster and less clunky at doing multi-stage attacks than other techniques. This message doesn't really merit an entire chapter, especially given that the book has another chapter specifically about XHR-based worms. Also, Chapter 14 ("Testing Ajax Applications") consists of a high-level description of a few vulnerability scanning tools, with minimal attention to Ajax. Given that the rest of the book can be summarised as "Lots of things can go wrong in Ajax applications", it would have been nice to see some actual testing methodology here - specifically, something to help a penetration tester who is familiar with conventional web applications and who one day says: "Ah, this application uses Ajax; what else do I need to do to test it properly?".

Despite the caveats described, I would recommend this book to a reader who already has a good grounding of web application security knowledge and who wants to learn more about Ajax. (Indeed, the relative size of my pro and con remarks is not in proportion to their relative significance.) The book's main strengths lie in good expositions of many Ajax-related technologies and frameworks, and in clear illustrations of how web applications (Ajax-based and otherwise) can be vulnerable. However, if you need detailed and reliable information about how to attack or defend Ajax applications, this may not be the book for you.

Tuesday, 11 March 2008

Web application security training - Black Hat Europe

Myself and Marcus (my co-author for The Web Application Hacker's Handbook) will be in Amsterdam later this month for Black Hat. As before, we'll be delivering the Web Application (In)security course. This covers practical techniques for attacking web applications, from the most basic hacks through to advanced exploitation methods. It is a roughly equal mix of presentations and hands-on lab sessions. Some highlights include:

  • exploiting SQL injection using second-order attacks, filter bypasses, query chaining and fully blind exploitation;

  • breaking authentication and access control mechanisms;

  • reverse engineering ActiveX and Java applets to bypass client-side controls;

  • exploiting cross-site scripting to log keystrokes, port scan the victim’s computer and network, and execute custom payloads;

  • exploiting LDAP and command injection; and

  • uncovering common logic flaws found in web applications.

Class numbers have just been extended due to popular demand, so sign up quickly if you'd like to attend. If you are in Amsterdam but not on the course, let me know and we can catch up.


Wednesday, 16 January 2008

John Heasman is blogging

John Heasman, one of my colleagues at NGS, has just started blogging. For anyone who doesn't know him, John is one of the most talented and inventive security researchers around, having reported numerous bugs in enterprise software products, and developed new ideas in areas such as rootkit research.

John is going to be talking about all kinds of software security, including webappsec topics like browser security and Java. He also shows his good education in his choice of blog title. I urge everyone to check it out.

Sunday, 6 January 2008

When good XSRF defence turns bad

Talk about cross-site request forgery bugs seems to be in vogue, with various explanations of what the vulnerability is, and how to avoid it. As awareness has increased, and more developers attempt to defend against XSRF, it is not uncommon to find cases where someone has followed a standard piece of advice, but achieves nothing in terms of preventing attacks.

An application is vulnerable to XSRF if an "important" user action is performed using requests all of whose non-cookie parameters can be determined in advance by an attacker. For example, in a banking application a user might perform a funds transfer using the following request:

POST /TransferFunds.asp HTTP/1.0
Host: mybank.com
Content-Length: 48
Cookie: sessid=191r309ru13d10219029r31r90f1re029e
FromAccount=current&ToSortCode=123456&
ToAccountNumber=12345678&Amount=1000.00&When=now

An attacker wishing to induce a victim to transfer funds to his account can forge a request containing all of the necessary parameters with the exception of the cookie containing the session token. If this request is initiated from a web site the attacker controls, at a time when the user is logged in to the banking application, then the user's browser will automatically add the cookie parameter, and so the funds transfer will be carried out.

Now, a common recommendation for preventing XSRF attacks is that "important" actions like funds transfers should be implemented in two steps. In response to the first request, the application sends a nonce (an unpredictable value) to the client, which is submitted as a parameter in the second request. The application verifies the nonce in the second request, and only performs the action if this is valid.

The thought behind this defence is that the two-step approach confirms that the action originated from an authentic user, and not from a third-party web site. Although code running in an attacker's web page can initiate requests to the bank, the browser's same origin policy prevents it from accessing the bank's responses, and so the attacker's code will be unable to retrieve the nonce required for the second request. However, given the vague way in which the defence is often characterised, a developer who isn't thinking for themselves may be forgiven for getting it wrong.

I recently came across an application which had previously been full of XSRF flaws. Developers had reimplemented numerous functions using two steps, by issuing and validating a nonce. However, to enhance usability, the second step was implemented as an HTTP redirect. So the preceding request returned a response like the following:

HTTP/1.0 302 Object Moved
Location: /TransferFunds2.asp?nonce=120491746317280

The user's browser follows the redirect, thereby submitting the nonce (together with the user's session cookie), which is validated by the server. But the defence achieves nothing, because the user's browser behaves in just the same way if the first request originated from a third-party web site. The fact that the same origin policy prevents the attacker's code from accessing the bank's responses is irrelevant because it does not need to - it just relies upon the browser to process and resubmit the nonce in the normal way. A lot of development time had been wasted.

For the nonce-based defence to be effective, the request in which the nonce is resubmitted must result from some informed user interaction. For example, instead of performing a redirect, the first response could display the details of the proposed transfer, with the nonce in a hidden form field, which is submitted using "confirm" or "cancel" buttons. Because code on the attacker's web page cannot access this response, it cannot parse out the nonce and resubmit it. If performing actions over two stages is undesirable for usability reasons, then the nonce can be placed into the original form used to initiate the action. Provided the application properly ties the nonce to the session in which it was issued, then (in the absence of another vulnerability) an attacker will be unable to determine all of the necessary parameters to the original request, and so the main prerequisite for an XSRF attack to get going is not fulfilled.

 

Copyright (c) 2007 PortSwigger. All rights reserved.