babydev详解
程序分析
首先打开文件系统查看初始化的脚本init
1 |
|
发现程序加载了一个mychrdev.ko
的模块,漏洞就应该在这个内核模块中。看名字应该是一个字符设备的驱动程序。
将这个模块从文件系统中拷贝出来,用IDA打开它,进行分析。可以看到程序主要有几个主要的函数llseek
,read
,write
,open
,ioctl
。
结合模块的名字大致能知道每个函数的作用,read
,write
,open
就是重写了orw
操作,ioctl
大概是它自定义了一个操作,llseek
实现的就是重定位读写指针的功能。
ioctl
这个函数通过0x1111
命令泄漏了一些信息给我们。通过它我们能知道v9
,v10
,v11
,v12
和md
的值。通过分析我们知道v9
是当前的进程号,v10
是当前程序的名称,v11
,v12
缓冲区的一些信息,md
则直接将缓冲区的地址mydata
告诉了我们。
read
,write
&& llseek
驱动程序主要维护三个值,一个是文件的读写指针,没次打开文件的时候都会被重新设置为0;文件的头指针,指向文件内容开始的地方,它存放在mydata+0x10000
中,表示文件内容的起始地址相对于mydata
的偏移;三是文件的大小,它存放在mydata+0x1008
中。
在llseek
中可以重制文件指针的值,并且返回重制以后文件指针的值。它有三种模式
- 当
mod==0
时,会重制文件指针为a2
- 当
mod==1
时,将文件指针跳转到当前地址+a2
的位置 - 当
mod==2
时,会将指针跳到文件倒数第|a2|
(这里a2要是个负数)个位置
这里可以看到llseek
无法将文件指针设置为一个负数。
查看read
函数,在copy_to_user
函数第二个参数s_n + base + mydata
表示要拷贝的内核空间的地址,这里存在一个整型漏洞,s_n+base
是负数的时候就可以跳转到my_data
之前的地址。其中s_n是文件指针的值,我们无法通过llseek
将其设置为负数,因此要想跳到my_data
之前的位置进行操作要考虑在base
上(mydata+0x10000
)做文章。
查看write函数,发现其同样存在整型漏洞,只要能将my_data+0x10000
的位置,设置为负数就能够对mydata之前的地址进行操作。
仔细观察发现write还有一个漏洞。*(_QWORD *)(mydata + 0x10008) += n;
每次写成功之后都会吧写的内容的大小加到mydata + 0x10008
上,和llseek
配合就能够使得mydata+0x10008
值超过0x10000
,使得我们能够通过write
随意修改mydata+0x10000
和mydata+0x10008
上的内容,从而实现对任意地址的读写操作。
漏洞利用
首先为了绕过write
的检查,先写0x10000
的内容,再将文件指针设置为0
,再写0x10000
的内容上去使得my_data+0x10008
的值变成0x20000,这样就能随意写my_data+0x10000
和my_data+0x10008
的内容。
1 | int fd = open("/dev/mychrdev", O_WRONLY); |
然后我们就能够对任意地址进行读写,因为进程的cred
结构在mydata
之前,我们就主要跳到mydata
之前进行操作。
1 | fd = open("/dev/mychrdev", O_WRONLY); |
利用char target[16] = "try2findmep4nda";prctl(PR_SET_NAME, target);
将target
写进内核空间中,在内核空间中targe
t的那个地址靠近cred指针
,因此只要利用任意读爆破出它的地址就能够知道cred
地址,利用任意写将cred
中的前0x28
个字节设置为0。这里可以参考P4nda大神的博客。
1 | print_hex(buf,0x100); |
最后实现提权。
EXP
1 |
|
这篇博客已经在发布在安全客上https://www.anquanke.com/post/id/223468