Skip navigation

One-click Attack

Mitigate Web Security Pitfalls

asp:cover story

 

One-click Attack

Mitigate Web Security Pitfalls

 

By Eric Rachner

 

Imagine that you've been presented with this hyperlink: http://j-random-site.com. For the sake of discussion, let's put aside the particulars of how this link has come to your attention and focus on this one question: If you click that link, what's the worst that could happen?

 

There's the obvious potential for a barrage of unwanted frames popping up, under, and around your display almost as fast as you can close them. Or, your browser might display a security warning - but you know better than to override it.

 

You're probably also aware that the target of any given link might be a page which could exploit a cross-site script vulnerability in some other Web-based application. But cross-site script is so 1999. It's an old problem, and Web developers understand it well enough that we're generally able to click on strange links without much worry.

 

There are, however, other security pitfalls inherent to Web programming, and the purpose of this article is to dissect a particular attack that has gone without due attention. At Microsoft, we call it the "one-click attack."

 

The one-click attack is a technique whereby an attacker constructs a form and a query string, then causes somebody else's browser to submit them as part of a request to a Web application - possibly without the victim ever knowing what happened. The term "one-click attack" reflects that the victim only needs to be lured into following a link supplied by the attacker. This technique was first noted in 2000 by Jim Fulton, who called it a "client-side Trojan," and again in 2003 by Gavin Zuchlinski, who called it "client automation" (an especially apt description, perhaps, but also potentially confusing, since "client automation" has a different meaning in the context of COM programming).

 

The Basic Idea

To start with, let's contrive a simple scenario to illustrate the problem. Imagine a typical Web application on the Internet, with a page for users to update their e-mail address. (Since ASP.NET's ViewState functionality complicates matters slightly, this example uses classic ASP instead. We'll address the ASP.NET case in more detail later.) Figure 1 shows what the hypothetical e-mail update form might look like.

 


Figure 1: A typical e-mail update page.

 

Let's also imagine that this application, like many others on the Internet, offers a persistent sign-in option whereby cookies are used to spare users the trouble of manually signing in when they visit the site later. This implies that for the lifetime of the persistent cookie(s), all requests made by a given client will be considered pre-authenticated by the application.

 

Now that the stage is set, let's recall the link to http://j-random-site.com that was presented earlier and imagine that the link is part of an attempted one-click attack against a user of this application. Let's also suppose that the attacker has used a compelling social pretext to lure the victim to click the link.

 

Figure 2 shows the source code for the attacker's Web page. Unlike most malicious hackers, the author of this nasty page has thoughtfully commented his code.

 

BMW For Sale

1992 BMW 850

Black on black, leather interior.

Low miles, lovingly pampered all its life.

First $65.00 takes it.

 

  "http://some-vulnerable-app.com/UpdateEmail.asp"

name="ExploitForm" method="post">

  value="[email protected]">

  value="[email protected]">

  value="Update">

 

 

Figure 2: The attacker's malicious Web page.

 

As one can see, when the victim's browser loads the page listed in Figure 2, it will display what appears to be a simple, static page without a hint of anything suspicious. But within the invisible IFRAME, a form of the attacker's choice is submitted to the vulnerable application - by the victim's own browser! Figure 3 shows what the request might look like.

 

