mirror of
https://github.com/Pennyw0rth/NetExec.git
synced 2026-11-07 09:26:01 -10:00
Merge pull request #1310 from termanix/nfs-shares-db
Add Shares and Export DB for NFS
This commit is contained in:
4 files changed
+158
-6
No files matched your search
@@ -271,6 +271,7 @@ class nfs(connection):
|
||||
|
||||
read_perm, write_perm, exec_perm = self.get_permissions(file_handle)
|
||||
self.mount.umnt(self.auth)
|
||||
self.db.add_share(self.host, (read_perm, write_perm, exec_perm), (convert_size(used_space), "/", convert_size(total_space)), share, network)
|
||||
self.logger.highlight(f"{self.auth['uid']:<11}{'r' if read_perm else '-'}{'w' if write_perm else '-'}{('x' if exec_perm else '-'):<7}{convert_size(used_space) + '/' + convert_size(total_space):<16} {share:<30} {', '.join(network) if network else 'No network':<15}")
|
||||
except Exception as e:
|
||||
self.logger.fail(f"Failed to list share: {share} - {e}")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKeyConstraint, Integer, PrimaryKeyConstraint, UniqueConstraint, String, select
|
||||
from sqlalchemy import Boolean, Column, ForeignKeyConstraint, Integer, PrimaryKeyConstraint, UniqueConstraint, String, func, select
|
||||
from sqlalchemy.dialects.sqlite import Insert # used for upsert
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
@@ -56,12 +56,17 @@ class database(BaseDB):
|
||||
class Share(Base):
|
||||
__tablename__ = "shares"
|
||||
id = Column(Integer)
|
||||
lir_id = Column(Integer)
|
||||
data = Column(String)
|
||||
host = Column(String)
|
||||
read_perm = Column(Boolean)
|
||||
write_perm = Column(Boolean)
|
||||
exec_perm = Column(Boolean)
|
||||
storage = Column(String)
|
||||
share = Column(String)
|
||||
access = Column(String)
|
||||
|
||||
__table_args__ = (
|
||||
PrimaryKeyConstraint("id"),
|
||||
ForeignKeyConstraint(["lir_id"], ["loggedin_relations.id"]),
|
||||
UniqueConstraint("host", "share"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -132,3 +137,45 @@ class database(BaseDB):
|
||||
if updated_ids:
|
||||
nxc_logger.debug(f"add_host() - Host IDs Updated: {updated_ids}")
|
||||
return updated_ids
|
||||
|
||||
def add_share(self, host, permission, storage, share, access):
|
||||
"""Check if this share is already in the DB, if not add it."""
|
||||
read_perm, write_perm, exec_perm = permission
|
||||
used, _, total = storage
|
||||
storage_str = f"{used} / {total}"
|
||||
access_str = ", ".join(access) if access else ""
|
||||
|
||||
new_share = {
|
||||
"host": host,
|
||||
"read_perm": read_perm,
|
||||
"write_perm": write_perm,
|
||||
"exec_perm": exec_perm,
|
||||
"storage": storage_str,
|
||||
"share": share,
|
||||
"access": access_str,
|
||||
}
|
||||
|
||||
self.db_execute(
|
||||
Insert(self.SharesTable).on_conflict_do_nothing(),
|
||||
new_share,
|
||||
)
|
||||
|
||||
def get_shares(self, filter_term=None):
|
||||
"""Return shares from the database, optionally filtered by host or share path."""
|
||||
if self.is_share_valid(filter_term):
|
||||
q = select(self.SharesTable).filter(self.SharesTable.c.id == filter_term)
|
||||
elif filter_term:
|
||||
like_term = func.lower(f"%{filter_term}%")
|
||||
q = select(self.SharesTable).filter(
|
||||
self.SharesTable.c.host.like(like_term) | self.SharesTable.c.share.like(like_term)
|
||||
)
|
||||
else:
|
||||
q = select(self.SharesTable)
|
||||
return self.db_execute(q).all()
|
||||
|
||||
def is_share_valid(self, share_id):
|
||||
"""Check if this share ID is valid."""
|
||||
q = select(self.SharesTable).filter(self.SharesTable.c.id == share_id)
|
||||
results = self.db_execute(q).all()
|
||||
nxc_logger.debug(f"is_share_valid(shareID={share_id}) => {len(results) > 0}")
|
||||
return len(results) > 0
|
||||
@@ -1,4 +1,4 @@
|
||||
from nxc.nxcdb import DatabaseNavigator, print_help, print_table
|
||||
from nxc.nxcdb import DatabaseNavigator, print_help, print_table, write_csv
|
||||
|
||||
|
||||
class navigator(DatabaseNavigator):
|
||||
@@ -48,3 +48,107 @@ class navigator(DatabaseNavigator):
|
||||
|
||||
data.append([host_id, ip, hostname, port, supported_version, root_escape])
|
||||
print_table(data, title="Hosts")
|
||||
|
||||
def display_shares(self, shares):
|
||||
"""
|
||||
shares: list of rows (tuples) -> expects columns:
|
||||
(id, host, read_perm, write_perm, exec_perm, storage, share, access)
|
||||
"""
|
||||
data = [["ID", "Host", "R", "W", "X", "Storage", "Share", "Access"]]
|
||||
for row in shares:
|
||||
(
|
||||
sid, host, r, w, x,
|
||||
storage_str, share_path, access_str
|
||||
) = row
|
||||
data.append([
|
||||
sid, host,
|
||||
"True" if r else "False",
|
||||
"True" if w else "False",
|
||||
"True" if x else "False",
|
||||
storage_str,
|
||||
share_path,
|
||||
access_str,
|
||||
])
|
||||
print_table(data, title="Shares")
|
||||
|
||||
def do_shares(self, line):
|
||||
filter_term = line.strip() or None
|
||||
shares = self.db.get_shares(filter_term=filter_term)
|
||||
|
||||
if shares:
|
||||
self.display_shares(shares)
|
||||
else:
|
||||
print("No shares found.")
|
||||
|
||||
def help_shares(self):
|
||||
help_string = """
|
||||
shares [host_filter]
|
||||
Lists all shares. Optionally filters by host substring.
|
||||
Columns:
|
||||
ID, Host, Read (True/False), Write, Execute, Storage ("used / total"), Share path, Access list
|
||||
"""
|
||||
print_help(help_string)
|
||||
|
||||
def do_export(self, line):
|
||||
if not line:
|
||||
print("[-] not enough arguments")
|
||||
return
|
||||
line = line.split()
|
||||
command = line[0].lower()
|
||||
|
||||
if command == "hosts":
|
||||
if len(line) < 2:
|
||||
print("[-] invalid arguments, export hosts <filename>")
|
||||
return
|
||||
filename = line[2]
|
||||
hosts = self.db.get_hosts()
|
||||
csv_header = ("id", "ip", "hostname", "port", "version", "root_escape")
|
||||
write_csv(filename, csv_header, hosts)
|
||||
print("[+] NFS hosts exported")
|
||||
elif command == "shares":
|
||||
if len(line) < 3:
|
||||
print("[-] invalid arguments, export shares <simple|detailed> <filename>")
|
||||
return
|
||||
|
||||
filename = line[2]
|
||||
mode = line[1].lower()
|
||||
shares = self.db.get_shares()
|
||||
|
||||
if mode == "simple":
|
||||
csv_header = ("id", "host", "read", "write", "exec", "storage", "share", "access")
|
||||
write_csv(filename, csv_header, shares)
|
||||
print("[+] NFS shares exported")
|
||||
elif mode == "detailed":
|
||||
csv_header = ("id", "host", "version", "read", "write", "exec", "storage", "share", "access")
|
||||
# hosts columns: (id, ip, hostname, port, nfs_version, root_escape)
|
||||
version_by_ip = {host[1]: host[4] for host in self.db.get_hosts()}
|
||||
formatted = []
|
||||
for row in shares:
|
||||
entry = (
|
||||
row[0], # id
|
||||
row[1], # host
|
||||
version_by_ip.get(row[1], ""), # nfs_version
|
||||
row[2], # read
|
||||
row[3], # write
|
||||
row[4], # exec
|
||||
row[5], # storage
|
||||
row[6], # share
|
||||
row[7], # access
|
||||
)
|
||||
formatted.append(entry)
|
||||
write_csv(filename, csv_header, formatted)
|
||||
print("[+] NFS shares exported")
|
||||
else:
|
||||
print(f"[-] No such export option: {line[1]}")
|
||||
else:
|
||||
print(f"[-] Export command '{command}' is not supported for NFS. Supported: hosts, shares")
|
||||
|
||||
def help_export(self):
|
||||
help_string = """
|
||||
export hosts <filename>
|
||||
exports: id, ip, hostname, port, version, root_escape
|
||||
export shares <simple|detailed> <filename>
|
||||
simple - exports: id, host, read, write, exec, storage, share, access
|
||||
detailed - exports: id, host, version, read, write, exec, storage, share, access
|
||||
"""
|
||||
print_help(help_string)
|
||||
Generated
+1
-1
@@ -2274,7 +2274,7 @@ develop = false
|
||||
type = "git"
|
||||
url = "https://github.com/Pennyw0rth/NfsClient"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "9b61e0a6385743923ccffa6a80f8dbbf0714121d"
|
||||
resolved_reference = "a8fea2c163bf665dc6a8e35bb50e7baf81e2c6ed"
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
|
||||
Reference in new issue
Block a user