看从零到一第一遍有没懂的地方记录一下
长篇大论:
现代操作系统往往有比较完善的MPU机制,可以按照内存页的粒度设置进程的内存使用权限。内存权限分别有可读(R)、可写(W)和可执行(X)。一旦CPU执行了没有可执行权限的内存上的代码,操作系统会立即终止程序。
在默认情况下,基于漏洞缓解的规则,程序中不会存在同时具有可写和可执行权限的内存,所以无法通过修改程序的代码段或者数据段来执行任意代码。针对这种漏洞缓解机制,有一种通过返回到程序中特定的指令序列从而控制程序执行流程的攻击技术,被称为返回导向式编程(Return-Oriented Programming,ROP)。
利用以ret(0xc3)指令结尾的指令片段(gadget)构建一条ROP链,来实现任意指令执行,最终实现任意代码执行。具体步骤为:寻找程序可执行的内存段中所有的ret指令,然后查看在ret前的字节是否包含有效指令;如果有,则标记片段为一个可用的片段,找到一系列这样的以ret结束的指令后,则将这些指令的地址按顺序放在栈上;这样,每次在执行完相应的指令后,其结尾的ret指令会将程序控制流传递给栈顶的新的Gadget继续执行。栈上的这段连续的Gadget就构成了一条ROP链,从而实现任意指令执行。
过程:
例程:https://buuoj.cn/challenges#[第六章 CTF之PWN章]ROP
用ROPgadget --binary rop
得到所有gadget
太少了,所以要搞到libc的加载地址,然后在里面找syscall
用这个payload可以获得puts的地址(pop_rdi是pop rdi的gadget)。
具体原理:
返回地址在0x12个字节后面,
执行到leave
时pop_rdi
在栈顶(上面的被清理了)
执行到ret
时,会跳到pop_rdi
所在的地址,并把栈顶的pop_rdi
弹出去,所以之后栈顶是puts_got
puts_got
是指向puts
真实地址的指针
然后执行pop_rdi
,将puts_got
放进rdi
。
ret到puts
,根据调用约定(参考程序已有的puts调用),rdi
是puts
的参数,所以地址就被输出了。