需要准备编译环境:
wget http://archive.ubuntu.com/ubuntu/pool/universe/d/dwarves-dfsg/dwarves_1.17-1_amd64.deb
dpkg -i dwarves_1.17-1_amd64.deb
[BTF: .tmp_vmlinux.btf: pahole version v1.15 is too old, need at least v1.16](https://askubuntu.com/questions/1280414/btf-tmp-vmlinux-btf-pahole-version-v1-15-is-too-old-need-at-least-v1-16)
0x2 漏洞分析 & 动态调试
Patch:
Using tp_reserve to calculate netoff can overflow as tp_reserve is unsigned int and netoff is unsigned short.
This may lead to macoff receving a smaller value then sizeof(struct virtio_net_hdr), and if po->has_vnet_hdr is set, an out-of-bounds write will occur when calling virtio_net_hdr_from_skb.
net/packet/af_packet.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 29bd405adbbd..d37435906859 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2168,7 +2168,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
int skb_len = skb->len;
unsigned int snaplen, res;
unsigned long status = TP_STATUS_USER;
- unsigned short macoff, netoff, hdrlen;
+ unsigned short macoff, hdrlen;
+ unsigned int netoff;
struct sk_buff *copy_skb = NULL;
struct timespec64 ts;
__u32 ts_status;
@@ -2237,6 +2238,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
macoff = netoff - maclen;
+ if (netoff > USHRT_MAX) {
+ atomic_inc(&po->tp_drops);
+ goto drop_n_restore;
if (po->tp_version <= TPACKET_V2) {
if (macoff + snaplen > po->rx_ring.frame_size) {
if (po->copy_thresh &&
Patch中描述的很详细,在tpacket_rcv中,由于netoff和tp_reserve的size不一致导致整数溢出漏洞,从而越界读写。
// /net/packet/af_packet.c:2231
netoff = TPACKET_ALIGN(po->tp_hdrlen +
(maclen < 16 ? 16 : maclen)) +
po->tp_reserve;
这个地方,po->tp_reserve值为0xffb4,得到的maclen为0xe,经过(maclen < 16 ? 16 : maclen) 计算,maclen值变成0x10,po->tp_hdrlen的值为0x43,经计算,netoff值0x10004,由于netoff是unsigned short类型,所以截断导致netoff值为0x4。
pwndbg> p $eax
$1 = 65540
➜ ~ rax2 65540
0x10004
然后经过一个计算,通过netoff设置maclen的值为0:
if (po->has_vnet_hdr) {
netoff += sizeof(struct virtio_net_hdr);
do_vnet = true;
macoff = netoff - maclen;
前期设置netoff和maclen都是为了越界访问做准备:
if (do_vnet &&
virtio_net_hdr_from_skb(skb, h.raw + macoff -
sizeof(struct virtio_net_hdr),
vio_le(), true, 0))
virtio_net_hdr_from_skb是个inline函数,h.raw为存储一个内核地址0xffffc90000269000,maclean的值为0,sizeof(struct virtio_net_hdr)为0xa,计算的值存在rdx中,rdx=0xffffc90000268ff6:
0xffffc90000268ff6: Cannot access memory at address 0xffffc90000268ff6
此时进入virtio_net_hdr_from_skb:
static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
struct virtio_net_hdr *hdr,
bool little_endian,
bool has_data_valid,
int vlan_hlen)
memset(hdr, 0, sizeof(*hdr)); /* no info leak */
导致越界访问,内核Crash。
Crash
0x3 POC
0x4 Reference
CVE-2020-14386:Linux内核AF_PACKET权限提升漏洞分析
oss-security - CVE-2020-14386: Linux kernel: af_packet.c