首页
社区
课程
招聘
[原创]Linux Kernel Pwn_2_Kernel UAF
发表于: 2020-10-3 11:16 6136

[原创]Linux Kernel Pwn_2_Kernel UAF

2020-10-3 11:16
6136

基于 CISCN 2017 babyfriver

解压出来并没有我们想要的驱动文件,需要我们手动提取。

启动脚本如下:

可以看到.ko文件在/lib/modules/4.4.72/babydriver.ko

一开始cpio解压出来都是乱码,然后file了一下,发现他是个gz压缩的文件orz。。。。

注意,本题没有直接给vmlinux,所以我们需要手动提取:./extract-vmlinux ./bzImage > vmlinux

extract-vmlinux代码如下:

首先,调用alloc_chrdev_region(&babydev_no, 0LL, 1LL, "babydev")

让内核分配一个尚未使用的主设备号。

其函数原型如下:

此时申请好了设备号。

然后调用:cdev_init(&cdev_0, &fops);

来将字符设备对应的cdev结构体与file_operations对应起来。

其中cdev结构体如下:https://elixir.bootlin.com/linux/latest/source/include/linux/cdev.h

后面调用v7 = device_create(v5, 0LL, babydev_no, 0LL, "babydev");

创建一个设备,并注册它到sysfs中。

整体来说就是开了一个0x40的空间,然后记录他的起始地址在babydev_struct.device_buf,大小在:babydev_struct.device_buf_len

release会把开的空间放掉

就是利用copy from user来写和读

首先检查command是不是0x10001.

释放之前的空间,然后利用kmalloc申请一个新的,再设置size

输出提示语句然后返回。

通过init脚本,我们可以发现——flag文件在root底下。但是如果我们直接登录的话默认是ctf用户,所以目标就是完成提权。

可以看到在ioctl运行结束时,babydev_struct.device_buf_len已经变成了0xa8

由于指针被第二次申请的覆盖了,实际上close(fd1)时是free的第二次申请的chunk(此时大小已经是0xa8)

0x3e8就是1000

至此,完成了Kernel UAF的提权 : ) PWN!!!

https://blog.csdn.net/weixin_42314225/article/details/81112217

https://blog.csdn.net/zhoujiaxq/article/details/7646013

https://www.cnblogs.com/king-77024128/articles/2684317.html

https://bbs.pediy.com/thread-261728.htm

 
 
#!/bin/sh
 
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
 
insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh
 
umount /proc
umount /sys
poweroff -d 0  -f
#!/bin/sh
 
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
 
insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh
 
umount /proc
umount /sys
poweroff -d 0  -f
 
 
 
#!/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
#!/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
 
 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
    /*
 
    dev :alloc_chrdev_region函数向内核申请下来的设备号
 
    baseminor :次设备号的起始
 
    count: 申请次设备号的个数
 
    name :执行 cat /proc/devices显示的名称
 
    */
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
    /*
 
    dev :alloc_chrdev_region函数向内核申请下来的设备号
 
    baseminor :次设备号的起始
 
    count: 申请次设备号的个数
 
    name :执行 cat /proc/devices显示的名称
 
    */
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
 
 
 
struct cdev {
    struct kobject kobj;    //每个cdev都是一个kobject
    struct module *owner;   //指向实现驱动的模块
    const struct file_operations *ops;//操作这个字符文件的方法
    struct list_head list;// 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
    dev_t dev;                  // 起始设备编号
    unsigned int count;         // 设备范围号大小
} __randomize_layout;
struct cdev {
    struct kobject kobj;    //每个cdev都是一个kobject
    struct module *owner;   //指向实现驱动的模块
    const struct file_operations *ops;//操作这个字符文件的方法
    struct list_head list;// 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头
    dev_t dev;                  // 起始设备编号
    unsigned int count;         // 设备范围号大小
} __randomize_layout;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//