文章目录
基础知识
寄存器:重要寄存器
- 重要寄存器:rsp/esp,pc,rbp/ebp,rax/eax,rdi,rdx,rcx
栈:一种先进先出的数据结构
- 程序中的栈:
- 内存中的一块区域,用栈的结构来管理,从高地址向低地址增长
- 寄存器esp代表栈顶(最低栈地址)
- 压栈(入栈)
push std->[esp]=sth,esp=esp-4
- 弹栈(出栈)
pop std->sth=[esp],esp=esp+4
- 栈用于保存函数调用信息和局部变量
- 需要详细了解栈溢出漏洞的形式
函数调用
- 函数约定
_stdcall,_cdecl,_fastcall,_thiscall,_nakedcall,_pascal
- 参数传递,取决于调用约定,默认如下
X86从右向左入栈,X64优先寄存器,参数过多时才入栈 - 函数调用相关指令解读
- call func -> push pc,jmp func
- leave -> mov esp,ebp,pop ebp
- ret -> pop pc
栈溢出的保护机制
栈上的数据被当做指令来执行
- 数据执行保护机制(NX/DEP)
- 绕过方法:ROP
让攻击者难以找到shellcode地址
- 地址空间布局随机化(ASLR)
- 绕过方法:infoleak、ret2dlresolve、ROP
检测Stack Overflow(栈溢出)
- Stack Canary/Cookie
- 绕过方法:infoleak
现在NX+Stack Canary+ASLR基本是标配
栈溢出的利用方法
现代栈溢出利用技术基础:ROP
- 现在的程序默认都开启数据执行保护机制和地址空间布局随机化(NX+ASLR)这两种技术,传统的覆盖shellcode地址跳到shellcode那里执行的技术来玩儿,所以,现代就用ROP这种方式来绕过这两种技术
- 一种代码复用技术,即利用程序中已经存在的代码行,通过控制栈调用来劫持控制流,不用注入什么其他的代码
- 它的一种简化版本是Ret2libc
CTF中ROP技术的常见套路
- 第一次触发漏洞,通过ROP泄露libc的地址(address)(如puts_got),计算system地址,然后返回到一个可以重新触发漏洞的位置(如main的开头),再次触发漏洞,通过ROP调用system("/bin/sh"),来拿到shell
- 直接execve("/bin/sh",["/bin/sh"],NULL)拿到shell,通常静态链接时比较常用
- Defcon 2015 Qualifier:R0pbaby
- AliCTF 2016:vss
- PlaidCTF 2013:ropasaurusrex
利用signal机制的ROP技术(SROP)
- SROP(Sigreturn Oriented Programming)
- 利用系统Signal Dispatch的一个机制,系统Signal Dispatch之前会把所有寄存器压入栈,然后调用signal handler,signal handler返回时会将栈的内容还原到寄存器中
- 如果事先填充栈,然后直接调用signal return,那在返回的时候就可以变相控制寄存器的值
- 用的不是特别多但是有时候很好用
- defcon 2015 qualifier fukeup(比较难)
- 建议自己写一个domo测试
没有binary怎么办(BROP)
- BROP(Blind Return Oriented Programming)
- 用的不是特别多但是在CTF比赛中出现过
BROP的目标
- 在拿不到binary的情况下进行ROP
BROP的条件
- 必须先存在一个已知的stack overflow漏洞,而且攻击者知道如何触发这个漏洞
- 服务器进程在crash之后会重新复活,并且重新复活的进程不会被re-rand
- HCTF 2016 出题人跑路了(pwn50)
以上这两种技术是ROP的分类(SROP和BROP),在CTF中不常见,但是的确出现过这种技术的题目
劫持栈指针(Stack Pivot)
- 一种较为特殊的方法,相当重要的方法
- 将栈指针劫持到非栈区域,在别处操作栈指针
- 向目标缓冲区填入栈数据(如ROP Chains),然后劫持esp到目标缓冲区。劫持esp的方法有很多,最常用的就是ROP时利用可以直接改写esp的gadget,如pop esp,ret
劫持栈指针的动机
- 溢出字节数有限,无法完成ROP
- 栈地址未知且无法泄露,但是某些利用技术需要知道栈地址(ret2dlresolve)
- 劫持esp到攻击者控制的区域,也就变相的控制了栈中的数据,从而可以使非栈溢出的控制刘劫持攻击也可以完成ROP
Stack Pivot的条件
- 存在地址已知且内容可控的buffer
- bss段,由于bss段尾端通常具有很大的空余空间(pagesize-usedsize),所以bss段尾端往往是Stack Pivot的目标
- 堆块,如果堆地址已经泄露且数据可被控制,那堆也可以作为Stack Pivot的目标
- 控制流可劫持
- 存在劫持栈指针的gadgets
- 如pop esp,ret,除此之外还有很多,要具体binary具体分析
- EKOPARTY CTF 2016 fuckzing-exploit-200(基于栈溢出的Stack Pivot)
- HACKIM CTF 2015 -Exploition 5(基于堆溢出的Stack Pivot)
利用动态链接绕过ASLR
- 动态链接的过程就是从函数名到函数地址转换的过程,所以我们可以通过动态链接器解析任何函数,且无需任何leak
- ret2dl resolve,fake linkmap
- 该技术较为复杂
前置技能
- 了解动态链接的过程,《程序员的自我修养》
- 伪造动态链接的相关数据结构如linkmap,relplt等
利用地址低12bit绕过ASLR
- Partial Overwrite
- 这是一种较为重要的技术,经常用到的一个技术
- 利用ASLR中低12bit不会被随机化的特性来绕过ASLR
- 在PIE开启的情况下,一个32位地址的高20位会被随机化,但是低12位是不变的。所以,可通过只改写低12位来绕过PIE,不仅仅在栈溢出中使用,在各种利用中经常使用。
- HCTF 2016 fheap(基于堆溢出的Partial Overwrite)
绕过Stack Canary
以上所有套路,一旦遇到Stack Canary均无法使用
- 改写指针与局部变量、leak canary、overwrite canary
- CTF中的不常用的技术,除非题目将栈溢出显露出来,否则一般情况下不会使用这种技术
绕过思路
- 不覆盖Stack Canary,只覆盖Stack Canary前的局部变量,指针
- 已经几乎不可行,编译器都会做一种防护,因为编译器会根据内存占有大小从小到大排列变量,
- 但是在某些情况依然可用,需要结合实例分析
- Leak Canary
- 可以通过格式化printf泄露,Canary一般从00开始
- Overwrite Canary(重写)
- Canary在TLS中地址被随机化
- 很少见
溢出位数不够用
- 覆盖ebp,Partial Overwrite
- Func 1:
call func2
leave(mov esp ebp,pop ebp)
ret (pop ip)- Func 2:
Stack Overflow
leave(mov esp ebp,pop ebp)
ret (pop ip)
可以覆盖Func2的ebp,会影响到Func1的esp,进而影响Func1的ip,完成对控制流的劫持。可以部分覆盖返回地址。
- XMAN 2016 广外女生-pwn
- Codegate CTF Finals 2015,chess