linux_kernal_pwn ciscn2017_babydriver

It's kernal pwn

Three documents were given

boot.sh: a script used to start the shell of the kernel. It uses qemu. The protection measures are related to different startup parameters of qemu
bzImage: packaged kernel code. vmlinx is usually extracted through it. It is also here to find gadget s
rootfs.cpio: file system image, that is, the file system adopted by the kernel

Like the user state pwn, the protection is not checked first.
It can be seen that only smep is on.
What is this? Let's talk about the kernel state protection first

Kernel protection starts from four aspects: isolation, access control, exception detection and randomization.
Isolation is divided into user code not executable, user data not accessible, and a KPTI.

What we call smep is that user code is not executable.

This is an introduction to CTF wiki

Initially, when executing code in kernel mode, you can directly execute code in user mode. If the attacker controls the execution flow in the kernel, he can execute the code in user state. Since the user state code is controllable by the attacker, it is easier to attack. In order to prevent this attack, researchers propose that when it is in kernel state, it can not execute user state code.

Then we need to unzip the file system files he gave, find the init file and see what is mounted.

mkdir core
cp rootfs.cpio ./core
cd core
mv ./rootfs.cpio rootfs.cpio.gz     
 #Because cpio has been compressed by gzip, you must change the name before gunzip can recognize it
gunzip ./rootfs.cpio.gz 
#gunzip decompress it for a while before cpio can recognize it, otherwise it will report abnormal numbers
cpio -idmv < ./rootfs.cpio 
#cpio is the decompression instruction - idmv is its four parameters
#-i or -- extract executes the copy in mode to restore the backup file.
#-d or -- make directories cpio will create its own directory if necessary.
#-v or -- verbose displays the execution process of the instruction in detail.
#-m or preserve modification time does not change the change time of the file

View init file

Mount moun t files outside the Linux system
chown command is used to set the file owner and file association group. chown needs the permission of super user root to execute this command. You can see that the flag is thrown to root.
chmod commands that control user permissions on files
exec set file stream
The insmod command is used to load modules
Finally, poweroff turns off the power

So after watching for a long time, I actually loaded babydriver for illustration Ko this module, the problem is in it.

So how to take out this module? We still have a file that is useless, that is, bzImage, which is the compression of kernel code.

Vmlinux can be extracted by using the extract vmlinux script in the script in the linux kernel source code
vmlinux is the kernel file.

./extract-vmlinux ./bzImage > vmlinux

This/ Extract vmlinux may have some kernel stripped it, so you can only write your own shell script.

#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
#
# (c) 2011      Corentin Chary <corentin.chary@gmail.com>
#
# ----------------------------------------------------------------------

check_vmlinux()
{
    # Use readelf to check if it's a valid ELF
    # TODO: find a better to way to check that it's really vmlinux
    #       and not just an elf
    readelf -h $1 > /dev/null 2>&1 || return 1

    cat $1
    exit 0
}

try_decompress()
{
    # The obscure use of the "tr" filter is to work around older versions of
    # "grep" that report the byte offset of the line instead of the pattern.

    # Try to find the header ($1) and decompress from here
    for pos in `tr "$1\n$2" "\n$2=" < "$img" | grep -abo "^$2"`
    do
        pos=${pos%%:*}
        tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
        check_vmlinux $tmp
    done
}

# Check invocation:
me=${0##*/}
img=$1
if  [ $# -ne 1 -o ! -s "$img" ]
then
    echo "Usage: $me <kernel-image>" >&2
    exit 2
fi

# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap "rm -f $tmp" 0

# That didn't work, so retry after decompression.
try_decompress '\037\213\010' xy    gunzip
try_decompress '\3757zXZ\000' abcde unxz
try_decompress 'BZh'          xy    bunzip2
try_decompress '\135\0\0\0'   xxx   unlzma
try_decompress '\211\114\132' xy    'lzop -d'
try_decompress '\002!L\030'   xxx   'lz4 -d'
try_decompress '(\265/\375'   xxx   unzstd

# Finally check for uncompressed images or objects:
check_vmlinux $img

# Bail out:
echo "$me: Cannot find vmlinux." >&2

Write this script on the desktop, or somewhere else.
Then give me permission

chmod +x ./extract-vmlinux

Then you can use the above sentence to extract vmlinux.

After the preliminary work is prepared, we know that the problem is in the babydriver module, and we pull it to IDA.

The function directory is here.

open
kmem_cache_alloc_trace is used to request memory from the buffer.
It is to apply for the memory of 0x40, and then the address and size are stored in the structure.

read
Compare device_buf_len and length, execute copy_to_user, just like what is written in the buffer, this buffer is obviously transmitted from the user state.

write
write is obviously the opposite of read, copy from.
Is to write to the kernel.

ioctl
Enter 65537 to free buf and then apply for len size memory.

release

It's release, but obviously, as we are familiar with, there is no clean pointer, there is uaf. And the structure is a global variable.
That is, if we open two devices at the same time, the second time will overwrite the space allocated for the first time, because babydev_struct is global. Similarly, if the first is released, the second is actually released.

How to use
First, we will introduce a cred structure.

struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
    kuid_t      uid;        /* real UID of the task */
    kgid_t      gid;        /* real GID of the task */
    kuid_t      suid;       /* saved UID of the task */
    kgid_t      sgid;       /* saved GID of the task */
    kuid_t      euid;       /* effective UID of the task */
    kgid_t      egid;       /* effective GID of the task */
    kuid_t      fsuid;      /* UID for VFS ops */
    kgid_t      fsgid;      /* GID for VFS ops */
    unsigned    securebits; /* SUID-less security management */
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;  /* caps we're permitted */
    kernel_cap_t    cap_effective;  /* caps we can actually use */
    kernel_cap_t    cap_bset;   /* capability bounding set */
    kernel_cap_t    cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYS
    unsigned char   jit_keyring;    /* default keyring to attach requested
                     * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key  *process_keyring; /* keyring private to this process */
    struct key  *thread_keyring; /* keyring private to this thread */
    struct key  *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;  /* subjective LSM security */
#endif
    struct user_struct *user;   /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;  /* supplementary groups for euid/fsgid */
    struct rcu_head rcu;        /* RCU deletion hook */
};

The kernel records the permissions of a process. More specifically, it is recorded in the cred structure. There is a cred structure in each process. This structure saves the permissions and other information of the process (uid, gid, etc.). If the cred of a process can be modified, the permissions of the process will also be modified.

What's our idea
open / dev/babydev twice first, fd1,fd2
Modify babydevice through ioctl_ t->device_ buf_ Len is the size of cred structure (0xa8)
Closing one of fd1 will cause babydevice_ Trelease
fork, create a process due to the released babydevice_t and cred are the same size, and babydevice will be used_ T as cred structure
Modify the uid of cred through write(fd2,buf), and the gid is 0

Finally, we come to the part of getshell, how to get shell.
First, we drop exp into the tmp directory of the core.

cp exp core/tmp

Then repackage the kernel.

cd core
find . | cpio -o --format=newc > rootfs.cpio

Then take out the packaged ones.

cp rootfs.cpio ..

Then come out and start

cd ..
./boot.sh


Then it's ok.

Keywords: Cyber Security CTF

Added by point86 on Sun, 19 Dec 2021 07:45:05 +0200