Skip to content

Commit bbacf19

Browse files
p0rtL6p0rtL6
andauthored
[enhancement] Major refactoring (#92)
* Added from_string method to TestResult enum * split task preparation and execution into seperate functions * Update debug + verbose printing * Refactor reporter, standardize print formatting, and add logging * Fix test result parsing * Fix merge error * Add enhancement in issue #75 --------- Co-authored-by: p0rtL6 <p0rtl.pue@gmail.com>
1 parent bb39f6f commit bbacf19

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+788
-908
lines changed

coercer/__main__.py

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,7 @@
1010
import sys
1111
from sectools.network.domains import is_fqdn
1212
from sectools.network.ip import is_ipv4_cidr, is_ipv4_addr, is_ipv6_addr, expand_cidr, expand_port_range
13-
from coercer.core.Reporter import Reporter
14-
from coercer.structures.Credentials import Credentials
15-
from coercer.core.modes.scan import action_scan
16-
from coercer.core.modes.coerce import action_coerce
17-
from coercer.core.modes.fuzz import action_fuzz
18-
from coercer.core.loader import find_and_load_coerce_methods
19-
from coercer.network.smb import try_login
20-
from coercer.network.utils import can_listen_on_port
21-
13+
from coercer.core.Reporter import create_reporter
2214

2315
VERSION = "2.4.3"
2416

@@ -35,10 +27,13 @@ def parseArgs():
3527
parser = argparse.ArgumentParser(add_help=True, description="Automatic windows authentication coercer using various methods.")
3628
parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Verbose mode (default: False)")
3729
parser.add_argument("--debug", default=False, action="store_true", help="Debug mode (default: False)")
30+
parser.add_argument("--disable-escape-codes", default=False, action="store_true", help="Disable ANSI escape codes")
3831

3932
# Creating the "scan" subparser ==============================================================================================================
4033
mode_scan = argparse.ArgumentParser(add_help=False)
4134
mode_scan.add_argument("-v", "--verbose", default=False, action="store_true", help="Verbose mode (default: False)")
35+
mode_scan.add_argument("--debug", default=False, action="store_true", help="Debug mode (default: False)")
36+
mode_scan.add_argument("--disable-escape-codes", default=False, action="store_true", help="Disable ANSI escape codes")
4237
# Advanced configuration
4338
mode_scan_advanced_config = mode_scan.add_argument_group("Advanced options")
4439
mode_scan_advanced_config.add_argument("--export-json", default=None, type=str, help="Export results to specified JSON file.")
@@ -76,10 +71,16 @@ def parseArgs():
7671
mode_scan_targets_listener.add_argument("-i", "--interface", default=None, help="Interface to listen on incoming authentications.")
7772
mode_scan_targets_listener.add_argument("-I", "--ip-address", default=None, help="IP address to listen on incoming authentications.")
7873
mode_scan_targets_listener.add_argument("--path-ip", default=None, help="IP address to use when generating exploit paths.")
74+
# Logging
75+
mode_scan_logging = mode_scan.add_argument_group("Logging")
76+
mode_scan_logging.add_argument("--minimum-log-level", default=0, help="Minimum logging level (integer).")
77+
mode_scan_logging.add_argument("--log-file", default=None, help="Path for the file to log to (enables logging).")
7978

8079
# Creating the "fuzz" subparser ==============================================================================================================
8180
mode_fuzz = argparse.ArgumentParser(add_help=False)
8281
mode_fuzz.add_argument("-v", "--verbose", default=False, action="store_true", help="Verbose mode (default: False)")
82+
mode_fuzz.add_argument("--debug", default=False, action="store_true", help="Debug mode (default: False)")
83+
mode_fuzz.add_argument("--disable-escape-codes", default=False, action="store_true", help="Disable ANSI escape codes")
8384
# Advanced configuration
8485
mode_fuzz_advanced_config = mode_fuzz.add_argument_group("Advanced configuration")
8586
mode_fuzz_advanced_config.add_argument("--export-json", default=None, type=str, help="Export results to specified JSON file.")
@@ -116,10 +117,16 @@ def parseArgs():
116117
mode_fuzz_targets_listener.add_argument("-i", "--interface", default=None, help="Interface to listen on incoming authentications.")
117118
mode_fuzz_targets_listener.add_argument("-I", "--ip-address", default=None, help="IP address to listen on incoming authentications.")
118119
mode_fuzz_targets_listener.add_argument("--path-ip", default=None, help="IP address to use when generating exploit paths.")
120+
# Logging
121+
mode_fuzz_logging = mode_fuzz.add_argument_group("Logging")
122+
mode_fuzz_logging.add_argument("--minimum-log-level", default=0, help="Minimum logging level (integer).")
123+
mode_fuzz_logging.add_argument("--log-file", default=None, help="Path for the file to log to (enables logging).")
119124

120125
# Creating the "coerce" subparser ==============================================================================================================
121126
mode_coerce = argparse.ArgumentParser(add_help=False)
122127
mode_coerce.add_argument("-v", "--verbose", default=False, action="store_true", help="Verbose mode (default: False)")
128+
mode_coerce.add_argument("--debug", default=False, action="store_true", help="Debug mode (default: False)")
129+
mode_coerce.add_argument("--disable-escape-codes", default=False, action="store_true", help="Disable ANSI escape codes")
123130
# Advanced configuration
124131
mode_coerce_advanced_config = mode_coerce.add_argument_group("Advanced configuration")
125132
mode_coerce_advanced_config.add_argument("--delay", default=None, type=int, help="Delay between attempts (in seconds)")
@@ -150,7 +157,11 @@ def parseArgs():
150157
# Listener
151158
listener_group = mode_coerce.add_argument_group("Listener")
152159
listener_group.add_argument("-l", "--listener-ip", required=True, type=str, help="IP address or hostname of the listener machine")
153-
160+
# Logging
161+
mode_coerce_logging = mode_coerce.add_argument_group("Logging")
162+
mode_coerce_logging.add_argument("--minimum-log-level", default=0, help="Minimum logging level (integer).")
163+
mode_coerce_logging.add_argument("--log-file", default=None, help="Path for the file to log to (enables logging).")
164+
154165
# Adding the subparsers to the base parser
155166
subparsers = parser.add_subparsers(help="Mode", dest="mode", required=True)
156167
mode_scan_parser = subparsers.add_parser("scan", parents=[mode_scan], help="Tests known methods with known working paths on all methods, and report when an authentication is received.")
@@ -171,12 +182,20 @@ def parseArgs():
171182

172183

173184
def main():
174-
available_methods = find_and_load_coerce_methods()
175-
176185
lmhash, nthash, options = parseArgs()
186+
create_reporter(options, options.verbose)
177187

178-
reporter = Reporter(verbose=options.verbose, options=options)
188+
from coercer.core.Reporter import reporter
189+
from coercer.structures.Credentials import Credentials
190+
from coercer.core.modes.scan import action_scan
191+
from coercer.core.modes.coerce import action_coerce
192+
from coercer.core.modes.fuzz import action_fuzz
193+
from coercer.network.smb import try_login
194+
from coercer.network.utils import can_listen_on_port
195+
from coercer.core.loader import find_and_load_coerce_methods
179196

197+
available_methods = find_and_load_coerce_methods()
198+
180199
# Parsing targets
181200
targets = []
182201
if options.target_ip is not None:
@@ -186,9 +205,9 @@ def main():
186205
f = open(options.targets_file, 'r')
187206
targets = sorted(list(set([line.strip() for line in f.readlines()])))
188207
f.close()
189-
reporter.print_verbose("Loaded %d targets." % len(targets))
208+
reporter.print_info("Loaded %d targets." % len(targets))
190209
else:
191-
print("[!] Could not open targets file '%s'." % options.targets_file)
210+
reporter.print_error("Could not open targets file '%s'." % options.targets_file)
192211
sys.exit(0)
193212

194213
# Sort uniq on targets list
@@ -211,8 +230,7 @@ def main():
211230
target = urllib.parse.urlparse(target).netloc
212231
final_targets.append(target)
213232
else:
214-
if options.debug:
215-
print("[debug] Target '%s' was not added." % target)
233+
reporter.print_warn("Target '%s' was not added." % target, debug=True)
216234

217235
# Sort
218236
targets = sorted(list(set(final_targets)))
@@ -233,26 +251,26 @@ def main():
233251
for target in targets:
234252
reporter.print_info("Scanning target %s" % target)
235253
# Checking credentials if any
236-
if not "msrpc" in options.filter_transport_name or try_login(credentials, target, verbose=options.verbose):
254+
if not "msrpc" in options.filter_transport_name or try_login(credentials, target):
237255
try:
238256
# Starting action
239-
action_coerce(target, available_methods, options, credentials, reporter)
257+
action_coerce(target, available_methods, options, credentials)
240258
except Exception as e:
241259
reporter.print_warn("An unexpected error occurred: %s" % e)
242260
elif options.mode == "scan":
243261
reporter.print_info("Starting scan mode")
244262
if credentials.is_anonymous():
245263
reporter.print_info("No credentials provided, trying to connect with a NULL session.")
246264
if not can_listen_on_port("0.0.0.0", 445):
247-
reporter.print_warn("Cannot listen on port tcp/%d. Are you root or are other servers running?" % 445)
265+
reporter.print_error("Cannot listen on port tcp/%d. Are you root or are other servers running?" % 445)
248266
else:
249267
for target in targets:
250268
reporter.print_info("Scanning target %s" % target)
251269
# Checking credentials if any
252-
if not "msrpc" in options.filter_transport_name or try_login(credentials, target, verbose=options.verbose):
270+
if not "msrpc" in options.filter_transport_name or try_login(credentials, target):
253271
try:
254272
# Starting action
255-
action_scan(target, available_methods, options, credentials, reporter)
273+
action_scan(target, available_methods, options, credentials)
256274
except Exception as e:
257275
reporter.print_warn("An unexpected error occurred: %s" % e)
258276
# Reporting results
@@ -268,15 +286,15 @@ def main():
268286
if credentials.is_anonymous():
269287
reporter.print_info("No credentials provided, trying to connect with a NULL session.")
270288
if not can_listen_on_port("0.0.0.0", 445):
271-
reporter.print_warn("Cannot listen on port tcp/%d. Are you root or are other servers running?" % 445)
289+
reporter.print_error("Cannot listen on port tcp/%d. Are you root or are other servers running?" % 445)
272290
else:
273291
for target in targets:
274292
reporter.print_info("Fuzzing target %s" % target)
275293
# Checking credentials if any
276-
if not "msrpc" in options.filter_transport_name or try_login(credentials, target, verbose=options.verbose):
294+
if not "msrpc" in options.filter_transport_name or try_login(credentials, target):
277295
try:
278296
# Starting action
279-
action_fuzz(target, available_methods, options, credentials, reporter)
297+
action_fuzz(target, available_methods, options, credentials)
280298
except Exception as e:
281299
reporter.print_warn("An unexpected error occurred: %s" % e)
282300
# Reporting results
@@ -287,7 +305,7 @@ def main():
287305
if options.export_sqlite is not None:
288306
reporter.exportSQLITE(options.export_sqlite)
289307

290-
print("[+] All done! Bye Bye!")
308+
reporter.print_ok("All done! Bye Bye!")
291309

292310
if __name__ == '__main__':
293311
main()

0 commit comments

Comments
 (0)