avatar

CTF-Pwn-ROP详解

0x01 32位程序栈溢出

Ret2libc 溢出

5B pop ebx
5E pop esi
5F pop edi
5D pop ebp
IDA中,按 alt + B 选择hex 搜索 5B 5E 5F 5D 即可定位Pop3_ret的位置

一次溢出地址,算出偏移,跳转到至libc
junk + ebp + ret_address + Pop3_ret + 参数1 + 参数2 + … + 参数n + ret_vul
junk + ebp + ret_address + Pop3_ret + 参数1 + 参数2 + … + 参数n + getShell_address

复杂的多次跳转溢出

junk + ebp +ret_address1 + Pop3_ret + 参数1 + ret_address2 + Pop3_ret + 参数1 + 参数2 +ret_address3 _ Pop3_ret + 参数 ……

0x02 64位程序栈溢出

在x64程序中,最麻烦的莫过于传参,这里我将对传参的技巧进行详细的讲解

传参顺序 RDI, RSI, RDX, RCX, R8, R9

只需要在通过ROPgadget工具就可以获得程序中的大部分RET和POP代码

1
ROPgadget --binary 程序名 --only "ret|pop"

但是我平时遇到的题目大部分并没有 pop rdx pop rcx等
其实只要通过奇数位执行代码就会出现上述指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pop rdx=5A
pop rdi=5F
pop rbx=5B
pop rcx=59
pop rsp=5C
pop rsi=5E
pop rbp=5D
pop r8=41 58
pop r9=41 59

pop r10=41 5A
pop r11=41 5B
pop r12=41 5C
pop r13=41 5D
pop r14=41 5E
pop r15=41 5F

由上述代码可以知道,通过奇数位可以得到以下代码

1
2
3
4
5
pop r10 -> pop rdx
pop r15 -> pop rdi
pop r11 -> pop rbx
pop rcx -> pop r9
pop rsp -> pop r12

这样就几乎满足了大部分的参数传递问题
只要参数逻辑传递正确,且过程中参数保证不会被改变,基本上都可以拿到shell了,如果出现参数改变,那就要对ret_address进行调整,对其进行偏移,最好直接一步到关键call上。

0x02 GOT表覆盖

程序有canary保护,现在存在一个流程 read printf read printf,两次read的长度都比较短,那么可以考虑覆盖got表
第一次read 读入 __stack_chk_fail 的 got地址,然后第二次read时,找到第一次输入数据的偏移,然后向这个偏移写入可以重复rop的偏移

根据实验!!(百度杯 what_the_fuck)

第一次读入 __stack_chk_fail 的 got地址
第二次读入 “%.2435d%12$hn %9%$s%10$ld”
解释:先输出2435(即0x983)个字符,后面的%12$hn向第12偏移(即第一次输入的__stack_chk_fail 的 got地址)所指向的地址写入0x983
注意注意!!!由于延迟绑定,__stack_chk_fail_got的值在第一次调用时的值为__stack_chk_fail__plt + 1,由于 __stack_chk_fail__plt 和 用户代码(例如main、sub_405531等)十分近,所以通过%xd就可以控制一开始输出文本的长度,并将其长度的数值写入__stack_chk_fail_got,实现覆盖got,所以每次执行__stack_chk_fail_got都会跳转到覆盖后的地址(第一次__stack_chk_fail_got+一开始输出文本的长度)

为了更清楚表示,以下将列出具体变化

1
2
3
4
5
6
触发__stack_chk_fail后,
执行 call __stack_chk_fail_plt
跳转到 0x00000000004006C0 jmp cs:off_601020
由于没有绑定 ds:ptr[601020] = 0x00000000004006C6
跳转到 0x00000000004006C6 push 1
所以这里存在漏洞,覆盖

__stack_chk_fail_got(ds:ptr[0x0000000000601020] = 00000000004006C6) 由于延迟绑定所以它的值等于 __stack_chk_fail_plt + 6 (00000000004006C0 + 6) 至于为什么加6,这是因为下一个汇编指令的地址是 00000000004006C6

猜测:学长说通过修改read_got可以在下一次执行read的时候跳转到int 0x80,因为在libc中read_plt下方很近的位置就有int 0x80,假设我们可以配好eax等参数,便可以直接跳转到int 0x80,进入中断函数,直接拿到shell

文章作者: 咲夜南梦
文章链接: http://yoursite.com/2019/03/28/CTF-Pwn-ROP%E8%AF%A6%E8%A7%A3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论