-
-
-
-
1 - import enum 2 - import platform 3 - import uuid 4 - 5 - from aardwolf.commons.target import RDPTarget 6 - 7 - 8 - class RDPCredentialTypes(enum.Enum): 9 - NTLM_NT = 'ntlm-nt' 10 - NTLM_PASSWORD = 'ntlm-password' 11 - NTLM_PWPROMPT = 'ntlm-pwprompt' 12 - NTLM_PWHEX = 'ntlm-pwhex' 13 - NTLM_PWB64 = 'ntlm-pwb64' 14 - SSPI_NTLM = 'sspi-ntlm' 15 - SSPI_KERBEROS = 'sspi-kerberos' 16 - MULTIPLEXOR_NTLM = 'multiplexor-ntlm' 17 - MULTIPLEXOR_KERBEROS = 'multiplexor-kerberos' 18 - KERBEROS_PASSWORD = 'kerberos-password' 19 - KERBEROS_PWPROMPT = 'kerberos-pwprompt' 20 - KERBEROS_PWHEX = 'kerberos-pwhex' 21 - KERBEROS_PWB64 = 'kerberos-pwb64' 22 - KERBEROS_AES = 'kerberos-aes' 23 - KERBEROS_RC4 = 'kerberos-rc4' 24 - KERBEROS_CCACHE = 'kerberos-ccache' 25 - KERBEROS_KIRBI = 'kerberos-kirbi' 26 - NEGOEX_CERTFILE = 'negoex_certfile' 27 - 28 - class RDPCredentialsSecretType(enum.Enum): 29 - NT = 'NT' 30 - PASSWORD = 'PASSWORD' 31 - PWPROMPT = 'PWPROMPT' 32 - PWHEX = 'PWHEX' 33 - PWB64 = 'PWB64' 34 - AES = 'AES' 35 - RC4 = 'RC4' 36 - CCACHE = 'CCACHE' 37 - KEYTAB = 'KEYTAB' 38 - KIRBI = 'KIRBI' 39 - NONE = 'NONE' 40 - CERTFILE = 'CERTFILE' 41 - CERTSTORE = 'CERTSTORE' 42 - 43 - class RDPAuthProtocol(enum.Enum): 44 - NONE = 'NONE' 45 - PLAIN = 'PLAIN' 46 - NTLM = 'NTLM' 47 - KERBEROS = 'KERBEROS' 48 - SSPI_KERBEROS = 'SSPI_KERBEROS' 49 - SSPI_NTLM = 'SSPI_NTLM' 50 - MULTIPLEXOR_NTLM = 'MULTIPLEXOR_NTLM' 51 - MULTIPLEXOR_KERBEROS = 'MULTIPLEXOR_KERBEROS' 52 - MULTIPLEXOR_SSL_NTLM = 'MULTIPLEXOR_SSL_NTLM' 53 - MULTIPLEXOR_SSL_KERBEROS = 'MULTIPLEXOR_SSL_KERBEROS' 54 - MPN_SSL_NTLM = 'MPN_SSL_NTLM' 55 - MPN_NTLM = 'MPN_NTLM' 56 - MPN_SSL_KERBEROS = 'MPN_SSL_KERBEROS' 57 - MPN_KERBEROS = 'MPN_KERBEROS' 58 - WSNET_NTLM = 'WSNET_NTLM' 59 - WSNET_KERBEROS = 'WSNET_KERBEROS' 60 - SSPIPROXY_KERBEROS = 'SSPIPROXY_KERBEROS' 61 - SSPIPROXY_NTLM = 'SSPIPROXY_NTLM' 62 - NEGOEX = 'NEGOEX' 63 - 64 - class RDPCredential: 65 - def __init__(self, 66 - username:str = None, 67 - domain:str = None, 68 - secret:str = None, 69 - secret_type:RDPCredentialsSecretType = None, 70 - authentication_type:RDPAuthProtocol = None, 71 - settings = None, 72 - target:RDPTarget = None): 73 - self.username = username 74 - self.domain = domain 75 - self.secret = secret 76 - self.secret_type = secret_type #RDPCredentialsSecretType 77 - self.target = target #for kerberos authentication 78 - 79 - self.authentication_type = authentication_type #kerberos or NTLM or ... 80 - self.settings = settings 81 - 82 - #domain/user/auth_type/secret_type:secret@target_ip_hostname_fqdn:target_port/dc_ip 83 - 84 - def __str__(self): 85 - t = '==== RDPCredential ====\r\n' 86 - for k in self.__dict__: 87 - t += '%s: %s\r\n' % (k, self.__dict__[k]) 88 - 89 - return t 90 - 91 - @staticmethod 92 - def from_credential_string(s): 93 - """ 94 - Making users life more conveinent 95 - """ 96 - return RDPCredential.from_connection_string(s + '@') 97 - 98 - @staticmethod 99 - def from_connection_string(s): 100 - creds = RDPCredential() 101 - 102 - t, target = s.rsplit('@', 1) 103 - creds.domain, t = t.split('/', 1) 104 - creds.username, t = t.split('/', 1) 105 - if t.find('/') != -1: 106 - auth_type , t = t.split('/', 1) 107 - secret_type, creds.secret = t.split(':',1) 108 - creds.secret_type = RDPCredentialsSecretType(secret_type.upper()) 109 - else: 110 - auth_type = t 111 - creds.secret_type = RDPCredentialsSecretType.NONE 112 - creds.authentication_type = RDPAuthProtocol(auth_type.upper().replace('-','_')) 113 - 114 - #sanity check 115 - if creds.secret_type == [RDPCredentialsSecretType.NT, RDPCredentialsSecretType.RC4]: 116 - try: 117 - bytes.fromhex(creds.secret) 118 - if len(creds.secret) != 32: 119 - raise Exception() 120 - except Exception as e: 121 - raise Exception('This is not a valid NT hash') 122 - 123 - elif creds.secret_type == RDPCredentialsSecretType.AES: 124 - try: 125 - bytes.fromhex(creds.secret) 126 - if len(creds.secret) != 32 or len(creds.secret) != 64: 127 - raise Exception() 128 - except Exception as e: 129 - raise Exception('This is not a valid NT hash') 130 - 131 - elif creds.secret_type == RDPCredentialsSecretType.CCACHE: 132 - try: 133 - with open(creds.secret, 'rb') as f: 134 - a = 1 135 - except Exception as e: 136 - raise Exception('Could not open CCACHE file!') 137 - 138 - 139 - if creds.authentication_type == RDPAuthProtocol.NTLM: 140 - if creds.secret_type not in [RDPCredentialsSecretType.NT, RDPCredentialsSecretType.RC4, RDPCredentialsSecretType.PASSWORD]: 141 - raise Exception('NTLM authentication requires either password or NT hash or RC4 key to be specified as secret!') 142 - 143 - elif creds.authentication_type == RDPAuthProtocol.KERBEROS: 144 - if creds.secret_type not in [RDPCredentialsSecretType.NT, RDPCredentialsSecretType.RC4, RDPCredentialsSecretType.PASSWORD, RDPCredentialsSecretType.AES, RDPCredentialsSecretType.CCACHE]: 145 - raise Exception('KERBEROS authentication requires either password or NT hash or RC4 key or AES key or CCACHE file to be specified as secret!') 146 - 147 - elif creds.authentication_type in [RDPAuthProtocol.SSPI_NTLM, RDPAuthProtocol.SSPI_KERBEROS]: 148 - if platform.system() != 'Windows': 149 - raise Exception('SSPI authentication on works on windows!') 150 - 151 - return creds 152 - 153 - class RDPKerberosCredential: 154 - def __init__(self): 155 - self.mode = 'CLIENT' 156 - self.connection = None #KerberosCredential 157 - self.target = None #KerberosTarget 158 - self.ksoc = None #KerberosSocketAIO 159 - self.ccred = None 160 - 161 - class RDPKerberosSSPICredential: 162 - def __init__(self): 163 - self.mode = 'CLIENT' 164 - self.client = None 165 - self.password = None 166 - self.target = None 167 - 168 - class RDPNTLMSSPICredential: 169 - def __init__(self): 170 - self.mode = 'CLIENT' 171 - self.client = None 172 - self.passwrd = None 173 - 174 - class RDPNTLMCredential: 175 - def __init__(self): 176 - self.username = None 177 - self.domain = '' 178 - self.password = None 179 - self.workstation = None 180 - self.is_guest = False 181 - self.nt_hash = None 182 - self.lm_hash = None 183 - 184 - class RDPWSNETCredential: 185 - def __init__(self): 186 - self.mode = 'CLIENT' 187 - self.type = 'NTLM' 188 - self.username = '<CURRENT>' 189 - self.domain = '<CURRENT>' 190 - self.password = '<CURRENT>' 191 - self.target = None 192 - self.is_guest = False 193 - 194 - class RDPSSPIProxyCredential: 195 - def __init__(self): 196 - self.mode = 'CLIENT' 197 - self.type = 'NTLM' 198 - self.username = '<CURRENT>' 199 - self.domain = '<CURRENT>' 200 - self.password = '<CURRENT>' 201 - self.target = None 202 - self.is_guest = False 203 - self.host = '127.0.0.1' 204 - self.port = 9999 205 - self.proto = 'ws' 206 - self.agent_id = None 207 - 208 - class RDPMultiplexorCredential: 209 - def __init__(self): 210 - self.mode = 'CLIENT' 211 - self.type = 'NTLM' 212 - self.username = '<CURRENT>' 213 - self.domain = '<CURRENT>' 214 - self.password = '<CURRENT>' 215 - self.target = None 216 - self.is_guest = False 217 - self.is_ssl = False 218 - self.mp_host = None 219 - self.mp_port = None 220 - self.mp_username = None 221 - self.mp_domain = None 222 - self.mp_password = None 223 - self.agent_id = None 224 - 225 - def get_url(self): 226 - url_temp = 'ws://%s:%s' 227 - if self.is_ssl is True: 228 - url_temp = 'wss://%s:%s' 229 - url = url_temp % (self.mp_host, self.mp_port) 230 - return url 231 - 232 - def parse_settings(self, settings): 233 - self.mp_host = settings['host'][0] 234 - self.mp_port = settings['port'][0] 235 - if self.mp_port is None: 236 - self.mp_port = '9999' 237 - if 'user' in settings: 238 - self.mp_username = settings.get('user')[0] 239 - if 'domain' in settings: 240 - self.mp_domain = settings.get('domain')[0] 241 - if 'password' in settings: 242 - self.mp_password = settings.get('password')[0] 243 - self.agent_id = settings['agentid'][0] 244 - 245 - class RDPMPNCredential: 246 - def __init__(self): 247 - self.mode = 'CLIENT' 248 - self.type = 'NTLM' 249 - self.username = '<CURRENT>' 250 - self.domain = '<CURRENT>' 251 - self.password = '<CURRENT>' 252 - self.target = None 253 - self.is_guest = False 254 - self.is_ssl = False 255 - self.mp_host = None 256 - self.mp_port = 9999 257 - self.mp_username = None 258 - self.mp_domain = None 259 - self.mp_password = None 260 - self.agent_id = None 261 - self.operator = None 262 - self.timeout = 5 263 - 264 - 265 - def get_url(self): 266 - url_temp = 'ws://%s:%s' 267 - if self.is_ssl is True: 268 - url_temp = 'wss://%s:%s' 269 - url = url_temp % (self.mp_host, self.mp_port) 270 - return url 271 - 272 - def parse_settings(self, settings): 273 - self.mp_host = settings['host'][0] 274 - if 'port' in settings: 275 - self.mp_port = settings.get['port'][0] 276 - if 'user' in settings: 277 - self.mp_username = settings.get('user')[0] 278 - if 'domain' in settings: 279 - self.mp_domain = settings.get('domain')[0] 280 - if 'password' in settings: 281 - self.mp_password = settings.get('password')[0] 282 - self.agent_id = uuid.UUID(settings['agentid'][0]) 283 - 284 - def test(): 285 - s = 'TEST/victim/ntlm/nt:[email protected]:3389' 286 - creds = RDPCredential.from_connection_string(s) 287 - print(str(creds)) 288 - 289 - s = 'TEST/victim/[email protected]:3389/aaaa' 290 - creds = RDPCredential.from_connection_string(s) 291 - 292 - print(str(creds)) 293 - 294 - if __name__ == '__main__': 295 - test() -
1 + import enum 2 + from typing import List 3 + from asysocks.unicomm.common.proxy import UniProxyProto, UniProxyTarget 4 + from aardwolf.commons.iosettings import RDPIOSettings 5 + 6 + from aardwolf.commons.target import RDPTarget, RDPConnectionDialect 7 + from uniauth.common.credentials import UniCredential 8 + from aardwolf.connection import RDPConnection 9 + from aardwolf.vncconnection import VNCConnection 10 + import ipaddress 11 + import copy 12 + 13 + 14 + class RDPConnectionFactory: 15 + def __init__(self, iosettings=None, target:RDPTarget = None, credential:UniCredential = None, proxies:List[UniProxyTarget] = None): 16 + self.credential = credential 17 + self.target = target 18 + self.proxies = proxies 19 + self.iosettings = iosettings 20 + 21 + def get_connection(self, iosettings): 22 + tio = copy.deepcopy(iosettings) 23 + credential = self.get_credential() 24 + target = self.get_target() 25 + if target.dialect is not None: 26 + if target.dialect == RDPConnectionDialect.RDP: 27 + return RDPConnection(target, credential, tio) 28 + elif target.dialect == RDPConnectionDialect.VNC: 29 + return VNCConnection(target, credential, tio) 30 + else: 31 + raise Exception('Unknown dialect %s' % target.dialect) 32 + raise Exception('Either target or dialect must be defined first!') 33 + 34 + def create_connection_newtarget(self, ip_or_hostname, iosettings): 35 + tio = copy.deepcopy(iosettings) 36 + credential = self.get_credential() 37 + 38 + target = self.get_target() 39 + try: 40 + ipaddress.ip_address(ip_or_hostname) 41 + target.ip = ip_or_hostname 42 + target.hostname = None 43 + except: 44 + target.hostname = ip_or_hostname 45 + target.ip = ip_or_hostname 46 + 47 + if target.dialect == RDPConnectionDialect.RDP: 48 + return RDPConnection(target, credential, tio) 49 + elif target.dialect == RDPConnectionDialect.VNC: 50 + return VNCConnection(target, credential, tio) 51 + else: 52 + raise Exception('Unknown dialect %s' % target.dialect) 53 + 54 + def get_proxy(self) -> List[UniProxyTarget]: 55 + return copy.deepcopy(self.target.proxies) 56 + 57 + def get_target(self) -> RDPTarget: 58 + return copy.deepcopy(self.target) 59 + 60 + def get_credential(self) -> UniCredential: 61 + return copy.deepcopy(self.credential) 62 + 63 + def get_settings(self) -> RDPIOSettings: 64 + return copy.deepcopy(self.iosettings) 65 + 66 + @staticmethod 67 + def from_url(connection_url, iosettings): 68 + target = RDPTarget.from_url(connection_url) 69 + credential = UniCredential.from_url(connection_url) 70 + return RDPConnectionFactory(iosettings, target, credential) 71 + 72 + 73 + def __str__(self): 74 + t = '==== RDPConnectionFactory ====\r\n' 75 + for k in self.__dict__: 76 + val = self.__dict__[k] 77 + if isinstance(val, enum.IntFlag): 78 + val = val 79 + elif isinstance(val, enum.Enum): 80 + val = val.name 81 + 82 + t += '%s: %s\r\n' % (k, str(val)) 83 + 84 + return t 85 + 86 + 87 + 88 + if __name__ == '__main__': 89 + url_tests = [ 90 + #'RDP://10.10.10.2', 91 + #'RDP://10.10.10.2:9000', 92 + #'RDP-tcp://10.10.10.2', 93 + #'RDP-tcp://10.10.10.2:9000', 94 + #'RDP-udp://10.10.10.2:138', 95 + #'RDP+ntlm-password://domain\\[email protected]', 96 + #'RDP-tcp+ntlm-password://domain\\user:[email protected]', 97 + #'RDP-tcp+ntlm-password://domain\\user:[email protected]:10000', 98 + #'RDP-tcp+ntlm-nt://domain\\user:[email protected]', 99 + #'RDP+ntlm-nt://domain\\user:[email protected]', 100 + #'RDP+ntlm-nt://domain\\user:[email protected]', 101 + #'RDP-tcp+kerberos-password://domain\\alma:[email protected]', 102 + #'RDP-tcp+kerberos-aes://domain\\alma:[email protected]', 103 + #'RDP-tcp+kerberos-aes://domain\\alma:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@10.10.10.10.2', 104 + #'RDP-tcp+kerberos-nt://domain\\alma:[email protected]', 105 + #'RDP-tcp+kerberos-rc4://domain\\alma:[email protected]', 106 + #'RDP+sspi://10.10.10.10.2', 107 + #'RDP+sspi-ntlm://10.10.10.10.2', 108 + #'RDP+sspi-kerberos://10.10.10.10.2', 109 + #'RDP+multiplexor://10.10.10.10.2', 110 + #'RDP+multiplexor-ssl://10.10.10.10.2', 111 + #'RDP+multiplexor-ssl-ntlm://10.10.10.10.2', 112 + #'RDP+multiplexor-ssl-kerberos://10.10.10.10.2', 113 + #'RDP://10.10.10.2/?timeout=10', 114 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2', 115 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5', 116 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1', 117 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma', 118 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma&dc=10.10.10.2&dns=8.8.8.8', 119 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5-ssl&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma&dc=10.10.10.2&dns=8.8.8.8', 120 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1', 121 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1&proxyagentid=alma', 122 + #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1&proxyagentid=alma&proxytimeout=111', 123 + 'RDP://10.10.10.2/C$/test/tst111.dmp?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyhost=127.0.0.1&proxyport=1&proxyagentid=alma&proxytimeout=111', 124 + 125 + ] 126 + for url in url_tests: 127 + print('===========================================================================') 128 + print(url) 129 + try: 130 + dec = RDPConnectionFactory.from_url(url) 131 + creds = dec.get_credential() 132 + target = dec.get_target() 133 + RDPfile = dec.get_file() 134 + print(RDPfile) 135 + except Exception as e: 136 + import traceback 137 + traceback.print_exc() 138 + print('ERROR! Reason: %s' % e) 139 + input() 140 + else: 141 + print(str(creds)) 142 + print(str(target)) 143 + input() 144 + 145 + 146 + -
1 - import ipaddress 2 - import enum 3 - from urllib.parse import urlparse, parse_qs 4 - 5 - from asysocks.common.clienturl import SocksClientURL 6 - 7 - 8 - def stru(x): 9 - return str(x).upper() 10 - 11 - class RDPProxySecretType(enum.Enum): 12 - NONE = 'NONE' 13 - PLAIN = 'PLAIN' 14 - 15 - class RDPProxyType(enum.Enum): 16 - ASYSOCKS = 'ASYSOCKS' 17 - 18 - class RDPProxy: 19 - def __init__(self): 20 - self.type:RDPProxyType = None 21 - self.target = None 22 - 23 - @staticmethod 24 - def from_params(url_str): 25 - proxy = RDPProxy() 26 - url = urlparse(url_str) 27 - if url.query is None: 28 - return None 29 - 30 - query = parse_qs(url.query) 31 - if 'proxytype' not in query and 'sametype' not in query: 32 - return None 33 - 34 - if query['proxytype'][0].upper() in ['SOCKS5', 'SOCKS4', 'SOCKS4A', 'SOCKS5-SSL', 'SOCKS4-SSL', 'SOCKS4A-SSL', 'WSNET', 'WSNETWS', 'WSNETWSS']: 35 - proxy.type = RDPProxyType.ASYSOCKS 36 - else: 37 - raise NotImplementedError(query['proxytype'][0].upper()) 38 - 39 - if proxy.type == RDPProxyType.ASYSOCKS: 40 - cu = SocksClientURL.from_params(url_str) 41 - cu[-1].endpoint_port = 3389 42 - proxy.target = cu 43 - else: 44 - raise NotImplementedError('Unknown proxy type "%s"' % proxy.type) 45 - 46 - return proxy 47 - 48 - def __str__(self): 49 - t = '==== RDPProxy ====\r\n' 50 - for k in self.__dict__: 51 - t += '%s: %s\r\n' % (k, self.__dict__[k]) 52 - 53 - return t 54 - 55 - def test(): 56 - t = ['socks5://10.10.10.1', 57 - 'socks5+ssl://10.10.10.1', 58 - 'socks5+ssl://admin:[email protected]', 59 - ] 60 - for x in t: 61 - s = RDPProxy.from_connection_string(x) 62 - print(str(s)) 63 - 64 - 65 - if __name__ == '__main__': 66 - test() -
-
1 - import enum 2 - from urllib.parse import urlparse, parse_qs 3 - from aardwolf.commons.credential import RDPCredential, RDPCredentialsSecretType, RDPAuthProtocol 4 - from aardwolf.commons.proxy import RDPProxy 5 - from aardwolf.commons.target import RDPTarget, RDPConnectionDialect, RDPConnectionProtocol 6 - from aardwolf.commons.authbuilder import AuthenticatorBuilder 7 - from aardwolf.connection import RDPConnection 8 - from aardwolf.vncconnection import VNCConnection 9 - from getpass import getpass 10 - import base64 11 - import ipaddress 12 - import copy 13 - 14 - 15 - class RDPConnectionURL: 16 - def __init__(self, connection_url, target = None, cred = None, proxy = None): 17 - self.connection_url = connection_url 18 - 19 - #credential 20 - self.authentication_protocol = None 21 - self.secret_type = None 22 - self.domain = None 23 - self.username = None 24 - self.secret = None 25 - self.is_anonymous = None 26 - self.auth_settings = {} 27 - self.cred = cred 28 - 29 - #target 30 - self.dialect = None 31 - self.protocol = None 32 - self.hostname = None 33 - self.dc_ip = None 34 - self.port = None 35 - self.ip = None 36 - self.timeout = 5 37 - self.serverip = None 38 - self.fragment = None 39 - self.path = None 40 - self.compression = False 41 - self.scheme = None 42 - 43 - self.target = target 44 - 45 - #proxy 46 - self.proxy = proxy 47 - 48 - if self.connection_url is not None: 49 - self.parse() 50 - 51 - def get_connection(self, iosettings): 52 - tio = copy.deepcopy(iosettings) 53 - credential = self.get_credential() 54 - target = self.get_target() 55 - if target.dialect is not None: 56 - if target.dialect == RDPConnectionDialect.RDP: 57 - return RDPConnection(target, credential, tio) 58 - elif target.dialect == RDPConnectionDialect.VNC: 59 - return VNCConnection(target, credential, tio) 60 - else: 61 - raise Exception('Unknown dialect %s' % target.dialect) 62 - raise Exception('Either target or dialect must be defined first!') 63 - 64 - def create_connection_newtarget(self, ip_or_hostname, iosettings): 65 - tio = copy.deepcopy(iosettings) 66 - credential = self.get_credential() 67 - credential.target = ip_or_hostname 68 - 69 - target = self.get_target() 70 - try: 71 - ipaddress.ip_address(ip_or_hostname) 72 - target.ip = ip_or_hostname 73 - target.hostname = None 74 - except: 75 - target.hostname = ip_or_hostname 76 - target.ip = ip_or_hostname 77 - 78 - #spneg = AuthenticatorBuilder.to_credssp(credential, target) 79 - if target.dialect == RDPConnectionDialect.RDP: 80 - return RDPConnection(target, credential, tio) 81 - elif target.dialect == RDPConnectionDialect.VNC: 82 - return VNCConnection(target, credential, tio) 83 - else: 84 - raise Exception('Unknown dialect %s' % target.dialect) 85 - 86 - def get_file(self): 87 - return RDPFile.from_RDPurl(self) 88 - 89 - def get_proxy(self): 90 - if self.proxy is not None: 91 - copy.deepcopy(self.proxy) 92 - return None 93 - 94 - def get_target(self): 95 - if self.target is not None: 96 - return copy.deepcopy(self.target) 97 - 98 - if self.ip is not None and self.hostname is None: 99 - try: 100 - ipaddress.ip_address(self.ip) 101 - except: 102 - self.hostname = self.ip 103 - if self.serverip is not None: 104 - self.ip = self.serverip 105 - 106 - t = RDPTarget( 107 - ip = self.ip, 108 - port = self.port, 109 - hostname = self.hostname, 110 - timeout = self.timeout, 111 - dc_ip= self.dc_ip, 112 - domain = self.domain, 113 - proxy = self.get_proxy(), 114 - protocol=self.protocol, 115 - dialect=self.dialect 116 - ) 117 - return t 118 - 119 - def get_credential(self): 120 - if self.cred is not None: 121 - return copy.deepcopy(self.cred) 122 - return RDPCredential( 123 - username = self.username, 124 - domain = self.domain, 125 - secret = self.secret, 126 - secret_type = self.secret_type, 127 - authentication_type = self.authentication_protocol, 128 - settings = self.auth_settings, 129 - target = self.ip 130 - ) 131 - 132 - 133 - def scheme_decoder(self, scheme): 134 - self.schemes = scheme.upper().split('+') 135 - 136 - connection_tags = self.schemes[0].split('-') 137 - if len(connection_tags) > 1: 138 - self.dialect = RDPConnectionDialect(connection_tags[0]) 139 - self.protocol = RDPConnectionProtocol(connection_tags[1]) 140 - else: 141 - self.dialect = RDPConnectionDialect(connection_tags[0]) 142 - self.protocol = RDPConnectionProtocol.TCP 143 - 144 - if len(self.schemes) == 1: 145 - # user did not specify a requested auth scheme, defaulting to plain 146 - # later on when server requires credssp this will be converted to NTLM 147 - self.authentication_protocol = RDPAuthProtocol.PLAIN 148 - self.secret_type = RDPCredentialsSecretType.PASSWORD 149 - return 150 - 151 - auth_tags = self.schemes[1].replace('-','_') 152 - try: 153 - self.authentication_protocol = RDPAuthProtocol(auth_tags) 154 - except: 155 - auth_tags = self.schemes[1].split('-') 156 - #print(auth_tags) 157 - if len(auth_tags) > 1: 158 - if auth_tags[0] == 'MULTIPLEXOR': 159 - if auth_tags[1] == 'SSL': 160 - if len(auth_tags) == 2: 161 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_SSL_NTLM 162 - else: 163 - if auth_tags[2] == 'NTLM': 164 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_SSL_NTLM 165 - elif auth_tags[2] == 'KERBEROS': 166 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_SSL_KERBEROS 167 - else: 168 - if auth_tags[1] == 'NTLM': 169 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_NTLM 170 - elif auth_tags[1] == 'KERBEROS': 171 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_KERBEROS 172 - elif auth_tags[0] == 'SSPI': 173 - if auth_tags[1] == 'NTLM': 174 - self.authentication_protocol = RDPAuthProtocol.SSPI_NTLM 175 - elif auth_tags[1] == 'KERBEROS': 176 - self.authentication_protocol = RDPAuthProtocol.SSPI_KERBEROS 177 - else: 178 - self.authentication_protocol = RDPAuthProtocol(auth_tags[0]) 179 - self.secret_type = RDPCredentialsSecretType(auth_tags[1]) 180 - else: 181 - if auth_tags[0] == 'MULTIPLEXOR': 182 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_NTLM 183 - elif auth_tags[0] == 'MULTIPLEXOR_SSL': 184 - self.authentication_protocol = RDPAuthProtocol.MULTIPLEXOR_SSL_NTLM 185 - if auth_tags[0] == 'SSPI': 186 - self.authentication_protocol = RDPAuthProtocol.SSPI_NTLM 187 - else: 188 - self.authentication_protocol = RDPAuthProtocol(auth_tags[0]) 189 - if self.authentication_protocol == RDPAuthProtocol.KERBEROS: 190 - raise Exception('For kerberos auth you need to specify the secret type in the connection string!') 191 - 192 - def __str__(self): 193 - t = '==== RDPConnectionURL ====\r\n' 194 - for k in self.__dict__: 195 - val = self.__dict__[k] 196 - if isinstance(val, enum.IntFlag): 197 - val = val 198 - elif isinstance(val, enum.Enum): 199 - val = val.name 200 - 201 - t += '%s: %s\r\n' % (k, str(val)) 202 - 203 - return t 204 - 205 - def parse(self): 206 - url_e = urlparse(self.connection_url) 207 - self.scheme_decoder(url_e.scheme) 208 - 209 - if url_e.username is not None: 210 - if url_e.username.find('\\') != -1: 211 - self.domain , self.username = url_e.username.split('\\') 212 - if self.domain == '.': 213 - self.domain = None 214 - else: 215 - self.domain = None 216 - self.username = url_e.username 217 - 218 - self.secret = url_e.password 219 - 220 - if self.secret_type == RDPCredentialsSecretType.PWPROMPT: 221 - self.secret_type = RDPCredentialsSecretType.PASSWORD 222 - self.secret = getpass() 223 - 224 - if self.secret_type == RDPCredentialsSecretType.PWHEX: 225 - self.secret_type = RDPCredentialsSecretType.PASSWORD 226 - self.secret = bytes.fromhex(self.secret).decode() 227 - 228 - if self.secret_type == RDPCredentialsSecretType.PWB64: 229 - self.secret_type = RDPCredentialsSecretType.PASSWORD 230 - self.secret = base64.b64decode(self.secret).decode() 231 - 232 - if self.secret is None and self.username is None: 233 - self.is_anonymous = True 234 - 235 - if self.authentication_protocol == RDPAuthProtocol.NTLM and self.secret_type is None: 236 - if self.is_anonymous == True: 237 - self.secret_type = RDPCredentialsSecretType.NONE 238 - else: 239 - if len(self.secret) == 32: 240 - try: 241 - bytes.fromhex(self.secret) 242 - except: 243 - self.secret_type = RDPCredentialsSecretType.NT 244 - else: 245 - self.secret_type = RDPCredentialsSecretType.PASSWORD 246 - 247 - elif self.authentication_protocol in [RDPAuthProtocol.SSPI_KERBEROS, RDPAuthProtocol.SSPI_NTLM, 248 - RDPAuthProtocol.MULTIPLEXOR_NTLM, RDPAuthProtocol.MULTIPLEXOR_KERBEROS]: 249 - if self.username is None: 250 - self.username = '<CURRENT>' 251 - if self.domain is None: 252 - self.domain = '<CURRENT>' 253 - if self.secret is None: 254 - self.secret = '<CURRENT>' 255 - 256 - 257 - self.ip = url_e.hostname 258 - if url_e.port: 259 - self.port = url_e.port 260 - elif self.protocol == RDPConnectionProtocol.TCP and self.dialect == RDPConnectionDialect.RDP: 261 - self.port = 3389 262 - elif self.protocol == RDPConnectionProtocol.TCP and self.dialect == RDPConnectionDialect.VNC: 263 - self.port = 5800 264 - #elif self.protocol == RDPConnectionProtocol.QUIC: 265 - # self.port = 443 266 - else: 267 - raise Exception('Port must be provided!') 268 - 269 - if url_e.path not in ['/', '', None]: 270 - self.path = url_e.path 271 - 272 - 273 - # recognized parameters : 274 - # dc -> domain controller IP 275 - # proxytype -> proxy protocol 276 - # proxyuser -> username for proxy auth 277 - # proxypass -> password for proxy auth 278 - # 279 - # 280 - proxy_present = False 281 - if url_e.query is not None: 282 - query = parse_qs(url_e.query) 283 - for k in query: 284 - if k.startswith('proxy') is True: 285 - proxy_present = True 286 - if k == 'dc': 287 - self.dc_ip = query[k][0] 288 - elif k == 'timeout': 289 - self.timeout = int(query[k][0]) 290 - elif k == 'serverip': 291 - self.serverip = query[k][0] 292 - elif k == 'fragment': 293 - self.fragment = int(query[k][0]) 294 - elif k == 'dns': 295 - self.dns = query[k] #multiple dns can be set, so not trimming here 296 - elif k == 'compress': 297 - self.compression = int(query[k][0]) 298 - elif k.startswith('auth'): 299 - self.auth_settings[k[len('auth'):]] = query[k] 300 - elif k.startswith('same'): 301 - self.auth_settings[k[len('same'):]] = query[k] 302 - 303 - if proxy_present is True: 304 - self.proxy = RDPProxy.from_params(self.connection_url) 305 - 306 - if __name__ == '__main__': 307 - from aardwolf.commons.interfaces.file import RDPFile 308 - url_tests = [ 309 - #'RDP://10.10.10.2', 310 - #'RDP://10.10.10.2:9000', 311 - #'RDP-tcp://10.10.10.2', 312 - #'RDP-tcp://10.10.10.2:9000', 313 - #'RDP-udp://10.10.10.2:138', 314 - #'RDP+ntlm-password://domain\\[email protected]', 315 - #'RDP-tcp+ntlm-password://domain\\user:[email protected]', 316 - #'RDP-tcp+ntlm-password://domain\\user:[email protected]:10000', 317 - #'RDP-tcp+ntlm-nt://domain\\user:[email protected]', 318 - #'RDP+ntlm-nt://domain\\user:[email protected]', 319 - #'RDP+ntlm-nt://domain\\user:[email protected]', 320 - #'RDP-tcp+kerberos-password://domain\\alma:[email protected]', 321 - #'RDP-tcp+kerberos-aes://domain\\alma:[email protected]', 322 - #'RDP-tcp+kerberos-aes://domain\\alma:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@10.10.10.10.2', 323 - #'RDP-tcp+kerberos-nt://domain\\alma:[email protected]', 324 - #'RDP-tcp+kerberos-rc4://domain\\alma:[email protected]', 325 - #'RDP+sspi://10.10.10.10.2', 326 - #'RDP+sspi-ntlm://10.10.10.10.2', 327 - #'RDP+sspi-kerberos://10.10.10.10.2', 328 - #'RDP+multiplexor://10.10.10.10.2', 329 - #'RDP+multiplexor-ssl://10.10.10.10.2', 330 - #'RDP+multiplexor-ssl-ntlm://10.10.10.10.2', 331 - #'RDP+multiplexor-ssl-kerberos://10.10.10.10.2', 332 - #'RDP://10.10.10.2/?timeout=10', 333 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2', 334 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5', 335 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1', 336 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma', 337 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma&dc=10.10.10.2&dns=8.8.8.8', 338 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=socks5-ssl&proxyserver=127.0.0.1&proxyuser=admin&proxypass=alma&dc=10.10.10.2&dns=8.8.8.8', 339 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1', 340 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1&proxyagentid=alma', 341 - #'RDP://10.10.10.2/?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyserver=127.0.0.1&proxyagentid=alma&proxytimeout=111', 342 - 'RDP://10.10.10.2/C$/test/tst111.dmp?timeout=10&dc=10.10.10.2&proxytype=multiplexor&proxyhost=127.0.0.1&proxyport=1&proxyagentid=alma&proxytimeout=111', 343 - 344 - ] 345 - for url in url_tests: 346 - print('===========================================================================') 347 - print(url) 348 - try: 349 - dec = RDPConnectionURL(url) 350 - creds = dec.get_credential() 351 - target = dec.get_target() 352 - RDPfile = dec.get_file() 353 - print(RDPfile) 354 - except Exception as e: 355 - import traceback 356 - traceback.print_exc() 357 - print('ERROR! Reason: %s' % e) 358 - input() 359 - else: 360 - print(str(creds)) 361 - print(str(target)) 362 - input() 363 - 364 - 365 - -
skipped 12 lines 13 13 from aardwolf.keyboard import VK_MODIFIERS 14 14 from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT 15 15 from aardwolf.commons.target import RDPTarget 16 - from aardwolf.commons.credential import RDPCredential, RDPCredentialsSecretType 16 + from uniauth.common.credentials import UniCredential 17 + from uniauth.common.constants import UniAuthSecret, UniAuthProtocol 17 18 from aardwolf.commons.cryptolayer import RDPCryptoLayer 18 - from aardwolf.network.tpkt import TPKTNetwork 19 19 from aardwolf.network.x224 import X224Network 20 - from aardwolf.network.selector import NetworkSelector 20 + from uniauth.common.credentials.credssp import CREDSSPCredential 21 21 22 22 from aardwolf.protocol.x224.constants import SUPP_PROTOCOLS, NEG_FLAGS 23 23 from aardwolf.protocol.x224.server.connectionconfirm import RDP_NEG_RSP skipped 39 lines 63 63 64 64 from aardwolf.protocol.fastpath import TS_FP_UPDATE_PDU, FASTPATH_UPDATETYPE, FASTPATH_FRAGMENT, FASTPATH_SEC, TS_FP_UPDATE 65 65 from aardwolf.commons.queuedata import RDPDATATYPE, RDP_KEYBOARD_SCANCODE, RDP_KEYBOARD_UNICODE, RDP_MOUSE, RDP_VIDEO 66 - from aardwolf.commons.authbuilder import AuthenticatorBuilder 67 - from aardwolf.channels import Channel 66 + from aardwolf.channels import MCSChannel 68 67 from aardwolf.commons.iosettings import RDPIOSettings 69 68 69 + from asysocks.unicomm.client import UniClient 70 + from asysocks.unicomm.common.connection import UniConnection 71 + from aardwolf.network.tpkt import TPKTPacketizer 72 + 73 + from aardwolf.network.tpkt import CredSSPPacketizer 74 + from asysocks.unicomm.common.packetizers import Packetizer 70 75 71 76 class RDPConnection: 72 - def __init__(self, target:RDPTarget, credentials:RDPCredential, iosettings:RDPIOSettings): 77 + def __init__(self, target:RDPTarget, credentials:UniCredential, iosettings:RDPIOSettings): 73 78 """RDP client connection object. After successful connection the two asynchronous queues named `ext_out_queue` and `ext_in_queue` 74 79 can be used to communicate with the remote server 75 80 skipped 14 lines 90 95 self.ext_out_queue = asyncio.Queue() 91 96 self.ext_in_queue = asyncio.Queue() 92 97 93 - 94 - self.__tpkgnet = None 98 + self.__connection:UniConnection = None 95 99 self._x224net = None 96 - self.__transportnet = None #TCP/SSL/SOCKS etc. 97 100 self.__t125_ber_codec = None 98 101 self._t125_per_codec = None 99 102 self.__t124_codec = None skipped 13 lines 113 116 self.__channel_task = {} #name -> channeltask 114 117 115 118 116 - self.__fastpath_reader_task = None 117 119 self.__external_reader_task = None 118 120 self.__x224_reader_task = None 119 121 self.client_x224_flags = 0 120 122 self.client_x224_supported_protocols = self.iosettings.supported_protocols 121 123 self.cryptolayer:RDPCryptoLayer = None 122 - self.__fastpath_in_queue = None 123 124 self.__desktop_buffer = None 124 125 self.desktop_buffer_has_data = False 125 126 skipped 60 lines 186 187 if self.__external_reader_task is not None: 187 188 self.__external_reader_task.cancel() 188 189 189 - if self.__fastpath_reader_task is not None: 190 - self.__fastpath_reader_task.cancel() 191 - 192 190 if self.__x224_reader_task is not None: 193 191 self.__x224_reader_task.cancel() 194 - 195 - if self._x224net is not None: 196 - await self._x224net.disconnect() 197 - 198 - if self.__tpkgnet is not None: 199 - await self.__tpkgnet.disconnect() 200 192 201 193 return True, None 202 194 except Exception as e: skipped 14 lines 217 209 Tuple[bool, Exception]: _description_ 218 210 """ 219 211 try: 220 - self.__fastpath_in_queue = asyncio.Queue() 221 - self.__transportnet, err = await NetworkSelector.select(self.target) 222 - if err is not None: 223 - raise err 224 212 225 - # starting lower-layer transports 226 - _, err = await self.__transportnet.connect() 227 - if err is not None: 228 - raise err 229 - 230 - # TPKT network handles both TPKT and FASTPATH packets 231 - # This object is also capable to dynamically switch 232 - # to SSL/TLS when needed (without reconnecting) 233 - self.__tpkgnet = TPKTNetwork(self.__transportnet) 234 - _, err = await self.__tpkgnet.run() 235 - if err is not None: 236 - raise err 237 - 238 - 239 - self.__fastpath_reader_task = asyncio.create_task(self.__fastpath_reader()) 213 + packetizer = TPKTPacketizer() 214 + client = UniClient(self.target, packetizer) 215 + self.__connection = await client.connect() 240 216 241 217 # X224 channel is on top of TPKT, performs the initial negotiation 242 218 # between the server and our client (restricted admin mode, authentication methods etc) 243 219 # are set here 244 - self._x224net = X224Network(self.__tpkgnet) 245 - _, err = await self._x224net.run() 246 - if err is not None: 247 - raise err 220 + self._x224net = X224Network(self.__connection) 221 + if self.client_x224_supported_protocols is None and self.credentials is not None: 222 + if self.credentials.protocol in [UniAuthProtocol.NTLM, UniAuthProtocol.KERBEROS]: 223 + if self.credentials.secret is not None and self.credentials.stype not in [UniAuthSecret.PASSWORD, UniAuthSecret.PWPROMPT, UniAuthSecret.PWHEX, UniAuthSecret.PWB64]: 224 + # user provided some secret but it's not a password 225 + # here we request restricted admin mode 226 + self.client_x224_flags = NEG_FLAGS.RESTRICTED_ADMIN_MODE_REQUIRED 227 + self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL |SUPP_PROTOCOLS.HYBRID 228 + else: 229 + self.client_x224_flags = 0 230 + self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL | SUPP_PROTOCOLS.HYBRID_EX | SUPP_PROTOCOLS.HYBRID 248 231 249 - if self.client_x224_supported_protocols is None and self.credentials is not None: 250 - if self.credentials.secret is not None and self.credentials.secret_type not in [RDPCredentialsSecretType.PASSWORD, RDPCredentialsSecretType.PWPROMPT, RDPCredentialsSecretType.PWHEX, RDPCredentialsSecretType.PWB64]: 251 - # user provided some secret but it's not a password 252 - # here we request restricted admin mode 253 - self.client_x224_flags = NEG_FLAGS.RESTRICTED_ADMIN_MODE_REQUIRED 254 - self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL |SUPP_PROTOCOLS.HYBRID 255 - elif self.credentials.secret is None and self.credentials.username is None: 232 + elif self.credentials.stype == UniAuthSecret.NONE: #and self.credentials.username is None: 256 233 # not sending any passwords, hoping HYBRID is not required 257 234 self.client_x224_flags = 0 258 235 self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL 259 236 else: 260 237 self.client_x224_flags = 0 261 - self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL | SUPP_PROTOCOLS.HYBRID_EX | SUPP_PROTOCOLS.HYBRID 238 + self.client_x224_supported_protocols = SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL 262 239 263 240 logger.debug('Client protocol flags: %s' % self.client_x224_flags) 264 241 logger.debug('Client protocol offer: %s' % self.client_x224_supported_protocols) skipped 12 lines 277 254 self.x224_protocol = self.x224_connection_reply.selectedProtocol 278 255 self.x224_flag = self.x224_connection_reply.flags 279 256 logger.debug('Server selected protocol: %s' % self.x224_protocol) 280 - #print(self.x224_flag) 281 257 if SUPP_PROTOCOLS.SSL in self.x224_protocol or SUPP_PROTOCOLS.HYBRID in self.x224_protocol or SUPP_PROTOCOLS.HYBRID_EX in self.x224_protocol: 282 - from aardwolf.transport.ssl import SSLClientTunnel 283 - _, err = await self.__tpkgnet.switch_transport(SSLClientTunnel) 284 - if err is not None: 285 - raise err 258 + await self.__connection.wrap_ssl() 286 259 287 260 # if the server expects HYBRID/HYBRID_EX authentication we do that here 288 261 # This is basically credSSP skipped 1 lines 290 263 _, err = await self.credssp_auth() 291 264 if err is not None: 292 265 raise err 266 + 267 + #switching back to tpkt 268 + self.__connection.change_packetizer(TPKTPacketizer()) 293 269 294 270 else: 295 271 # old RDP protocol is used skipped 8 lines 304 280 305 281 # All steps below are required as stated in the following 'documentation' 306 282 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/1d263f84-6153-4a16-b329-8770be364e1b 283 + logger.debug('Establish channels...') 307 284 _, err = await self.__establish_channels() 308 285 if err is not None: 309 286 raise err skipped 59 lines 369 346 if self.credentials is None: 370 347 raise Exception('No auth API nor credentials were supplied!') 371 348 372 - self.authapi = AuthenticatorBuilder.to_credssp(self.credentials, self.target) 373 - # credSSP authentication exchange happens on the 'wire' directly 374 - # without the use of TPKT or X224 so we have to suspend those layers 375 - _, err = await self.__tpkgnet.suspend_read() 376 - if err is not None: 377 - raise err 378 349 379 - # credSSP auth requires knowledge of the server's public key 380 - pubkey = await self.__tpkgnet.transport.get_server_pubkey() 350 + self.authapi = CREDSSPCredential([self.credentials]).build_context() 351 + 352 + self.__connection.change_packetizer(CredSSPPacketizer()) 353 + 354 + certificate = self.__connection.get_peer_certificate() 381 355 382 356 # credSSP auth happends here 383 357 token = None 384 - data, to_continue, err = await self.authapi.authenticate(token, flags = None, pubkey = pubkey) 358 + data, to_continue, err = await self.authapi.authenticate(token, flags = None, certificate = certificate, spn=self.target.to_target_string()) 385 359 if err is not None: 386 360 raise err 387 361 388 - await self.__tpkgnet.transport.out_queue.put(data) 362 + await self.__connection.write(data) 389 363 390 364 for _ in range(10): 391 - token, err = await self.__tpkgnet.transport.in_queue.get() 392 - if err is not None: 393 - raise err 394 - 395 - data, to_continue, err = await self.authapi.authenticate(token, flags = None, pubkey = pubkey) 365 + token = await self.__connection.read_one() 366 + data, to_continue, err = await self.authapi.authenticate(token, flags = None, certificate = certificate, spn=self.target.to_target_string()) 396 367 if err is not None: 397 368 raise err 398 369 399 370 if to_continue is False: 400 371 # credSSP auth finished, flushing remaining data 401 372 if data is not None: 402 - await self.__tpkgnet.transport.out_queue.put(data) 373 + await self.__connection.write(data) 403 374 404 375 # if HYBRID_EX auth was selected by the server, the server MUST send 405 376 # an extra packet informing us if the credSSP auth was successful or not 406 377 if SUPP_PROTOCOLS.HYBRID_EX in self.x224_protocol: 407 - authresult_raw, err = await self.__tpkgnet.transport.in_queue.get() 408 - if err is not None: 409 - raise err 410 - 378 + self.__connection.change_packetizer(Packetizer()) 379 + authresult_raw = await self.__connection.read_one() 411 380 authresult = int.from_bytes(authresult_raw, byteorder='little', signed=False) 412 381 #print('Early User Authorization Result PDU %s' % authresult) 413 382 if authresult == 5: 414 383 raise Exception('Authentication failed! (early user auth)') 415 - 416 - 417 - 418 - _, err = await self.__tpkgnet.conitnue_read() 419 - if err is not None: 420 - raise err 421 384 return True, None 422 385 423 - await self.__tpkgnet.transport.out_queue.put(data) 424 - 425 - _, err = await self.__tpkgnet.conitnue_read() 426 - if err is not None: 427 - raise err 386 + await self.__connection.write(data) 428 387 429 388 except Exception as e: 430 389 return None, e skipped 154 lines 585 544 } 586 545 587 546 conf_create_req = self.__t125_ber_codec.encode('ConnectMCSPDU',('connect-initial', initialconnect)) 588 - conf_create_req = bytes(conf_create_req) 589 - #print(conf_create_req) 547 + await self._x224net.write(bytes(conf_create_req)) 590 548 591 - await self._x224net.out_queue.put(conf_create_req) 592 - _, response_raw, err = await self._x224net.in_queue.get() 593 - if err is not None: 594 - raise err 549 + response_raw = await self._x224net.read() 595 550 server_res_raw = response_raw.data 596 551 server_res_t125 = self.__t125_ber_codec.decode('ConnectMCSPDU', server_res_raw) 597 - #print(server_res_t125) 598 552 if server_res_t125[0] != 'connect-response': 599 553 raise Exception('Unexpected response! %s' % server_res_t125) 600 554 if server_res_t125[1]['result'] != 'rt-successful': skipped 24 lines 625 579 self.__joined_channels[name].channel_id = scnet.channelIdArray[i] 626 580 self.__channel_id_lookup[scnet.channelIdArray[i]] = self.__joined_channels[name] 627 581 628 - self.__joined_channels['MCS'] = Channel('MCS') #TODO: options? 582 + self.__joined_channels['MCS'] = MCSChannel() #TODO: options? 629 583 self.__joined_channels['MCS'].channel_id = scnet.MCSChannelId 630 584 self.__channel_id_lookup[scnet.MCSChannelId] = self.__joined_channels['MCS'] 631 585 skipped 7 lines 639 593 # the parser could not decode nor encode this data correctly. 640 594 # therefore we are sending these as bytes. it's static 641 595 # (even according to docu) 642 - await self._x224net.out_queue.put(bytes.fromhex('0400010001')) 596 + await self._x224net.write(bytes.fromhex('0400010001')) 643 597 return True, None 644 598 except Exception as e: 645 599 return None, e 646 600 647 601 async def __attach_user(self): 648 602 try: 649 - await self._x224net.out_queue.put(bytes.fromhex('28')) 650 - _, response, err = await self._x224net.in_queue.get() 651 - if err is not None: 652 - raise err 603 + request = self._t125_per_codec.encode('DomainMCSPDU', ('attachUserRequest', {})) 604 + await self._x224net.write(request) 605 + response = await self._x224net.read() 653 606 response_parsed = self._t125_per_codec.decode('DomainMCSPDU', response.data) 654 607 if response_parsed[0] != 'attachUserConfirm': 655 - raise Exception('Unexpected response! %s' % response_parsed) 608 + raise Exception('Unexpected response! %s' % response_parsed[0]) 656 609 if response_parsed[1]['result'] != 'rt-successful': 657 - raise Exception('Server returned error! %s' % response_parsed) 610 + raise Exception('Server returned error! %s' % response_parsed[0]) 658 611 self._initiator = response_parsed[1]['initiator'] 659 612 660 613 return True, None skipped 4 lines 665 618 try: 666 619 for name in self.__joined_channels: 667 620 joindata = self._t125_per_codec.encode('DomainMCSPDU', ('channelJoinRequest', {'initiator': self._initiator, 'channelId': self.__joined_channels[name].channel_id})) 668 - await self._x224net.out_queue.put(bytes(joindata)) 669 - _, response, err = await self._x224net.in_queue.get() 670 - if err is not None: 671 - raise err 621 + await self._x224net.write(bytes(joindata)) 622 + response = await self._x224net.read() 672 623 673 624 x = self._t125_per_codec.decode('DomainMCSPDU', response.data) 674 625 if x[0] != 'channelJoinConfirm': skipped 95 lines 770 721 raise Exception('License error! tokenInhibitConfirm:result not successful') 771 722 else: 772 723 raise Exception('tokenInhibitConfirm did not show up in reply!') 773 - #print('license ok') 774 724 775 725 return True, None 776 726 except Exception as e: skipped 14 lines 791 741 data_start_offset = 4 792 742 793 743 data = data[data_start_offset:] 794 - # TEST 795 744 shc = TS_SHARECONTROLHEADER.from_bytes(data) 796 745 if shc.pduType != PDUTYPE.DEMANDACTIVEPDU: 797 746 raise Exception('Unexpected reply! Expected DEMANDACTIVEPDU got "%s" instead!' % shc.pduType.name) 798 - 799 - #### TEST END 800 747 801 748 res = TS_DEMAND_ACTIVE_PDU.from_bytes(data) 802 749 for cap in res.capabilitySets: 803 - #print(cap) 804 750 if cap.capabilitySetType == CAPSTYPE.GENERAL: 805 751 cap = typing.cast(TS_GENERAL_CAPABILITYSET, cap.capability) 806 752 if EXTRAFLAG.ENC_SALTED_CHECKSUM in cap.extraFlags and self.cryptolayer is not None: 807 753 self.cryptolayer.use_encrypted_mac = True 808 - #print(res) 809 - #print('================================== SERVER IN ENDS HERE ================================================') 810 754 811 755 caps = [] 812 756 # now we send our capabilities skipped 167 lines 980 924 # dont activate it before this!!!! 981 925 982 926 try: 983 - while True: 984 - is_fastpath, response, err = await self._x224net.in_queue.get() 985 - if err is not None: 986 - raise err 927 + self.__connection.packetizer.packetizer_control("X224") 928 + 929 + async for is_fastpath, response in self.__connection.read(): 930 + #is_fastpath, response, err = await self._x224net.in_queue.get() 931 + #if err is not None: 932 + # raise err 987 933 988 934 if response is None: 989 935 raise Exception('Server terminated the connection!') 990 936 991 937 if is_fastpath is False: 992 - #print('__x224_reader data in -> %s' % response.data) 993 938 x = self._t125_per_codec.decode('DomainMCSPDU', response.data) 994 - #print('__x224_reader decoded data in -> %s' % str(x)) 995 939 if x[0] != 'sendDataIndication': 996 940 #print('Unknown packet!') 997 941 continue skipped 15 lines 1013 957 print('Decrypted data: %s' % data) 1014 958 print('Original MAC : %s' % sec_hdr.dataSignature) 1015 959 print('Calculated MAC: %s' % mac) 1016 - await self.__channel_id_lookup[x[1]['channelId']].raw_in_queue.put((data, None)) 960 + await self.__channel_id_lookup[x[1]['channelId']].process_channel_data(data) 1017 961 else: 1018 962 #print('fastpath data in -> %s' % len(response)) 1019 963 fpdu = TS_FP_UPDATE_PDU.from_bytes(response) skipped 12 lines 1032 976 print('Calculated MAC: %s' % mac) 1033 977 raise Exception('Signature mismatch') 1034 978 fpdu.fpOutputUpdates = TS_FP_UPDATE.from_bytes(data) 1035 - await self.__fastpath_in_queue.put((fpdu, None)) 979 + await self.__process_fastpath(fpdu) 1036 980 1037 981 except asyncio.CancelledError: 1038 982 return None, None skipped 3 lines 1042 986 finally: 1043 987 await self.terminate() 1044 988 1045 - async def __fastpath_reader(self): 989 + async def __process_fastpath(self, fpdu): 1046 990 # Fastpath was introduced to the RDP specs to speed up data transmission 1047 991 # by reducing 4 useless layers from the traffic. 1048 992 # Transmission on this channel starts immediately after connection sequence skipped 1 lines 1050 994 # interesting note: it seems newer servers (>=win2016) only support this protocol of sending 1051 995 # high bandwith traffic. If you disable fastpath (during connection sequence) you won't 1052 996 # get images at all 1053 - try: 1054 - while True: 1055 - fpdu, err = await self.__fastpath_in_queue.get() 1056 - if err is not None: 1057 - raise err 1058 - if fpdu is None: 1059 - raise Exception('Server terminated the connection!') 1060 997 1061 - try: 1062 - if fpdu.fpOutputUpdates.fragmentation != FASTPATH_FRAGMENT.SINGLE: 1063 - print('WARNING! FRAGMENTATION IS NOT IMPLEMENTED! %s' % fpdu.fpOutputUpdates.fragmentation) 1064 - if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.BITMAP: 1065 - for bitmapdata in fpdu.fpOutputUpdates.update.rectangles: 1066 - self.desktop_buffer_has_data = True 1067 - res, image = RDP_VIDEO.from_bitmapdata(bitmapdata, self.iosettings.video_out_format) 1068 - self.__desktop_buffer.paste(image, [res.x, res.y, res.x+res.width, res.y+res.height]) 1069 - await self.ext_out_queue.put(res) 1070 - #else: 1071 - # #print(fpdu.fpOutputUpdates.updateCode) 1072 - # #if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.CACHED: 1073 - # # print(fpdu.fpOutputUpdates) 1074 - # #if fpdu.fpOutputUpdates.updateCode not in [FASTPATH_UPDATETYPE.CACHED, FASTPATH_UPDATETYPE.POINTER]: 1075 - # # print('notbitmap %s' % fpdu.fpOutputUpdates.updateCode.name) 1076 - except Exception as e: 1077 - # the decoder is not perfect yet, so it's better to keep this here... 1078 - traceback.print_exc() 1079 - return 1080 - 1081 - except asyncio.CancelledError: 1082 - return None, None 1083 - 998 + try: 999 + if fpdu.fpOutputUpdates.fragmentation != FASTPATH_FRAGMENT.SINGLE: 1000 + print('WARNING! FRAGMENTATION IS NOT IMPLEMENTED! %s' % fpdu.fpOutputUpdates.fragmentation) 1001 + if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.BITMAP: 1002 + for bitmapdata in fpdu.fpOutputUpdates.update.rectangles: 1003 + self.desktop_buffer_has_data = True 1004 + res, image = RDP_VIDEO.from_bitmapdata(bitmapdata, self.iosettings.video_out_format) 1005 + self.__desktop_buffer.paste(image, [res.x, res.y, res.x+res.width, res.y+res.height]) 1006 + await self.ext_out_queue.put(res) 1007 + #else: 1008 + # #print(fpdu.fpOutputUpdates.updateCode) 1009 + # #if fpdu.fpOutputUpdates.updateCode == FASTPATH_UPDATETYPE.CACHED: 1010 + # # print(fpdu.fpOutputUpdates) 1011 + # #if fpdu.fpOutputUpdates.updateCode not in [FASTPATH_UPDATETYPE.CACHED, FASTPATH_UPDATETYPE.POINTER]: 1012 + # # print('notbitmap %s' % fpdu.fpOutputUpdates.updateCode.name) 1084 1013 except Exception as e: 1014 + # the decoder is not perfect yet, so it's better to keep this here... 1085 1015 traceback.print_exc() 1086 - return None, e 1087 - finally: 1088 - await self.terminate() 1016 + return 1017 + 1018 + 1089 1019 1090 1020 async def send_key_virtualkey(self, vk, is_pressed, is_extended, scancode_hint = None, modifiers = VK_MODIFIERS(0)): 1091 1021 try: skipped 163 lines 1255 1185 if 'cliprdr' not in self.__joined_channels: 1256 1186 logger.debug('Got clipboard data but no clipboard channel setup!') 1257 1187 continue 1258 - await self.__joined_channels['cliprdr'].in_queue.put(indata) 1188 + await self.__joined_channels['cliprdr'].process_user_data(indata) 1259 1189 1260 1190 except asyncio.CancelledError: 1261 1191 return None, None skipped 5 lines 1267 1197 1268 1198 async def handle_out_data(self, dataobj, sec_hdr, datacontrol_hdr, sharecontrol_hdr, channel_id, is_fastpath): 1269 1199 try: 1270 - #while True: 1271 - # dataobj, sec_hdr, datacontrol_hdr, sharecontrol_hdr, channel_id, is_fastpath = await self.data_in_queue.get() 1272 1200 if is_fastpath is False: 1273 1201 #print('Sending data on channel "%s(%s)"' % (self.name, self.channel_id)) 1274 1202 data = dataobj.to_bytes() skipped 41 lines 1316 1244 'userData': userdata 1317 1245 } 1318 1246 userdata_wrapped = self._t125_per_codec.encode('DomainMCSPDU', ('sendDataRequest', data_wrapper)) 1319 - await self._x224net.out_queue.put(userdata_wrapped) 1247 + await self._x224net.write(userdata_wrapped) 1320 1248 1321 1249 else: 1322 1250 raise NotImplementedError("Fastpath output is not yet implemented") skipped 6 lines 1329 1257 1330 1258 async def amain(): 1331 1259 try: 1332 - from aardwolf.commons.url import RDPConnectionURL 1260 + from aardwolf.commons.factory import RDPConnectionFactory 1333 1261 from aardwolf.commons.iosettings import RDPIOSettings 1334 1262 1335 1263 iosettings = RDPIOSettings() 1336 1264 url = 'rdp+ntlm-password://TEST\\Administrator:[email protected]' 1337 - rdpurl = RDPConnectionURL(url) 1265 + rdpurl = RDPConnectionFactory.from_url(url) 1338 1266 conn = rdpurl.get_connection(iosettings) 1339 1267 _, err = await conn.connect() 1340 1268 if err is not None: skipped 12 lines -
-
1 - import traceback 2 - import uuid 3 - import asyncio 4 - import logging 5 - import copy 6 - import json 7 - 8 - from aardwolf import logger 9 - from aardwolf.commons.url import RDPConnectionURL 10 - from aardwolf.commons.iosettings import RDPIOSettings 11 - from aardwolf.examples.scancommons.targetgens import ListTargetGen, FileTargetGen 12 - from aardwolf.examples.scancommons.internal import EnumResult, EnumResultStatus, EnumProgress 13 - from aardwolf.protocol.x224.constants import SUPP_PROTOCOLS 14 - from aardwolf.commons.queuedata.constants import VIDEO_FORMAT 15 - from tqdm import tqdm 16 - 17 - class EnumResultFinal: 18 - def __init__(self, obj, otype, err, target, target_id): 19 - self.obj = obj 20 - self.otype = otype 21 - self.err = err 22 - self.target = target 23 - self.target_id = target_id 24 - 25 - 26 - def __str__(self): 27 - if self.err is not None: 28 - return '[E] %s | %s | %s' % (self.target, self.err) 29 - 30 - elif self.otype == 'result': 31 - return '[R] %s | %s | %s' % (self.target_id, self.target, self.obj) 32 - 33 - elif self.otype == 'progress': 34 - return '[P][%s/%s][%s] %s' % (self.obj.total_finished, self.obj.total_targets, str(self.obj.gens_finished), self.obj.current_finished) 35 - 36 - else: 37 - return '[UNK]' 38 - 39 - def to_dict(self): 40 - if self.otype == 'result': 41 - return { 42 - 'target' : self.target, 43 - 'target_id' : self.target_id, 44 - 'result': str(self.obj), 45 - 'otype': self.otype, 46 - } 47 - 48 - def to_tsv(self): 49 - if self.otype == 'result': 50 - return '%s\t%s\t%s' % (self.target, self.target_id, self.obj) 51 - 52 - def to_json(self): 53 - t = self.to_dict() 54 - if t is not None: 55 - return json.dumps(self.to_dict()) 56 - 57 - 58 - class RDPLoginScanner: 59 - def __init__(self, rdp_url, iosettings:RDPIOSettings, worker_count = 10, out_file = None, out_format = 'str', show_pbar = True, task_q = None, ext_result_q = None): 60 - self.target_gens = [] 61 - self.rdp_mgr = rdp_url 62 - if isinstance(rdp_url, RDPConnectionURL) is False: 63 - self.rdp_mgr = RDPConnectionURL(rdp_url) 64 - self.worker_count = worker_count 65 - self.task_q = task_q 66 - self.res_q = None 67 - self.workers = [] 68 - self.result_processing_task = None 69 - self.out_file = out_file 70 - self.show_pbar = show_pbar 71 - self.ext_result_q = ext_result_q 72 - self.enum_url = False 73 - self.out_format = out_format 74 - 75 - self.iosettings = iosettings 76 - self.flags_test = [SUPP_PROTOCOLS.HYBRID_EX, None] 77 - 78 - self.__gens_finished = False 79 - self.__total_targets = 0 80 - self.__total_finished = 0 81 - self.__total_errors = 0 82 - 83 - async def __executor(self, tid, target): 84 - try: 85 - for i, proto in enumerate(self.flags_test): 86 - result = 'YES' 87 - if proto == None: 88 - result = 'MAYBE' 89 - ios = copy.deepcopy(self.iosettings) 90 - ios.supported_protocols = proto 91 - async with self.rdp_mgr.create_connection_newtarget(target, ios) as connection: 92 - _, err = await connection.connect() 93 - if err is not None: 94 - result = 'NO' 95 - 96 - if result == 'NO' and i != (len(self.flags_test)-1): 97 - # avoid printing NO multiple times 98 - continue 99 - 100 - await self.res_q.put(EnumResult(tid, target, result, status = EnumResultStatus.RESULT)) 101 - if result == 'YES': 102 - break 103 - 104 - except asyncio.CancelledError: 105 - return 106 - except Exception as e: 107 - traceback.print_exc() 108 - await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR)) 109 - finally: 110 - await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.FINISHED)) 111 - 112 - 113 - async def worker(self): 114 - try: 115 - while True: 116 - indata = await self.task_q.get() 117 - if indata is None: 118 - return 119 - 120 - tid, target = indata 121 - try: 122 - await asyncio.wait_for(self.__executor(tid, target), timeout=10) 123 - except asyncio.CancelledError: 124 - return 125 - except asyncio.TimeoutError as e: 126 - await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR)) 127 - await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.FINISHED)) 128 - continue 129 - except Exception as e: 130 - logger.exception('worker') 131 - continue 132 - except asyncio.CancelledError: 133 - return 134 - 135 - except Exception as e: 136 - return e 137 - 138 - async def result_processing(self): 139 - try: 140 - pbar = None 141 - if self.show_pbar is True: 142 - pbar = {} 143 - pbar['targets'] = tqdm(desc='Targets ', unit='', position=0) 144 - pbar['yes'] = tqdm(desc='Yes ', unit='', position=1) 145 - pbar['no'] = tqdm(desc='No ', unit='', position=2) 146 - pbar['maybe'] = tqdm(desc='Maybe ', unit='', position=3) 147 - pbar['errors'] = tqdm(desc='Errors ', unit='', position=4) 148 - pbar['finished'] = tqdm(desc='Finished', unit='', position=5) 149 - 150 - out_buffer = [] 151 - final_iter = False 152 - while True: 153 - try: 154 - if self.__gens_finished is True and self.show_pbar is True and pbar['targets'].total is None: 155 - pbar['targets'].total = self.__total_targets 156 - for key in pbar: 157 - pbar[key].refresh() 158 - 159 - if self.ext_result_q is not None: 160 - out_buffer = [] 161 - 162 - if len(out_buffer) >= 100 or final_iter and self.ext_result_q is None: 163 - out_data = '' 164 - if self.out_format == 'str': 165 - out_data = '\r\n'.join([str(x) for x in out_buffer]) 166 - elif self.out_format == 'tsv': 167 - for line in [x.to_tsv() for x in out_buffer]: 168 - if line is not None: 169 - out_data += line + '\r\n' 170 - elif self.out_format == 'json': 171 - for line in [x.to_json() for x in out_buffer]: 172 - if line is not None: 173 - out_data += line + '\r\n' 174 - if self.out_file is None: 175 - print(out_data) 176 - else: 177 - with open(self.out_file, 'a', newline = '') as f: 178 - f.write(out_data) 179 - if self.show_pbar is True: 180 - for key in pbar: 181 - pbar[key].refresh() 182 - 183 - out_buffer = [] 184 - out_data = '' 185 - 186 - if final_iter: 187 - asyncio.create_task(self.terminate()) 188 - return 189 - try: 190 - er = await asyncio.wait_for(self.res_q.get(), timeout = 5) 191 - except asyncio.TimeoutError: 192 - if self.show_pbar is True: 193 - for key in pbar: 194 - pbar[key].refresh() 195 - 196 - if self.__total_finished == self.__total_targets and self.__gens_finished is True: 197 - final_iter = True 198 - continue 199 - 200 - if er.status == EnumResultStatus.FINISHED: 201 - self.__total_finished += 1 202 - if self.show_pbar is True: 203 - pbar['finished'].update(1) 204 - 205 - obj = EnumProgress(self.__total_targets, self.__total_finished, self.__gens_finished, er.target) 206 - if self.ext_result_q is not None: 207 - await self.ext_result_q.put(EnumResultFinal(obj, 'progress', None, er.target, er.target_id)) 208 - out_buffer.append(EnumResultFinal(obj, 'progress', None, er.target, er.target_id)) 209 - if self.__total_finished == self.__total_targets and self.__gens_finished is True: 210 - final_iter = True 211 - continue 212 - 213 - if er.status == EnumResultStatus.RESULT: 214 - if self.ext_result_q is not None: 215 - await self.ext_result_q.put(EnumResultFinal(er.result, 'result', None, er.target, er.target_id)) 216 - out_buffer.append(EnumResultFinal(er.result, 'result', None, er.target, er.target_id)) 217 - if self.show_pbar is True: 218 - if er.result in ['YES', 'NO', 'MAYBE']: 219 - pbar[er.result.lower()].update(1) 220 - 221 - 222 - if er.status == EnumResultStatus.ERROR: 223 - self.__total_errors += 1 224 - if self.show_pbar is True: 225 - pbar['errors'].update(1) 226 - 227 - 228 - except asyncio.CancelledError: 229 - return 230 - except Exception as e: 231 - logger.exception('result_processing inner') 232 - asyncio.create_task(self.terminate()) 233 - return 234 - except asyncio.CancelledError: 235 - return 236 - except Exception as e: 237 - logger.exception('result_processing') 238 - asyncio.create_task(self.terminate()) 239 - finally: 240 - if self.ext_result_q is not None: 241 - await self.ext_result_q.put(EnumResultFinal(None, 'finished', None, None, None)) 242 - 243 - async def terminate(self): 244 - for worker in self.workers: 245 - worker.cancel() 246 - if self.result_processing_task is not None: 247 - self.result_processing_task.cancel() 248 - 249 - async def setup(self): 250 - try: 251 - if self.res_q is None: 252 - self.res_q = asyncio.Queue(self.worker_count) 253 - self.result_processing_task = asyncio.create_task(self.result_processing()) 254 - if self.task_q is None: 255 - self.task_q = asyncio.Queue(self.worker_count) 256 - 257 - for _ in range(self.worker_count): 258 - self.workers.append(asyncio.create_task(self.worker())) 259 - 260 - return True, None 261 - except Exception as e: 262 - return None, e 263 - 264 - async def __generate_targets(self): 265 - if self.enum_url is True: 266 - self.__total_targets += 1 267 - await self.task_q.put((str(uuid.uuid4()), self.rdp_mgr.get_target().get_hostname_or_ip())) 268 - 269 - for target_gen in self.target_gens: 270 - async for uid, target, err in target_gen.generate(): 271 - if err is not None: 272 - print('Target gen error! %s' % err) 273 - break 274 - 275 - self.__total_targets += 1 276 - await self.task_q.put((uid, target)) 277 - await asyncio.sleep(0) 278 - 279 - self.__gens_finished = True 280 - 281 - async def run(self): 282 - try: 283 - _, err = await self.setup() 284 - if err is not None: 285 - raise err 286 - 287 - gen_task = asyncio.create_task(self.__generate_targets()) 288 - 289 - await asyncio.gather(*self.workers) 290 - await self.result_processing_task 291 - return True, None 292 - except Exception as e: 293 - logger.exception('run') 294 - return None, e 295 - 296 - async def amain(): 297 - import argparse 298 - import sys 299 - 300 - parser = argparse.ArgumentParser(description='RDP login scanner', formatter_class=argparse.RawDescriptionHelpFormatter) 301 - parser.add_argument('-v', '--verbose', action='count', default=0) 302 - parser.add_argument('-w', '--worker-count', type=int, default=50, help='Parallell count') 303 - parser.add_argument('-o', '--out-file', help='Output file path.') 304 - parser.add_argument('--progress', action='store_true', help='Show progress bar. Pleasue use out-file with this!!') 305 - parser.add_argument('--tsv', action='store_true', help='Output results in TSV format') 306 - parser.add_argument('--json', action='store_true', help='Output results in JSON format') 307 - parser.add_argument('-s', '--stdin', action='store_true', help='Read targets from stdin') 308 - parser.add_argument('url', help='Connection URL base, target can be set to anything. Example: "rdp+ntlm-password://TEST\\victim:[email protected]"') 309 - parser.add_argument('targets', nargs='*', help = 'Hostname or IP address or file with a list of targets') 310 - 311 - args = parser.parse_args() 312 - 313 - if args.verbose >=1: 314 - logger.setLevel(logging.WARNING) 315 - 316 - if args.verbose > 2: 317 - print('setting deepdebug') 318 - logger.setLevel(1) #enabling deep debug 319 - asyncio.get_event_loop().set_debug(True) 320 - logging.basicConfig(level=logging.DEBUG) 321 - 322 - oformat = 'str' 323 - if args.tsv is True: 324 - oformat = 'tsv' 325 - if args.json is True: 326 - oformat = 'json' 327 - 328 - rdp_url = args.url 329 - 330 - iosettings = RDPIOSettings() 331 - iosettings.channels = [] 332 - iosettings.video_out_format = VIDEO_FORMAT.RAW 333 - iosettings.clipboard_use_pyperclip = False 334 - 335 - enumerator = RDPLoginScanner( 336 - rdp_url, 337 - iosettings, 338 - worker_count = args.worker_count, 339 - out_file = args.out_file, 340 - show_pbar = args.progress, 341 - out_format=oformat, 342 - ) 343 - 344 - notfile = [] 345 - if len(args.targets) == 0 and args.stdin is True: 346 - enumerator.target_gens.append(ListTargetGen(sys.stdin)) 347 - else: 348 - for target in args.targets: 349 - try: 350 - f = open(target, 'r') 351 - f.close() 352 - enumerator.target_gens.append(FileTargetGen(target)) 353 - except: 354 - notfile.append(target) 355 - 356 - if len(notfile) > 0: 357 - enumerator.target_gens.append(ListTargetGen(notfile)) 358 - 359 - if len(enumerator.target_gens) == 0: 360 - enumerator.enum_url = True 361 - 362 - await enumerator.run() 363 - 364 - def main(): 365 - asyncio.run(amain()) 366 - 367 - if __name__ == '__main__': 368 - main() -
1 - import traceback 2 - import uuid 3 - import asyncio 4 - import logging 5 - 6 - from aardwolf import logger 7 - from aardwolf.commons.url import RDPConnectionURL 8 - from aardwolf.commons.iosettings import RDPIOSettings 9 - from aardwolf.examples.scancommons.targetgens import ListTargetGen, FileTargetGen 10 - from aardwolf.examples.scancommons.internal import EnumResult, EnumResultStatus, EnumProgress 11 - from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT 12 - from tqdm import tqdm 13 - 14 - class EnumResultFinal: 15 - def __init__(self, obj, otype, err, target, target_id): 16 - self.obj = obj 17 - self.otype = otype 18 - self.err = err 19 - self.target = target 20 - self.target_id = target_id 21 - 22 - 23 - def __str__(self): 24 - if self.err is not None: 25 - return '[E] %s | %s | %s' % (self.target, self.err) 26 - 27 - elif self.otype == 'result': 28 - return '[S] %s ' % (self.target) 29 - 30 - elif self.otype == 'progress': 31 - return '[P][%s/%s][%s] %s' % (self.obj.total_finished, self.obj.total_targets, str(self.obj.gens_finished), self.obj.current_finished) 32 - 33 - else: 34 - return '[UNK]' 35 - 36 - 37 - class RDPScreenGrabberScanner: 38 - def __init__(self, rdp_url, iosettings, worker_count = 10, out_dir = None, screentime = 5, show_pbar = True, task_q = None, res_q = None, ext_result_q = None): 39 - self.target_gens = [] 40 - self.rdp_mgr = rdp_url 41 - if isinstance(rdp_url, RDPConnectionURL) is False: 42 - self.rdp_mgr = RDPConnectionURL(rdp_url) 43 - self.worker_count = worker_count 44 - self.task_q = task_q 45 - self.res_q = res_q 46 - self.screentime = screentime 47 - self.workers = [] 48 - self.result_processing_task = None 49 - self.out_dir = out_dir 50 - self.show_pbar = show_pbar 51 - self.ext_result_q = ext_result_q 52 - self.enum_url = False 53 - 54 - self.iosettings = iosettings 55 - 56 - self.__gens_finished = False 57 - self.__total_targets = 0 58 - self.__total_finished = 0 59 - self.__total_errors = 0 60 - 61 - async def __executor(self, tid, target): 62 - connection = None 63 - try: 64 - connection = self.rdp_mgr.create_connection_newtarget(target, self.iosettings) 65 - _, err = await connection.connect() 66 - if err is not None: 67 - raise err 68 - 69 - await asyncio.sleep(self.screentime) 70 - 71 - except asyncio.CancelledError: 72 - return 73 - except Exception as e: 74 - traceback.print_exc() 75 - await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR)) 76 - finally: 77 - if connection is not None and connection.desktop_buffer_has_data is True: 78 - buffer = connection.get_desktop_buffer(VIDEO_FORMAT.PIL) 79 - 80 - if self.ext_result_q is None: 81 - filename = 'screen_%s_%s.png' % (target, tid) 82 - buffer.save(filename,'png') 83 - await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.RESULT)) 84 - else: 85 - await self.res_q.put(EnumResult(tid, target, buffer, status = EnumResultStatus.RESULT)) 86 - 87 - await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.FINISHED)) 88 - 89 - 90 - async def worker(self): 91 - try: 92 - while True: 93 - indata = await self.task_q.get() 94 - if indata is None: 95 - return 96 - 97 - tid, target = indata 98 - try: 99 - await asyncio.wait_for(self.__executor(tid, target), timeout=self.screentime+5) 100 - except asyncio.CancelledError: 101 - return 102 - except asyncio.TimeoutError as e: 103 - await self.res_q.put(EnumResult(tid, target, None, error = e, status = EnumResultStatus.ERROR)) 104 - await self.res_q.put(EnumResult(tid, target, None, status = EnumResultStatus.FINISHED)) 105 - continue 106 - except Exception as e: 107 - logger.exception('worker') 108 - continue 109 - except asyncio.CancelledError: 110 - return 111 - 112 - except Exception as e: 113 - return e 114 - 115 - async def result_processing(self): 116 - try: 117 - pbar = None 118 - if self.show_pbar is True: 119 - pbar = {} 120 - pbar['targets'] = tqdm(desc='Targets ', unit='', position=0) 121 - pbar['screencaps'] = tqdm(desc='Screencaps ', unit='', position=1) 122 - pbar['errors'] = tqdm(desc='Conn Errors ', unit='', position=2) 123 - 124 - out_buffer = [] 125 - final_iter = False 126 - while True: 127 - try: 128 - if self.__gens_finished is True and self.show_pbar is True and pbar['targets'].total is None: 129 - pbar['targets'].total = self.__total_targets 130 - for key in pbar: 131 - pbar[key].refresh() 132 - 133 - if self.ext_result_q is not None: 134 - out_buffer = [] 135 - 136 - if len(out_buffer) >= 10 or final_iter and self.ext_result_q is None: 137 - out_data = '' 138 - if self.show_pbar is False: 139 - out_data = '\r\n'.join([str(x) for x in out_buffer]) 140 - print(out_data) 141 - else: 142 - for key in pbar: 143 - pbar[key].refresh() 144 - 145 - out_buffer = [] 146 - out_data = '' 147 - 148 - if final_iter: 149 - asyncio.create_task(self.terminate()) 150 - return 151 - try: 152 - er = await asyncio.wait_for(self.res_q.get(), timeout = 5) 153 - except asyncio.TimeoutError: 154 - if self.show_pbar is True: 155 - for key in pbar: 156 - pbar[key].refresh() 157 - 158 - if self.__total_finished == self.__total_targets and self.__gens_finished is True: 159 - final_iter = True 160 - continue 161 - 162 - if er.status == EnumResultStatus.FINISHED: 163 - self.__total_finished += 1 164 - if self.show_pbar is True: 165 - pbar['finished'].update(1) 166 - 167 - obj = EnumProgress(self.__total_targets, self.__total_finished, self.__gens_finished, er.target) 168 - if self.ext_result_q is not None: 169 - await self.ext_result_q.put(EnumResultFinal(obj, 'progress', None, er.target, er.target_id)) 170 - out_buffer.append(EnumResultFinal(obj, 'progress', None, er.target, er.target_id)) 171 - if self.__total_finished == self.__total_targets and self.__gens_finished is True: 172 - final_iter = True 173 - continue 174 - 175 - if er.status == EnumResultStatus.RESULT: 176 - if self.ext_result_q is not None: 177 - await self.ext_result_q.put(EnumResultFinal(er.result, 'result', None, er.target, er.target_id)) 178 - out_buffer.append(EnumResultFinal(er.result, 'result', None, er.target, er.target_id)) 179 - if self.show_pbar is True: 180 - pbar['screencaps'].update(1) 181 - 182 - if er.status == EnumResultStatus.ERROR: 183 - self.__total_errors += 1 184 - if self.show_pbar is True: 185 - pbar['errors'].update(1) 186 - 187 - 188 - except asyncio.CancelledError: 189 - return 190 - except Exception as e: 191 - logger.exception('result_processing inner') 192 - asyncio.create_task(self.terminate()) 193 - return 194 - except asyncio.CancelledError: 195 - return 196 - except Exception as e: 197 - logger.exception('result_processing') 198 - asyncio.create_task(self.terminate()) 199 - finally: 200 - if self.ext_result_q is not None: 201 - await self.ext_result_q.put(EnumResultFinal(None, 'finished', None, None, None)) 202 - 203 - async def terminate(self): 204 - for worker in self.workers: 205 - worker.cancel() 206 - if self.result_processing_task is not None: 207 - self.result_processing_task.cancel() 208 - 209 - async def setup(self): 210 - try: 211 - if self.res_q is None: 212 - self.res_q = asyncio.Queue(self.worker_count) 213 - self.result_processing_task = asyncio.create_task(self.result_processing()) 214 - if self.task_q is None: 215 - self.task_q = asyncio.Queue(self.worker_count) 216 - 217 - for _ in range(self.worker_count): 218 - self.workers.append(asyncio.create_task(self.worker())) 219 - 220 - return True, None 221 - except Exception as e: 222 - return None, e 223 - 224 - async def __generate_targets(self): 225 - if self.enum_url is True: 226 - self.__total_targets += 1 227 - await self.task_q.put((str(uuid.uuid4()), self.rdp_mgr.get_target().get_hostname_or_ip())) 228 - 229 - for target_gen in self.target_gens: 230 - async for uid, target, err in target_gen.generate(): 231 - if err is not None: 232 - print('Target gen error! %s' % err) 233 - break 234 - 235 - self.__total_targets += 1 236 - await self.task_q.put((uid, target)) 237 - await asyncio.sleep(0) 238 - 239 - self.__gens_finished = True 240 - 241 - async def run(self): 242 - try: 243 - _, err = await self.setup() 244 - if err is not None: 245 - raise err 246 - 247 - gen_task = asyncio.create_task(self.__generate_targets()) 248 - 249 - await asyncio.gather(*self.workers) 250 - await self.result_processing_task 251 - return True, None 252 - except Exception as e: 253 - logger.exception('run') 254 - return None, e 255 - 256 - async def amain(): 257 - import argparse 258 - import sys 259 - 260 - parser = argparse.ArgumentParser(description='RDP/VNC Screen grabber', formatter_class=argparse.RawDescriptionHelpFormatter) 261 - parser.add_argument('-v', '--verbose', action='count', default=0) 262 - parser.add_argument('--screentime', type=int, default=5, help='Time to wait for desktop image') 263 - parser.add_argument('-w', '--worker-count', type=int, default=50, help='Parallell count') 264 - parser.add_argument('-o', '--out-dir', help='Output directory path.') 265 - parser.add_argument('--progress', action='store_true', help='Show progress bar') 266 - parser.add_argument('-s', '--stdin', action='store_true', help='Read targets from stdin') 267 - parser.add_argument('--res', default = '1024x768', help='Resolution in "WIDTHxHEIGHT" format. Default: "1024x768"') 268 - parser.add_argument('url', help='Connection URL base, target can be set to anything. Example: "rdp+ntlm-password://TEST\\victim:[email protected]"') 269 - parser.add_argument('targets', nargs='*', help = 'Hostname or IP address or file with a list of targets') 270 - 271 - args = parser.parse_args() 272 - 273 - if args.verbose >=1: 274 - logger.setLevel(logging.DEBUG) 275 - 276 - if args.verbose > 2: 277 - print('setting deepdebug') 278 - logger.setLevel(1) #enabling deep debug 279 - asyncio.get_event_loop().set_debug(True) 280 - logging.basicConfig(level=logging.DEBUG) 281 - 282 - rdp_url = args.url 283 - width, height = args.res.upper().split('X') 284 - height = int(height) 285 - width = int(width) 286 - 287 - iosettings = RDPIOSettings() 288 - iosettings.channels = [] 289 - iosettings.video_width = width 290 - iosettings.video_height = height 291 - iosettings.video_bpp_min = 15 #servers dont support 8 any more :/ 292 - iosettings.video_bpp_max = 32 293 - iosettings.video_out_format = VIDEO_FORMAT.PNG #PIL produces incorrect picture for some reason?! TODO: check bug 294 - iosettings.clipboard_use_pyperclip = False 295 - 296 - enumerator = RDPScreenGrabberScanner( 297 - rdp_url, 298 - iosettings, 299 - worker_count = args.worker_count, 300 - out_dir = args.out_dir, 301 - show_pbar = args.progress, 302 - screentime = args.screentime, 303 - ) 304 - 305 - notfile = [] 306 - if len(args.targets) == 0 and args.stdin is True: 307 - enumerator.target_gens.append(ListTargetGen(sys.stdin)) 308 - else: 309 - for target in args.targets: 310 - try: 311 - f = open(target, 'r') 312 - f.close() 313 - enumerator.target_gens.append(FileTargetGen(target)) 314 - except: 315 - notfile.append(target) 316 - 317 - if len(notfile) > 0: 318 - enumerator.target_gens.append(ListTargetGen(notfile)) 319 - 320 - if len(enumerator.target_gens) == 0: 321 - enumerator.enum_url = True 322 - 323 - await enumerator.run() 324 - 325 - def main(): 326 - asyncio.run(amain()) 327 - 328 - if __name__ == '__main__': 329 - main() -
-
-
-
-
-
-
-
-
-
-
-
-
skipped 12 lines 13 13 from typing import cast 14 14 15 15 from aardwolf import logger 16 - from aardwolf.network.selector import NetworkSelector 17 16 from aardwolf.transport.tcpstream import TCPStream 18 17 from unicrypto.symmetric import DES, MODE_ECB 19 18 20 19 from aardwolf.commons.target import RDPTarget 21 - from aardwolf.commons.credential import RDPCredential, RDPAuthProtocol 22 20 from aardwolf.commons.queuedata import RDPDATATYPE, RDP_KEYBOARD_SCANCODE, RDP_KEYBOARD_UNICODE, \ 23 21 RDP_MOUSE, RDP_VIDEO, RDP_CLIPBOARD_READY, RDP_CLIPBOARD_DATA_TXT, RDP_CLIPBOARD_NEW_DATA_AVAILABLE, \ 24 22 RDP_BEEP skipped 3 lines 28 26 from aardwolf.keyboard.layoutmanager import KeyboardLayoutManager 29 27 from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT 30 28 from aardwolf.commons.iosettings import RDPIOSettings 29 + from uniauth.common.credentials import UniCredential 30 + from uniauth.common.constants import UniAuthSecret 31 31 32 32 from PIL import Image 33 33 try: skipped 17 lines 51 51 ZRLE_ENCODING = 16 52 52 53 53 class VNCConnection: 54 - def __init__(self, target:RDPTarget, credentials:RDPCredential, iosettings:RDPIOSettings): 54 + def __init__(self, target:RDPTarget, credentials:UniCredential, iosettings:RDPIOSettings): 55 55 self.target = target 56 56 self.credentials = credentials 57 57 self.authapi = None skipped 4 lines 62 62 self.server_name = None 63 63 self.disconnected_evt = asyncio.Event() #this will be set if we disconnect for whatever reason 64 64 self.server_supp_security_types = [] 65 - self.__selected_security_type = 1 if self.credentials.authentication_type == RDPAuthProtocol.NONE else 2 #currently we only support these 2 65 + self.__selected_security_type = 1 if self.credentials.stype == UniAuthSecret.NONE else 2 #currently we only support these 2 66 66 self.__refresh_screen_task = None 67 67 self.__reader_loop_task = None 68 68 self.__external_reader_task = None skipped 748 lines 817 817 async def amain(): 818 818 try: 819 819 logger.setLevel(1) 820 - from aardwolf.commons.url import RDPConnectionURL 820 + from aardwolf.commons.factory import RDPConnectionFactory 821 821 from aardwolf.commons.iosettings import RDPIOSettings 822 822 823 823 iosettings = RDPIOSettings() 824 824 iosettings.video_out_format = VIDEO_FORMAT.RAW 825 825 826 826 url = 'vnc+plain://alma:[email protected]:5900' 827 - url = RDPConnectionURL(url) 827 + url = RDPConnectionFactory.from_url(url) 828 828 connection = url.get_connection(iosettings) 829 829 830 830 _, err = await connection.connect() skipped 16 lines -