Yesterday I spotted the news about a new Java 0day being exploited in the wild and soon after a POC was released: http://pastie.org/4594319.
I decided to analyse this code to understand what is the vulnerability that triggers the exploit. Here is a brief description of my findings.
The code instantiates a Statement object that will be used to run the setSecurityManager() method of the System class. The purpose is to set the Security Manager to null, which means escaping the Java sandbox. Of course, you can't do this directly and here comes the exploit!
The Statement object contains a field named "acc", which is a AccessControlContext (a sort of security descriptor) that specifies the permissions allowed for the Statement object itself. This field is normally not accessible from the code outside the Statement class, so the exploit needs to find a way to modify it.
It does so by using the getField() method of the sun.awt.SunToolkit object: this function returns a given field from a given object; in this case it returns Statement.acc. At this point the game is over because the malicious code can just create a new AccessControlContext object, assign to it full permissions and then replace the old restricted Statement.acc with the new unrestricted one.
Mistery solved? Not yet: the tricky part is in obtaining an instance of the object sun.awt.SunToolkit, that is supposed to be a restricted package. The exploit does this by calling Class.forName(); this method simply returns an object from its name.
This is how I understand the code (and I'm no Java expert), but I read this blog entry that has a slightly different explanation. In their analysis, the authors see another method that accomplishes the task: com.sun.beans.finder.ClassFinder.
I don't know what this is about: do they have a different POC or sample? It does seem so!
Also they say that the exploit itself relies on the possibility of instantiating the sun.awt.SunToolkit object through the com.sun.beans.finder.ClassFinder object. This would mean that in the POC I have analysed the vulnerability is in the Class.forName() method, that is, there are TWO different vulnerabilities (one in ClassFinder and one in Class.forName()).
However, debugging the exploit in Java version 1.6 (jre6) it did not work: the Class.forName() object successfully instantiated the sun.awt.SunToolkit object, but then the use of its getField() method threw an exception. Instead, the method works fine in version 1.7 (jre7). To make it short:
So, even if version 1.6 allows the instantiation of the sun.awt.SunToolkit object, it prevents it from accessing the private Statement.acc field, which seems correct. It seems that the bug is really in version 1.7, in the access to the Statement.acc. Or maybe none of the two is supposed to happen: sun.awt.SunToolkit must not be instantiated to restricted code, and the Statement.acc field must not be accessed by anyone.
I will look forward to new results.
*UPDATE* [28 August 2012]
1) Now we can refer to the above vulnerability as "CVE-2012-4681".
2) A new analysis, based on the same POC I documented, has been published today: http://thexploit.com/sec/java-facepalm-suntoolkit-getfield-vulnerability/ . So, yes, it seems that getField() is the culprit, or at least it's one of them...
*UPDATE 2* [28 August 2012]
A more in-depth analysis is finally out: http://immunityproducts.blogspot.com.ar/2012/08/java-0day-analysis-cve-2012-4681.html
As I though there are two different 0days: one that allows you to get a reference to the restricted class sun.awt.SunToolkit, and the other one (getField()) that lets you access a private field of a class. The missing detail (classFinder()) is also solved: it is used in the internal implementation of the execute() method of the Expression object.
Monday, August 27, 2012
Friday, August 17, 2012
FinFish's trick... not so legendary!
This post is about a trick that Finfish uses to appear (well, at least, "to try to appear"!) as a normal, non malicious program.
First of all you can immediately notice that this sample is a simple loader: you can have a look at the IDA navigation bar to spot a tiny code section in contrast to a huge resource section.
This tells us that something is hidden somewhere in the resources. The payloads, in fact, are encrypted and stored in the dialog type resources. Here's a quick verification test, that shows that something is wrong in the dialog data:
But let's go back to the curious trick we mentioned, and let's begin by analyzing the code. If we start looking from the entry point we notice... absolutely nothing!
At a first glance nothing suggests that we are analyzing a malware, as we only go through some common APIs.
Of course, a deeper reading reveals the trick: in the middle of some legitimate calls we find a suspicious function. The thing that more captured my attention is that it makes use of the VirtualProtect API different times, apparently without any good reason, as we will see later.
For now let's start from the beginning:
.text:004011F5 push 0 ; lpModuleName
.text:004011F7 call ds:GetModuleHandleW
.text:004011FD mov ebp, eax ; MZ header
.text:004011FF mov eax, [ebp+3Ch] ; MZ.elfanew = PE offset
.text:00401202 mov esi, [eax+ebp+80h] ; import table RVA
.text:00401209 mov eax, [esi+ebp+0Ch] ; import name RVA
This code gets the handle of the application itself and then it reads: the MZ header; the PE offset; the import table RVA; the first import name RVA.
.text:0040120D add esi, ebp ; virtual address of the image import descriptor
...
.text:00401223 add eax, ebp
.text:00401225 push offset aUser32_dll_0 ; "user32.dll"
.text:0040122A push eax ; char *
.text:0040122B call __stricmp
.text:00401230 add esp, 8
...
.text:00401237 add esi, 14h
.text:0040123A mov [esp+18h+var_4], esi
.text:0040123E jmp loc_4012E6
...
.text:004012E6 mov eax, [esi+0Ch]
.text:004012E9 test eax, eax
.text:004012EB jnz loc_401223
Then the malware calculates the virtual addresses of the first image import descriptor, its import name address, and begins a loop over the import names looking for "user32.dll".
.text:00401243 mov edi, [esi]
.text:00401245 mov esi, [esi+10h]
.text:00401248 mov eax, [edi+ebp]
.text:0040124B add edi, ebp
.text:0040124D add esi, ebp
...
.text:00401257 jmp short loc_401260
...
.text:00401260 lea ecx, [eax+ebp+2] ; Name
.text:00401264 push offset aRegisterclasse ; "RegisterClassExW"
.text:00401269 push ecx ; char *
.text:0040126A call __stricmp
.text:0040126F add esp, 8
.text:00401272 test eax, eax
.text:00401274 jnz short loc_401297
...
.text:00401297 mov edx, [edi]
.text:00401299 lea eax, [edx+ebp+2]
.text:0040129D push offset aCreatewindowex ; "CreateWindowExW"
.text:004012A2 push eax ; char *
.text:004012A3 call __stricmp
.text:004012A8 add esp, 8
.text:004012AB test eax, eax
.text:004012AD jnz short loc_4012D0
...
.text:004012D0 mov eax, [edi+4]
.text:004012D3 add edi, 4
.text:004012D6 add esi, 4
.text:004012D9 test eax, eax
.text:004012DB jnz short loc_401260
Here the code saves the content of the OriginalFirstThunk and the FirstThunk fields of the IMAGE_IMPORT_DESCRIPTOR. Then, it loops over every IMAGE_IMPORT_BY_NAME.Name looking for the RegisterClassExW and the CreateWindowExW APIs.
Once they are found it does the following:
[RegisterClassExW]
.text:00401276 lea edx, [esp+18h+flOldProtect]
.text:0040127A push edx ; lpflOldProtect
.text:0040127B push 40h ; flNewProtect
.text:0040127D push 4 ; dwSize
.text:0040127F push esi ; lpAddress
.text:00401280 call ebx ; VirtualProtect
.text:00401282 lea eax, [esp+18h+flOldProtect]
.text:00401286 push eax ; lpflOldProtect
.text:00401287 mov dword ptr [esi], offset BadFunc1 ; FirstThunk overwrite
.text:0040128D mov ecx, [esp+1Ch+flOldProtect]
.text:00401291 push ecx ; flNewProtect
.text:00401292 push 4 ; dwSize
.text:00401294 push esi ; lpAddress
.text:00401295 call ebx ; VirtualProtect
[CreateWindowExW]
.text:004012AF lea ecx, [esp+18h+flOldProtect]
.text:004012B3 push ecx ; lpflOldProtect
.text:004012B4 push 40h ; flNewProtect
.text:004012B6 push 4 ; dwSize
.text:004012B8 push esi ; lpAddress
.text:004012B9 call ebx ; VirtualProtect
.text:004012BB lea edx, [esp+18h+flOldProtect]
.text:004012BF push edx ; lpflOldProtect
.text:004012C0 mov dword ptr [esi], offset BadFunc2 ; FirstThunk overwrite
.text:004012C6 mov eax, [esp+1Ch+flOldProtect]
.text:004012CA push eax ; flNewProtect
.text:004012CB push 4 ; dwSize
.text:004012CD push esi ; lpAddress
.text:004012CE call ebx ; VirtualProtect
Basically, it changes the protection of the memory containing the import addresses, using the VirtualProtect API; then it overwrites the FirstThunk entry, related to the RegisterClassExW and CreateWindowExW APIs, with a malicious offset.
In this way, every time one of these APIs is called it won't be executed and, instead, the code located at the malicious offset will be run. Even debugging the code, if we don't step into the calls, nothing will suggest that the code is being hijacked.
As we can see the ones above seem to be normal, legitimate, calls, but they are really hijacked to the malicious routines. And here is the trick in action in the debugger:
Note that this is not API hooking, but only a simple trick that works in the executable itself: it's not the API code being overwritten, it is the FirstThunk of the malicious executable.
What can I say... It's not a very advanced deception trick, but a curious one at least: come on guys, you can do better!
First of all you can immediately notice that this sample is a simple loader: you can have a look at the IDA navigation bar to spot a tiny code section in contrast to a huge resource section.
This tells us that something is hidden somewhere in the resources. The payloads, in fact, are encrypted and stored in the dialog type resources. Here's a quick verification test, that shows that something is wrong in the dialog data:
But let's go back to the curious trick we mentioned, and let's begin by analyzing the code. If we start looking from the entry point we notice... absolutely nothing!
At a first glance nothing suggests that we are analyzing a malware, as we only go through some common APIs.
Of course, a deeper reading reveals the trick: in the middle of some legitimate calls we find a suspicious function. The thing that more captured my attention is that it makes use of the VirtualProtect API different times, apparently without any good reason, as we will see later.
For now let's start from the beginning:
.text:004011F5 push 0 ; lpModuleName
.text:004011F7 call ds:GetModuleHandleW
.text:004011FD mov ebp, eax ; MZ header
.text:004011FF mov eax, [ebp+3Ch] ; MZ.elfanew = PE offset
.text:00401202 mov esi, [eax+ebp+80h] ; import table RVA
.text:00401209 mov eax, [esi+ebp+0Ch] ; import name RVA
This code gets the handle of the application itself and then it reads: the MZ header; the PE offset; the import table RVA; the first import name RVA.
.text:0040120D add esi, ebp ; virtual address of the image import descriptor
...
.text:00401223 add eax, ebp
.text:00401225 push offset aUser32_dll_0 ; "user32.dll"
.text:0040122A push eax ; char *
.text:0040122B call __stricmp
.text:00401230 add esp, 8
...
.text:00401237 add esi, 14h
.text:0040123A mov [esp+18h+var_4], esi
.text:0040123E jmp loc_4012E6
...
.text:004012E6 mov eax, [esi+0Ch]
.text:004012E9 test eax, eax
.text:004012EB jnz loc_401223
Then the malware calculates the virtual addresses of the first image import descriptor, its import name address, and begins a loop over the import names looking for "user32.dll".
.text:00401243 mov edi, [esi]
.text:00401245 mov esi, [esi+10h]
.text:00401248 mov eax, [edi+ebp]
.text:0040124B add edi, ebp
.text:0040124D add esi, ebp
...
.text:00401257 jmp short loc_401260
...
.text:00401260 lea ecx, [eax+ebp+2] ; Name
.text:00401264 push offset aRegisterclasse ; "RegisterClassExW"
.text:00401269 push ecx ; char *
.text:0040126A call __stricmp
.text:0040126F add esp, 8
.text:00401272 test eax, eax
.text:00401274 jnz short loc_401297
...
.text:00401297 mov edx, [edi]
.text:00401299 lea eax, [edx+ebp+2]
.text:0040129D push offset aCreatewindowex ; "CreateWindowExW"
.text:004012A2 push eax ; char *
.text:004012A3 call __stricmp
.text:004012A8 add esp, 8
.text:004012AB test eax, eax
.text:004012AD jnz short loc_4012D0
...
.text:004012D0 mov eax, [edi+4]
.text:004012D3 add edi, 4
.text:004012D6 add esi, 4
.text:004012D9 test eax, eax
.text:004012DB jnz short loc_401260
Here the code saves the content of the OriginalFirstThunk and the FirstThunk fields of the IMAGE_IMPORT_DESCRIPTOR. Then, it loops over every IMAGE_IMPORT_BY_NAME.Name looking for the RegisterClassExW and the CreateWindowExW APIs.
Once they are found it does the following:
[RegisterClassExW]
.text:00401276 lea edx, [esp+18h+flOldProtect]
.text:0040127A push edx ; lpflOldProtect
.text:0040127B push 40h ; flNewProtect
.text:0040127D push 4 ; dwSize
.text:0040127F push esi ; lpAddress
.text:00401280 call ebx ; VirtualProtect
.text:00401282 lea eax, [esp+18h+flOldProtect]
.text:00401286 push eax ; lpflOldProtect
.text:00401287 mov dword ptr [esi], offset BadFunc1 ; FirstThunk overwrite
.text:0040128D mov ecx, [esp+1Ch+flOldProtect]
.text:00401291 push ecx ; flNewProtect
.text:00401292 push 4 ; dwSize
.text:00401294 push esi ; lpAddress
.text:00401295 call ebx ; VirtualProtect
[CreateWindowExW]
.text:004012AF lea ecx, [esp+18h+flOldProtect]
.text:004012B3 push ecx ; lpflOldProtect
.text:004012B4 push 40h ; flNewProtect
.text:004012B6 push 4 ; dwSize
.text:004012B8 push esi ; lpAddress
.text:004012B9 call ebx ; VirtualProtect
.text:004012BB lea edx, [esp+18h+flOldProtect]
.text:004012BF push edx ; lpflOldProtect
.text:004012C0 mov dword ptr [esi], offset BadFunc2 ; FirstThunk overwrite
.text:004012C6 mov eax, [esp+1Ch+flOldProtect]
.text:004012CA push eax ; flNewProtect
.text:004012CB push 4 ; dwSize
.text:004012CD push esi ; lpAddress
.text:004012CE call ebx ; VirtualProtect
Basically, it changes the protection of the memory containing the import addresses, using the VirtualProtect API; then it overwrites the FirstThunk entry, related to the RegisterClassExW and CreateWindowExW APIs, with a malicious offset.
In this way, every time one of these APIs is called it won't be executed and, instead, the code located at the malicious offset will be run. Even debugging the code, if we don't step into the calls, nothing will suggest that the code is being hijacked.
As we can see the ones above seem to be normal, legitimate, calls, but they are really hijacked to the malicious routines. And here is the trick in action in the debugger:
Note that this is not API hooking, but only a simple trick that works in the executable itself: it's not the API code being overwritten, it is the FirstThunk of the malicious executable.
What can I say... It's not a very advanced deception trick, but a curious one at least: come on guys, you can do better!
Subscribe to:
Posts (Atom)