recon-setup 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
recon_setup/main.py ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/python3
2
+
3
+ import argparse
4
+ import ctypes
5
+ import libtmux
6
+ import os
7
+ import signal
8
+ import subprocess
9
+ import sys
10
+ from time import sleep
11
+
12
+ from recon_setup.utils.context import Context
13
+ from recon_setup.utils.htb_cli import spawn_machine
14
+ from recon_setup.watchers.nmap_watcher import watch_nmap
15
+
16
+ def set_death_signal(sig=signal.SIGHUP):
17
+ """Send SIGHUP to the forked process when its parent dies"""
18
+ libc = ctypes.CDLL("libc.so.6")
19
+ PR_SET_PDEATHSIG = 1
20
+ libc.prctl(PR_SET_PDEATHSIG, sig)
21
+
22
+ def verify_connection(ip):
23
+ """Verify connection with target IP."""
24
+ reachable = False
25
+ while not reachable:
26
+ try:
27
+ subprocess.check_call(
28
+ ["ping", "-c1", "-W.5", ip],
29
+ stdout=subprocess.DEVNULL,
30
+ stderr=subprocess.DEVNULL
31
+ )
32
+ reachable = True
33
+ except subprocess.CalledProcessError:
34
+ reachable = False
35
+ sleep(0.5)
36
+ sleep(1)
37
+
38
+ def main():
39
+ # Initialize parser
40
+ parser = argparse.ArgumentParser(description="Automate the setup and enumeration process for offensive security labs.")
41
+
42
+ # Initialize session arguments
43
+ session_group = parser.add_argument_group("Session Arguments", "Arguments related to session configuration")
44
+ session_group.add_argument("session_name", type=str, help="Name of the tmux session to be created")
45
+ session_group.add_argument("-v", "--vpn_path", type=str, help="Path of your VPN file")
46
+ session_group.add_argument("-s", "--session_path", type=str, default="/workspace/machines", help="Path to where the session will be created (default: /workspace/machines)")
47
+ session_group.add_argument("-i", "--ip", type=str, help="IP address of the target machine")
48
+
49
+ # Initialize HTB CLI arguments
50
+ htb_cli_group = parser.add_argument_group("HTB CLI Arguments", "Arguments related to HTB CLI functionality")
51
+ htb_cli_group.add_argument("--spawn", type=str, help="Spawn the target machine via the HTB API instead of providing an IP (requires HTB_TOKEN env var)")
52
+ htb_cli_group.add_argument("-n", "--new_release", action="store_true", help="Wait until T-60s before the scheduled release time (7pm UTC) and spawn automatically via the HTB API")
53
+
54
+ # Initialize automation arguments
55
+ automation_group = parser.add_argument_group("Automation Arguments", "Arguments related to automated tasking")
56
+ automation_group.add_argument("-a", "--automate", action="store_true", help="Optional flag to enable automated tasks on the fly from nmap scan results")
57
+ automation_group.add_argument("-u", "--username", type=str, help="Username to supply automated tasks (AD only)")
58
+ automation_group.add_argument("-p", "--password", type=str, help="Password to supply automated tasks (AD only)")
59
+ automation_group.add_argument("-d", "--debug", action="store_true", help="Enable debug mode for automation")
60
+
61
+ # Initialize parser
62
+ args = parser.parse_args()
63
+
64
+ # Define local variables
65
+ session_name = args.session_name
66
+ vpn_path = os.path.abspath(args.vpn_path) if args.vpn_path is not None else None
67
+ session_path = args.session_path
68
+ ip = args.ip
69
+ spawn = args.spawn
70
+ new_release = args.new_release
71
+ automate = args.automate
72
+ user = args.username
73
+ password = args.password
74
+ debug = args.debug
75
+
76
+ # Define file paths
77
+ scan_script_file = "/opt/scripts/scan_machine.py"
78
+ target_dir = os.path.join(session_path, session_name)
79
+ nmap_dir = os.path.join(target_dir, "nmap")
80
+ log_dir = os.path.join(target_dir, "logs")
81
+ tmux_pipe_file = os.path.join(log_dir, "tmux_pipe.log")
82
+ log_file = os.path.join(log_dir, "task.log")
83
+ debug_file = os.path.join(log_dir, "debug.log")
84
+ users_file = os.path.join(target_dir, "valid_users.txt")
85
+ creds_file = os.path.join(target_dir, "creds.txt")
86
+
87
+ # Check for nmap wrapper script
88
+ if not os.path.isfile(scan_script_file):
89
+ print(f"Error: Required nmap wrapper script not found at {scan_script_file}", file=sys.stderr)
90
+ sys.exit(1)
91
+
92
+ # Initialize HTB machine if relevant arguments are included
93
+ if spawn:
94
+ ip = spawn_machine(spawn, new_release)
95
+ if not ip:
96
+ print("Error: HTB CLI failed to provide an IP")
97
+ sys.exit(1)
98
+ else:
99
+ ip = args.ip
100
+
101
+ # Initialize environment
102
+ os.makedirs(nmap_dir, exist_ok=True)
103
+ os.chdir(target_dir)
104
+
105
+ # Initialize tmux session
106
+ server = libtmux.Server()
107
+ session = server.new_session(
108
+ session_name=session_name,
109
+ window_name="services",
110
+ attach=False
111
+ )
112
+ initial_window = session.active_window
113
+ openvpn_pane = initial_window.active_pane
114
+ if os.path.isfile(vpn_path):
115
+ openvpn_pane.send_keys(f"sudo openvpn {vpn_path}")
116
+ ligolo_pane = openvpn_pane.split(direction=libtmux.pane.PaneDirection.Below)
117
+ ligolo_pane.send_keys("mkdir -p ligolo; cd ligolo; proxy -selfcert")
118
+ ip_pane = openvpn_pane.split(direction=libtmux.pane.PaneDirection.Right)
119
+ ip_pane.send_keys("command -v htb-gui &>/dev/null && htb-gui; ipa")
120
+ updog_pane = ligolo_pane.split(direction=libtmux.pane.PaneDirection.Right)
121
+ updog_pane.send_keys("updog -p 8000 -d ~/staging")
122
+ base_window = session.new_window(window_name=ip)
123
+
124
+ # Initialize panes
125
+ nmap_pane = base_window.active_pane
126
+ base_pane = base_window.split(direction=libtmux.pane.PaneDirection.Right)
127
+
128
+ # Fork process to attach to tmux session while executing additional tasks
129
+ pid = os.fork()
130
+ if pid == 0:
131
+ set_death_signal()
132
+
133
+ # Establish connection with VPN and initialize the base window
134
+ if os.path.isfile(vpn_path):
135
+ verify_connection(ip)
136
+ sleep(1)
137
+ session.select_window(2)
138
+ base_pane.select()
139
+
140
+ # Initialize automation if argument is provided
141
+ if automate:
142
+ os.makedirs(log_dir, exist_ok=True)
143
+ for file in [tmux_pipe_file, debug_file, users_file, creds_file]:
144
+ if os.path.isfile(file):
145
+ os.remove(file)
146
+
147
+ # Clear log file while also making sure it exists
148
+ with open(log_file, 'w'):
149
+ pass
150
+
151
+ # Initialize stdio depending on debug flag
152
+ with open(os.devnull, "wb") as null:
153
+ if debug:
154
+ with open(debug_file, "wb") as log:
155
+ os.dup2(null.fileno(), 0)
156
+ os.dup2(log.fileno(), 1)
157
+ os.dup2(log.fileno(), 2)
158
+ else:
159
+ os.dup2(null.fileno(), 0)
160
+ os.dup2(null.fileno(), 1)
161
+ os.dup2(null.fileno(), 2)
162
+
163
+ # Initialize context
164
+ context = Context(session, nmap_pane, nmap_dir, tmux_pipe_file, log_file, users_file, creds_file, ip)
165
+ if user and password:
166
+ context.add_initial_cred(user.lower(), password=password)
167
+
168
+ # Initialize task logging
169
+ log_pane = base_pane.split(size=20)
170
+ sleep(1)
171
+ log_pane.send_keys(f"clear && tail -n +0 -f {log_file}")
172
+
173
+ # Initialize nmap scanning and automation
174
+ os.remove(tmux_pipe_file)
175
+ nmap_pane.cmd("pipe-pane", f"cat >> {tmux_pipe_file}")
176
+ nmap_pane.send_keys(f"{scan_script_file} {ip}")
177
+ watch_nmap(context)
178
+ elif ip:
179
+ nmap_pane.send_keys(f"{scan_script_file} {ip}")
180
+ os._exit(0)
181
+ else:
182
+ os.execvp("tmux", ["tmux", "attach-session", "-t", session_name])
183
+
184
+ if __name__ == "__main__":
185
+ main()
File without changes
@@ -0,0 +1,93 @@
1
+ import re
2
+ import subprocess
3
+
4
+ from recon_setup.utils.logger import write_log
5
+
6
+ _NEGATIVE = re.compile(r"\[\-\]")
7
+
8
+
9
+ def anonymous_bind(context):
10
+ cmd = ["nxc", "ldap", context.ip, "-u", "", "-p", ""]
11
+ try:
12
+ p = subprocess.run(cmd, capture_output=True, text=True, timeout=8, check=False)
13
+ except Exception as e:
14
+ write_log(context.log_file, f"NetExec ldap subprocess error: {e}", "ERROR")
15
+ return False
16
+ out = p.stdout or ""
17
+ return not _NEGATIVE.search(out)
18
+
19
+
20
+ def enum_smb_shares(context):
21
+ def run_nxc(user, password):
22
+ cmd = f"nxc smb {context.ip} -u {user} "
23
+ if password:
24
+ cmd += f"-p '{password}' -k "
25
+ else:
26
+ cmd += f"-p '' "
27
+ cmd += f"--shares"
28
+ # write_log(context.log_file, f"Running: {cmd}", "INFO")
29
+ try:
30
+ p = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=8, check=False)
31
+ return p.stdout or ""
32
+ except Exception as e:
33
+ write_log(context.log_file, f"Subprocess exception: {e}", "ERROR")
34
+ return ""
35
+
36
+ def parse_shares(raw):
37
+ lines = [ln.rstrip("\n") for ln in raw.splitlines()]
38
+ hdr_idx = None
39
+ for i, ln in enumerate(lines):
40
+ if "Share" in ln and "Permissions" in ln and "Remark" in ln:
41
+ hdr_idx = i
42
+ break
43
+ if hdr_idx is None or hdr_idx + 2 >= len(lines):
44
+ return []
45
+
46
+ header = lines[hdr_idx]
47
+ try:
48
+ c_share = header.index("Share")
49
+ c_perm = header.index("Permissions")
50
+ c_remark = header.index("Remark")
51
+ except ValueError:
52
+ return []
53
+
54
+ shares = []
55
+ for ln in lines[hdr_idx + 2 :]:
56
+ if "SMB" not in ln or not ln.strip() or ln.strip().startswith("-----"):
57
+ continue
58
+ pad = ln + " " * max(0, c_remark + 1 - len(ln))
59
+ name = pad[c_share:c_perm].strip()
60
+ perms = pad[c_perm:c_remark].strip()
61
+ remark = pad[c_remark:].strip()
62
+ if not name or name.startswith("-----"):
63
+ continue
64
+ access = [p.strip() for p in perms.split(",") if p.strip()] if perms else []
65
+ shares.append({"name": name, "remark": remark, "access": access})
66
+
67
+ return shares
68
+
69
+ # 1) user/pass
70
+ if context.creds_exist():
71
+ user, password = context.get_initial_cred()
72
+ if user is not None:
73
+ out = run_nxc(user, password)
74
+ shares = parse_shares(out)
75
+ if shares:
76
+ return shares, "user/pass"
77
+ write_log(context.log_file, "Share enum with provided credentials failed; trying null session", "INFO")
78
+
79
+ # 2) null
80
+ out = run_nxc("''", "")
81
+ shares = parse_shares(out)
82
+ if shares:
83
+ write_log(context.log_file, "Null authentication is enabled for reading shares", "SUCCESS")
84
+ return shares, "null"
85
+
86
+ # 3) guest
87
+ out = run_nxc("a", "")
88
+ shares = parse_shares(out)
89
+ if shares:
90
+ write_log(context.log_file, "Guest authentication is enabled for reading shares", "SUCCESS")
91
+ return shares, "guest"
92
+
93
+ return None, None