POST http://some-vulnerable-app.com/UpdateEmail.asp HTTP/1.1

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0;

  Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,

  application/vnd.ms-excel, application/vnd.ms-powerpoint,

  application/msword, application/x-shockwave-flash, */*

Accept-Language: en-us

Cookie: UID=johndoe; PWD=pass!word1; StaySignedIn=1

Content-Length: 84

Host: some-vulnerable-app.com

Referer: http://j-random-site.com

Content-Type: application/x-www-form-urlencoded

[email protected]&txt

  [email protected]&btnSubmit=Update

Figure 3: The HTTP request, which a victim's browser will send to the target application upon viewing the attacker's malicious Web page.

 

Notice how the victim's browser has supplied the persistent cookies to the server, which effectively pre-authenticates the request. (This example assumes that the user selected the "keep me signed in" option.) And notice that the server simply has no way of knowing whether the user ever saw the input fields, because the HTTP specification provides only that they should be expressed as name = value pairs, with no further information to describe them.

 

You might also have noticed how the only indication of this request's true nature is in the Referer field. Hold that thought, as we'll come back to it.

 

Now that the imaginary request has been submitted, how would the attacker determine whether the attack was a success? In this case, we can imagine that the application offers the typical "Forgotten password" facility, which the attacker can use to retrieve the victim's password by having it e-mailed to the new address.

 

Assumptions

Let's review what a would-be attacker needs to attempt a one-click attack. The first prerequisite is a social pretext that will lure the victim into clicking a hyperlink. One should take this as a given, since it only takes a little imagination to dream up any number of lures. But even more fundamentally, if computing is ever to be trustworthy, then we should all be able to click on unfamiliar hyperlinks without having to worry about our browsers being hijacked.

 

The attacker also needs a Web site to host the page that launches the attack - but not always. If the target application uses HTTP GET requests to do its thing, then the attacker has the option of using a bare hyperlink to launch the attack. If the vulnerable page in Figure 1 had used the GET method instead of the POST method, then the URL to exploit it might look like this:

 

http://some-vulnerable-app.com/UpdateEmail.asp?txtEmail1=

  [email protected]&txtEmail2=

  [email protected]&btnSubmit=Update

 

However, one hopes that cases like this are rare, since RFC 2616 very sensibly states, "... the GET and HEAD methods SHOULD NOT have the significance of taking an action other than [data] retrieval."

 

Finally, a would-be attacker needs sufficient knowledge of the target application to construct request data, which the application will accept. Generally, this means that the attacker needs to know the URL of the vulnerable page, the name of every form field and query string parameter in the page, and what sort of value is expected for each of them.

 

Limitations

In a sense, the one-click attack is a "write-only" technique. The attacker can use it to submit arbitrary requests, but not to view ensuing responses. This is because as soon as the request has been submitted, the frame containing it is in the domain of the target application. From this point onward, the browser's cross-frame security ensures that this frame can only be accessed by scripts that belong to documents from the same site, denying the attacker a means to directly view the results. To learn the outcome of a one-click attack, the attacker needs a way to observe the results indirectly, such as through the functionality of the application itself.

 

The attacker is also limited as to what parts of the victim's request he can control using this technique. The query string parameters and form fields are completely within the attacker's control, but cookies are out of reach. Some of the request headers, such as User-Agent, are also out of the attacker's reach, but others, such as Server and Referer, can be controlled to varying degrees.

 

A Sophisticated Attack

To show the full extent of what's possible with the one-click technique, let's contrive a more sophisticated attack scenario.

 

This article includes a mocked-up application called "Direct Deposit" (see end of article for download details). Its purpose is for the employees of Example.com to specify the bank account wherein their paychecks should be deposited. Because Direct Deposit is hypothetically deployed at http://DirectDeposit in a Windows-based intranet, it uses Windows-integrated authentication.

 

With most articles, the sample application is intended to serve as a positive illustration. That's not the case here. Direct Deposit is deliberately bad code and you are accordingly cautioned against borrowing from it unless you're sure you know what you're doing! However, Direct Deposit is functional enough to follow along as we dissect this attack.

 

Let's imagine an attacker, and in the tradition of security analysis, let's call her Mallory. Mallory is an unscrupulous, soon-to-be former employee of Example.com, and her intention is to use the one-click technique against the Direct Deposit application to divert the electronic paychecks of as many of her co-workers as possible to her own bank account.

 

Mallory's first step is to reconnoiter the Direct Deposit application. She observes that before a user can access the main page of the application, they must agree to the terms of use by posting a form to http://DirectDeposit/AgreementRequired.aspx.

 

This indicates to Mallory that her attack will require multiple form submissions: The first form will be posted to AgreementRequired.aspx so as to ensure that her victims are permitted to post requests to other parts of the application.

 

By viewing the HTML source for AgreementRequired.aspx (excerpted in Figure 4), Mallory observes that her bogus form must supply values for three input fields: btnSubmit, txtIAgree, and __VIEWSTATE.

 

  action="http://DirectDeposit/AgreementRequired.aspx"

  method=post encType=multipart/form-data>

  

    value=dDw5MDY0MjE3MTc7Oz52PvgxQDiXzYRwvr6AhlidJFUNrA==

    name=__VIEWSTATE ID="Hidden2">

  

    name=btnSubmit>

  

    value="I Agree">

Figure 4: The malicious form targeting AgreementRequired.aspx in the Direct Deposit application.

 

Supplying values for the first two of these input fields is straightforward, but because Direct Deposit uses EnableViewStateMac, Mallory can't simply fabricate a value for __VIEWSTATE. If she were to try it, ASP.NET would reject it along with the rest of the form in which it was embedded. But that's just a minor speed bump. Although Mallory can't fabricate a value for the __VIEWSTATE field, she knows that she can simply extract the one that was given to her by the server, and transfer it into the malicious form she's constructing. Because the transplanted ViewState blob was originally generated by the application server, Mallory knows it will pass the server's integrity check. (This implies that Mallory could not fabricate a form that ASP.NET would accept if she could not access the page herself. Of course, if she couldn't access the page, she would also have a much harder time of learning the names of the form fields it expects.)

 

Now that Mallory has constructed a form that will be accepted by AgreementRequired.aspx, she proceeds to the primary target of her attack: DDMain.aspx. Mallory observes that DDMain.aspx also expects three input fields: btnSubmit, txtAccountNum, and __VIEWSTATE. These input fields aren't substantially different from those in AgreementRequired.aspx, so constructing a form for DDMain.aspx is easy. The source for this form is listed in Figure 5.

 

  action=http://DirectDeposit/DDMain.aspx method=post>

  

    value=dDwtMjcyMTYzMzQyOzs+v8KEz44WadWz8i6jPCmTs6UeiAc=

    name=__VIEWSTATE id="Hidden1">

  

    name=btnSubmit>

  

    value="123000045-98765432">

Figure 5: The malicious form targeting DDMain.aspx in the Direct Deposit application. Note that the value for the txtAccountNum is the number of the bank account to which Mallory intends to direct the paychecks of her victims.

 

With her bogus forms ready to go, Mallory embeds them in a Web page such as BMWForSale.htm, which is included with this article. The critical part of this Web page is listed in Figure 6.

 

 

 

Figure 6: The script that Mallory's malicious Web page uses to submit the bogus forms.

 

All Mallory needs at this point is a site on which to host this page, and a convincing social pretext to lure her victims to it. For the sake of this example, we'll suppose that her Web page advertises a car for sale at an impossibly low price. Perhaps an animation of a dancing baby would have a wider appeal? There's always singing hamsters...

 

Whatever the bait may be, Mallory can tempt her victims by sending them a spoofed e-mail message, or by posting the link on the cork board next to the water cooler. When they visit her Web site, they won't see anything out of the ordinary, and nobody will be the wiser until pay day.

 

One-click Attacks and Other Authentication Methods

It's also important to understand how different types of authentication affect the attack scenario. In the preceding examples, we saw how the convenience of persistent cookies and Windows-integrated authentication can be used against us by attackers; they enable the browser to submit a request without prompting the user for any additional information.

 

When HTTP basic, HTTP digest, or client-certificate authentication is used, one-click attacks are still possible, but the attack scenarios are somewhat constrained. Because the victim's browser must possess a set of cached credentials or a session cookie by which to authenticate the bogus request, a one-click attack will only succeed if the user happens to be logged in to the target application at the time.

 

(Windows-integrated authentication is not always automatic. If the host name for the application is fully qualified - http://server.com as opposed to http://server - then Internet Explorer will prompt the user to enter their credentials instead of attempting silent authentication. As an exception, if the fully-qualified server name happens to be in the user's trusted sites list, then silent authentication will be attempted.)

 

This might not seem likely in a case such as Direct Deposit, but an attacker's ability to choose the right moment can vary depending on the circumstances. For instance, consider an application that shares data between users, such as a message forum, auction site, or workspace collaboration system. Every time a user adds or changes data that is visible to other users, it provides an indication that they are currently logged in.

 

Further Implications

One should recognize that a one-click attack can be launched from outside the boundaries of the organization whose application is being targeted. Mallory's Web site can be located anywhere on the 'Net, and everything will work fine as long as her victims' browsers can access both her site and the Direct Deposit site.

 

Another interesting side effect of using the victim's browser to submit the bogus request is that a review of the server logs will show that the illicit request(s) originated from the victim's own IP address, using the victim's own account. Perhaps the only hint that the victim did not intend to submit the request is in the Referer field.

 

Countering the One-click Attack

Some application developers try to mitigate one-click attacks by checking the Referer field. This is a fairly cheap and effective solution, but it has its drawbacks. The main problem is dealing with the issues that arise because not every legitimate request will have a Referer field, such as when the request is made via document.location or window.open. Moreover, the routine that validates the Referer field needs to be made bulletproof, which is easier said than done.

 

The industrial-strength solution to the problem of one-click attacks is to somehow prevent the attacker from being able to assemble request data that the server will accept. This is done by requiring a field in the request to contain an element of data that the attacker can't supply.

 

A hypothetical solution is to put a cryptographic hash of the user's session ID in a hidden input field, which we'll call "MagicNumber". When processing a request, the server computes the hash of the user's session ID and compares it to the value supplied in the MagicNumber field. If it matches, then we know the request is legitimate, because the attacker should have no way of knowing another user's session ID.

 

For applications that use GET requests instead of POST requests to perform important functions, the same approach works in theory: we simply move the magic number into a query string parameter. Unfortunately, this exposes a lot of problems that illustrate why GET requests are inappropriate for commands that have side effects.

 

The magic number is sensitive information, and if another user learns it, then they are able to launch a one-click attack. By putting the magic number in the query string, you run the risk of "leaking" it in any number of ways: If your application can link to other sites, then the magic number will leak to those sites via the HTTP Referer field. The magic number will also linger in the URL history of your users' browsers. There's also a usability concern, in that people are conditioned to use copy and paste to share hyperlinks. With sensitive information in the URL, not only is it exposed by users who share hyperlinks, but you also have to worry about a potential usability issue when the magic number doesn't match the user's session.

 

If your application uses HTTP GET requests to perform commands with side effects, I emphatically urge you to consider using HTTP POST requests instead. Not only will you spare yourself the troubles I've just described, but you'll also avail yourself of an excellent solution to this problem now available in version 1.1 of the Microsoft .NET Framework: ViewStateUserKey.

 

ViewStateUserKey

ViewStateUserKey is a string property of the System.Web.UI.Page class, and it works much like the hypothetical solution described above. ViewStateUserKey must be set during the Page_Init phase, and to be effective, the value assigned to it must be unique to the current user. The value of ViewStateUserKey is then used as a factor in computing the cryptographic hash, which is used as the ViewState MAC.

 

The effect of this is to make every ViewState MAC as unique to the current user as the value of ViewStateUserKey.

 

During the subsequent Page_Load phase, ASP.NET determines whether the page request is a client postback. If so, then ASP.NET compares the MAC of the user-supplied ViewState field with the one it just calculated during the Page_Init phase. If they match, then the request is known to be authentic, and processing continues. If they don't match, then ASP.NET throws an HttpException.

 

Here's a simple illustration of how to use ViewStateUserKey in an application that uses one of the authentication methods provided by Internet Information Server:

 

override protected void OnInit(EventArgs e)

{

  // ...

  ViewStateUserKey=Request.ServerVariables["REMOTE_USER"];

  // ...

}

 

When using forms-based authentication, the remote user is considered anonymous. In that case, the REMOTE_USER server variable is blank, so we need an alternative value that is still unique to the current user. The session ID is nicely suited for that purpose:

 

override protected void OnInit(EventArgs e)

{

  // ...

  ViewStateUserKey = Session.SessionID;

  // ...

}

 

To demonstrate ViewStateUserKey in action, the Direct Deposit application that accompanies this article has been equipped with a setting named VulnerableToAttack in its web.config file. When this option is set to anything other than "1", DDMain.aspx will use ViewStateUserKey to protect users of this application against one-click attacks.

 

If you've gone to the trouble of installing the Direct Deposit application and verified that Mallory's attack does in fact work, now is the time to "secure" Direct Deposit by setting VulnerableToAttack to "0".

 

If only it was nearly so easy in real life. But as far as one-click attacks are concerned, it is.

 

The sample code in this article is available for download.

 

Eric Rachner has studied computer security since his teen years and now brings his obsession to bear upon Microsoft's IT department as a Senior Security Analyst. In this capacity, Eric searches for ways to attack and abuse the applications upon which Microsoft operates, and works with application teams across the company to reduce risk through improved software design.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish