Monday, December 18, 2017

aPAColypse now: Exploiting Windows 10 in a Local Network with WPAD/PAC and JScript

by Ivan Fratric, Thomas Dullien, James Forshaw and Steven Vittitoe

Intro

Many widely-deployed technologies, viewed through 20/20 hindsight, seem like an odd or unnecessarily risky idea. Engineering decisions in IT are often made with imperfect information and under time pressure, and some oddities of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IT stack can best be explained with “it seemed like a good idea at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time”. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 personal view of some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 authors of this post, WPAD (“Web Proxy Auto Discovery Protocol” - and more specifically “Proxy Auto-Config”), is one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se oddities.

At some point in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 very early days of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Internet - prior to 1996 - engineers at Netscape decided that JavaScript was a good language to write configuration files in. The result was PAC - a configuration file format that works as follows: The browser connects to a pre-configured server, downloads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC file, and executes a particular Javascript function to determine proper proxy configuration. Why not? It certainly is more expressive and less verbose than (let’s say) XML, and seems a reasonable way to provide configurations to many clients.

PAC itself was coupled with a protocol called WPAD - a protocol that makes it unnecessary for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser to have a pre-configured server to connect to. Instead, WPAD allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computer to query cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local network to determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server from which to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC file.

Somehow this technology ended up being an IETF draft which expired in 1999, and now, in 2017, every Windows machine will ask cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local network: “Hey, where can I find a Javascript file to execute?”. This can happen via a number of mechanisms: DNS, WINS, but - perhaps most interestingly - DHCP.

In recent years, browser exploits have mutated from being primarily DOM-oriented to targeting Javascript engines directly, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mere mention that we can get Javascript execution over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 network without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser was motivating. An initial investigation revealed that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JS Engine responsible for executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se configuration files was jscript.dll - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 legacy JS Engine that also powered IE7 and IE8 (and is still reachable in IE11 in IE7/8 compatibility mode if appropriate script attributes are used). This is both good and bad - on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one hand, it means that not every Chakra bug is automatically a local network remote attack, but on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hand, it means that some pretty old code will be responsible for executing our Javascript.

Security researchers have previously warned about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangers of WPAD. But, as far as we know, this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first time that an attack against WPAD is demonstrated that results in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 complete compromise of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD user’s machine.

Windows is certainly not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only piece of software that implements WPAD. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems and applications do as well. For example Google Chrome also has a WPAD implementation, but in Chrome’s case, evaluating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JavaScript code from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC file happens inside a sandbox. And ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems that support WPAD don’t enable it by default. This is why Windows is currently cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most interesting target for this sort of attack.

Web Proxy Auto-Discovery

As mentioned above, WPAD will query DHCP and DNS (in that order) to obtain a URL to connect to - apparently LLMNR and Netbios can also be used if no response from DNS is available. Some peculiarities of WPAD-over-DNS enable surprising attack vectors.

Attack scenario: Local network via DHCP

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most common scenario, a machine will query cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local DHCP server using option code 252. The DHCP server replies with a string - like “http://server.domain/proxyconfig.pac”, which specifies a URL from which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 configuration file should be fetched. The client cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n proceeds to fetch this file, and execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents as Javascript.
In a local network, an attacker can simply impersonate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DHCP server - eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r by ARP games or by racing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 legitimate DHCP. The attacker can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n provide a URL where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 malicious Javascript file is hosted.

Attack scenario: Remote over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internet via privileged position and DNS

Aside from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local-network attack scenario, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that lookup for WPAD may also happen via DNS creates a secondary attack scenario. Many users configure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir computers to perform DNS lookups against one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public, globally visible DNS servers (such as 8.8.8.8, 8.8.4.4, 208.67.222.222 and 208.67.220.220). In such a scenario, a machine will send DNS queries (such as wpad.local) to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server which sits outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local network. An attacker in a privileged position on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 network (e.g. a gateway, or any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r upstream host) can monitor cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DNS queries and spoof a reply, directing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client to download and execute a malicious Javascript file.

Setups like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se seem to be common - according to this Wikipedia entry, a nontrivial proportion of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 traffic that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DNS root servers see are .local requests.

Attack scenario: Remote over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internet via malicious wpad.tld

