-
-
[原创]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期)