虽然栈溢出感觉有点简单,但是还是得练习一下比赛的时候会遇到的难题吧。还有栈溢出里面比较难的知识点应用。
2015-ZCTF guess
程序分析
程序开启了 NX 和 CANNARY。
程序总体逻辑如下所示,漏洞点在 gets(s) 函数,存在一个溢出。同时 读取的flag 文件是一直存储在 bss 一个固定地址的。现在就是想办法把这个flag 泄露出来。由于有 CANNARY,像直接 ROP泄露是有困难的。这道题 就需要用到 stack-smash 的知识。
Stack-Smash
在程序加了 canary 保护之后,如果覆盖了 栈中的Cannary值,程序就会报错。而 stack smash 技巧则就是利用打印这一信息的程序来得到我们想要的内容。这是因为在程序启动 canary 保护之后,如果发现 canary 被修改的话,程序就会执行 __stack_chk_fail
函数来打印 argv[0] 指针所指向的字符串,正常情况下,这个指针指向了程序名。其代码如下
1 | void __attribute__ ((noreturn)) __stack_chk_fail (void) |
主函数 main(int argc, char** grav) 的参数值中 argv[0] 就是程序的名字,出现异常时会显示在错误信息的后面。所以只要用特定地址 覆盖栈中 argv[0] 的地址就可以达到任意地址泄露的目的,从而达到泄露原 flag 信息的目的。
我们首先在 main函数的首条指令下断点,此时可以看到 main数的 argv[0] 地址是 0x7fffffff6f58,存储的就是 程序名。
然后,我们在 gets() 函数下断点,发现其存储地址为 0x7fffffffde30。
所以,如果我们通过溢出 修改了上面 argv[0] 的地址为 flag 存储的内存地址,在最后程序崩溃时就能够泄露flag 信息。
EXP
这道题,我们首先要去爆破一下 flag 的长度,然后泄露 flag,最后异或解码就行。
1 | from pwn import * |
下图中,就是泄露出来经过异或后的 flag 信息:
2015-Codegate-Chess
这道题,做了半天,还是会遇到一点问题。后面补上。
2015-RCTF-WelPwn
程序分析
一个十分明显的 栈溢出漏洞。但是在 copy时,会有一个 检测所输入的 字符串 是否为空,以及在数组末尾加了 0。但是这里其实存在一个 堆里会用到的 off-by-one 漏洞,程序在 S2[i] = 0,其实时会将 RBP 的末尾一字节给覆盖为 \x00,也就是使得 RBP 值变小了。然后在 返回到 main 函数时,其时 RBP 发生了改变,最后的 ret地址也发生了改变。
漏洞分析
这道题,有两种思路吧。
一是 在 上面 echo 函数溢出覆盖 ret 指针为 pop4_ret,因为我们输入的字符就在 ret后作为 参数,所以只需要把最开始的 0x20 数据Pop掉,后面就可以正常布置 ROP 链了。
二是,利用上面的 off-by-one 漏洞,栈喷射,使得 ret地址返回到我们的 ROP链上。
EXP
为了联系一下 csu_init 函数ROP 链利用,也写了一个相关利用链。
1 | from pwn import * |
然后,也看了别人一个比较暴力的做法。通过栈喷射,在栈内部署大量的 ROP地址,然后当 ebp地址末尾被 \x00 覆盖后,就有一定几率命中我们的 ROP。
1 | from pwn import * |
Stack pivoting
劫持栈指针指向攻击者所能控制的内存处,然后再在相应的位置进行 ROP。
使用场景
- 可以控制的栈溢出的字节数较少,难以构造较长的 ROP 链
- 开启了 PIE 保护,栈地址未知,我们可以将栈劫持到已知的的区域
- 其他漏洞难以利用,需要进行转换,比如说将栈劫持到对空间,从而在堆上写 ROP 及进行堆漏洞利用
条件
此外,利用 stack pivoting 有以下几个要求:
- 可以控制程序执行流
- 可以控制 SP 指针。控制栈指针会使用 ROP,常见的控制栈指针的 gadgets 一般是
1 | pop rsp|esp |
此外,通过 libc_csu_init 函数中的偏移,也会有 pop rsp.
还有 fake frame。
- 存在可以控制内容的内存,有:
- bbs段,由于进程按页分配内存,分配给 bss 段的内存大小至少一个页(4k, 0x1000)大小。然而一般 bss 段的内容用不了太大空间,并且 bss 段分配的内存页拥有读写权限
- heap,这个需要能够泄露堆地址
2015-XCTF-b0verfl0w
题目很简单,运用栈迁移,可以成功执行ROP链。当然也有一个 栈具有 RWX权限,可以直接执行shellcode。
1 | from pwn import * |
frmae faking
构造一个虚假的栈帧来控制程序的执行流。
原理
两种方式:
- 控制程序 EIP
- 控制程序 EBP
其最终都是控制程序的执行流。在frame faking中,我们所利用的技巧便是同时控制 EBP 和 EIP,在控制程序执行流的同时,也改变程序栈帧的位置。
即利用栈溢出将栈上构造为如下格式:
1 | buffer padding | fake ebp | leave ret addr |
- 函数的返回地址被覆盖为执行 leave ret的地址,函数在正常执行完自己的 leave ret 后,还会再次执行一次 Leave ret
- 其中 fake ebp 为我们构造的栈帧的基地址,一般假栈帧如下:
1 | fake ebp: |
程序正常执行时,会正常申请空间,同时在栈上也会满足该函数对应的参数,所以程序会正常执行
程序结束时,其又会执行两次 Leave ret addr,所以如果在 ebp2 处布置了对应的内容,那么就可以一直控制程序的执行流程
2018-OVER
利用方法和上面差不多
2018-XNUCA-gets
这道题主要是一种新的思路,即对一个 地址的低2两字节进行覆盖为 one_gadget地址,然后不断爆破尝试试其能撞为 正确的 one_gadgets地址。
具体分析可见:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/fancy-rop-zh/#2018-xnuca-gets
爆破EXP
1 | from pwn import * |
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2020/08/10/栈溢出训练/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!