A particular oddity of WPAD is that it recursively walks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local machine name to find domains to query. If a machine is called “laptop01.us.division.company.com”, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following domains are supposedly queried in order:

  • wpad.us.division.company.com
  • wpad.division.company.com
  • wpad.company.com
  • wpad.com

This has (according to this Wikipedia entry) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 past led to people registering wpad.co.uk and redirecting traffic to an online auction site. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r quoting from that entry:

Through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD file, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker can point users' browsers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own proxies and intercept and modify all of WWW traffic. Although a simplistic fix for Windows WPAD handling was applied in 2005, it only fixed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 problem for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 .com domain. A presentation at Kiwicon showed that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 world was still critically vulnerable to this security hole, with a sample domain registered in New Zealand for testing purposes receiving proxy requests from all over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 country at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rate of several a second. Several of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wpad.tld domain names (including COM, NET, ORG, and US) now point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client loopback address to help protect against this vulnerability, though some names are still registered (wpad.co.uk).
Thus, an administrator should make sure that a user can trust all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DHCP servers in an organisation and that all possible wpad domains for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 organisation are under control. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's no wpad domain configured for an organisation, a user will go to whatever external location has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next wpad site in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 domain hierarchy and use that for its configuration. This allows whoever registers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wpad subdomain in a particular country to perform a man-in-cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365-middle attack on large portions of that country's internet traffic by setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves as a proxy for all traffic or sites of interest.

The IETF draft, on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hand, explicitly asks for clients to only allow “canonical” (e.g. non-top-level domains). We have not investigated to what extent clients implement this, or if second-level domains (such as .co.uk) are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 culprit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 historical cases of traffic redirection.

Eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r way: Bugs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Javascript engine under consideration can be exploited remotely via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internet if one manages to register wpad.$TLD for a given organization’s TLD, provided said TLD is not explicitly blacklisted by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client implementation. Given that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IETF draft from 1999 refers to a list of TLDs from 1994 (RFC1591), it is unlikely that clients have been updated to reflect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proliferation of new TLDs.

Our attempts to register wpad.co.$TLD for a variety of TLDs were not (yet) successful.

Bugs

We spent some time looking for bugs in jscript.dll and employed both manual analysis and fuzzing. JScript initially posed some challenge because a lot of “features” useful for triggering bugs in JavaScript engines can’t be used in JScript, simply due to it being too old to support cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. For example:

  • There are no multiple arrays types (int array, float array etc.). Thus confusing one array type for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r is not possible.
  • There are not as many optimizations (“fast paths”) as in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 newer, faster JavaScript engines. These fast paths are often cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source of bugs.
  • It is not possible to define a getter/setter on a generic JavaScript object. It is possible to call defineProperty but only on DOM objects which doesn’t work for us as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re won’t be a DOM in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD process. Even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were, a lot of JScript functions will simply fail when called on a DOM object with a message “JScript object expected”.
  • It is impossible to change an object’s prototype once it is created (i.e. cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no “__proto__” property).

However, JScript does suffer from more “old-school” vulnerability classes such as use-after-free. JScript’s garbage collector is described in this old MSDN article. JScript uses a non-generational mark-and-sweep garbage collector. Essentially, whenever a garbage collection is triggered, it marks all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JScript objects. Then it scans cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m starting from a set of “root” objects (sometimes also referred to as “scavengers”) and clears cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mark from all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objects it encounters. All cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 objects that are still marked get deleted. One recurring problem is that local variables on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack aren’t added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of root objects by default, meaning that a programmer needs to remember to add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 garbage collector’s root list, especially if those variables refer to objects that can be deleted during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function’s lifetime.

Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r possible types of vulnerabilities include buffer overflows, uninitialized variables etc.

For fuzzing, we used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 grammar-based Domato fuzzing engine and wrote a new grammar specifically for JScript. We identified interesting built-in properties and functions to add to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 grammar by looking at EnsureBuiltin methods of various JScript objects. The JScript grammar has been added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Domato repository here.

Between fuzzing and manual analysis we identified seven security vulnerabilities. They are summarized in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 table below:

Vulnerability class
Vulnerabilities affecting IE8 mode
Vulnerabilities affecting IE7 mode
Use-after-free
Heap overflow
Uninitialized variable
Out-of-bounds read
Total
7
5

