Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXP: test with aardwolf] [xfreerdp] Open pandorabox... #6

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions nxc/protocols/xfreerdp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import os
import re
import sys
import uuid
import subprocess

from datetime import datetime
from termcolor import colored

from nxc.connection import connection
from nxc.logger import NXCAdapter
from nxc.config import process_secret
from nxc.config import host_info_colors

success_flag = "Authentication only, exit status 0"

status_dict = {
"(Account has been locked)": ["ERRCONNECT_ACCOUNT_LOCKED_OUT"],
"(Account has been disabled)": ["ERRCONNECT_ACCOUNT_DISABLED [0x00020012]"],
"(Account was expired)": ["0x0002000D", "0x00000009"],
"(Not support NLA)": ["ERRCONNECT_SECURITY_NEGO_CONNECT_FAILED [0x0002000C]"],
"(Password expired)": ["0x0002000E", "0x0002000F", "0x00020013"],
"(RDP login failed)": ["0x00020009", "0x00020014"],
"Failed": ["Resource temporarily unavailable", "Broken pipe", "ERRCONNECT_CONNECT_FAILED [0x00020006]", "Connection timed out", "Connection reset by peer"]
}

class xfreerdp(connection):

def __init__(self, args, db, host):
self.output_filename = None
self.domain = None
self.nla = False

connection.__init__(self, args, db, host)

def proto_logger(self):
self.logger = NXCAdapter(
extra={
"protocol": "XFREERDP",
"host": self.host,
"port": self.port,
"hostname": self.hostname,
}
)

def print_host_info(self):
nla = colored(f"nla:{self.nla}", host_info_colors[3], attrs=["bold"]) if self.nla else colored(f"nla:{self.nla}", host_info_colors[2], attrs=["bold"])
if not self.nla:
self.logger.display(f"Old version OS which means not support NLA (name:{self.host}) {nla}")
else:
self.logger.display(f"(name:{self.hostname}) {nla}")
return True

def create_conn_obj(self):
if not os.getenv("DISPLAY"):
self.logger.fail("Run xfreerdp failed, please check the $DISPLAY environment variable. (for more details, please check https://github.com/FreeRDP/FreeRDP/issues/7129)")
sys.exit(1)
try:
connection = subprocess.Popen(f'xfreerdp /v:"{self.host}" /port:{self.port} +auth-only /d:"{uuid.uuid4()!s}" /u:"{uuid.uuid4()!s}" /p:"{uuid.uuid4()!s}" /cert:tofu /tls-seclevel:0 /timeout:{self.args.rdp_timeout * 1000}', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
# For New/Old certificate, /cert-tofu can't handle it.
connection.stdin.write(b"Y\n")
connection.stdin.flush()
output_error = connection.stderr.read().decode("utf-8")
if any(single_word in output_error for single_word in status_dict["Failed"]):
return False
else:
CN_match = re.search(r"CN = (\S+)", output_error)
if CN_match:
hostname = CN_match.group(1)
self.nla = True
self.hostname = hostname
self.logger.extra["hostname"] = hostname
else:
self.logger.extra["hostname"] = self.host

self.output_filename = os.path.expanduser(f"~/.cme/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}".replace(":", "-"))

if self.args.domain:
self.domain = self.args.domain

if self.args.local_auth:
self.domain = self.hostname

return True
except Exception as e:
self.logger.error(str(e))
return False

def plaintext_login(self, domain, username, password):
self.domain = domain
self.username = username
self.password = password
try:
connection = subprocess.Popen(f'xfreerdp /v:"{self.host}" /port:{self.port} +auth-only /d:"{self.domain}" /u:"{self.username}" /p:"{self.password}" /cert:ignore /tls-seclevel:0 /timeout:{self.args.rdp_timeout * 1000}', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
output_error = connection.stderr.read().decode("utf-8")
if success_flag in output_error:
self.admin_privs = True
self.logger.success(f"{self.domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}")
else:
for k, v in status_dict.items():
if any(single_word in output_error for single_word in v):
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.password)} {k}")
return False

if not self.args.continue_on_success:
return True

except Exception as e:
self.logger.error(str(e))

def hash_login(self, domain, username, ntlm_hash):
self.domain = domain
self.username = username
if ntlm_hash.find(":") != -1:
self.logger.info("RDP with PTH not support LM hash, will not use LM hash")
_, self.nthash = ntlm_hash.split(":")
else:
self.nthash = ntlm_hash
try:
connection = subprocess.Popen(f'xfreerdp /v:"{self.host}" /port:{self.port} +auth-only /d:"{self.domain}" /u:"{self.username}" /p:"" /pth:{self.nthash} /sec:nla /cert:ignore /tls-seclevel:0 /timeout:{self.args.rdp_timeout * 1000}', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
output_error = connection.stderr.read().decode("utf-8")
if success_flag in output_error:
self.admin_privs = True
self.logger.success(f"{self.domain}\\{self.username}:{process_secret(self.nthash)} {self.mark_pwned()}")
else:
for k, v in status_dict.items():
if any(single_word in output_error for single_word in v):
self.logger.fail(f"{self.domain}\\{self.username}:{process_secret(self.nthash)} {k}")
return False

if not self.args.continue_on_success:
return True

except Exception as e:
self.logger.error(str(e))
Empty file.
19 changes: 19 additions & 0 deletions nxc/protocols/xfreerdp/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class database:

def __init__(self, conn):
self.conn = conn

@staticmethod
def db_schema(db_conn):
db_conn.execute("""CREATE TABLE "credentials" (
"id" integer PRIMARY KEY,
"username" text,
"password" text
)""")

db_conn.execute("""CREATE TABLE "hosts" (
"id" integer PRIMARY KEY,
"ip" text,
"hostname" text,
"port" integer
)""")
5 changes: 5 additions & 0 deletions nxc/protocols/xfreerdp/db_navigator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from nxc.nxcdb import DatabaseNavigator


class navigator(DatabaseNavigator):
pass
10 changes: 10 additions & 0 deletions nxc/protocols/xfreerdp/proto_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def proto_args(parser, parents):
xfreerdp_parser = parser.add_parser("xfreerdp", help="own stuff using RDP (xfreerdp)", parents=parents)
xfreerdp_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes")
xfreerdp_parser.add_argument("--port", type=int, default=3389, help="Custom RDP port")
xfreerdp_parser.add_argument("--rdp-timeout", type=int, default=5, help="RDP timeout on socket connection, defalut is %(default)ss")

dgroup = xfreerdp_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")
return parser