Compare commits

..
Author SHA1 Message Date
mpgn 77b3797da6 drop 3.8 2024-06-20 09:16:16 +00:00
mpgn 5e416d7b3e disable defender 2024-06-20 09:15:12 +00:00
mpgn b565bed227 Update checker.yml
Signed-off-by: mpgn <[email protected]>
2024-06-19 19:07:37 +02:00
mpgn 9c41c567b9 Update checker.yml
Signed-off-by: mpgn <[email protected]>
2024-06-19 18:56:08 +02:00
mpgn 3fa07310c5 add rdp 2024-06-19 16:53:27 +00:00
mpgn 7b5ca88243 add rdp 2024-06-19 16:41:51 +00:00
mpgn b77fc9d919 add rdp 2024-06-19 16:36:54 +00:00
mpgn bb030122fe add rdp 2024-06-19 16:31:01 +00:00
mpgn 21e320af41 add rdp 2024-06-19 16:28:44 +00:00
mpgn cf2c0a3b66 add rdp 2024-06-19 16:26:09 +00:00
mpgn 75ba2e2afd add ssh 2024-06-19 15:09:03 +00:00
mpgn 06e45dd23f test 2024-06-19 15:00:49 +00:00
mpgn a866ab825c rename worflow run
debug

debug

debug

debug

debug

debug

Update windows-run.yml

Signed-off-by: mpgn <[email protected]>

debug

debug

debug

debug

debug

debug

debug

debug

debug
2024-06-19 11:17:36 +00:00
mpgn 6243163ad8 remove dev from poetry 2024-06-16 19:01:13 +00:00
mpgn da344b3b5f fix procdump module name 2024-06-16 19:00:01 +00:00
mpgn eb8ceab063 change workflow name 2024-06-16 18:53:40 +00:00
mpgn a2507f85b0 add worflow for windows 2024-06-16 18:50:53 +00:00
11 changed files with 190 additions and 530 deletions

No files matched your search