At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time of publishing this blog post, all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs have been fixed by Microsoft.

The table breaks down cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerabilities by class and compatibility mode required to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. JScript in WPAD is equivalent to running a script in IE7 compatibility mode, which means that, although we found 7 vulnerabilities, “only” 5 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m can be triggered in WPAD. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r vulnerabilities can still be used against Internet Explorer (including IE11) when put into IE8 compatibility mode by a malicious webpage.

Exploit


Understanding JScript VARs and Strings

Since in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remainder of this blogpost we’re going to talk about JScript VARs and Strings a lot, it is useful to describe cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se before going deeper into how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploits work.

JScript VAR is a 24-byte (on 64-bit builds) structure that represents a JavaScript variable and is essentially cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VARIANT data structure described in this MSDN article. In most cases (sufficient to follow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit) its memory layout looks like this:

Offset
Size
Description
0
2
Variable type, 3 for integer, 5 for double, 8 for string etc.
8
8
Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type, eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r an immediate value or a pointer
16
8
Unused for most types

For example, we can represent a double precision number by a VAR that has 5 written in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 2 bytes (indicating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 double type), followed by an actual double value at offset 8. The last 8 bytes are going to be unused but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are going to be copied around if a value of anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r VAR is copied from this VAR.

A JScript string is a type of VAR that has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type 8 and a pointer at offset 8. The pointer points into a BSTR structure described here. On 64-bit builds BSTR layout looks like this:

Offset
Size
Description
0
4
Unused
4
4
String length in bytes not counting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 null character at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end
8
length+2
String characters (16-bit) followed by a null character

A String VAR points directly to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 character array, which means that, to obtain a String's length, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer needs to be decremented by 4 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re. Note that BSTRs are handled by OleAut32.dll and are allocated on a separate heap (i.e. a different heap than is being used for ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r JScript objects).

Freeing of BSTRs is also different than for most objects because, instead of directly freeing a BSTR, when SysFreeString is called, it first puts a string in a cache controlled by OleAut32.dll. This mechanism is described in detail in Heap Feng Shui in JavaScript.

Stage 1: Infoleak

The purpose of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak will be to obtain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of a string in memory whose content we fully control. We won’t be leaking any executable module addresses at this point, that will come later. Instead, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 goal is to defeat high-entropy heap randomization and make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second stage of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit reliable without having to use heap spraying.

For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak we’re going to use this bug in RegExp.lastParen. To understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug let’s first take a closer look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory layout of jscript!RegExpFncObj which corresponds to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JScript RegExp object. At offset 0xAC RegExpFncObj contains a buffer of 20 integers. Actually cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se are 10 pairs of integers: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first element of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pair is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start index into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input string and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second element is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end index. Whenever RegExp.test, RegExp.exec or String.search with a RegExp parameter encounter a capturing group (parencá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ses in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RegExp syntax), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start and end index of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 match are stored here. Obviously in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is space for only 10 matches, so only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 10 matches are stored in this buffer.

However, if RegExp.lastParen is called and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were more than 10 capturing groups, RegExpFncObj::LastParen will happily use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of capturing groups as an index into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer, leading to out-of-bounds read. Here is a PoC:

 var r= new RegExp(Array(100).join('()'));
 ''.search(r);
 alert(RegExp.lastParen);

The 2 indices (let’s call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m start_index and end_index) are read outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer and can thus be made arbitrarily large. Assuming this first out-of-bounds access doesn’t cause a crash, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values in those indices are larger than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input string, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n a second out-of-bounds access is going to occur which allows us to read a outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input string. The string content read out-of-bounds like this is going to be returned to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller in a String variable where it can be examined.

This second out-of-bounds read is what we’re going to use, but first we need to figure out how to get controlled data into start_index and end_index. Fortunately, looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 layout of RegExpFncObj, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is data we control after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 index buffer: RegExp.input value. By setting RegExp.input to an integer value and using a RegExp composed of 41 sets of empty parencá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ses, when  RegExp.lastParen gets called, start_index is going to be 0 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end_index is going to be whatever value we wrote to RegExp.input.

