Breaking the bank in the style of smash the stack!
Not just XSS ...
Recently, many people are paying attention to the vulnerabilities of software used in the banking sector: in particular, the news about XSS class vulnerabilities on various banks' websites has recently been heard. The public is indignant, and the media is noisy. But, after all, banks are not only rich in web content. Since the late 2000s, I have been collecting vulnerabilities in ActiveX modules that banks proudly distribute to their users, namely, customers of the remote banking service (DBS) system. Once a year I took one or two systems and picked them up. Starting just like that, for the sake of curiosity (I started this business while still an employee of the bank) and continuing from research interest . As a result, in 3-4 years I revealed vulnerabilities in systems from such manufacturers as BSS, Inist, R-Style, CFT. Under the cut is information about one such vulnerability. Most of the description is given to creating a simple exploit to execute arbitrary code on the client (Windows7, IE + DEP / ASLR). Perhaps this will be useful to those who would like to understand the principles of exploiting the old 'strcpy' bugs and creating ROP exploits.
Note
ALMOST all the vulnerabilities that were discussed are now fixed. Vulnerability, referred to specifically in this post - fixed a long time ago . This manufacturer of SBBS was chosen as the “victim” of this post precisely because it has a single entry point , which means it is enough to update ActiveX in one place. With other manufacturers it is more difficult, since it is necessary to update EVERY bank separately and independently, so there is a possibility that even for old vulnerabilities in BSS / Inist / R-Style, there are installations without an installed update. ')
The vulnerability itself was discovered and immediately fixed in 2010. Therefore, no harm from this post will not be anyone, just a lesson ... 8)
Start
Users of the “Internet Bank” system receive an “appendage” in the form of an ActiveX component. This software is responsible for working with EDS, documents, etc. It is written, as a rule, in C or C ++. And this means that things like string format errors, buffer overflows, or errors when working with pointers are quite typical of this type of software. The analyzed component was automatically installed for all clients who wanted to work with certificates directly from a special web page:
The component is installed, great. However, it should be noted that this module could only be activated from the bank's domain, which means that to exploit the vulnerabilities of this module, you must either use bugs such as XSS, or be a “man in the middle”. This greatly reduces the likelihood of an attack, but does not make it unrealistic.
Vulnerability Search
Classics of the genre when searching for holes is, of course, fuzzing. In order to "professing" this software, you can use, for example, the well-known tool - COMRaider. How to use it, I described in the magazine "Hacker". The essence of the technology is simple: all methods and properties of an ActiveX class are invoked with various parameters, for example, with long lines or lines with format specifiers inserted. And if there is an error, for example, a buffer overflow during the processing of a long line, an exception is registered (the application simply crashes). But here's the ill luck: after performing fuzzing, COMRaider did not find a single exceptional situation, not a single vulnerability - everything went without any problems. Hooray, no holes!
Or…
However, there were doubts about this. All the same DLL'ka code is dated as much as 2006. Having loaded the library into IDA and casting a quick glance at the number of calls to such a famous function like strcpy, I was hardly surprised. All this seemed to hint that there was still a vulnerability.
All of these calls can also be analyzed in IDA (in parallel, you can also use a debugger, such as Immunity Debugger, for a faster understanding of the situation and to analyze dynamic calls). Through indefinite analysis of the code and a certain number of launches in the debugger, it was revealed that part of the fuzzer functional simply MISSED, since there were explicit checks on the value of other properties of the method. That is, fuzzer analyzed all the methods and properties separately, and therefore part of the code was simply not covered (oh, this fuzz-dam). For example, the LogFileName property specified the name of the log file. And this name is stored in a special buffer and is used when calling OTHER methods that generate log entries. While fuzzer just tried to do this:
object.LogFileName=long_buff;
As such, the vulnerability is not here. It is further, in the error handling function when creating a log file (let's call it vuln). This function is called if LogEnabled> 0, LogFileName is set, and the file with the log could not be created. To compile the vector of operation, you need to trace the path to this function. However, in IDA 6.2 this is extremely easy to do: Proximity browser + Find Path. This functionality helps to find the path between any calls, if any. For example:
It can be seen that there is a path to the vuln function (and, as a result, to strcpy) from the public method of the ImportKey () class. In fact, almost all class methods are trying to write something to the log, so this way is from many:
This tells us that in order to call the vuln function (from ImportKey), we must execute one by one:
In other cases, the vuln function is simply not called. This is about the fact that a single fuzzing is often not enough, he does not know about many subtleties ... Now let's look at what, actually, is a mistake. To do this, I will provide a simplified piece of the vuln function code, directly with the strcpy call:
The essence of the function is the generation of an error message, the template itself is contained in the variable errorText. In the loop, the template (errorText) is copied to the variable bufferOut. This automatically substitutes the% 1 in the file name - fileName. The substitution is done by calling strcpy. The filename is specified by the public method LogFileName. Accordingly, if the LogFileName is long, then the bufferOut boundaries will occur. Classic buffer overflow. But the humor is that with a sufficiently long fileName value, the errorText will be overwritten. And since the loop goes to the end (zero byte) errorText, then it will go on indefinitely, destroying the stack to the very end (beginning). After all, after overwriting (buffer overflow due to strcpy call), errorText will contain the fileName part, and the pointer will be less than bufferOut. It turns out that in this loop, the current errorText pointer will point to the middle of bufferOut, the pointer of which will increase. This will result in looping.
Thus, before strcpy:
After strcpy:
And at the end of the looping an exceptional situation, since it went beyond the stack:
Obviously, we also overwritten the pointer to the exception handler ...
Exploitation
This chapter can be regarded as a description of the good old exploit on the overflow. Classics of the genre! So, we rewrote SEH and created an exceptional situation where bufferOut started pointing to an unknown place “behind” the stack. As a result, the program should transfer control to the pointer:
Our task is to slip a pointer to a shellcode instead of a pointer to a handler. Since IE works with DEP protection, this option does not roll, because our shellcode (on the stack or on the heap) is in memory, not marked as executable. We are lucky that the ActiveX DBO module is compiled without the support of ASLR and SafeSEH. The first gives us knowledge of the addresses of instructions in memory from this library, and the second - the ability to use any of these addresses as an exception handler. Thus, the problem of bypassing ASLR (partially) and SafeSEH (completely) disappeared by itself. After all, here comes the so-called return-oriented programming (ROP). How ROP works can be found in an article from the Hacker magazine. The essence is simple: as a handler, we point out instructions from the same module, from the .text section, which, of course, is executable, and the addresses here are known to us. As a result, these instructions will be executed, and in order to take control after they have been worked out, the last instruction must be a return instruction - RET / RET n (or JMP / CALL reg, but this is less common). Then the following instruction will be taken from the stack as a saved exit address. But we still do not control the stack (when processing SEH, the stack “moves”). Therefore, the first such instruction should be redirecting ESP to the area controlled by us. In this area there will be other return-execution addresses. For example, instead of a pointer to SEH, we put the following address (must be ASCII): 10014324
0x10014324: MOV ESP, 3b1002b1 / RETN
As a result, ESP will point to 0x3b1002b1. But the memory at this address is free, and as a result RETN cannot take the address of the next instruction ...
Heapspray
Once the memory is free, then you can take it. This problem is solved banal - heap spray. We use javascript to create large arrays with our data. In IE9 (and there is something in the new FF) there is protection against heap spray-nozzle and bubble, but given the small randomization, it is also solved, but in IE8 everything is simpler:
var data= ROP_NOP + ROP + SHELLCODE; bk=data.substring(0,data.length); while(bk.length<0x40000) bk = bk+bk; var h1=new Array(); h1[0] = bk; for (var i = 1 ; i < 1800 ; i++) { h1[i]= h1[0].substring(0,h1[0].length ); }
ROP_NOP - this data should go to the address 0x3b1002b1. This value will be the address for RET after MOV ESP, 0x3b1002b1. ROP_NOP must be a large enough piece, with the repeated address of the RETN instruction. This is a kind of ROP nop analog. For example, at address 0x1001023c is the instruction RETN.
ROP is a set of addresses for instructions that collectively search for the function kernel32.VirtualProtect, and then call it on our heap, making it executable (bypass DEP in this way). Then you can transfer control to the heap itself.
SHELLCODE - control is transferred here after calling VirtualProtect. Here is the shellcode (specific instructions).
This is the case where we hammer memory up to the vulnerability trigger. Then, when the exception handler is called, the ESP pointer will point to the data under our control where the ROP program will be located. Exploit video example and ROP programs:
Thanks to the developer for an adequate response to security problems. With the old version of the module in the system does not start up!
With you was d00kie . I hope it was fun. Hack'em'all 8)
PS
For the curious, I cite the ROP program:
//0x10009de4 -- POP EDI # POP ESI # POP EBX # RETN //0xffffffcc -- EDI (-52) //0x11111111 -- ESI //0x1002b074 – kernel32.flushinstructioncache EBX //0x10013f67 -- ADD EDI,DWORD PTR DS:[EBX] # RETN // VirtualProtect ( [EBX]-52=VirtualProtect ... 8) //0x1000bf35 -- MOV EAX,EDI # POP EDI # POP ESI # RETN 04 VirtualProtect EAX //0x11111111 -- trash //0x11111111 -- trash //0x1002879d -- PUSH EAX # RETN // call VirtualProtect //0x11111111 -- trash //0x1000fd72 -- JMP ESP <--- VP, ESP, //0x3b1002b5 -- (1) <--- VP //0x00010000 – (2) ( , ) //0x00000040 – (3) RWX <--- //0x3b1002b1 – (4) //0x90909090 -- VP, (NOP)
Since EIP and ESP will point to the same page, this will have a bad effect on the shellcode (for example, a WinExec call will not work). Therefore, before the shellcode (which I took from the meta-exploit), you need to manually put the “reducing agent” of the old pointer to the stack: