From 56506cc902d1405a9967e7670610b630e0df74fe Mon Sep 17 00:00:00 2001
From: Alex Romero <ntalexio2@gmail.com>
Date: Thu, 8 Dec 2022 16:49:51 -0500
Subject: [PATCH 1/3] add enumdesktops command to windows python meterpreter

---
 python/meterpreter/ext_server_stdapi.py | 109 +++++++++++++++++++++++-
 1 file changed, 108 insertions(+), 1 deletion(-)

diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py
index ef179ba1..350ddb4b 100644
--- a/python/meterpreter/ext_server_stdapi.py
+++ b/python/meterpreter/ext_server_stdapi.py
@@ -669,7 +669,11 @@ TLV_TYPE_TERMINAL_COLUMNS      = TLV_META_TYPE_UINT    | 2601
 ##
 TLV_TYPE_IDLE_TIME             = TLV_META_TYPE_UINT    | 3000
 TLV_TYPE_KEYS_DUMP             = TLV_META_TYPE_STRING  | 3001
-TLV_TYPE_DESKTOP               = TLV_META_TYPE_STRING  | 3002
+
+TLV_TYPE_DESKTOP               = TLV_META_TYPE_GROUP   | 3004
+TLV_TYPE_DESKTOP_SESSION       = TLV_META_TYPE_UINT    | 3005
+TLV_TYPE_DESKTOP_STATION       = TLV_META_TYPE_STRING  | 3006
+TLV_TYPE_DESKTOP_NAME          = TLV_META_TYPE_STRING  | 3007
 
 ##
 # Event Log
@@ -744,6 +748,9 @@ VER_PLATFORM_WIN32s               = 0x0000
 VER_PLATFORM_WIN32_WINDOWS        = 0x0001
 VER_PLATFORM_WIN32_NT             = 0x0002
 
+# Windows Access Controls
+MAXIMUM_ALLOWED                   = 0x02000000
+
 WIN_AF_INET  = 2
 WIN_AF_INET6 = 23
 
@@ -2755,6 +2762,106 @@ def stdapi_ui_get_idle_time(request, response):
     response += tlv_pack(TLV_TYPE_IDLE_TIME, idle_time)
     return ERROR_SUCCESS, response
 