+179
View File
@@ -0,0 +1,179 @@
name: NetExec Checker
on:
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
lint:
uses: ./.github/workflows/lint.yml
nxc-smb:
runs-on: windows-latest
needs: [lint]
# technique stolen from @Hackndo my best friend for life <3
steps:
- name: Create new user
run: |
net user nxc Pwn3d!!! /add
- name: Add to local admin
run: |
net localgroup Administrators nxc /add
- name: Update registry key
run: |
REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\system /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
- name: 👾 Disable defender
run: start-process -filepath powershell -ArgumentList '/c','Set-MpPreference -DisableRealtimeMonitoring $true' -verb RunAs
shell: powershell
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install poetry
run: |
pipx install poetry --python python3.11
poetry --version
poetry env info
- name: Install libraries without dev group
run: |
poetry install
- name: Dumping sam
run: |
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! --sam
- name: Dumping lsa
run: |
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! --lsa
- name: Dumping dpapi
run: |
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! --dpapi
- name: Dumping with lsassy
run: |
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! -M lsassy
- name: Exec command
run: |
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! -x whoami --exec-method=smbexec
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! -x whoami --exec-method=wmiexec
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! -x whoami --exec-method=atexec
poetry run netexec smb %COMPUTERNAME% -u nxc -p Pwn3d!!! -x whoami --exec-method=mmcexec
- name: Dumping with procdump
run: |
poetry run netexec --verbose smb %COMPUTERNAME% %COMPUTERNAME% %COMPUTERNAME% -u nxc -p Pwn3d!!! -M procdump
nxc-winrm:
runs-on: windows-latest
needs: [lint]
# technique stolen from @Hackndo my best friend for life <3
steps:
- name: Create new user
run: |
net user nxc Pwn3d!!! /add
- name: Add to local admin
run: |
net localgroup Administrators nxc /add
- name: Update registry key
run: |
REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\system /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
- name: Enable WinRM
run: |
winrm quickconfig -quiet
- name: 👾 Disable defender
run: start-process -filepath powershell -ArgumentList '/c','Set-MpPreference -DisableRealtimeMonitoring $true' -verb RunAs
shell: powershell
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install poetry
run: |
pipx install poetry --python python3.11
poetry --version
poetry env info
- name: Install libraries without dev group
run: |
poetry install
- name: Dumping sam
run: |
poetry run netexec winrm 127.0.0.1 -u nxc -p Pwn3d!!! --sam
- name: Dumping lsa
run: |
poetry run netexec winrm 127.0.0.1 -u nxc -p Pwn3d!!! --lsa
- name: Exec command
run: |
poetry run netexec winrm 127.0.0.1 -u nxc -p Pwn3d!!! -x whoami
nxc-rdp:
runs-on: windows-latest
needs: [lint]
# technique stolen from @Hackndo my best friend for life <3
steps:
- name: Create new user
run: |
net user nxc Pwn3d!!! /add
- name: Add to local admin
run: |
net localgroup Administrators nxc /add
- name: Update registry key
run: |
REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\system /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
- name: Update registry key rdo
run: |
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
- name: Open Firewall
run: |
netsh advfirewall firewall add rule name="Open Remote Desktop" protocol=TCP dir=in localport=3389 action=allow
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install poetry
run: |
pipx install poetry --python python3.11
poetry --version
poetry env info
- name: Install libraries without dev group
run: |
poetry install
- name: Checking rdp
run: |
poetry run netexec rdp 127.0.0.1
- name: Checking auth
run: |
poetry run netexec rdp 127.0.0.1 -u nxc -p Pwn3d!!!
nxc-ssh:
runs-on: ubuntu-latest
needs: [lint]
# technique stolen from @Hackndo my best friend for life <3
steps:
- name: Enable ssh
run: |
sudo apt update
sudo apt install openssh-server
sudo systemctl status ssh
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install poetry
run: |
pipx install poetry --python python3.11
poetry --version
poetry env info
- name: Install libraries without dev group
run: |
poetry install
- name: Connection with user
run: |
poetry run netexec ssh 127.0.0.1
+2 -5
View File
@@ -1,14 +1,11 @@
name: Lint Python code with ruff
name: lint
# Caching source: https://gist.github.com/gh640/233a6daf68e9e937115371c0ecd39c61?permalink_comment_id=4529233#gistcomment-4529233
on:
push:
on: [workflow_call]
jobs:
lint:
runs-on: ubuntu-latest
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
steps:
- uses: actions/checkout@v3
View File
Whitespace-only changes.
+1 -3
View File
@@ -2,8 +2,6 @@ name: NetExec Tests
on:
workflow_dispatch:
pull_request_review:
types: [submitted]
jobs:
build:
@@ -14,7 +12,7 @@ jobs:
max-parallel: 5
matrix:
os: [ubuntu-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Install poetry
-365
View File
@@ -1,365 +0,0 @@
# Module by @syl
# https://twitter.com/5yrull
# S-1-5-21-3750225908-897271529-2201778708-1103
# 'S-1-5-21-3750225908-897271529-2201778708'
# (Pdb) resp = lsad.hLsarQueryInformationPolicy2(dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
# (Pdb) domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
# (Pdb) domainSid
# 'S-1-5-21-3750225908-897271529-2201778708'
# (Pdb) lsat.hLsarLookupSids(dce, policyHandle, ['S-1-5-21-3750225908-897271529-2201778708-1103'],lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
# <impacket.dcerpc.v5.lsat.LsarLookupSidsResponse object at 0x7f041cba9d50>
# (Pdb) resp = lsat.hLsarLookupSids(dce, policyHandle, ['S-1-5-21-3750225908-897271529-2201778708-1103'],lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
# (Pdb) resp
# <impacket.dcerpc.v5.lsat.LsarLookupSidsResponse object at 0x7f041cba9de0>
# (Pdb) resp.__dict__
# {'_isNDR64': False, 'fields': {'ReferencedDomains': <impacket.dcerpc.v5.lsat.PLSAPR_REFERENCED_DOMAIN_LIST object at 0x7f041cba9ea0>, 'TranslatedNames': <impacket.dcerpc.v5.lsat.LSAPR_TRANSLATED_NAMES object at 0x7f041cba9450>, 'MappedCount': <impacket.dcerpc.v5.ndr.NDRULONG object at 0x7f041cba9270>, 'ErrorCode': <impacket.dcerpc.v5.ndr.NDRULONG object at 0x7f041cba8460>}}
# (Pdb) resp.TranslatedNames
# *** AttributeError: 'LsarLookupSidsResponse' object has no attribute 'TranslatedNames'
# (Pdb) resp["TranslatedNames"]
# <impacket.dcerpc.v5.lsat.LSAPR_TRANSLATED_NAMES object at 0x7f041cba9450>
# (Pdb) resp["TranslatedNames"].__dict__
# {'_isNDR64': False, 'fields': {'Entries': <impacket.dcerpc.v5.ndr.NDRULONG object at 0x7f041cba9960>, 'Names': <impacket.dcerpc.v5.lsat.PLSAPR_TRANSLATED_NAME_ARRAY object at 0x7f041cba9a80>}}
# (Pdb) resp["TranslatedNames"]["Names"]
# [<impacket.dcerpc.v5.lsat.LSAPR_TRANSLATED_NAME object at 0x7f041cba8a60>]
# (Pdb) resp["TranslatedNames"]["Names"][0].__dict_
# *** AttributeError: 'LSAPR_TRANSLATED_NAME' object has no attribute '__dict_'
# (Pdb) resp["TranslatedNames"]["Names"][0].__dict__
# {'_isNDR64': False, 'fields': {'Use': <impacket.dcerpc.v5.samr.SID_NAME_USE object at 0x7f041cba8c70>, 'Name': <impacket.dcerpc.v5.dtypes.RPC_UNICODE_STRING object at 0x7f041cba8be0>, 'DomainIndex': <impacket.dcerpc.v5.ndr.NDRLONG object at 0x7f041cba8ca0>}}
# (Pdb) resp["TranslatedNames"]["Names"][0]["name"]
# *** KeyError: 'name'
# (Pdb) resp["TranslatedNames"]["Names"][0]["Name"]
# 'George.Hotz'
# (Pdb)
from impacket.smbconnection import SessionError
from impacket.dcerpc.v5 import transport, lsat, lsad
from impacket.dcerpc.v5.samr import SID_NAME_USE
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from impacket.dcerpc.v5.rpcrt import DCERPCException
from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR
from winacl.functions.constants import SE_OBJECT_TYPE
from winacl.dtyp.ace import FILE_ACCESS_MASK
from termcolor import colored
def get_dce_from_smb(conn):
string_binding = rf"ncacn_np:{conn.remoteName}[\pipe\lsarpc]"
rpctransport = transport.DCERPCTransportFactory(string_binding)
rpctransport.set_dport(conn.port)
rpctransport.setRemoteHost(conn.host)
rpctransport.set_credentials(conn.username, conn.password, conn.domain, conn.lmhash, conn.nthash)
return rpctransport.get_dce_rpc()
def lookup_sid(dce, sid):
dce.connect()
dce.bind(lsat.MSRPC_UUID_LSAT)
policy_response = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
policy_handle = policy_response["PolicyHandle"]
sid_response = lsat.hLsarLookupSids(dce, policy_handle, [sid],lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
sid_name = sid_response["TranslatedNames"]["Names"][0]["Name"]
dce.disconnect()
return sid_name
AVAILABLE_FILTERS = ["ace_mask", "ace_sid", "owner", "group"]
READ_CONTROL = 0x00020000
FILE_SHARE_READ = 0x00000001
FILE_NON_DIRECTORY_FILE = 0x00000040
FILE_DIRECTORY_FILE = 0x00000001
IMPERSONATION = 0x00000002
class SMBACLEnumerator:
AVAILABLE_FILTERS = ["ace_mask", "ace_sid", "owner", "group"]
def __init__(self, connection, context, module_options) -> None:
self.connection = connection
self.smb_connection = connection.conn
self.context = context
self.module_options = module_options
self.filters = self.parse_filters(self.module_options)
self.shares = self.get_shares()
def get_shares(self):
shares = self.connection.shares()
def parse_filters(self, options: dict) -> dict:
...
def get_security_descriptor() -> None:
...
# def get_security_descriptor(file, smb_connection, context, share_name, file_path, filters=None):
# """
# Retrieve the security descriptor of a file on a share.
# Parameters
# ----------
# file: File object.
# smb_connection: SMB connection instance.
# context: Context for logging.
# share_name: Name of the SMB share.
# file_path: Path of the file.
# filters: Optional filters for security descriptor components.
# Returns
# -------
# None: If the file does not meet the filter criteria.
# """
# file_full_path = file_path + file.get_longname()
# tree_id = smb_connection.connectTree(share_name)
# file_id = create_file(smb_connection, tree_id, file_full_path)
# try:
# security_descriptor = smb_connection._SMBConnection.query_sec_info(tree_id, file_id)
# security_descriptor_obj = parse_security_descriptor(security_descriptor)
# if filters and not passes_filters(security_descriptor_obj, filters):
# return
# log_security_descriptor(context, file_full_path, security_descriptor_obj)
# except SessionError as e:
# context.log.debug(e)
# def create_file(smb_connection, tree_id, file_full_path):
# """Create a file in the SMB share and return its ID."""
# return smb_connection.createFile(
# tree_id,
# file_full_path,
# desiredAccess=READ_CONTROL,
# shareMode=FILE_SHARE_READ,
# creationOption=FILE_NON_DIRECTORY_FILE,
# creationDisposition=FILE_DIRECTORY_FILE,
# fileAttributes=0,
# impersonationLevel=IMPERSONATION,
# )
# def parse_security_descriptor(security_descriptor):
# """Parse the security descriptor from bytes to an object."""
# return SECURITY_DESCRIPTOR.from_bytes(
# security_descriptor, object_type=SE_OBJECT_TYPE.SE_FILE_OBJECT
# )
# def passes_filters(security_descriptor_obj, filters):
# """
# Check if the security descriptor object passes the given filter criteria.
# Parameters
# ----------
# security_descriptor_obj: The security descriptor object.
# filters: Dictionary of filter criteria.
# Returns
# -------
# bool: True if the descriptor passes all filters, False otherwise.
# """
# for filter_type, values in filters.items():
# if filter_type == "owner" and str(security_descriptor_obj.Owner) not in values:
# return False
# if filter_type == "group" and str(security_descriptor_obj.Group) not in values:
# return False
# if filter_type == "ace_sid":
# sids = [str(ace.Sid) for ace in security_descriptor_obj.Dacl.aces]
# if not any(sid in sids for sid in values):
# return False
# if filter_type == "ace_mask":
# masks = [FILE_ACCESS_MASK(ace.Mask).name for ace in security_descriptor_obj.Dacl.aces]
# if not any(mask in masks for mask in values):
# return False
# return True
def log_security_descriptor(context, file_path, descriptor_obj):
"""
Log details about the security descriptor.
Parameters
----------
context: Context for logging.
file_path: Path of the file.
descriptor_obj: The security descriptor object.
"""
context.log.success(file_path)
context.log.highlight(f"\t Owner: {descriptor_obj.Owner}")
context.log.highlight(f"\t Group: {descriptor_obj.Group}")
context.log.highlight(f"\t SACL: {descriptor_obj.Sacl}")
if descriptor_obj.Dacl is not None:
context.log.highlight(f"\t ACEs[{len(descriptor_obj.Dacl.aces)}]: ")
for ace in descriptor_obj.Dacl.aces:
ace_str = str(ace)
if "\r\n" in ace_str:
ace_formatted = ace_str.split("\r\n")[:-1]
for line in ace_formatted:
context.log.highlight(f"\t\t {line}")
context.log.highlight("\t\t " + len(ace_formatted[0]) * "=")
else:
context.log.highlight(f"\t\t {ace_str}")
def get_security_descriptor(conn, file, smb_connection, context, share_name, file_path, filters=None):
"""Retrieving the security descriptor of a certain file on a share"""
tree_id = smb_connection.connectTree(share_name)
dce = get_dce_from_smb(conn)
desired_access = 0x00020000 # READ_CONTROL
share_mode = 0x00000001 # FILE_SHARE_READ
create_options = 0x00000040 # FILE_NON_DIRECTORY_FILE
file_attrs = 0
create_disposition = 0x00000001 # FILE_DIRECTORY_FILE
file_full_path = file_path + file.get_longname()
try:
file_id = smb_connection.createFile(
tree_id,
file_full_path,
desiredAccess=desired_access,
shareMode=share_mode,
creationOption=create_options,
creationDisposition=create_disposition,
fileAttributes=file_attrs,
impersonationLevel=0x00000002, # Impersonation
)
security_descriptor = smb_connection._SMBConnection.query_sec_info(tree_id, file_id)
security_descriptor_obj = SECURITY_DESCRIPTOR.from_bytes(
security_descriptor, object_type=SE_OBJECT_TYPE.SE_FILE_OBJECT
)
if filters:
for _filter, values in filters.items():
if _filter == "owner" and str(security_descriptor_obj.Owner) not in values:
return
if _filter == "group" and str(security_descriptor_obj.Group) not in values:
return
if _filter == "ace_sid":
sids = [str(_ace.Sid) for _ace in security_descriptor_obj.Dacl.aces]
for sid in values:
if sid in sids:
continue
else:
return
if _filter == "ace_mask":
masks = [FILE_ACCESS_MASK(_ace.Mask).name for _ace in security_descriptor_obj.Dacl.aces]
for mask in values:
if mask in masks:
continue
else:
return
context.log.success(file_full_path)
# Also get the user's group to mark it as Owned
# smb_connection._SMBConnection._SMB__userName
username = smb_connection._SMBConnection._SMB__userName
owner = lookup_sid(dce, str(security_descriptor_obj.Owner))
if owner == username:
owner = owner + colored("Pwn3d!", "red")
group = lookup_sid(dce, str(security_descriptor_obj.Group))
context.log.highlight(f"\t Owner: {owner}")
context.log.highlight(f"\t Group: {group}")
context.log.highlight(f"\t SACL: {security_descriptor_obj.Sacl}")
if security_descriptor_obj.Dacl is not None:
context.log.highlight("\t ACEs:")
for ace in security_descriptor_obj.Dacl.aces:
ace_sid_name = lookup_sid(dce, str(ace.Sid))
mask_formatted = FILE_ACCESS_MASK(ace.Mask).name
if mask_formatted is None:
continue
owned = colored(" (Own3d!)", "red") if ace_sid_name == username else ""
context.log.highlight(f"\t\t {ace_sid_name} - {mask_formatted}{owned}")
except SessionError as e:
context.log.debug(e)
def enumerate_files(conn, smb_connection, share_name, current_path, context, filters=None):
"""Recursive function that enumerates the directories"""
try:
print(f"{smb_connection=} {share_name=} {current_path=}")
file_list = smb_connection.listPath(share_name, current_path + "*")
for file in file_list:
if (
file.is_directory()
and not file.is_system()
and file.get_longname() not in [".", ".."]
):
next_path = current_path + file.get_longname() + "/"
context.log.debug(f"Trying to enumrate files in {next_path}")
enumerate_files(conn, smb_connection, share_name, next_path, context, filters)
if not file.is_directory() and not file.is_system():
context.log.debug(f"File Found {file.get_longname()}")
get_security_descriptor(conn, file, smb_connection, context, share_name, current_path, filters)
except SessionError as e:
context.log.error(e)
class NXCModule:
"""
A module to enumerate the ACLs of the files in a share.
Module by @syl, thanks to @Skelsec and @NeffIsBack
"""
name = "enum_share_acls"
description = ""
supported_protocols = ["smb"]
opsec_safe = False
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.context = context
self.module_options = module_options
self.server = None
self.file_path = None
self.lnk_path = None
self.lnk_name = None
self.cleanup = None
def options(self, context, module_options):
"""
group-mem: Specify group-mem to call the module
GROUP: Specify the GROUP option to query for that group's members
Usage:
nxc smb 127.0.0.1 -u administrator -p 'Password123!' -M enum_share_acls -o FILTER="ace_mask=FILE_ALL_ACCESS,ace_sid=S-1-5-32-544"
nxc smb 127.0.0.1 -u administrator -p 'Password123!' -M enum_share_acls -o FILTER="ace_mask=FILE_ALL_ACCESS|READ_CONTROL,ace_sid=S-1-5-32-544"
nxc smb 127.0.0.1 -u administrator -p 'Password123!' -M enum_share_acls -o FILTER="owner=S-1-5-32-544"
"""
self.FILTER = None
if "FILTER" in module_options:
self.FILTER = module_options["FILTER"]
def parse_filters(self):
parsed_filters = self.FILTER.split(",")
filters_dict = {}
masks = [m.name for m in FILE_ACCESS_MASK]
for _filter in parsed_filters:
splitted_filter = _filter.split("=")
if splitted_filter[0] not in AVAILABLE_FILTERS:
return None, f"{_filter} filter isn't available, please choose between {AVAILABLE_FILTERS}"
for __filter in AVAILABLE_FILTERS:
if __filter in _filter:
filters_dict[__filter] = splitted_filter[1]
if "|" in filters_dict[__filter]:
filters_dict[__filter] = filters_dict[__filter].split("|")
else:
filters_dict[__filter] = [splitted_filter[1],]
# Verify the filters
if "ace_mask" in filters_dict:
for mask_name in filters_dict["ace_mask"]:
if mask_name not in masks:
return None, f"{mask_name} ace_mask isn't valid, please choose between {mask_name}"
return filters_dict, None
def on_login(self, context, connection):
shares = connection.shares()
smb_connection = connection.conn
filters = None
if self.FILTER:
filters, err = self.parse_filters()
if err is not None:
context.log.error(err)
for share in shares:
share_name = share["name"]
context.log.display(f"Enumerating ACLs on share: {share_name}")
enumerate_files(connection, smb_connection, share_name, "/", context, filters)
+1
View File
@@ -78,6 +78,7 @@ class NXCModule:
dump = True
else:
context.log.fail("Process lsass.exe error un dump, try with verbose")
sys.exit(1)
if dump:
regex = r"([A-Za-z0-9-]*.dmp)"
-5
View File
@@ -34,7 +34,6 @@ from nxc.helpers.misc import gen_random_string, validate_ntlm
from nxc.logger import NXCAdapter
from nxc.protocols.smb.firefox import FirefoxTriage
from nxc.protocols.smb.kerberos import kerberos_login_with_S4U
from nxc.protocols.smb.smb_share import SMB_Share_Enumeration
from nxc.servers.smb import NXCSMBServer
from nxc.protocols.smb.wmiexec import WMIEXEC
from nxc.protocols.smb.atexec import TSCH_EXEC
@@ -755,10 +754,6 @@ class smb(connection):
return response
def shares(self):
if self.args.shares != True:
smb_enumeration = SMB_Share_Enumeration(self, self.conn)
return smb_enumeration.enumerate_path(self.args.shares)
temp_dir = ntpath.normpath("\\" + gen_random_string())
permissions = []
+1 -1
View File
@@ -33,7 +33,7 @@ def proto_args(parser, parents):
cred_gathering_group.add_argument("--user", dest="userntds", type=str, help="Dump selected user from DC")
mapping_enum_group = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
mapping_enum_group.add_argument("--shares", nargs="?", default=False, const=True, help="enumerate shares and access")
mapping_enum_group.add_argument("--shares", action="store_true", help="enumerate shares and access")
mapping_enum_group.add_argument("--no-write-check", action="store_true", help="Skip write check on shares (avoid leaving traces when missing delete permissions)")
mapping_enum_group.add_argument("--filter-shares", nargs="+", help="Filter share by access, option 'read' 'write' or 'read,write'")
mapping_enum_group.add_argument("--sessions", action="store_true", help="enumerate active sessions")
-144
View File
@@ -1,144 +0,0 @@
from nxc.protocols import smb
from impacket.smbconnection import SMBConnection, SessionError
from impacket.smb import SharedFile
from impacket.dcerpc.v5 import transport, lsat, lsad
from winacl.dtyp.security_descriptor import SECURITY_DESCRIPTOR
from winacl.functions.constants import SE_OBJECT_TYPE
from winacl.dtyp.ace import FILE_ACCESS_MASK
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from termcolor import colored
from typing import List
class SMB_Share_Enumeration:
def __init__(self, nxc_conn: smb, smb_conn: SMBConnection):
self.nxc_conn = nxc_conn
self.smb_conn = smb_conn
self.logger = nxc_conn.logger
def enumerate_path(self, path: str):
self.share = path.split(":")[0]
self.path = path.split(":")[1]
self.shares = [share["shi1_netname"][:-1] for share in self.nxc_conn.conn.listShares()]
if self.share not in self.shares:
self.logger.fail(f"Share {self.share} not found")
return
else:
self.logger.display(f"Enumerating ACLs on share: {self.share}")
self.enumerate_files(self.share, self.path, recursive=True)
def enumerate_files(self, share, path: str, recursive: bool = False):
"""Recursive function that enumerates the directories"""
self.logger.debug(f"Trying to enumrate files in {path}")
if path[-2:] not in ["\\", "/"]:
path += "/"
path.replace("\\", "/")
try:
file_list: List[SharedFile] = self.smb_conn.listPath(share, path + "*")
for file in file_list:
if (
file.is_directory()
and not file.is_system()
and file.get_longname() not in [".", ".."]
and recursive
):
next_path = path + file.get_longname() + "/"
self.enumerate_files(share, next_path, recursive=True)
if not file.is_directory() and not file.is_system():
self.logger.debug(f"File Found {file.get_longname()}")
self.get_security_descriptor(file, share, path)
except SessionError as e:
self.logger.error(e)
def get_security_descriptor(self, file, share, path, filters=None):
"""Retrieving the security descriptor of a certain file on a share"""
tree_id = self.smb_conn.connectTree(share)
dce = self.get_dce_from_smb()
desired_access = 0x00020000 # READ_CONTROL
share_mode = 0x00000001 # FILE_SHARE_READ
create_options = 0x00000040 # FILE_NON_DIRECTORY_FILE
file_attrs = 0
create_disposition = 0x00000001 # FILE_DIRECTORY_FILE
file_full_path = path + file.get_longname()
try:
file_id = self.smb_conn.createFile(
tree_id,
file_full_path,
desiredAccess=desired_access,
shareMode=share_mode,
creationOption=create_options,
creationDisposition=create_disposition,
fileAttributes=file_attrs,
impersonationLevel=0x00000002, # Impersonation
)
security_descriptor = self.smb_conn._SMBConnection.query_sec_info(tree_id, file_id)
security_descriptor_obj = SECURITY_DESCRIPTOR.from_bytes(
security_descriptor, object_type=SE_OBJECT_TYPE.SE_FILE_OBJECT
)
if filters:
for _filter, values in filters.items():
if _filter == "owner" and str(security_descriptor_obj.Owner) not in values:
return
if _filter == "group" and str(security_descriptor_obj.Group) not in values:
return
if _filter == "ace_sid":
sids = [str(_ace.Sid) for _ace in security_descriptor_obj.Dacl.aces]
for sid in values:
if sid in sids:
continue
else:
return
if _filter == "ace_mask":
masks = [FILE_ACCESS_MASK(_ace.Mask).name for _ace in security_descriptor_obj.Dacl.aces]
for mask in values:
if mask in masks:
continue
else:
return
self.logger.success(file_full_path)
# Also get the user's group to mark it as Owned
# self.smb_conn._SMBConnection._SMB__userName
username = self.smb_conn._SMBConnection._SMB__userName
owner = self.lookup_sid(dce, str(security_descriptor_obj.Owner))
if owner == username:
owner = owner + colored("Pwn3d!", "red")
group = self.lookup_sid(dce, str(security_descriptor_obj.Group))
self.logger.highlight(f"\t Owner: {owner}")
self.logger.highlight(f"\t Group: {group}")
self.logger.highlight(f"\t SACL: {security_descriptor_obj.Sacl}")
if security_descriptor_obj.Dacl is not None:
self.logger.highlight("\t ACEs:")
for ace in security_descriptor_obj.Dacl.aces:
ace_sid_name = self.lookup_sid(dce, str(ace.Sid))
mask_formatted = FILE_ACCESS_MASK(ace.Mask).name
if mask_formatted is None:
continue
owned = colored(" (Own3d!)", "red") if ace_sid_name == username else ""
self.logger.highlight(f"\t\t {ace_sid_name} - {mask_formatted}{owned}")
except SessionError as e:
self.logger.debug(e)
def get_dce_from_smb(self):
string_binding = rf"ncacn_np:{self.nxc_conn.remoteName}[\pipe\lsarpc]"
rpctransport = transport.DCERPCTransportFactory(string_binding)
rpctransport.set_dport(self.nxc_conn.port)
rpctransport.setRemoteHost(self.nxc_conn.host)
rpctransport.set_credentials(self.nxc_conn.username, self.nxc_conn.password, self.nxc_conn.domain, self.nxc_conn.lmhash, self.nxc_conn.nthash)
return rpctransport.get_dce_rpc()
def lookup_sid(self, dce, sid):
dce.connect()
dce.bind(lsat.MSRPC_UUID_LSAT)
policy_response = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES)
policy_handle = policy_response["PolicyHandle"]
sid_response = lsat.hLsarLookupSids(dce, policy_handle, [sid],lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta)
sid_name = sid_response["TranslatedNames"]["Names"][0]["Name"]
dce.disconnect()
return sid_name
Generated
+6 -6
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "aardwolf"
@@ -2092,7 +2092,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""}
typing-extensions = ">=4.6.0"
[package.extras]
@@ -2310,13 +2310,13 @@ watchdog = ["watchdog (>=2.3)"]
[[package]]
name = "winacl"
version = "0.1.9"
version = "0.1.8"
description = "ACL/ACE/Security Descriptor manipulation library in pure Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "winacl-0.1.9-py3-none-any.whl", hash = "sha256:31ba781a35f3b1bd3c2ece994816c0a5fe113c73018689ab6118010ac0d15099"},
{file = "winacl-0.1.9.tar.gz", hash = "sha256:af70c2ec30178bf9e3c8a1c48c25e8781235fe2c1b321adb46e2f2ae1f8d4aab"},
{file = "winacl-0.1.8-py3-none-any.whl", hash = "sha256:f17028422c0b2dc6752c801350d37878919804728b776289521dc83622982a45"},
{file = "winacl-0.1.8.tar.gz", hash = "sha256:44271a302562de51629f68ef1540d40f3a26e7bc0173646b01a6779cedad644c"},
]
[package.dependencies]
@@ -2351,4 +2351,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = "^3.8.0"
content-hash = "87a6d3ee944dc3aa0122c51ec72fbdc6e6070eda5b812232a1c84ca967f9397e"
content-hash = "7bde962ed54681c55a1249f2f8272ebf64109417f2a28293eab3eea90ce05c6f"
-1
View File
@@ -64,7 +64,6 @@ rich = "^13.3.5"
sqlalchemy = "^2.0.4"
termcolor = ">=2.4.0"
terminaltables = "^3.1.0"
winacl = "^0.1.9"
xmltodict = "^0.13.0"
[tool.poetry.group.dev.dependencies]