If we make an input string adjacent to a freed string, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n by reading after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bounds of input string, we can obtain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap metadata such as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r free heap segments (Left, Right and Parent node in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 red-black tree of heap chunks, see Windows 10 Segment Heap Internals for more information). Image 1 shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant objects at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 moment of infoleak.

Image 1: Heap infoleak layout

We are using 20000 bytes-long strings as input in order for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m not to be allocated on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Low Fragmentation Heap (LFH can only be used for allocations of 16K bytes and smaller) since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap metadata for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LFH is different and does not include useful pointers in Windows 10 Segment Heap. Additionally, LFH introduces randomness that would affect our ability to place cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input string next to a freed string.

By reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap metadata out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 returned string, we can obtain an address of a freed string. Then, if we allocate a string of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed string, it might be placed at this address and we achieved our goal, that is we know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of memory of a string whose content we control.

The whole infoleak process looks like this:

  1. Allocate 1000 10000-character strings (note: 10000 characters == 20000 bytes).
  2. Free every second one.
  3. Trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 info leak bug. Use one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining strings as an input strings and read 20080 bytes.
  4. Analyze cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 leaked string and obtain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer to one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed strings.
  5. Allocate 500 strings of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same length as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed strings (10000 characters) with a specially crafted content.

The content of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 specially crafted strings is not important at this stage, but will be important in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next one, so it will be described cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re. Also note that, by examining heap metadata, we can easily determine which heap implementation cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process is using (Segment Heap vs NT heap).

Images 2 and 3 show heap visualization created using Heap History Viewer at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak. Green stripes represent allocated blocks (occupied by strings), grey stripes represent allocated blocks that are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n freed by later allocated again (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stings we free and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reallocate after triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak bug) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 white stripes represent data that is never allocated (guard pages). You can see how strings get allocated as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time passes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n half of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m are freed (grey ones) and sometime later get allocated again (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stripes become green).

We can see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are going to be guard pages after every 3 allocations of this size. Our exploit is never actually going to touch any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se guard pages (it reads too little data past cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string for that to occur) but in ⅓ of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cases cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re won’t be a free string after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input string for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 expected heap metadata will be missing. We can, however, easily detect this case and eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 infoleak bug using anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r input string or silently abort cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit (note: we didn’t trigger any memory corruption up to this point).

Image 2: Heap Diagram: Showing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 evolution of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap over time
Image 3: Step-by-step illustration of leaking a pointer to a string.

Stage 2: Overflow

In stage 2 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit we’re going to use this heap overflow bug in Array.sort. In case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of elements in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input array to Array.sort is larger than Array.length / 2, JsArrayStringHeapSort (called by Array.sort if a comparison function isn’t specified) is going to allocate a temporary buffer of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of elements currently in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array (note: can be smaller than array.lenght). It is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n going to attempt to retrieve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding elements for every array index from 0 to Array.length and, if that element exists, add it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer and convert to string. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array doesn’t change during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of JsArrayStringHeapSort, this will work fine. However, JsArrayStringHeapSort converts array elements into strings which can trigger toString() callbacks. If during one of those toString() callbacks elements are added to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y were previously undefined, an overflow is going to occur.

To understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug and its exploitability better let’s take a closer look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer we’ll overflow out of. It is already mentioned that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array will have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same size as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of elements currently in input array (to be exact, it is going to be number of elements + 1). Each element of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array is going to be 48 bytes in size (in a 64-bit build) with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following structure:

Offset
Size
Descripion
0
8
Pointer to a string VAR after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original VAR at offset 16 is converted to string
8
4
Index (int) of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current element
16
24
VAR holding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original array element
40
4
int 0 or 1 depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type of VAR at offset 16

During JsArrayStringHeapSort, each element of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array with index < array.length is retrieved, and if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 element is defined cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following happens:

  1. The array element is read into VAR at offset 16
  2. The original VAR is converted into a string VAR. A pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string VAR is written at offset 0.
  3. At offset 8, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 index of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current element in array is written
  4. Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original VAR type, 0 or 1 is written at offset 40

Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 temporary buffer, we don’t control a lot of it directly. If an array member is a string, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n at offsets 0 and 24 we’re going to have a pointer that, when dereferenced, at offset 8 contains anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data we control. This is, however, one level of indirection larger than what would be useful to us in most situations.

