Jakou adresu hlásí Windows pro relaci RDP?
Rozhraní WTS API vrací hlášenou adresu klienta pro jednu relaci RDP, registr nikoli. Ptejte se na správné ID relace a dekódujte IPv4 nebo IPv6 správně.
Relace RDP potřebuje adresu klienta a zřejmé zdroje se neshodují. CLIENTNAME dává název počítače, registr drží hodnoty z jiných relací a protějšek socketu může být adresa brány nebo NAT, ne klienta. Windows sice hlášenou adresu klienta pro každou relaci vystavuje, ale musíte se zeptat na správnou relaci a odpověď správně dekódovat.
Ukotvěte hledání k jedné relaci
Začněte relací, ne osobou. Hostitel RDP v Windows může nést několik relací pro jeden účet a adresa viděná před opětovným připojením už nemusí odpovídat živému spojení. Zaznamenejte ID relace, uživatele a čas pozorování společně. ID relace je klíč, který API očekává, a je to hodnota, která brání záměně dvou relací.
Na hostiteli nainstalované nástroje relací vypisují, co existuje; administrativní pohled používejte jen tehdy, když na něj máte nárok. Potvrďte zvolené ID bezprostředně před dotazem, protože recyklované ID vrací data jiné relace.
qwinsta
Zavolejte WTSQuerySessionInformationW pro toto ID
API Služeb terminálu odpovídá na otázku informační třídou 14, WTSClientAddress. Malý obal ctypes na Windows stačí a vyhne se čtení hive jiného uživatele. Kód níže bere explicitní ID relace a ptá se místního hostitele.
import ctypes
import ipaddress
import socket
from ctypes import wintypes
class WTS_CLIENT_ADDRESS(ctypes.Structure):
_fields_ = [("AddressFamily", wintypes.DWORD),
("Address", ctypes.c_ubyte * 20)]
wts = ctypes.WinDLL("Wtsapi32", use_last_error=True)
query = wts.WTSQuerySessionInformationW
query.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.c_int,
ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(wintypes.DWORD)]
query.restype = wintypes.BOOL
free = wts.WTSFreeMemory
free.argtypes = [ctypes.c_void_p]
def client_address(session_id):
buf = ctypes.c_void_p()
size = wintypes.DWORD()
if not query(None, session_id, 14, ctypes.byref(buf), ctypes.byref(size)):
raise ctypes.WinError(ctypes.get_last_error())
try:
if size.value < ctypes.sizeof(WTS_CLIENT_ADDRESS):
raise RuntimeError("short WTS buffer")
value = ctypes.cast(buf, ctypes.POINTER(WTS_CLIENT_ADDRESS)).contents
raw = bytes(value.Address)
if value.AddressFamily == socket.AF_INET:
addr = ipaddress.ip_address(raw[2:6])
elif value.AddressFamily == socket.AF_INET6:
addr = ipaddress.ip_address(raw[:16])
else:
return None
return None if addr.is_unspecified else str(addr)
finally:
free(buf)
Předejte celočíselné ID relace, které jste právě ověřili, a ponechte nulový handle serveru pro místní stroj. Neúspěšné volání berte jako chybu, ne jako chybějící adresu, protože obojí vede k různým závěrům. Dotaz na relaci jiného uživatele vyžaduje příslušné oprávnění Query Information, proto nástroj spouštějte pod účtem, kterému bylo uděleno, místo ad hoc eskalace. Vrácený buffer patří API a musí být uvolněn i tehdy, když dekódování selže.
Dekódujte rodinu a potom uvolněte buffer
Struktura není IP řetězec. Její pole rodiny rozhoduje, jak se čte dvacet bajtů adresy: IPv4 začíná na offsetu dva, zatímco IPv6 na offsetu nula. Nezadaná adresa znamená, že ji klient nenahlásil, což není totéž jako prázdný řetězec. Připojení přes Remote Desktop Gateway jsou dokumentována jako případ, kdy adresa není dostupná, takže relace přes bránu nehlásí nic užitečného o skutečné adrese klienta. Korespondence se pak musí přesunout do vlastních záznamů brány.
Během ladění tiskněte rodinu vedle adresy, protože neshoda mezi oběma je obvyklou příčinou věrohodné, ale špatné hodnoty. Uvolněte buffer pomocí WTSFreeMemory v bloku finally, aby ho výjimka neprosákla.
Oddělte hlášenou adresu od důkazu
Hlášená adresa klienta je tvrzení, které poskytl klient, ne důvěryhodné pozorování. Není to protějšek TCP, není to tabulka pronájmů brány VPN a není to veřejná adresa klienta. Když je skutečnou otázkou, který pronájem VPN patří nějaké osobě, korelujte ve stejnou dobu ověřený záznam relace serveru VPN. Když je otázkou, který zdroj dosáhl posluchače, čtěte tabulku spojení nebo povolené logy na straně serveru.
Tyto hodnoty držte v každém podpůrném nástroji označené odděleně. Chybějící výsledek WTS má zůstat chybějící, místo aby se nahradil libovolnou hodnotou registru z jiné relace. Zaznamenejte ID relace vedle adresy, protože stejný uživatel se může znovu připojit a dostat novou hodnotu. Ani jméno klienta, ani hlášená adresa nejsou dostatečným důkazem pro udělení přístupu.