+@register_function_if(has_windll)
+def stdapi_ui_desktop_enum(request, response):
+    global ui_desktop_enum_RESPONSE
+    ui_desktop_enum_RESPONSE = response
+
+    class EnumDesktops_Param(ctypes.Structure):
+        _fields_ = [
+            ('sessionId', ctypes.c_ulong),
+            ('cpStationName', ctypes.c_char_p)
+        ]
+
+    EnumDesktopCallbackPrototype = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.POINTER(EnumDesktops_Param))
+    EnumDesktopsA = ctypes.windll.user32.EnumDesktopsA
+    EnumDesktopsA.argtypes = [ctypes.c_void_p, EnumDesktopCallbackPrototype, ctypes.POINTER(EnumDesktops_Param)]
+    EnumDesktopsA.restype = ctypes.c_long
+
+    WindowStationCallbackPrototype = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.c_ulong)
+    EnumWindowStationsA = ctypes.windll.user32.EnumWindowStationsA
+    EnumWindowStationsA.argtypes = [WindowStationCallbackPrototype, ctypes.c_ulong]
+    EnumWindowStationsA.restype = ctypes.c_long
+
+    OpenWindowStationA = ctypes.windll.user32.OpenWindowStationA
+    OpenWindowStationA.argtypes = [ctypes.c_char_p, ctypes.c_long, ctypes.c_ulong]
+    OpenWindowStationA.restype = ctypes.c_void_p
+
+    CloseWindowStation = ctypes.windll.user32.CloseWindowStation
+    CloseWindowStation.argtypes = [ctypes.c_void_p]
+    CloseWindowStation.restype = ctypes.c_long
+
+    GetCurrentProcessId = ctypes.windll.kernel32.GetCurrentProcessId
+    GetCurrentProcessId.restype = ctypes.c_ulong
+
+    GetProcAddress = ctypes.windll.kernel32.GetProcAddress
+    GetProcAddress.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
+    GetProcAddress.restype = ctypes.c_void_p
+
+    ProcessIdToSessionIdPrototype = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_ulong, ctypes.POINTER(ctypes.c_ulong))
+
+    def get_session_id(pid):
+        pProcessIdToSessionId = None
+        dwSessionId = ctypes.c_ulong(0)
+
+        hKernel = ctypes.WinDLL('kernel32.dll')
+        if hKernel:
+            pProcessIdToSessionId = GetProcAddress(hKernel._handle, ctypes.c_char_p(b"ProcessIdToSessionId"))
+
+        if not pProcessIdToSessionId:
+            ctypes.windll.kernel32.FreeLibrary(None, handle=hKernel)
+            return dwSessionId
+
+        pProcessIdToSessionId = ProcessIdToSessionIdPrototype(pProcessIdToSessionId)
+        if not pProcessIdToSessionId(ctypes.c_ulong(pid), ctypes.byref(dwSessionId)):
+            dwSessionId = ctypes.c_ulong(-1)
+
+        ctypes.windll.kernel32.FreeLibrary(None, handle=hKernel)
+        return dwSessionId
+
+    @EnumDesktopCallbackPrototype
+    def desktop_enumdesktops_callback(lpszDesktop, lParam):
+        if not lParam or not lpszDesktop:
+            return True
+
+        if not lParam.contents.sessionId or not lParam.contents.cpStationName:
+            return True
+
+        entry  = bytes()
+        entry += tlv_pack(TLV_TYPE_DESKTOP_SESSION, lParam.contents.sessionId)
+        entry += tlv_pack(TLV_TYPE_DESKTOP_STATION, lParam.contents.cpStationName.decode())
+        entry += tlv_pack(TLV_TYPE_DESKTOP_NAME, lpszDesktop.decode())
+
+        global ui_desktop_enum_RESPONSE
+        ui_desktop_enum_RESPONSE += tlv_pack(TLV_TYPE_DESKTOP, entry)
+
+        return True
+
+    @WindowStationCallbackPrototype
+    def desktop_enumstations_callback(lpszWindowStation, lParam):
+        hWindowStation = OpenWindowStationA(lpszWindowStation, False, MAXIMUM_ALLOWED)
+        if not hWindowStation:
+            return False
+
+        param = EnumDesktops_Param()
+        param.sessionId = get_session_id(GetCurrentProcessId())
+        param.cpStationName = lpszWindowStation
+        EnumDesktopsA(hWindowStation, desktop_enumdesktops_callback, ctypes.pointer(param))
+
+        if hWindowStation:
+            CloseWindowStation(hWindowStation)
+
+        return True
+
+    success = EnumWindowStationsA(desktop_enumstations_callback, 0)
+    if not success:
+        return error_result_windows(), response
+
+    response = ui_desktop_enum_RESPONSE
+    del ui_desktop_enum_RESPONSE
+
+    return ERROR_SUCCESS, response
+
 @register_function_if(has_termios and has_fcntl)
 def stdapi_sys_process_set_term_size(request, response):
     channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']

From 414f2208fc81625351dc30e75df5caf4888f300d Mon Sep 17 00:00:00 2001
From: Alex Romero <ntalexio2@gmail.com>
Date: Wed, 11 Jan 2023 20:09:56 -0500
Subject: [PATCH 2/3] apply @smcintyre-r7 review changes

---
 python/meterpreter/ext_server_stdapi.py | 58 ++++++++++++-------------
 1 file changed, 28 insertions(+), 30 deletions(-)

diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py
index 350ddb4b..7b72da30 100644
--- a/python/meterpreter/ext_server_stdapi.py
+++ b/python/meterpreter/ext_server_stdapi.py
@@ -2764,27 +2764,33 @@ def stdapi_ui_get_idle_time(request, response):
 
 @register_function_if(has_windll)
 def stdapi_ui_desktop_enum(request, response):
-    global ui_desktop_enum_RESPONSE
-    ui_desktop_enum_RESPONSE = response
+    
+    from functools import partial
 
+    response_parts = []
     class EnumDesktops_Param(ctypes.Structure):
         _fields_ = [
             ('sessionId', ctypes.c_ulong),
             ('cpStationName', ctypes.c_char_p)
         ]
+    
+    if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
+        LPARAM = ctypes.c_long
+    elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
+        LPARAM = ctypes.c_longlong
 
-    EnumDesktopCallbackPrototype = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.POINTER(EnumDesktops_Param))
+    DESKTOPENUMPROCA = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.POINTER(EnumDesktops_Param))
     EnumDesktopsA = ctypes.windll.user32.EnumDesktopsA
-    EnumDesktopsA.argtypes = [ctypes.c_void_p, EnumDesktopCallbackPrototype, ctypes.POINTER(EnumDesktops_Param)]
+    EnumDesktopsA.argtypes = [ctypes.c_void_p, DESKTOPENUMPROCA, ctypes.POINTER(EnumDesktops_Param)]
     EnumDesktopsA.restype = ctypes.c_long
 