However, if a member of array is a double precision number, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n at offset 24 (corresponding to offset 8 into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original VAR) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of that number is going to be written and it is directly under our control. If we create a number with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same double representation as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer obtained in Stage 1, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we can use our overflow to overwrite a pointer somewhere after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer with a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory we directly control.

Now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 question becomes, what can we overwrite in this way to advance cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 possible answers presents itself if we take a closer look at how Objects work in JScript.

Each Object (more specifically, a NameList JScript object) is going to have a pointer to a hashtable. This hashtable is just an array of pointers. When a member element of an Object is accessed, a hash of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 element is computed. Then, a pointer at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offset corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lowest bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hash is dereferenced. This pointer points to a linked list of object elements and this linked list is traversed until we reached an element with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same name as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requested element. This is shown in image 4.

Image 4: JScript Object element internals

Note that, when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 element is less than 4 bytes, it is stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same structure as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VAR (element value). Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is going to be a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 element name. Name lengths <=4 are sufficient for us so we don’t need to go into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 details of this.

An Object hashtable is a good candidate to overwrite because:

  • We can control which elements of it are dereferenced by accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding object members. Elements we overwrite with data we don’t control will simply never be accessed.
  • We have limited control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hashtable size by controlling how many members cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding object has. For example a hashtable starts with 1024 bytes, but if we add more than 512 elements to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hashtable will be reallocated to 8192 bytes.
  • By overwriting a hashtable pointer with a pointer to data we control, we can create fake JScript vars in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data we control and access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m simply by accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding object members.

To perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overwrite reliably we do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

  1. Allocate and free a lot of memory blocks with size 8192. This will turn on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Low Fragmentation Heap for allocation of size 8192. This will ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer we are overflowing out of, as well as hashtable we are overflowing into will be allocated on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LFH. This is important because it means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re will be no ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r allocations of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r sizes nearby to spoil cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit attempt (since an LFH bucket can only contain allocations of a certain size). This in turn ensures that we will be overwriting exactly what we want with high reliability.
  2. Create 2000 objects, each containing 512 members. In this state, each object has a hashtable of 1024 bytes. However, adding just one more element to one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se objects will cause its hashtable to grow to 8192 bytes.
  3. Add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 513 element to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 1000 objects, causing 1000 allocations of 8192-byte hashtables.
  4. Trigger Array.sort with an array with length=300 and 170 elements. This allocates a buffer of size (170+1)*48=8208 bytes. Due to LFH granularity this object will be allocated in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same LFH bucket as 8192-byte hashtables.
  5. Immediately (in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 toString() method of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first array element) add 513th element to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second 1000 objects. This makes us pretty certain that by now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sort buffer is neighboring one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hashtables. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same toString() method also add more elements to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array which will cause it to grow out-of-bounds.

Image 5 shows heap visualization around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sort buffer (red line). You can see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sort buffer is surrounded by allocations of similar size which all correspond to Object hashtables. You can also observe cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LFH randomness in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sense that subsequent allocations are not necessarily on subsequent addresses, however this makes no difference for our exploit.

Image 5: Heap visualization around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow buffer

As mentioned previously, we crafted our overflow in such a way that some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hashtable pointers of an unlucky JScript object will get overwritten with pointers into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data we control. Now finally what exactly we put into this data comes into play: we crafted it in such a way that it contains 5 (fake) JavaScript variables:

  • Variable 1 just contains number 1337.
  • Variable 2 is of special type 0x400C. This type basically tells JavaScript that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual VAR is pointed to by pointer at offset 8, and this pointer should be dereferenced before reading or writing this variable. In our case, this pointer points 16 bytes before Variable 1. This basically means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last 8-byte qword of Variable 2 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 8-byte qword of Variable 1 overlap.
  • Variable 3, Variable 4 and Variable 5 are simple integers. What is special about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y contain numbers 5, 8 and 0x400C in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir last 8 bytes, respectively.

The state of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corrupted Object after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow is shown in image 6.

Image 6: State of objects after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow. Red areas indicate where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow occurred. Each box in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bottom row (except those marked as ‘...’) corresponds to 8 bytes. Data contained in ‘...’ boxes is omitted for clarity

