什么是netlink
Netlink是linux提供的用于内核和用户态进程之间的通信方式。
但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。
sendmsg
用一个nf_tables的例子来理解:
- sockaddr_nl中的nl_pid是0,表示发送给内核;nlmsg_hdr中的pid则用getpid()来获取,表示发送方是当前进程;
- begin_msg的type是NFNL_MSG_BATCH_BEGIN,关于这两个type看这篇文章:https://196082.github.io/2024/08/17/nftables/ ,在
nfnetlink_rev_batch
函数中会根据不同的type修改调status,所以正常结束时会进入到status == NFNL_BATCH_DONE
分支中,并执行ss->commit
。
如何在内核中处理netlink
继续对照这个https://github.com/randorisec/CVE-2022-34918-LPE-PoC/blob/main/src/nf_tables.c 代码来体会netlink是怎么在内核中工作的。
我们先来理解一下attr是干什么的:
可以看到attr的位置就是前面图中的消息体;
有几个问题:
- 如何约定的这些属性?🤔
- 内核收到这个消息之后如何转发给nf_tables?🤔
参考这篇文章:https://www.secrss.com/articles/44817
总结一下主要过程就是:
- 在nfnetlink_rcv_batch()函数中对netlink消息进行操作。从netlink消息中剥离出nftable载荷,并依次进行对应处理。进入nfnetlink_rcv_batch()函数,首先根据subsys_id获得nfnetlink_subsystem。
- 获得subsystem后,然后拿到子系统对应的回调客户端。通过nfnetlink_find_client()实现该功能。
- 然后再从netlink消息中剥离出netlink载荷,根据不同的消息类型进行不同的分发处理。
而我们snedmsg的时候会指定type:
所以,应该是指定好了type就会发送到特定的模块,然后对应模块会recv到,然后再进行处理?🤔
如何构造netlink的消息体
首先看笔者对nested_attr的理解的图:
然后再看一下某个内核cve的exp中是如何构造消息体的:
首先预算并分配空间:
然后把这个长度放到第二个iov上:
最后构造消息体本身,首先要有一个struct nfgenmsg
作为头:
下面来看看struct nfgenmsg
结构体定义:
其实就是一个attr的头。
再看看set_str8_attr
干了啥:
前4个字节搞长度和类型,然后后边紧跟的是内容;
经过实测+分析,发现我们传进去的消息体并不是一个“规范的nested_attr”,其头部4字节是一个struct nfgenmsg
结构体,这个结构体目前最有用的字段就是family
了,因此我们在构造netlink消息体的时候可以先假模假式地构造一个nested_attr,然后把前4个字节替换成struct nfgenmsg
,也就是\x01\x00\x00\x00
,至于这个family字段以后再分析,这里消息体的拆解应该是看了nlmsghdr中的长度字段还有type字段,然后拆解过程类似于普通的nested_attr🤔
此外笔者实测还发现一个问题,就是int类型的attr似乎是大端序存放的,😓
参考
https://zhuanlan.zhihu.com/p/85431412
https://blog.csdn.net/weixin_39094034/article/details/110432380
https://196082.github.io/2024/08/17/nftables/
https://github.com/randorisec/CVE-2022-34918-LPE-PoC/blob/main/src/nf_tables.c