Skip to content

Commit 7729139

Browse files
authored
Merge branch 'main' into bugfix/db-interface-error
2 parents 3b1eac7 + aef2675 commit 7729139

4 files changed

Lines changed: 91 additions & 14 deletions

File tree

nxc/logger.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import re
77
from nxc.console import nxc_console
8+
from nxc.paths import NXC_PATH
89
from termcolor import colored
910
from datetime import datetime
1011
from rich.text import Text
@@ -30,7 +31,7 @@ def setup_debug_logging():
3031
root_logger.setLevel(logging.INFO)
3132
elif debug_args.debug:
3233
nxc_logger.logger.setLevel(logging.DEBUG)
33-
root_logger.setLevel(logging.INFO)
34+
root_logger.setLevel(logging.DEBUG)
3435
else:
3536
nxc_logger.logger.setLevel(logging.ERROR)
3637
root_logger.setLevel(logging.ERROR)
@@ -163,15 +164,16 @@ def log_console_to_file(self, text, *args, **kwargs):
163164
If debug or info logging is not enabled, we still want display/success/fail logged to the file specified,
164165
so we create a custom LogRecord and pass it to all the additional handlers (which will be all the file handlers)
165166
"""
166-
if self.logger.getEffectiveLevel() >= logging.INFO and len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
167+
caller_frame = inspect.currentframe().f_back.f_back.f_back
168+
if len(self.logger.handlers): # will be 0 if it's just the console output, so only do this if we actually have file loggers
167169
try:
168170
for handler in self.logger.handlers:
169-
handler.handle(LogRecord("nxc", 20, "", kwargs, msg=text, args=args, exc_info=None))
171+
handler.handle(LogRecord("nxc", 20, pathname=caller_frame.f_code.co_filename, lineno=caller_frame.f_lineno, msg=text, args=args, exc_info=None))
170172
except Exception as e:
171173
self.logger.fail(f"Issue while trying to custom print handler: {e}")
172174

173175
def add_file_log(self, log_file=None):
174-
file_formatter = TermEscapeCodeFormatter("%(asctime)s - %(levelname)s - %(message)s")
176+
file_formatter = TermEscapeCodeFormatter("%(asctime)s | %(filename)s:%(lineno)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
175177
output_file = self.init_log_file() if log_file is None else log_file
176178
file_creation = False
177179

@@ -193,11 +195,10 @@ def add_file_log(self, log_file=None):
193195

194196
@staticmethod
195197
def init_log_file():
196-
newpath = os.path.expanduser("~/.nxc") + "/logs/" + datetime.now().strftime("%Y-%m-%d")
197-
if not os.path.exists(newpath):
198-
os.makedirs(newpath)
198+
newpath = NXC_PATH + "/logs/" + datetime.now().strftime("%Y-%m-%d")
199+
os.makedirs(newpath, exist_ok=True)
199200
return os.path.join(
200-
os.path.expanduser("~/.nxc"),
201+
NXC_PATH,
201202
"logs",
202203
datetime.now().strftime("%Y-%m-%d"),
203204
f"log_{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.log",

nxc/modules/hyperv-host.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def options(self, context, module_options):
2222
def on_admin_login(self, context, connection):
2323
self.context = context
2424

25-
path = "SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters"
25+
path = "SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters"
2626
key = "HostName"
2727

2828
try:

nxc/modules/maq.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from pyasn1.error import PyAsn1Error
2+
13

24
class NXCModule:
35
"""
@@ -21,9 +23,10 @@ def options(self, context, module_options):
2123
multiple_hosts = False
2224

2325
def on_login(self, context, connection):
24-
result = []
2526
context.log.display("Getting the MachineAccountQuota")
26-
searchFilter = "(objectClass=*)"
27-
attributes = ["ms-DS-MachineAccountQuota"]
28-
result = connection.search(searchFilter, attributes)
29-
context.log.highlight("MachineAccountQuota: %d" % result[0]["attributes"][0]["vals"][0])
27+
result = connection.search("(objectClass=*)", ["ms-DS-MachineAccountQuota"])
28+
try:
29+
maq = result[0]["attributes"][0]["vals"][0]
30+
context.log.highlight(f"MachineAccountQuota: {maq}")
31+
except PyAsn1Error:
32+
context.log.highlight("MachineAccountQuota: <not set>")

nxc/modules/powershell_history.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import traceback
2+
from os import makedirs
3+
from os.path import join, abspath
4+
from nxc.paths import NXC_PATH
5+
6+
7+
class NXCModule:
8+
"""Module by @357384n"""
9+
10+
name = "powershell_history"
11+
description = "Extracts PowerShell history for all users and looks for sensitive commands."
12+
supported_protocols = ["smb"]
13+
opsec_safe = True
14+
multiple_hosts = True
15+
16+
def options(self, context, module_options):
17+
"""To export all the history you can add the following option: -o export=True"""
18+
context.log.info(f"Received module options: {module_options}")
19+
self.export = bool(module_options.get("EXPORT", False))
20+
context.log.info(f"Option export set to: {self.export}")
21+
22+
def analyze_history(self, history):
23+
"""Analyze PowerShell history for sensitive information."""
24+
sensitive_keywords = [
25+
"password", "passwd", "passw", "secret", "credential", "key",
26+
"get-credential", "convertto-securestring", "set-localuser",
27+
"new-localuser", "set-adaccountpassword", "new-object system.net.webclient",
28+
"invoke-webrequest", "invoke-restmethod"
29+
]
30+
sensitive_commands = []
31+
for command in history:
32+
command_lower = command.lower()
33+
if any(keyword.lower() in command_lower for keyword in sensitive_keywords):
34+
sensitive_commands.append(command.strip())
35+
return sensitive_commands
36+
37+
def on_admin_login(self, context, connection):
38+
"""Main function to retrieve and analyze PowerShell history."""
39+
try:
40+
context.log.info("Retrieving PowerShell history...")
41+
command = 'powershell.exe "type C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt"'
42+
history = connection.execute(command, True).split("\n")
43+
if history:
44+
sensitive_commands = self.analyze_history(history)
45+
if sensitive_commands:
46+
context.log.highlight("Sensitive commands found in PowerShell history:")
47+
for command in sensitive_commands:
48+
context.log.highlight(f" {command}")
49+
else:
50+
context.log.info("No sensitive commands found in PowerShell history.")
51+
else:
52+
context.log.info("No PowerShell history found.")
53+
54+
# Check if export is enabled
55+
context.log.info(f"Export option is set to: {self.export}")
56+
if self.export and history:
57+
host = connection.host # Assuming 'host' contains the target IP or hostname
58+
filename = f"{host}_powershell_history.txt"
59+
export_path = join(NXC_PATH, "modules", "powershell_history")
60+
path = abspath(join(export_path, filename))
61+
makedirs(export_path, exist_ok=True)
62+
63+
context.log.info(f"Export enabled, writing history to {path}")
64+
try:
65+
with open(path, "w") as file:
66+
for cmd in history:
67+
file.write(cmd + "\n")
68+
context.log.highlight(f"PowerShell history written to: {path}")
69+
except Exception as e:
70+
context.log.fail(f"Failed to write history to {filename}: {e}")
71+
except Exception as e:
72+
context.log.fail(f"UNEXPECTED ERROR: {e}")
73+
context.log.debug(traceback.format_exc())

0 commit comments

Comments
 (0)