As level13
, running ls
gives us a binary file called level13
...
$> ./level13
UID 2013 started us but we we expect 4242
Using ltrace
we can see what this binary actually does.
$> ltrace ./level13
__libc_start_main(0x804858c, 1, 0xbffff7d4, 0x80485f0, 0x8048660 <unfinished ...>
getuid() = 2013
getuid() = 2013
printf("UID %d started us but we we expe"..., 2013UID 2013 started us but we we expect 4242
) = 42
exit(1 <unfinished ...>
+++ exited (status 1) +++
This binary makes a simple system call to get the UID of the user running it and want this UID to equal 4242
. One way we can achieve this is by a powerful feature of linker using environment variable LD_PRELOAD
. It grants us the hability to perform code injection to replace existing symbols that are pulled from other libraries like the standard ones.
First we'll need to create our own version of getuid()
.
#include <unistd.h>
uid_t getuid(void)
{
return 4242;
}
Like requested by the program, it will return
4242
no matter which user is running it.
Then we compile this if in a shared object library before adding it to the LD_PRELOAD
variable.
$> cd /tmp/
$> gcc -fPIC -shared -o lib.so getuid.c
$> export LD_PRELOAD="/tmp/lib.so"
The
-fPIC
option is needed to create position independant code, which means it can be placed anywhere in the memory without breaking anything. It needed most of the time wen you create shared libraries.
Final trick is to make a copy of the original binary to drop the setuid bit and run it as we would normaly do.
$> cp /home/user/level13/level13 /tmp/level13
$> /tmp/level13
your token is 2A31L79asukciNyi8uppkEuSx
So, 2A31L79asukciNyi8uppkEuSx
is the flag for this level.
-
Custom
getuid()
functionprintf '#include <unistd>\nuid_t getuid(void){return 4242;}\n' > /tmp/getuid.c