基本环境与工具
测试与脚本均在linux下比较好。靶机使用docker
可采用ubuntu16或者kali
需要的环境:
linux的基本开发环境。
gdb-peda:增强gdb功能。
checksec:检测可执行程序开启的保护。peda插件中含有,单独脚本也行。
apt依赖错误就换下源,阿里的源好用些,ubuntu的太旧了
WSL下还没折腾,暂时先用纯linux环境方便减少问题,开发就用vim,在linux下就不用图形ide了。
前置必知
python2.7
见博客-python速记
pwntool
https://pwntoolsdocinzh-cn.readthedocs.io/en/master/index.html#
py库,pwn的工具
LibcSearcher
https://github.com/lieanu/LibcSearcher
py库,用于根据libc中某函数的地址确定libc版本。linux下即使用ASLR保护,但是最低的12位不会变,因此可以确定。
gdb-peda
概观检测
gdb 文件名:直接加载
peda检查命令:
aslr:查看ASLR开关-aslr针对栈、堆、so的基地址随机化
checksec:检查二进制文件安全选项
vmmap查看内存映射信息。
调试
start :开始调试并停在入口
c:继续运行
r:重新开始运行
n:步过
s:步入
fini:运行至函数结束
断点:
b *0x地址:不写为当前
b 符号
info b:断点信息
delete num:删除断点
内存断点:
watch 表达式:写中断
rwarch 表达式:读中断
awatch 表达式:读或写
表达式表示的内容就是监视内容,如*(int *)0x08044530 为监控该地址的int类型数据
查看数据
stack n:查看栈数据
bt:查看栈帧
p $eax:查看寄存器-$表示取寄存器的值
vmmap:内存布局
x /数量x(进制)g(输出格式) 地址
ROPgadget
帮助寻找程序片段的工具,方便rop的利用。
–binary指定目标名。
–only搜索指定的汇编指令组合。
如:
–string 搜索指定字串位置
Stack溢出
demo1-alloff
|
|
|
|
编译:
结构
一直跟着ebp链就行。ebp指向的是存父调用的ebp栈地址。因此跟着ebp链可以确定调用顺序。esp始终是栈的极限位置,ebp始终过程的栈区域开始。ebp指向的位置存的是上次的ebp值。再往上一个地址长度的地址为上次的esp值,其地址上内容为返回地址。
exp
栈溢出基本步骤
栈溢出危险函数:
gets scanf vscanf sprintf strcpy strcat bcopy
填充长度-直接使用ida分析,跟准ebp和esp即可。
主要目的是影响执行流程
demo-canary
canary保护在函数一开始向栈中加入数作为检测,返回时检测该数是否被修改。
该值在linux下使用TLS(线程局部存储)中的随机数。
绕过:
方法一
泄露栈中的Canary
需要合适的输出函数,溢出俩次
exp
注意canary的值是小端存储的,还有io.recv()时会读取上次溢出时输出返回读剩下的数据,因为抽象出的输出输入都是流。
方法二:
one-by-one 爆破 Canary
每个线程的canary都不一样,但是fork后内存空间相同,可以使用爆破得到内存数据。
方法三:
劫持stack_chk_fail,直接修改got即可
方法四:
覆盖 TLS 中储存的 Canary 值
基本ROP
主要为了针对NX保护,数据页不可执行,防止直接向栈与堆中注入代码。
核心思想利用已有片段控制执行流程。
demo1-ret2text
https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text
直接返回到已有代码段利用。
调试到gets,$ebp-$eax=6c,所以返回地址在gets缓存的上方0x70处。
目标直接定位到执行system(“/bin/sh”)的语句
exp
demo2-ret2shellcode
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode
注入代码到内存中,之后返回利用
elf文件中标明不可执行的段实际加载后可能是能写的,在gdb中使用vmmap查看即可。
栈中可能受破坏,执行bss中的注入代码。
exp
demo3-ret2syscall
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall/rop
利用代码片段一点一点完成系统调用。
首先利用ROPgadget获得pop,ret这样可以利用栈修改寄存器的片段,与字串地址,之后全部放到栈中通过ret依次调用。
exp3:
demo4-ret2libc
控制流程执行libc中的函数,通常是返回至某个函数的plt处或者具体位置。
三个例子
第一个-已有system与sh字串
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc1/ret2libc1
直接在ida中找到system的plt,构建栈状态返回至system
第二个-有system无sh字串
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc2/ret2libc2
需要自己用gets读到bss中,因为栈的地址不确定所以栈中不好放数据。
第二个-无system无sh字串
https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2libc/ret2libc3/ret2libc3
这是由于目标程序没有使用libc的system,需要自己定位位置
首先泄露libc中某个函数的地址,由于延迟绑定,需要泄露已经执行过的函数。因为got表中存的是引用其他模块符号的地址,而got表是属于此模块的,因此可以通过固定的地址找到。之后根据泄露的地址确定libc版本与基地址,找到system与sh地址,返回到main再次执行程序不重启,执行system。
注意这集个libc程序ida都没有分析准确变量的偏移,因为变量是用esp计算的。且esp前后与FFF0h变化不同(为了访问速度对齐用的),因此第一次变量距离ebp为108,第二次为100。
LibcSearcher会提示多个库可选。
中级ROP
利用些巧妙的Gadgets
ret2csu
一个x64程序只开NX
利用的是编译器添加的libc初始化函数libc_csu_init
0x000000000040061A 可以用来控制rbx,rbp,r12,r13,r14,r15
0x0000000000400600 到 0x0000000000400609,通过r13、r14、r15d控制rdx、rsi、edi,控制r12 与 rbx可调用某地址的函数。
0x000000000040060D 控制 rbx+1 = rbp即可不跳转,继续执行返回。
exp:
因此可以利用一些类似的gcc编译进去的函数:
PC将地址处的数据给cpu,cpu解码成功就可以执行,因此可以将一些地址偏移从而执行想要的指令。—很好的思路!
例如刚才的0x000000000040061A
ret2reg
当数据地址可执行时。
溢出返回时某寄存器指向溢出地址,查找 call reg 或者 jmp reg 指令,将 EIP 设置为该指令地址,从而执行shellcode
BROP
高级ROP
ret2_dl_runtime_resolve
由于程序没有使用libc的sys,且无法定位libc的位置。
got的内容:
plt的实现:
_dl_runtime_resolve解析过程-根据该模块的重定位表对应项,找到符号表对应项,从而找到符号名字与位置。在已加载的符号表中搜索,进行重定位。
要求不会检查符号是否越界,解析依赖于给定的字串。
正常攻击:
无Canary保护
|
|
使用roputil:
|
|
SROP
信号是在软件层次上对中断机制的一种模拟。
执行流程:
内核发起signal、signal数据push到栈中、sigreturn syscall的位置 push 进栈、跳转至signal handler、转至 sigreturn code、stack 即栈上的内容全部 pop 回register ,流程又重新回到 user code
中间经过内核俩次,分别再执行handler前后。sigreturn为系统调用,作用是根据signal数据还原、返回其中指明的地址。
攻击原理:
伪造sigcontext 结构,push进stack中
栈溢出设置ret address在sigreturn syscall的gadget
将signal fram中的rip(eip)设置在syscall(int 0x80)
当sigreturn返回时,就可以执行syscall
这样做的好处是sigreturn利用栈中的数据将寄存器都设置成可控制的了,同时由于sp也可操控因此ip可以指向syscall;ret;之后在新栈中数据预先仍是sigcontext即可再执行一次上述行为。形成一条执行任意系统调用的链,并且参数全部可控。
可直接使用pwntool工具:
花式栈溢出
stack pivoting
劫持栈指针指向攻击者所能控制的内存处,然后再在相应的位置进行 ROP。
需求原因:
- 可以控制的栈溢出字节较少,需要构造长的rop
- 开启了pie,栈地址未知,需要劫持到已知区域
- 其它漏洞难以利用,我们需要进行转换,比如说将栈劫持到堆空间,从而在堆上写 rop 及进行堆漏洞利用
要求:
可以控制程序执行流、可以控制sp指针。
可控制内容的内存一般为bss与heap
frame faking
构造一个虚假的栈帧来控制程序的执行流。
构造溢出如下
buffer padding|fake ebp|leave ret addr|
构造假栈:
第一次程序的leave ret,会将ebp控制,之后再次执行leave ret控制esp,同时新的栈顶控制ebp
Stack smash
stack_chk_fail 函数来打印 argv[0] 指针所指向的字符串
因此可以利用开启canary的程序打印些内容。
partial overwrite
在开启了随机化(ASLR,PIE)后, 无论高位的地址如何变化,低 12 位的页内偏移始终是固定的, 也就是说如果我们能更改低位的偏移, 就可以在一定程度上控制程序的执行流, 绕过 PIE 保护。
可以暴力。
栈溢出利用总结
CANARY : 栈防护,在栈中传入值返回时检测
需要多次泄露
NX : 数据页不可执行
ROP
PIE : 地址随机化-针对可执行文件&so
爆破&rop泄露地址
后续做题进一步细分情况