-    WindowStationCallbackPrototype = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.c_ulong)
+    WINSTAENUMPROCA = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, LPARAM)
     EnumWindowStationsA = ctypes.windll.user32.EnumWindowStationsA
-    EnumWindowStationsA.argtypes = [WindowStationCallbackPrototype, ctypes.c_ulong]
+    EnumWindowStationsA.argtypes = [WINSTAENUMPROCA, LPARAM]
     EnumWindowStationsA.restype = ctypes.c_long
 
     OpenWindowStationA = ctypes.windll.user32.OpenWindowStationA
-    OpenWindowStationA.argtypes = [ctypes.c_char_p, ctypes.c_long, ctypes.c_ulong]
+    OpenWindowStationA.argtypes = [ctypes.c_char_p, ctypes.c_long, ctypes.c_bool]
     OpenWindowStationA.restype = ctypes.c_void_p
 
     CloseWindowStation = ctypes.windll.user32.CloseWindowStation
@@ -2798,29 +2804,20 @@ def stdapi_ui_desktop_enum(request, response):
     GetProcAddress.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
     GetProcAddress.restype = ctypes.c_void_p
 
-    ProcessIdToSessionIdPrototype = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_ulong, ctypes.POINTER(ctypes.c_ulong))
-
     def get_session_id(pid):
-        pProcessIdToSessionId = None
         dwSessionId = ctypes.c_ulong(0)
 
-        hKernel = ctypes.WinDLL('kernel32.dll')
-        if hKernel:
-            pProcessIdToSessionId = GetProcAddress(hKernel._handle, ctypes.c_char_p(b"ProcessIdToSessionId"))
-
-        if not pProcessIdToSessionId:
-            ctypes.windll.kernel32.FreeLibrary(None, handle=hKernel)
-            return dwSessionId
-
-        pProcessIdToSessionId = ProcessIdToSessionIdPrototype(pProcessIdToSessionId)
-        if not pProcessIdToSessionId(ctypes.c_ulong(pid), ctypes.byref(dwSessionId)):
+        ProcessIdToSessionId = ctypes.windll.kernel32.ProcessIdToSessionId
+        ProcessIdToSessionId.argtypes = [ctypes.c_ulong, ctypes.POINTER(ctypes.c_ulong)]
+        ProcessIdToSessionId.restype = ctypes.c_bool
+        
+        if not ProcessIdToSessionId(ctypes.c_ulong(pid), ctypes.byref(dwSessionId)):
             dwSessionId = ctypes.c_ulong(-1)
 
-        ctypes.windll.kernel32.FreeLibrary(None, handle=hKernel)
         return dwSessionId
 
-    @EnumDesktopCallbackPrototype
-    def desktop_enumdesktops_callback(lpszDesktop, lParam):
+
+    def desktop_enumdesktops_callback(response_parts, lpszDesktop, lParam):
         if not lParam or not lpszDesktop:
             return True
 
@@ -2832,21 +2829,23 @@ def stdapi_ui_desktop_enum(request, response):
         entry += tlv_pack(TLV_TYPE_DESKTOP_STATION, lParam.contents.cpStationName.decode())
         entry += tlv_pack(TLV_TYPE_DESKTOP_NAME, lpszDesktop.decode())
 
-        global ui_desktop_enum_RESPONSE
-        ui_desktop_enum_RESPONSE += tlv_pack(TLV_TYPE_DESKTOP, entry)
+        response_parts.append(tlv_pack(TLV_TYPE_DESKTOP, entry))
 
         return True
 
-    @WindowStationCallbackPrototype
+    @WINSTAENUMPROCA
     def desktop_enumstations_callback(lpszWindowStation, lParam):
         hWindowStation = OpenWindowStationA(lpszWindowStation, False, MAXIMUM_ALLOWED)
         if not hWindowStation:
-            return False
+            return True
+
+        callback = partial(desktop_enumdesktops_callback, response_parts)
+        callback = DESKTOPENUMPROCA(callback)
 
         param = EnumDesktops_Param()
         param.sessionId = get_session_id(GetCurrentProcessId())
         param.cpStationName = lpszWindowStation
-        EnumDesktopsA(hWindowStation, desktop_enumdesktops_callback, ctypes.pointer(param))
+        EnumDesktopsA(hWindowStation, callback, ctypes.pointer(param))
 
         if hWindowStation:
             CloseWindowStation(hWindowStation)
@@ -2857,8 +2856,7 @@ def stdapi_ui_desktop_enum(request, response):
     if not success:
         return error_result_windows(), response
 
