基本 ROP
ret2text
ret2text 即控制程序执行程序本身已有的的代码 (.text)。例子
ret2shellcode
ret2shellcode,即控制程序执行 shellcode 代码。一般来说,shellcode 需要我们自己填充。例子
ret2syscall
ret2syscall,即控制程序执行系统调用,获取 shell。例子
此时就需要用到 ROP 链了,得到 ROP 链可以使用 ROPgadget
这个工具,见 TIPS
ret2libc
ret2libc 即控制函数的执行 libc
中的函数,通常是返回至某个函数的 plt
处或者函数的具体位置 (即函数对应的 got
表项的内容)。
一般情况下,我们会选择执行 system("/bin/sh")
,故而此时我们需要知道 system
函数的地址。
中级 ROP
ret2csu
在 64 位程序中,函数的前 6 个参数是通过寄存器传递的,但是大多数时候,我们很难找到每一个寄存器对应的 gadgets
。 这时候,我们可以利用 x64
下的 __libc_csu_init
中的 gadgets
。
这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在。
BROP
黑盒测试的办法。见:http://www.scs.stanford.edu/brop/bittau-brop.pdf
高级 ROP
ret2dlresolve
要想弄懂这个 ROP 利用技巧,需要首先理解 ELF 文件的基本结构以及动态链接的基本过程:ElfFormat
在程序执行延迟绑定的函数时实际上会跳到 PLT
中保存的地址执行,这个地址上包含了三行函数。我们以 write@plt
举例:
|
|
- 第一行是直接跳转到
GOT
表中,write
函数的真实地址。程序刚加载时,GOT
表中的地址都是指向PLT
表的下一个位置,即上图中的496
; - 第二行将
0x20
入栈,准备将其作为参数调用函数; - 第三行调用
.plt
表、第 440 行的指令。这个位置的指令把link_map=*(GOT+4)
(即链接器的标识信息)作为参数推入栈中,然后调用*(GOT+8)
(保存的是_dl_runtime_resolve
函数的地址)。
上面的操作实际调用的是 _dl_runtime_resolve(link_map, reloc_arg)
,该函数会完成符号的解析,即将真实的 write
函数地址写入其 GOT
条目中,随后把控制权交给 write
函数。
_dl_runtime_resolve
是在 glibc-2.23/sysdeps/i386/dl-trampoline.S
中用汇编实现的。0xf7fededb
处即调用 _dl_fixup
,并且通过寄存器传参。