Skip to content
Rahul Sridhar edited this page Mar 28, 2021 · 7 revisions

Most binaries use shared libraries rather than static libraries: what this means is that the library is linked-in at load-time rather than at compile-time. ("Dynamic-linking vs. static linking").

Detecting dynamic linking

You can detect dynamic linking using the file command:

[~]> file <binary>
<binary>: ELF 32-bit LSB  executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=da6b1da4bbc22b3072aef80a990e75f9df778498, not stripped

This binary is statically linked.

[~]> file <binary>
<binary>: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=15638c51c0168f90b778e3ae1dee8e553bcf769b, not stripped

while this one is dynamically linked.

Identifying shared library paths

Use the ldd command:

[~]> ldd binary
	linux-gate.so.1 =>  (0xf7767000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7568000)
	/lib/ld-linux.so.2 (0xf7740000)

In most cases we're interested in the one that is called libc (libc.so.6 in this case).

Running a binary with a custom shared library (aka the LD_PRELOAD trick)

The libc on your computer is gonna be very different from the libc on my computer. So, if there's a CTF challenge where the binary is running on some remote machine, you need to craft your artisanal ROP-chain using the libc on the remote machine, not your machine. In these cases, the organizers will provide libc along with the vulnerable binary itself. (If they don't, that's a strong indication that the solution involves shellcode, and not ROP). To run the binary with this custom libc, you might need to patch your executable to use the correct dynamic linker (you can use change-ld.py to do this along with the corresponding ld file in glibc_versions). After patching, you can use LD_PRELOAD to specify the libc file:

LD_PRELOAD=/path/to/my/malloc.so /bin/ls

To debug the binary in gdb with a specified library, you can do:

gdb> set exec-wrapper env 'LD_PRELOAD=/path/to/sobject/yourobject.so'
gdb> file /path/to/binary/yourbin
gdb> r

The exec-wrapper ensures that the linked library is used only for the binary in question, and not bash or gdb itself.

Finding ROP gadgets in shared libraries

Simply use ROPGadget as you'd do normally:

ROPGadget --binary /lib/i386-linux-gnu/libc.so.6 > rop_gadgets

Find specific function offset in libc

If we leaked libc address of certain function successfully, we could use get libc base address by subtracting the offset of that function.

Manually

  • readelf -s $libc | grep ${function}@

E.g.

$ readelf -s libc-2.19.so | grep system@
    620: 00040310    56 FUNC    GLOBAL DEFAULT   12 __libc_system@@GLIBC_PRIVATE
   1443: 00040310    56 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0

Automatically

  • Use pwntools, then you can use it in your exploit script.

E.g.

from pwn import *

libc = ELF('libc.so')
system_off = libc.symbols['system']

Once you know libc's base address, you can set it in pwntools and then reference specific symbols:

elf = ELF('./my_binary')
libc = elf.libc
libc.address = printf_addr - libc.symbols.printf
system_addr = libc.symbols.system

Find '/bin/sh' or 'sh' in library

Need libc base address first

Manually

  • objdump -s libc.so | less then search 'sh'
  • strings -tx libc.so | grep /bin/sh

Automatically

E.g.

from pwn import *

libc = ELF('libc.so')
...
sh = base + next(libc.search('sh\x00'))
binsh = base + next(libc.search('/bin/sh\x00'))

Identifying libc from leaked addresses on a remote server

Use https://libc.blukat.me

Alternatives: libcdb.com, https://github.com/niklasb/libc-database