Exploitation 149
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
return(ret);
}
The important part of this function is shown in bold. The fork() function
starts a new process, and the execl() function is used to run the command
through /bin/sh with the appropriate command-line arguments.
The use of system() can sometimes cause problems. If a setuid program
uses system(), the privileges won’t be transferred, because /bin/sh has been
dropping privileges since version two. This isn’t the case with our exploit, but
the exploit doesn’t really need to be starting a new process, either. We can
ignore the fork() and just focus on the execl() function to run the command.
The execl() function belongs to a family of functions that execute com-
mands by replacing the current process with the new one. The arguments for
execl() start with the path to the target program and are followed by each of
the command-line arguments. The second function argument is actually the
zeroth command-line argument, which is the name of the program. The last
argument is a NULL to terminate the argument list, similar to how a null
byte terminates a string.
The execl() function has a sister function called execle(), which has one
additional argument to specify the environment under which the executing
process should run. This environment is presented in the form of an array of
pointers to null-terminated strings for each environment variable, and the
environment array itself is terminated with a NULL pointer.
With execl(), the existing environment is used, but if you use execle(),
the entire environment can be specified. If the environment array is just the
shellcode as the first string (with a NULL pointer to terminate the list), the
only environment variable will be the shellcode. This makes its address easy
to calculate. In Linux, the address will be 0xbffffffa, minus the length of the
shellcode in the environment, minus the length of the name of the executed
program. Since this address will be exact, there is no need for a NOP sled. All
that’s needed in the exploit buffer is the address, repeated enough times to
overflow the return address in the stack, as shown in exploit_nosearch_env.c.
exploit_notesearch_env.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80";
int main(int argc, char argv[]) {
char env[2] = {shellcode, 0};
unsigned int i, ret;