Monday, January 9, 2012

Is Your Online Bank Vulnerable To Currency Rounding Attacks?

A Hefty Discount Your Bank Never Intended To Give You

In the 12+ years of doing penetration tests against various critical environments, we've seen numerous online banking servers and found all sorts of vulnerabilities in them, including bugs that allowed users to take money from other users' accounts, make unlimited overdrafts on their own accounts, transfer negative amounts to other accounts (effectively sucking other users' money from these accounts) and even - frightening as it may sound - create unlimited amounts of money out of thin air. These types of critical bugs in financial systems are not nearly as rare as one would hope; in fact, our experience shows that at least one such defect exists in your online bank if it hasn't been thoroughly and regularly reviewed by different researchers with a lot of knowledge about banking vulnerabilities and attacks. (As a rule of thumb, if your bank's penetration testing call for proposals contains the word "scanning" or specifies the number of IP addresses to test instead of focusing on  application code and logic, their online systems are likely hosting various logical security flaws.)

While such vulnerabilities can allow an online thief to take a lot of money from bank's customers or the bank itself, doing so would positively qualify as a punishable criminal act in most jurisdictions.


Legally Exploitable Security Flaws

There exist, however, other types of logical security flaws in financial systems whose "exploitation" can be perfectly legal. One such flaw is in the way rounding is done in currency exchange and allows users to effectively influence the currency exchange rates to such extent that, for instance, they get 100 EUR (Euro) for 100 USD (US Dollar) even though 100 EUR would normally cost approximately 130 USD.

To our knowledge, this type of flaw was first described in the 2001 paper titled Assymetric Currency Rounding by M'Raïhi, Naccache and Tunstall of Gemplus, and later in a 2008 Corsaire paper Breaking the bank -Vulnerabilities in numeric processing within financial applications. We've been regularly detecting it in online banking systems we've reviewed. Here's how it works.


Currency Exchange 101

Banks usually have two exchange rates for currency exchange: the buying rate is the rate at which the bank will buy the foreign currency, while the selling rate is the rate at which they will sell it. These rates can be expressed either as direct quotations (how many units of local currency equal to one unit of foreign currency) or indirect quotations (how many units of foreign currency equal to one unit of local currency). According to Wikipedia, most countries are using direct quotations, while indirect quotations are used in Euro zone and a few others.

In this article, we'll be using numerals with decimal comma and a point as a thousands separator. For example, one thousand will be written as 1.000,00. (Apologies to the readers accustomed to different notations.)

Suppose a European bank is providing the following indirect quotation for US Dollar exchange rates:
  • Buying rate: 1,388 (you pay 1,388 USD for 1,00 EUR)
  • Selling rate: 1,364 (you get 1,364 USD for 1,00 EUR)
Obviously, exchange rates effectively provide a margin for the bank even if no separate commision is charged for the exchange. For example, if you buy 1.364,00 USD for 1.000,00 EUR and sell them back to the bank, you get 982,71 EUR, making a loss of 17,29 EUR.


Rounding

Banks typically operate with two decimal digits and while various calculations (e.g., interests or currency exchange) are done with higher precision, the final results are rounded to two decimal digits. Now, if the bank is fair, this rounding is done to the nearest number, i.e., such that 7,237 is rounded up to 7,24 while 2,221 is rounded down to 2,22. Whether a tie-breaking case such as 1,505 is rounded to 1,50 or 1,51 is of little relevance to this article.


How Much Do I Get For One Cent?

While we've seen that the above exchange rates provide a sensible business model for the bank in a typical case, security researchers are usually more interested in atypical cases, especially various corner cases. One such corner case is the lowest possible value, typically 0,01 - which in many currencies means one cent.

What happens if you want to convert one Euro cent to US Dollars? The European online bank will use the selling rate:

0,01 EUR * 1,364 USD/EUR = 0,01364 USD

After rounding, you will get 0,01 USD. Obviously you'll make a loss (a Dollar cent is worth less than a Euro cent in our example), which nominally doesn't look significant, but is actually a 27% loss.

But what if you reverse the direction and convert one USD cent to EUR? This time, the buying rate is used:

0,01 USD  / 1,388 USD/EUR = 0,0072046109510086455331412103746398 EUR

After rounding, you get 0,01 EUR. Now this is better: the rounding just made you an instant profit of 38,8%. Of course, the profit is nominally tiny and probably won't get you a cup of coffee anywhere in the world, but this procedure can be executed repeatedly in a fully automated way. By doing so a hundred times, 1 whole US Dollar can be exchanged for one whole Euro. A hundred thousand times, and one gets 1.000,00 EUR for 1.000,00 USD (a profit of 280,00 EUR). Even if one has to first buy the 1.000,00 USD at the same bank (at bank's selling rate) for 733,14 EUR, the total profit equals to 266,86 EUR. And this profit is from a legitimate use of service provided by the bank on their terms and in their controlled environment.


But How Many Cents Can I Sell?

Suppose an online bank can accept a hundred requests per second (a highly conservative assumption - large banks have to support thousands of requests per second in peak periods), an automated script can make 100*60*60*24 = 8.640.000 one-cent exchange requests per day, making a profit of about 23.000 EUR every 24 hours if this online bank provides currency exchange services around the clock.

Many online banks allow users to upload prepared packages with multiple requests, possibly thousands of them, which the server will then process in a batch. Obviously, this can further expedite the procedure.





Another Example: Japanese Yen

Our above example used two currencies of a similar value. The same method works with any two currencies, for instance with Euro and Japanese Yen (JPY). Suppose the bank's buying rate for Yen is 97,5949. Therefore, exchanging Yens to Euro would work like this:

0,50 JPY / 97,5949  JPY/EUR = 0,0051232185288370601332651603721096

After rounding, you get 0,01 EUR (a profit of over 95%).


Maximizing The Yield

By now it is clear that the profit comes from rounding errors. When working with two decimal digits, the maximum rounding error can be 0,005 for any individual operation. The closer the converted value is to 0,005 (but above it), the higher the profit will be when the number is rounded to 0,01 - and it is easier to maximize the profit using currencies with larger exchange rates (see the Yen example above), as they allow for a better "fine tuning" when trying to get as close to 0,005 as possible.

This also means that a single exchange operation can, at best, produce a profit of 0,005 units of the target currency, so obtaining a non-negligible nominal profit would certainly require hundreds of thousands of operations.


Is This Really Legal?

The essential property of this "exploitation" is that you're really not doing anything to bypass any security mechanisms the bank has put in place; you're simply using the provided functionality the way it was intended, and it is inconceivable that exchanging 1.00 USD would be legal, and exchanging 0.02 USD would also be legal, but entering 0.01 in the same form field would not be.

Note that the legality of this can change significantly if one has to actively "help" the system to accept the low amount. Even if there is just a client-side JavaScript validation in place preventing entering of too small values, which we all know can be easily bypassed, the law (at least European law) could interpret such bypassing as illegal.
Another catch may also be in the Terms and Conditions that a particular bank sets for their users. These could include provisions for annulling certain transactions, e.g., transactions by individual users not done manually through the intended user interface or erroneous transactions due to logical errors in the code.

We've had many conversations with our banking customers who were affected by this logical flaw, some of them losing more than 100.000 EUR or USD, and their final conclusion was always that they didn't have legal grounds for persecuting the "attacker", and would have to just swallow the loss and add some countermeasures to the code to prevent it from happening again.


Countermeasures

After becoming aware of this vulnerability, banks can employ various countermeasures to eliminate it:
  1. Charging a conversion fee. This is the simplest countermeasure, and even if the fee is really tiny (e.g., 0.01 EUR), it quickly takes away all the profit provided by the rounding.
  2. Setting a minimum conversion amount. Much like most physical exchange bureaus refuse to accept small change, an online currency exchange can refuse to exchange anything less than one unit of the "larger" currency - say, 1 EUR in the above examples. Note that exchanging an amount larger than 1 EUR can still provide up to 0,005 EUR profit from rounding error but this profit is more than neutralized by the difference between buying and selling rates.
  3. Always rounding to bank's benefit: While arguably unfair - and possibly disallowed by local banking regulations -, this is also an effective countermeasure.
  4. Limiting the number of operations per user: Obviously, harvesting a significant profit requires hundreds of thousands of exchange operations. An online bank can limit the allowed number of exchange operations for any individual user to, say, a thousand per day, without causing any problems to users.

Are There Any Other Legally Exploitable Security Flaws?

There are many. We decided to publish a blog post about this one because it's been publicly known for a decade, is being actively exploited, and we keep finding it in online banks we're reviewing. Many other flaws are not publicly known and it would be a disservice to our existing and future customers to reveal them.


Final Thoughts

This flaw is a great example of how things can go wrong when you take a simple, well-specified physical process such as manual currency exchange - and turn it into an online service. This particular case introduced two critical changes: first, the online service allows  hundreds of thousands of operations in a short period of time, while a physical exchange office could probably not  make more than two exchanges per minute. And second, the online system accepts the smallest of small change and doesn't find it either suspect or annoying that someone wants to exchange one cent over and over again - it just dutifully executes its code.

Actually, having looked at many different logical flaws we find in online banking systems, a lot of them exist only because the transformation of processes from the physical banking world into the complexity of an online application introduced many new and unwanted possibilities that would have been alarming, suspect or at least unacceptably irritating to the people involved in the same physical process.

And they say people are the weakest link in security.

Wednesday, January 4, 2012

Google Chrome HTTPS Address Bar Spoofing

The Fixed Bounty Bug Revealed

Last month Google awarded our security analyst Luka Treiber a Chromium Security Reward for a high-severity vulnerability fixed in version 16 of the Chrome web browser. Due to Chrome's automatic update mechanism we expect most browsers to be updated by now, which seems to be supported by StatCounter's Global Stats for January 2012, where Chrome 16 is the only Chrome version in the chart. Luka found this issue in Chrome 14 and we confirmed that Chrome 15 was vulnerable as well. This document presents the vulnerability in detail.

HTTPS-related  vulnerabilities tend to rate high on the severity scale as they allow attackers to make web site visitors - even the savvy ones who check for HTTPS evidence such as "https://" at the beginning of the URL - believe they are visiting a legitimate web site when they're actually on a malicious look-alike site. And when users trust a malicious web site, they will give it their credentials and personal data.

This bug is a nice addition to some other HTTPS-related vulnerabilities our security researchers have found in the past: bypassing HTTPS security warnings in Internet Explorer and Netscape Navigator (yes, we were breaking HTTPS back in 1999!), and Poisoning Cached HTTPS Documents in Internet Explorer. None of these break the cryptographic model or implementation supporting HTTPS, but rather exploit the integration of SSL/TLS into the browser and browser's presentation of security-relevant information to the user. These are often the weakest link in the security HTTPS is supposed to provide.


Chrome's Trigger-Happy Address Bar

There is an inconsistency in the way Chrome 14/15 renders some web page redirections in a way that allows an attacker to perform address bar spoofing, resulting in an HTTPS URL being displayed with the content from some other web site. Let's take, for example, a simple JavaScript redirection page located at http://source/sample.html that looks as follows:

location="http://target";

When observing the above code in execution all parts of Chrome's user interface behave consistently, meaning that as the address bar changes from URL of the source page to the target URL, the page content changes accordingly and promptly (at once). However, by altering the script like this:

location="view-source:http://source/redir.php?url=http://target";

one can see the address bar starts changing before the displayed page content is replaced. So for a split second the address bar displays the new address http://target while the DOM is still from the old address http://source/sample.html. The given example is composed of two tricks:

  1. Apparently the "view-source:" prefix causes the asynchronous behavior between the address bar and the DOM.
  2. The redir.php is an HTTP 302 redirection script used to cause a redirect from view-source:http://source/redir.php to http://target, thus removing the "view-source:" prefix from the URL (the goal is to spoof the address bar to a legitimate domain as will be shown later). After this redirection, the browser no longer displays the source code but renders the HTML of http://target.

All demonstrations provided below employ the above two tricks with http://target replaced by a Gmail login page address. However, to stop the redirection at the exact moment when the inconsistency between the address bar and the page contents is being exhibited, a further trick is used. Each of the three demonstrations employs a different trick to "freeze" the state of inconsistency for a long enough time for a user to enter his credentials.


Demonstration #1: Redirection To HTTPS On Port 80

In the first demonstration, https://proxy.google.com:80/ is used as target of the redir.php script to block the redirection for about 30 seconds. This occurs because Chrome is instructed to establish an HTTPS connection with a server on an HTTP port, which results in a 30-second hopeless handshake attempt between an SSL/TLS client and an HTTP server.

While the redirection using view-source:http://www.acrossecurity.com/redir.php?url=https://proxy.google.com:80/ is being fruitlessly attempted (and Chrome is already showing https://proxy.google.com:80/ in the address bar), a fake Gmail login page is displayed from attacker's web site. If username and password are entered and the submit button is pressed the data gets sent to http://www.acrossecurity.com (the "malicious" web site for the purpose of this blog post). Tests have shown that any unresponsive server script can be used instead of https://proxy.google.com:80, or even an invalid target URL such as view-source:http://xxxxxxxx.

Let's look at a video of this demonstration:



Demonstration #2: Using Google's Open Redirector

In the second demonstration, we avoid the suspicious 80 port in the URL by using some open redirector on https://*.google.com as the desired spoof URL. The demonstration is analogous to the previous one except that an additional redirect is used after the http://www.acrossecurity.com/redir.php script, and that https://proxy.google.com:80 is replaced with a download-throttling page http://www.acrossecurity.com/slow.php that delays the loading for an arbitrary amount of time. This time the URL that the redirection gets stuck on is https://www.google.com/url?q=http://www.acros.si/slow.php[...].

Let's see:



Demonstration #3: Delaying Redirection With A Blocked Modal Dialog

In the third and final demonstration, we manage to spoof the exact URL of the Gmail login page. To do that, a blocked modal dialog is used to stop the redirection instead of the "wrong port trick" with https://proxy.google.com:80 or the download-throttling slow.php employed in the previous demonstrations. A precondition in this case, for whatever reason, is that the user has come to the malicious web site from the spoofed-to-be host (in our case https://accounts.google.com) or another host in its 2nd level domain before address bar spoofing redirection begins. This precondition can easily be fulfilled using any one of the open redirectors on google.com servers.

An intentionally blocked modal dialog (blocked by Chrome's pop-up blocker) is used to stop the redirection after the address bar has already been updated with the new URL but the page content hasn't been refreshed yet. Like in the previous demonstrations, a fake login form is displayed, waiting for the user to provide his credentials. Curiously, any requests resulting from the form submittal are queued while the modal dialog is blocked. We solved this with a self-destruct script inside the modal dialog executing after the submit button has been pressed, thus releasing the said queue and allowing the credentials to be sent to attacker's server.

Let's see how this looks like (notice the blocked dialog icon on the right side of the address bar):



Practical Exploitability

While it would certainly be possible to trick some users into logging in to Gmail (or any other targeted web site) through links provided by a 3rd party malicious web site, most users are likely to visit their favorite web sites directly (by typing the host name) or using browser bookmarks. In this case, as long as the initial URL is non-HTTPS, a man-in-the-middle attacker (i.e., the guy sitting next to you in the coffee shop with free WiFi) can actively inject malicious code into the initial web site's HTML to exploit this vulnerability and present a legitimately-looking fake HTTPS login form to the user.

Finally, the hawk-eyed among you may have noticed that the spoofing is not entirely perfect: the icon left to the spoofed URL is a grey planet as is typical for HTTP addresses - and not a green lock as is typical for valid HTTPS addresses. However, while many users may notice the presence of "https" and consider it a guarantee of trust, they are less likely to notice the absence of a lock icon - especially since the visual identification of HTTPS URLs is different in different web browsers.

So could this vulnerability realistically be used for defeating HTTPS in actual attacks? We think so and so does Google - and we're glad this bug is now fixed. When more and more web sites are depending on the cryptographic security of HTTPS, this bug is a reminder that HTTPS is much more than just cryptography.