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.