Captivating talk by Mitja Kolsek at DeepSec 2011 conference, describing the methods and prevalent vectors of online banking attacks.
Mitja Kolsek is a computer and network security expert and the CEO of ACROS Security – a Slovenia-based company specializing in digital security research. He has a rich background in security analysis of products and systems and penetration testing – here is his view of the current state of online banking system and the challenges it has to tackle:
For as long as online banking exists, we have attacks against individual users. We read about that, there are a lot of cases. You probably get email everyday where someone is trying to hustle you into giving them your Barclays bank account, which you never used, and you are not even a customer of the bank. But this is something that’s being going on, and it’s really past and present thing.
The present thing – maybe you noticed that we get more and more attacks against corporate users because that’s more interesting, there is more money there. Well, it’s not just the future; it’s already present – direct attacks against online banking servers. And in the future, we can expect attacks against the Back-End systems because that’s where really the money is now.
Attacking individual users: so, the methods are known, the goal is to steal the user’s identity. Attackers are using many methods like phishing, cross-site scripting1, CSRF2; they install malware on users’ computers. We are not going to be talking about that today, this is just like an intro. The attackers have many problems because banks and users are fighting back and they are making this more and more difficult.
Attacks against corporate users: that’s more interesting because there is more money there. It won’t be a surprise to a bank to see a couple of million EUR being sent from the bank account of a corporation to another corporation, because that’s just daily business. It would be suspicious if an individual user did that, but not a company. So the organized crime is now going after the corporations as well.
And they have another problem, because if you want to attack individual users, you can just spam everyone, you can just buy an email list of one million emails, and just spam everyone. So, I get spammed with an attack that is aimed against users of a specific bank that I never even heard of. But they don’t care.
Let me show you an example: We found a lot of public directories of issued digital certificates. And specifically in our country, if you want to attack the company that’s listed below, you can find the name of the person who has been issued a digital certificate for that company, and also his email address (see image). And that’s all you need if you want to attack the company – just go attack this guy. If you want to attack a company, you can get this information, you know exactly who to target inside that company.
But that’s not the topic of our talk. The topic of our talk, really, is online banking servers. Now, in contrast to attacking users and corporations where the goal was to steal their identity and then do whatever the banking application allows any legitimate users to do, here the attacker is interested in finding vulnerabilities and exploiting them in the online banking server.
The method is hacking, and there is an additional problem here. The attacker does not know of the vulnerabilities yet, so he has to try to find them. So there is an additional stage where the attacker can be detected. Now, the advantages are obvious. Basically, all the money is in the bank, right, and not just all the money – even more money than all the money. What I mean by that is you can actually create new money, because banks do that all the time. Banks create new money every day and destroy money every day, we just don’t see that. Since the money is digital, it’s just a matter of a couple of bits and bytes in databases.
And the advantage for the attacker is that there is no social engineering required here, you don’t have to hustle or trick anyone into doing something, you are just attacking the server directly.
Now, let’s just move on to a couple of vulnerability types and attack types that I have prepared for you. This will be somewhat simplified to make them more generic, but if you are familiar with how banks work, it will be easy for you to just translate that to real case.
Direct resource access
This is one of the top vulnerabilities in all web applications, and online banking is mostly web based. Even if you have a thick client, there is usually HTTP going in the background. Let’s see what can happen here.
This is a really lame example. You wouldn’t believe that this would really exist in the real world. So, we have a bank and we have a URL, and the user ID in the URL. So, when I log into the bank and I click on this URL, I get my account balance data. But if I change the ID to something else, I get someone else’s account balance data (see image).
Who doesn’t believe that this actually exists in real world? Well, this really happened in Citibank – this exact thing. How lame is this? So, this is the actual state of security today in some of the largest banks.
But it’s not usually the case. Usually the arguments are Base641 encoded. So, when you do the pen testing and you see this, of course when you see the typical characters of Base64, of course the first thing you do is you decode this. And then when you find something like this, you just Base64 encode something else, and request it – and you get someone else’s account balance (see image). We actually see this in real world.
But the better banks have gone further. They are using URL encryption which is again Base64 encoded, so it’s not a problem per se if it’s encrypted or you have HEX (hexadecimal) encoding. So, whenever you see something like this, the first instinct of a hacker is: “Let’s try to decode this first and see what’s hiding behind.”
What’s usually hiding behind is a code like this (see image). We have the same types of params that we used in the previous examples. And then, we are encrypting this with some encryption key, and then we add that to the URL. So the application gives us a link that is encrypted, and when you click on the link, it receives the request and decrypts the link.
It’s much better than what we saw before because we would need to know the key to be able to encrypt something that the server would understand. But it still happens that it’s easy to guess the code if you are really motivated. And the second problem is that some people actually know the key: the people in the bank, the developers, those who have actually selected the key – know the key. So it’s not a really good idea if that guy who knows the key leaves the bank and then still knows the key. He can still get access to other people’s accounts.
The developers have been aware of this problem, and they usually make it a little bit more difficult for you to guess what’s going on in the background, so they add some salt to the original arguments, and they even add something random, which is a good idea. If you have some random data, then it would be more difficult to guess what’s happening in the background if you don’t know the code. But we still have that guy who knows this code and he knows this key.
Doing this is not a good substitute for actual server side authorization of the received request. So whenever you see something like this in the URL, you know there is something fishy going on in the background.
Well, we are not here to steal other people’s data. The bank robber wants to actually steal money, so we can apply this knowledge to stealing money. We have s similar case: we have a transfer URL here, we have source account, destination account, and an amount. This is like what I would do if I wanted to transfer money from my account to someone else’s.
And of course by the same token, if we change the source account, the application does not allow me to do this through the GUI, so I have to do it manually. But if I do this and there is no server side validation or if it can be bypassed, then money can be taken from someone else’s account instead of mine. So, how does this money transaction work? Let’s go a little bit deeper into that. We have the user with the browser, and the online banking server. First of all, the user decides that he wants to make a transfer of his funds, so the server provides an empty transaction form.
So you get an empty transaction form that you can fill out: you select your account, you select the other account, and the amount of money you want to transfer, and a few other data that’s not important here.
The server then sends you a filled-out form just for the confirmation, so that you don’t mistakenly send money to someone else; it allows you to confirm what you decided to do.
Now, there is server side validation in this step. Whenever I provide some data (I want to do this and that with my money), the server validates my data, and looks at it and says: “Okay, this guy is trying to do something, can he do that? Does he have authorization for this account? Is the amount okay?” and so on. And then the server returns that form to me, and when I confirm it, it can validate it again.
Well, the most important validation is the third one because after that’s been done, the server is going to say: “This validation is okay; I am going to send it to back-end.” And the back-end just does everything that the server tells it to do.
A frequent security error here is that the server only does validation number two. So if you change your data in the confirmation form, you can bypass that validation and give the back-end server whatever you want to give.
Next case is negative numbers. Would you believe that you can actually use negative numbers in online banking systems? Yes, you can. Negative numbers are surprisingly often overlooked. Let’s see. This is a typical line of code that does the validation of the amount that we selected. So, we want to transfer 100 EUR, and this is the line of code that says: “Is it okay? Does he have 100 EUR?”
If I want to transfer 3000 EUR and I have only 2000 on my account, this line is going to block my attempt. But if I want to transfer minus 100 EUR, this line will have no problem with that (see image). So checking for negative numbers is very often overlooked, maybe not in the entire online banking application, but if you can find one place where it’s overlooked, you can do some funny things with that.
This is the actual case. The attacker has an empty account balance, the victim has 100 EUR. The attacker transfers minus 100 EUR to the victim – very generous, right? So, the end result is that the attacker has 100 EUR, and the victim has zero. It is the same as the transaction from the victim to the attacker, only triggered by the attacker. This is really cool. This is a very little known problem. If you try to search Google for this specific problem, it’s really not easy to find references. I only found a couple. But in the banking sector, they are very well aware of that after our pen test.
And again, the business logic behind this is whatever the amount to transfer is, this amount will be subtracted from my account and added to his account, so if minus 100 is subtracted from my account, it’s like adding 100 to my account – minus and minus gives you a plus.
Let’s see something that we once came across: a similar case, we are going to transfer minus 100 EUR, but not to another person’s account – we are going to transfer it to our own savings account (see image). Now, savings accounts are just like any other account, but they have a specific property that they cannot have a negative balance, well, at least those that we see.
The advantage to the attacker is that if we want to transfer minus 100 EUR from a normal account to a savings account, this is what we get: we get 100 EUR, but we do not get minus 100 EUR on the savings account because it cannot have a negative value. Now, the business logic said: “That is not okay, I am not going to do that part of the transaction.”
What’s fun with this is that you can actually create new money. Before, we had a total of 0 EUR; now, we have a total of 100 EUR. So there is actually more money in existence because of this failed transaction.
Bypassing limit checks
Next thing – bypassing limit checks. Banks like to impose limits on us. They don’t like us to transfer one million EUR from our account if we don’t have them, and I understand them, I wouldn’t do that either. But due to errors in business logic, you can possibly find ways to bypass that, because maybe there is insufficient validation on the server or any other thing.
Let me just show you what can happen if you find that. You have two accounts: one with 100 EUR, the other one empty. If you can bypass the limitations, and it can be possible because code is written by people and we make errors, this is the case: no extra money is in existence now because the total is the same as before. But you actually have a huge minus on one account and a huge plus on the other account (see image).
Now, if you are an honest guy you would add those two together and say: “Yes, I owe the bank.” You would probably be worse off because you would be paying a lot of interest for the overdraft. But if you are a criminal, you would just take those million EUR and go to Hawaii, and forget about the debt to the bank. That is why the bank does not want you to make larger overdrafts than those that they authorized you. This was actually a case in one of our pen tests.
HTTP parameter pollution
Next case is HTTP parameter pollution. This is a class of HTTP vulnerabilities discovered by Luca Carettoni and Stefano di Paola. It’s a very good idea actually, let me show what this means in the banking system. So, we have a communication between the user and the online banking server which is running a JSP1 code at this time, and the back-end server running a PHP code. It’s not very usual for back-end servers to run PHP code, but for our example here, let’s suppose it’s so.
So, the user wants to transfer 100 EUR from account one to account two. Now, the code on this JSP server reads the arguments from the URL and does some validation. If not user authorized for source account – then error, so I cannot actually provide another person’s account to the server, they would reject that. And also, it makes a test that if I do not have enough disposable funds on my account, I also get an error. This is typical validation of the user data (see image).
Now, the code says: “If those checks were okay, then I am going to forward that request to the back-end server.” And it does that in another POST2 transaction, so it just forwards the same parameters that we used before to the back-end server. The back-end server reads those parameters and does the transaction, that’s it.
The HTTP parameter pollution is this: we add another argument to the URL, it doesn’t have to be a URL; it can be in POST data as well. So, we have two source arguments here. The guys who were researching HTTP parameter pollution analyzed various servers and frameworks in terms of what they do if they encounter two parameters with the same name. It turns out that in JSP it takes the first parameter. So, we have the same situation as before. Now, when the request is forwarded to the back-end, PHP takes the second parameter (see image). This can be used for bypassing validation on the first server.
But of course no one is that silly, so we have another layer of authorization in the back-end. We say if the user is not authorized for the source account, we are not going to complete the transaction. We can still do something else: we can change the amount, so now we have two amount arguments, and all validation will pass correctly, although the back-end will have the amount of 100,000 EUR which I do not have on my account (see image). So, this is a perfect example of a vulnerability that allows you to do some of the things that I mentioned before – bypassing limit checks.
SQL injection – well, it’s pretty boring, but you still have it everywhere, and of course you also have it in online banking systems. It’s funny that when we talk to the banks before the pen test, we always get skeptical looks, and they say: “No, SQL injection will not be possible in our case.” And they have hundreds of reasons. But we always find at least one case where you can do it, so it’s really an important vulnerability in banking systems as well.
You are familiar with this, this would be a typical case if someone hacked the SQL query for getting the exchange rate for a specific currency and then replaced the un-validated argument with something like this, this is a classic example (see image). But again, the bank robber is not here to steal data, he is here to steal money. Now, let’s steal money.
This is a typical simplified version of what happens when someone transfers money from one account to another (see image). So, we have an actual SQL transaction, which means that either all SQL queries will be successful or none of them, this is the basics of the banking industry. But we have two update queries, so whenever I transfer money from my account to another, the balance on my account has to be changed, and the balance of the other account has to be changed accordingly, so these are the two updates queries.
So, my account balance is going to be changed to zero, and the other account balance is going to be changed to 100. But if these arguments are not properly validated, this is a case how it would actually work in a normal case, and this would be an SQL injection. So, even though we have an SQL transaction, the first query will actually set my account balance to zero, but the second one will set the other account and my account balance back to 100. So, we again created money out of nothing (see image).
Forging bank’s digital signatures
This is something completely different: banking signatures. When you are using online banking, do your banks provide an option for you to make an online deposit so that you can then digitally sign the deposit’s agreement? You have this in many counties. Banks are really enthusiastic about these digital certificates and digital signatures because we have the laws that we need to make these signatures valid in the court, and we have the technology, and the users already have digital certificates, so why not automate some of these processes?
Let’s see how that works. If the user wants to deposit 100 EUR for 31 days, he makes a request to the banking server. And the server says: “Okay, this is the deposit agreement that you need to sign”, and it’s dynamically generated from the data that user has provided. The server also decides what the interest rate would be for your deposit. This is all the data that you get and you are supposed to sign.
So the user signs the deposit agreement and sends it to the server, and the server countersigns it with the banking certificate which is on the server. This is now a legal document enforceable in court. If you go to the court and take this digitally signed document, the court will say: “You are right. This is true. This is a real agreement between you and the bank”, even though there was no person on the other side.
Now, do you see a problem here? Well, a malicious user can modify the agreement, and if the server is not careful, it will sign the modified agreement (see image). The modified agreement can be anything, not just increasing the interest rate from 2% annually to 2,000,000% annually, which would be like a nice way to earn money, but the text of the agreement can also be changed. It may no longer be a deposit agreement, it can be a statement from the bank that all their real estate is now yours, or hypothetically (it would not work), but yes, anything can be changed, not just numbers.
Server-side code execution
As you can see everything I am talking about here requires zero user interaction. These are attacks against the banking servers, you don’t have to social-engineer anyone. So the server side code execution is nothing special in the banking industry. But the impact – what the attacker would like to do if he manages to execute code on the server – is to change the e-banking application code of course, because that code includes business logic that prevents you from doing some nasty things. And if you can remove that logic from the application, then you can attack the system.
And the Holy Grail for the attacker would be to get the opportunity and credentials to send direct requests to the back-end, because the back-end trusts the online banking server that it has done some validation before. So whatever the online server says to the back-end, the back-end can execute some additional validations, but it essentially has to trust that there was a user behind this request, even though the attacker was actually behind the request.
Currency rounding attack
A lot of you probably like vulnerabilities, you would like hypothetically to hack banks, but you don’t want to go to jail.
Now, I will give you a way to do that. This attack is about rounding and currency exchange. Suppose you have an exchange rate such that for one EUR you get 1.364 USD. If you exchange one EUR cent you should get this much USD (see image), but because of the rounding, you will get less, so you make a nice little loss. So, what should we do? Yes, make it the other way around. If we exchange one USD cent for one EUR cent, because of the rounding you will actually get one EUR cent. It’s funny that this has been known for a decade, and still many banks allow doing that.
So, what would be the algorithm that you use?
You start with 100 EUR for example, and you exchange that to USD. So you get 136.40 USD, and then you make a loop, you repeat that 13640 times, convert that one USD cent to one EUR cent – and you get 136.40 EUR of profit and go to stage one, so you do it as long as they let you (see image).
Now, how fast can you get rich doing that? If we assume ten exchanges per second, as there can be some throttling on the server side which prevents you from doing it too fast – so you have to join with your friends, so you all do it at the same time – you can have a daily profit of 2300 EUR and a nice monthly profit which lets your quit your job immediately. But your bank will probably notice that and will do something about it, and you will have to hop into another bank, so eventually no bank will be doing this anymore.
What can be improved here? You know, doing this for a month takes a long time, and probably someone will notice that something is happening. Whoever is in charge of your account in bank – they will see that you have hundreds of thousands of transactions, and it could trigger some suspicion. But if you want to optimize this, because of the rounding, you should get as close as possible to 0,005 because that’s where the rounding provides the biggest difference. But remember that you earn less than one cent in each transaction, so it’s really low profit.
In corporate banking, banks are making it easier for you. Well, of course companies can do that as well, but banks make it easier for you because they provide an option for you to package the request in single packets and sign those packets, and send those packets to the bank. So you can do more offline and just send it as a single packet to the bank. It works on my personal bank, and also our corporate bank allows us to do that.
Now, the countermeasure is really simple, and that’s what we tell our customers: just don’t let users exchange less than one whole unit of the larger currency. So if you prevent making the exchanges of less than one EUR for example, all this goes away, because everything you earn with the rounding there, you lose with the different exchange rates, the buy and sell exchange rates. This is a very simple countermeasure.
Getting away with e-banking fraud
Now, getting away with it. Why should we even care? If we are not actual bank robbers, why should we even learn about getting away with it? Well, we shouldn’t learn much about it because we don’t need that kind of knowledge. But we should know at least a bit about it because when you are talking to a bank and you show them their vulnerabilities, at some point some guy will tell you: “Yes, but you wouldn’t get away with it because someone would catch you.” So you need to have an answer to that because if the conversation stops at that point, it may even lead to their wrongful understanding that the vulnerabilities that they have don’t really matter. You want to make sure that they fix those vulnerabilities, and in order to do that they have to believe that someone could actually exploit them.
So, first thing to do: the attacker would have to avoid detection. When he wants to attack a bank, he first has to find some vulnerabilities in that bank. And in the process of finding these vulnerabilities, he would create some noise, and he might get noticed. And to access most of the bugs that we covered here, the attacker would have to actually log into the bank first.
Now, if you want to log into a bank account, you would have to have one. It means you have to come to the bank and present your ID, which means that you can be traced back. If they notice that user 123374 is doing something funny, like it looks like cross-site scripting or buffer overflow1 exploitation, then let’s send someone from the police to their home and see what they are doing.
So the attacker would have to hide behind someone. And we call this ‘user in the middle’, like ‘man in the middle’ or ‘browser in the middle’; the attacker is using the same technique. He hacks some legitimate user’s account and does all the hacking through that account. It’s not a new concept but it’s a way to hide the attacker’s identity.
Now, the most important thing that is not specific to hacking banks directly – criminals that are now stealing money from users and corporations are already having this problem and solving it really well – is how to break the money trail. You can transfer money from one bank to another but it’s still traceable, it’s still digital money, it’s still in the system. So their goal is to extract it from this digital banking system.
They are employing money mules, you are probably getting emails all the time trying to recruit you. They give you 10% of the proceeds if you just cash some cheques, or if you receive some money on your account and then withdraw the cash and give it to someone, so this is really classic.
But also, digital money in all sorts that exist today can be used to cover your tracks. This is what the attackers are doing today, and they will continue doing it. And one thing that we haven’t seen yet, or haven’t read about yet but which can possibly happen in the future is chaining the ‘users in the middle’. So when the attacker steals some money from the bank, he transfers that money not to his own account but to someone else’s account which has already been hacked in another country, and from that account to yet another hacked account to another country. So this means that he is buying a lot of time to actually extract that money from the system before the police comes.
Now, the perfect crime would be to create new money so that nobody loses anything. If you steal money from a user, the user will eventually notice that and he will say: “Alright, the bank, someone took my money, take a look into this.” But we’ve seen cases where money can be actually created out of nothing. It’s less likely that someone will notice that, because no one has lost anything, no one will complain. So it’s just new money.
This can be done either with types of attacks that I described before, or if you remember the first slide, in the future, future 2.0, there is the DBA in the back-end for instance – database admin can actually do that today. He has all the access to change numbers in the database, not in a transactional way but in a way that creates new money. So this can be really hard to track. If he knows what he is doing, and he knows what the validation controls are in the bank, he can actually create new money without anyone ever noticing.
Now, the attackers’ best friend – this is what we hear all the time and probably some of you are thinking right now: “This would not work on my system because of this and that reason.” And it probably wouldn’t, not everything would work on every system obviously, but for a bank it’s really critical if anything works anywhere in their system. And that is why testing is better than believing.
For the attacker, it’s very good that banks are adopting new technologies, trying to automate more and more processes. So we’ve seen automated deposits and the new threats that they bring. We will see automated small loans in the future, that’s for sure. Right now, the banks don’t want to automate loans because they have these complex procedures in the background. They want to see whether you are worthy to get a loan. But for small loans, they will start doing that at some point. And when one bank starts doing that, the others will follow quickly. And this will just expand the attack surface.