We can access Variable 1 by simply accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corrupted object at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct index (let’s call it index1) and similarly for Variables 2-5. In fact, we can detect which Object we corrupted by accessing index1 of all objects and seeing which now has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 1337.

Overlapping Variable 1 and Variable 2 has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect that we can change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type (first WORD) of Variable 1 into 5 (double), 8 (string) or 0x400C (pointer). We do this by reading Variable 2, 3 or 4 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read value into Variable 2. For example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 statement

corruptedobject[index2] = corruptedobject[index4];

Has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type of Variable 1 will be changed into a String (8), while all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r fields of Variable 1 will remain unchanged.

This layout gives us several very powerful exploitation primitives:

  • If we write some variable that contains a pointer into Variable 1, we can disclose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of this pointer by changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type of Variable 1 to double (5) and reading it out
  • We can disclose (read) memory at an arbitrary address by faking a String at that address. We can accomplish this by first writing a double value corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address we want to read into Variable 1 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type of Variable 1 toString (8).
  • We can write to an arbitrary address by first writing a numeric value corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address into Variable 1, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type of Variable 1 to 0x400C (pointer) and finally writing some data to Variable 1.

With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se exploit primitives, normally getting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code execution would be pretty simple, but since we’re exploiting Windows 10 we first need to bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Control Flow Guard (CFG).

Stage 3: CFG bypass

There are probably ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r known bypasses we could have used here, but it turns out that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are some very convenient bypasses (once attacker has a read/write primitive) specific to jscript.dll. We are going to exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 facts that:

  • Return addresses are not protected by CFG
  • Some Jscript objects have pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 native stack

Specifically, each NameTbl object (in Jscript, all JavaScript objects inherit from NameTbl), at offset 24 holds a pointer to CSession object. CSession object, at offset 80 holds a pointer to near cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 native stack.

Thus, with an arbitrary read, by following a chain of pointers from any JScript object, it is possible to retrieve a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 native stack. Then, with an arbitrary write, it is possible to overwrite a return address, bypassing CFG.

Stage 4: Getting code execution as Local Service

With all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit elements in place, we can now proceed to getting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code execution. We are doing it in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se steps:

  1. Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of jscript.dll from a vtable of any JScript object
  2. Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of kernel32.dll by reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 import table of jscript.dll
  3. Read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of kernelbase.dll by reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 import table of kernel32.dll
  4. Scan kernel32.dll for rop gadgets we are going to need
  5. Get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of WinExec from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 export table of kernel32.dll
  6. Leak cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack address as explained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous section
  7. Prepare cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ROP chain and write it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, starting with a return address closest to our leaked stack address.

The ROP chain we are using looks like this:

[address of RET]  //needed to align cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack to 16 bytes
[address of POP RCX; RET] //loads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first parameter into rcx
[address of command to execute]
[address of POP RDX; RET] //loads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second parameter into rdx
1
[address of WinExec]

By executing this ROP chain we are calling WinExec with a command we specified. For example, if we run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command ‘cmd’ we are going to see a command prompt being spawned, running as Local Service (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same user WPAD service runs as).

Unfortunately, from a child process running as Local Service, we can’t talk to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 network, but what we can do is drop our privilege escalation payload from memory to a disk location Local Service can write and execute it from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re.

Stage 5: Privilege escalation

While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Local Service account is a service account, it doesn’t have administrative privileges. This means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit is quite limited in what it can access and modify on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system, especially to persist after exploitation or after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system has been rebooted. While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s always likely to be an unfixed privilege escalation in Windows we don’t need to find a new vulnerability to escalate our privileges. Instead we can abuse a built-in feature to escalate from Local Service to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SYSTEM account. Let’s look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privileges that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 service account for WPAD has been granted:

Image 7: Service Access Token’s Privileges showing Impersonate Privilege

We’ve only got three privileges, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highlighted privilege, SeImpersonatePrivilege is important. This privilege allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 service to impersonate ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r users on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local system. The reason cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 service has impersonate privilege is it accepts requests from all users on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local system and might need to perform actions on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir behalf. However, as long as we can get an access token for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 account we want to impersonate we can get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full access rights of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token’s user account, including SYSTEM which would give us administrator rights on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local system.