-    response = ui_desktop_enum_RESPONSE
-    del ui_desktop_enum_RESPONSE
+    response += bytes().join(response_parts)
 
     return ERROR_SUCCESS, response
 

From 8c99076e9a9127fd0663f86bad02720f61c17939 Mon Sep 17 00:00:00 2001
From: Alex Romero <ntalexio2@gmail.com>
Date: Thu, 12 Jan 2023 13:03:42 -0500
Subject: [PATCH 3/3] apply @smcintyre-r7 patch on station 0 bug for python
 enumdesktops

---
 python/meterpreter/ext_server_stdapi.py | 35 +++++++++----------------
 1 file changed, 12 insertions(+), 23 deletions(-)

diff --git a/python/meterpreter/ext_server_stdapi.py b/python/meterpreter/ext_server_stdapi.py
index 7b72da30..597f3a94 100644
--- a/python/meterpreter/ext_server_stdapi.py
+++ b/python/meterpreter/ext_server_stdapi.py
@@ -1,4 +1,5 @@
 import fnmatch
+import functools
 import getpass
 import os
 import platform
@@ -2765,23 +2766,15 @@ def stdapi_ui_get_idle_time(request, response):
 @register_function_if(has_windll)
 def stdapi_ui_desktop_enum(request, response):
     
-    from functools import partial
-
     response_parts = []
-    class EnumDesktops_Param(ctypes.Structure):
-        _fields_ = [
-            ('sessionId', ctypes.c_ulong),
-            ('cpStationName', ctypes.c_char_p)
-        ]
-    
     if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
         LPARAM = ctypes.c_long
     elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
         LPARAM = ctypes.c_longlong
 
-    DESKTOPENUMPROCA = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, ctypes.POINTER(EnumDesktops_Param))
+    DESKTOPENUMPROCA = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, LPARAM)
     EnumDesktopsA = ctypes.windll.user32.EnumDesktopsA
-    EnumDesktopsA.argtypes = [ctypes.c_void_p, DESKTOPENUMPROCA, ctypes.POINTER(EnumDesktops_Param)]
+    EnumDesktopsA.argtypes = [ctypes.c_void_p, DESKTOPENUMPROCA, LPARAM]
     EnumDesktopsA.restype = ctypes.c_long
 
     WINSTAENUMPROCA = ctypes.WINFUNCTYPE(ctypes.c_long, ctypes.c_char_p, LPARAM)
@@ -2817,16 +2810,13 @@ def stdapi_ui_desktop_enum(request, response):
         return dwSessionId
 
 
-    def desktop_enumdesktops_callback(response_parts, lpszDesktop, lParam):
-        if not lParam or not lpszDesktop:
-            return True
-
-        if not lParam.contents.sessionId or not lParam.contents.cpStationName:
+    def desktop_enumdesktops_callback(response_parts, session_id, station_name, lpszDesktop, lParam):
+        if not station_name or not lpszDesktop:
             return True
 
         entry  = bytes()
-        entry += tlv_pack(TLV_TYPE_DESKTOP_SESSION, lParam.contents.sessionId)
-        entry += tlv_pack(TLV_TYPE_DESKTOP_STATION, lParam.contents.cpStationName.decode())
+        entry += tlv_pack(TLV_TYPE_DESKTOP_SESSION, session_id)
+        entry += tlv_pack(TLV_TYPE_DESKTOP_STATION, station_name)
         entry += tlv_pack(TLV_TYPE_DESKTOP_NAME, lpszDesktop.decode())
 
         response_parts.append(tlv_pack(TLV_TYPE_DESKTOP, entry))
@@ -2839,13 +2829,12 @@ def stdapi_ui_desktop_enum(request, response):
         if not hWindowStation:
             return True
 
-        callback = partial(desktop_enumdesktops_callback, response_parts)
+        callback = functools.partial(desktop_enumdesktops_callback, response_parts)
+        session_id = get_session_id(GetCurrentProcessId()).value
+        station_name = lpszWindowStation.decode()
+        callback = functools.partial(desktop_enumdesktops_callback, response_parts, session_id, station_name)
         callback = DESKTOPENUMPROCA(callback)
-
-        param = EnumDesktops_Param()
-        param.sessionId = get_session_id(GetCurrentProcessId())
-        param.cpStationName = lpszWindowStation
-        EnumDesktopsA(hWindowStation, callback, ctypes.pointer(param))
+        EnumDesktopsA(hWindowStation, callback, 0)
 
         if hWindowStation:
             CloseWindowStation(hWindowStation)