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.

Související články