Abusing impersonation is a known issue with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows security model (you can find more details by searching for Token Kidnapping). Microsoft have tried to make it harder to get an access token for a privileged user but it’s virtually impossible to close all possible routes. For example, James discovered a vulnerability in Windows’ implementation of DCOM which allows any user to get access to a SYSTEM access token. While Microsoft fixed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 direct privilege escalation vulnerability cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y didn’t, or perhaps couldn’t, fix cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token kidnapping issue. We can abuse this feature to capture cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SYSTEM token, impersonate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n completely compromise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system, such as installing a privileged service.

There’s an existing implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 token kidnapping via DCOM (RottenPotato) however cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation was designed for use with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Metasploit framework’s getsystem command which we’re not using. Therefore, we implemented our own simpler version in C++ which directly spawns an arbitrary process with a SYSTEM token using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CreateProcessWithToken API. As a bonus we were able to compile it to an executable of 11KiB in size, much smaller than RottenPotato, which made it easier to drop to disk and run from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ROP payload.

Tying it all togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r

When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service queries for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC file, we serve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit file which exploits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service and runs WinExec to drop and execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privilege escalation binary. This binary cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n executes a command (hardcoded ‘cmd’ in our case) as SYSTEM.

The exploit worked pretty reliably in our experiments, but it is interesting to note that a 100% reliable exploit isn’t required - if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit crashes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service, a new instance is going to get spawned when a client makes anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r request from WPAD service, so an attacker can just try again. There will be no indication in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 UI that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service has crashed, although Window Error Reporting will likely pick up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 crash and report it to Microsoft, provided that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user didn’t disable it.

In fact, our exploit doesn’t clean up gracefully and will crash cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service once it runs its payload, so if we keep serving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit PAC file after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 service has been exploited, it will just get exploited again. You can see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 effect of that in Image 7, which was taken after leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit server running for some minutes and making a lot of HTTP requests in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim machine.

Image 7: Did we leave cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit running for too long?

We’ll publish cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit source code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue tracker shortly.

Conclusion

Executing untrusted JavaScript code is dangerous, and executing it in an unsandboxed process is even more so. This is true even if it’s done by a relatively compact JavaScript engine such as jscript.dll. We identified 7 security vulnerabilities in it and successfully demonstrated reliable code execution from local network (and beyond) against a fully patched (at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time of writing) Windows 10 64-bit with Fall Creators Update installed.

Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs are now fixed, does this mean we are done and can go home? Unlikely. Although we spent a fair amount of time, effort and compute power on finding jscript.dll bugs, we make no claims that we found all of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. In fact, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are 7 bugs, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is likely to be an 8th. So if something doesn’t change it is quite possible we’ll see a chain like this used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wild someday (and that is, of course, optimistically assuming that attackers don’t have this capability already).

So, what can Microsoft do to make future attacks like this harder:

  • Disable WPAD by default. In fact, while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems support WPAD, Windows is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only one where it is enabled by default.
  • Sandbox cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JScript interpreter inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WPAD service. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interpreter needs to execute a JavaScript function with well defined inputs and return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output string, sandboxing it should be pretty straightforward. Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 simplicity of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input-output model, it would be great if Microsoft introduced a sandbox of comparable restrictiveness to seccomp-strict: Some processes really do not need more privileges than “receive a bit of data”, “perform a bit of computation”, “return a bit of data”.

In case you want to take action on your own, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only way to prevent this type of attack using new, currently unknown vulnerabilities, seems to be to completely disable cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WinHttpAutoProxySvc service. Sometimes this can’t be done in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Services UI (“Startup type” control will be grayed out) due to ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r services depending on WPAD, but it can be done via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding registry entry. Under “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinHttpAutoProxySvc” change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of “Start” from 3 (manual) to 4 (disabled).

These are some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 advices commonly found online when searching for “disabling WPAD” that did not work to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack in our experiments:

  • Turning off “Automatically detect settings” in Control Panel
  • Setting “WpadOverride” registry key
  • Putting “255.255.255.255 wpad” in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hosts file (this is going to stop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DNS variant but likely not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DHCP variant)