Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #662 from Porchetta-Industries/rdp
Browse files Browse the repository at this point in the history
Bump aardwolf to version 0.2.0
  • Loading branch information
mpgn authored Nov 8, 2022
2 parents 4562cea + 8fedcc4 commit be6b0ed
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 212 deletions.
6 changes: 3 additions & 3 deletions cme/modules/ldap-checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

import ldap3
import ssl
from msldap.commons.url import MSLDAPURLDecoder, MSLDAPClientConnection
import asyncio

from msldap.connection import MSLDAPClientConnection
from msldap.commons.factory import LDAPConnectionFactory

class CMEModule:
'''
Expand Down Expand Up @@ -70,7 +70,7 @@ def run_ldaps_noEPA(inputUser, inputPassword, dcTarget):
async def run_ldaps_withEPA(inputUser, inputPassword, dcTarget):
try:
url = 'ldaps+ntlm-password://'+inputUser + ':' + inputPassword +'@' + dcTarget
conn_url = MSLDAPURLDecoder(url)
conn_url = LDAPConnectionFactory.from_url(url)
ldaps_client = conn_url.get_client()
ldapsClientConn = MSLDAPClientConnection(ldaps_client.target, ldaps_client.creds)
_, err = await ldapsClientConn.connect()
Expand Down
147 changes: 99 additions & 48 deletions cme/protocols/rdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@
from cme.connection import *
from cme.helpers.logger import highlight
from cme.logger import CMEAdapter

from aardwolf import logger
from aardwolf.commons.url import RDPConnectionURL
from aardwolf.commons.factory import RDPConnectionFactory
from aardwolf.commons.queuedata.constants import VIDEO_FORMAT
from aardwolf.commons.iosettings import RDPIOSettings
from aardwolf.protocol.x224.constants import SUPP_PROTOCOLS
from aardwolf.commons.queuedata.constants import MOUSEBUTTON, VIDEO_FORMAT

logger.setLevel(logging.CRITICAL)

rdp_error_status = {
'-1073741711': 'STATUS_PASSWORD_EXPIRED',
'-1073741260': 'STATUS_ACCOUNT_LOCKED_OUT',
'-1073741710' : 'STATUS_ACCOUNT_DISABLED',
'-1073741421' : 'STATUS_ACCOUNT_EXPIRED',
'-1073741714' : 'STATUS_ACCOUNT_RESTRICTION',
'-1073741713' : 'STATUS_INVALID_LOGON_HOURS',
'-1073741712' : 'STATUS_INVALID_WORKSTATION',
'-1073741477' : 'STATUS_LOGON_TYPE_NOT_GRANTED',
'-1073741276' : 'STATUS_PASSWORD_MUST_CHANGE',
'-1073741790' : 'STATUS_ACCESS_DENIED',
'-1073741715' : 'STATUS_LOGON_FAILURE'
'0xc0000071': 'STATUS_PASSWORD_EXPIRED',
'0xc0000234': 'STATUS_ACCOUNT_LOCKED_OUT',
'0xc0000072' : 'STATUS_ACCOUNT_DISABLED',
'0xc0000193' : 'STATUS_ACCOUNT_EXPIRED',
'0xc000006E' : 'STATUS_ACCOUNT_RESTRICTION',
'0xc000006F' : 'STATUS_INVALID_LOGON_HOURS',
'0xc0000070' : 'STATUS_INVALID_WORKSTATION',
'0xc000015B' : 'STATUS_LOGON_TYPE_NOT_GRANTED',
'0xc0000224' : 'STATUS_PASSWORD_MUST_CHANGE',
'0xc0000022' : 'STATUS_ACCESS_DENIED',
'0xc000006d' : 'STATUS_LOGON_FAILURE',
'0xc000006a' : 'STATUS_WRONG_PASSWORD '
}

class rdp(connection):
Expand All @@ -34,9 +36,11 @@ def __init__(self, args, db, host):
self.domain = None
self.server_os = None
self.iosettings = RDPIOSettings()
self.iosettings.supported_protocols = ""
self.protoflags = self.protoflags = [SUPP_PROTOCOLS.RDP, SUPP_PROTOCOLS.SSL, SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.RDP, SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.HYBRID, SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.HYBRID_EX]
self.iosettings.channels = []
self.iosettings.video_out_format = VIDEO_FORMAT.RAW
self.iosettings.clipboard_use_pyperclip = False
self.protoflags_nla = [SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.RDP, SUPP_PROTOCOLS.SSL, SUPP_PROTOCOLS.RDP]
self.protoflags = [SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.RDP, SUPP_PROTOCOLS.SSL, SUPP_PROTOCOLS.RDP, SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.HYBRID, SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.HYBRID_EX]
width, height = args.res.upper().split('X')
height = int(height)
width = int(width)
Expand All @@ -45,7 +49,6 @@ def __init__(self, args, db, host):
self.iosettings.video_bpp_min = 15 #servers dont support 8 any more :/
self.iosettings.video_bpp_max = 32
self.iosettings.video_out_format = VIDEO_FORMAT.PNG #PIL produces incorrect picture for some reason?! TODO: check bug
self.iosettings.clipboard_use_pyperclip = False
self.output_filename = None
self.domain = None
self.server_os = None
Expand All @@ -63,6 +66,8 @@ def proto_args(parser, std_parser, module_parser):
rdp_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
rdp_parser.add_argument("--port", type=int, default=3389, help="Custom RDP port")
rdp_parser.add_argument("--rdp-timeout", type=int, default=1, help="RDP timeout on socket connection")
rdp_parser.add_argument("--nla-screenshot", action="store_true", help="Screenshot RDP login prompt if NLA is disabled")

dgroup = rdp_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
Expand All @@ -78,11 +83,12 @@ def proto_flow(self):
if self.create_conn_obj():
self.proto_logger()
self.print_host_info()
if self.login():
if hasattr(self.args, 'module') and self.args.module:
self.call_modules()
else:
self.call_cmd_args()
self.login()

if hasattr(self.args, 'module') and self.args.module:
self.call_modules()
else:
self.call_cmd_args()

def proto_logger(self):
self.logger = CMEAdapter(extra={'protocol': 'RDP',
Expand All @@ -100,14 +106,12 @@ def print_host_info(self):
self.nla))

def create_conn_obj(self):

for proto in self.protoflags:
self.check_nla()
for proto in reversed(self.protoflags):
try:
self.iosettings.supported_protocols = proto
self .url = 'rdp+ntlm-password://FAKE\\user:pass@' + self.host + ':' + str(self.args.port)
self.url = 'rdp+ntlm-password://FAKE\\user:pass@' + self.host + ':' + str(self.args.port)
asyncio.run(self.connect_rdp(self.url))
if str(proto) == "SUPP_PROTOCOLS.RDP" or str(proto) == "SUPP_PROTOCOLS.SSL" or str(proto) == "SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.RDP":
self.nla = False
except OSError as e:
if "Errno 104" not in str(e):
return False
Expand All @@ -132,9 +136,21 @@ def create_conn_obj(self):

return True

def check_nla(self):
for proto in self.protoflags_nla:
try:
self.iosettings.supported_protocols = proto
self.url = 'rdp+ntlm-password://FAKE\\user:pass@' + self.host + ':' + str(self.args.port)
asyncio.run(self.connect_rdp(self.url))
if str(proto) == "SUPP_PROTOCOLS.RDP" or str(proto) == "SUPP_PROTOCOLS.SSL" or str(proto) == "SUPP_PROTOCOLS.SSL|SUPP_PROTOCOLS.RDP":
self.nla = False
return
except:
pass

async def connect_rdp(self, url):
rdpurl = RDPConnectionURL(url)
self.conn = rdpurl.get_connection(self.iosettings)
connectionfactory = RDPConnectionFactory.from_url(url, self.iosettings)
self.conn = connectionfactory.create_connection_newtarget(self.hostname, self.iosettings)
_, err = await self.conn.connect()
if err is not None:
raise err
Expand All @@ -155,21 +171,29 @@ def plaintext_login(self, domain, username, password):
return True

except Exception as e:
reason = None
for word in rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]

self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password,
'({})'.format(reason) if reason else ''),
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red')
if "Authentication failed!" in str(e):
self.logger.success(u'{}\\{}:{} {}'.format(domain,
username,
password,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))
else:
reason = None
for word in rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password,
'({})'.format(reason) if reason else ''),
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red')
return False

def hash_login(self, domain, username, ntlm_hash):
try:
self.url = 'rdp+ntlm-nt://' + domain + '\\' + username + ':' + ntlm_hash + '@' + self.host + ':' + str(self.args.port)
print(self.url)
asyncio.run(self.connect_rdp(self.url))

self.admin_privs = True
Expand All @@ -183,16 +207,24 @@ def hash_login(self, domain, username, ntlm_hash):
return True

except Exception as e:
reason = None
for word in rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]

self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
ntlm_hash,
'({})'.format(reason) if reason else ''),
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red')
if "Authentication failed!" in str(e):
self.logger.success(u'{}\\{}:{} {}'.format(domain,
username,
ntlm_hash,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')))
else:
reason = None
for word in rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"

self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
ntlm_hash,
'({})'.format(reason) if reason else ''),
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red')

return False

Expand All @@ -209,3 +241,22 @@ async def screen(self):
def screenshot(self):
asyncio.run(self.screen())

async def nla_screen(self):
# Otherwise it crash
self.iosettings.supported_protocols = None

# Anonymous auth: https://github.com/skelsec/asyauth/pull/1
self.url = 'rdp+simple-password://' + self.host + ':' + str(self.args.port)

await self.connect_rdp(self.url)
await asyncio.sleep(int(self.args.screentime))

if self.conn is not None and self.conn.desktop_buffer_has_data is True:
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
filename = os.path.expanduser('~/.cme/screenshots/{}_{}_{}.png'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
buffer.save(filename,'png')
self.logger.highlight("NLA Screenshot saved {}".format(filename))

def nla_screenshot(self):
if not self.nla:
asyncio.run(self.nla_screen())
Loading

0 comments on commit be6b0ed

Please sign in to comment.