diff --git a/.gitmodules b/.gitmodules index 1e1135de..5e3f7b0d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,10 @@ path = pupy/external/LaZagne url = https://github.com/AlessandroZ/LaZagne shallow = false +[submodule "pupy/external/changeme"] + path = pupy/external/changeme + url = https://github.com/AlessandroZ/changeme + shallow = false [submodule "pupy/external/impacket"] path = pupy/external/impacket url = https://github.com/CoreSecurity/impacket diff --git a/pupy/modules/changeme.py b/pupy/modules/changeme.py new file mode 100644 index 00000000..bf80d2bf --- /dev/null +++ b/pupy/modules/changeme.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# Author: AlessandroZ + +from pupylib.PupyModule import * +from pupylib.utils.credentials import Credentials +from netaddr import * +from pupylib.utils.changeme.load_creds import Credentials as changeme_creds +import os + +__class_name__="Changeme" + +ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),"..")) + +@config(cat="creds", compat=["linux", "windows"]) +class Changeme(PupyModule): + """ + Default Credential Scanner + """ + dependencies = { + 'all': [ + 'OpenSSL', + 'ftplib', 'zipfile', 'telnetlib', + '_LWPCookieJar', '_MozillaCookieJar', 'Cookie', 'cookielib', + 'cgi', 'mimetypes', 'email', 'logutils', + 'urllib3', 'requests', + 'xml','_elementtree', 'calendar', 'xml', 'xml.etree', + 'changeme' + ] + } + + + def init_argparse(self): + header = """ + ##################################################### +# _ # +# ___| |__ __ _ _ __ __ _ ___ _ __ ___ ___ # +# / __| '_ \ / _` | '_ \ / _` |/ _ \ '_ ` _ \ / _ \\ # +# | (__| | | | (_| | | | | (_| | __/ | | | | | __/ # +# \___|_| |_|\__,_|_| |_|\__, |\___|_| |_| |_|\___| # +# |___/ # +# # +# Default Credential Scanner # + ##################################################### + """ + + example = ''' +Examples: +>> run changeme -c web --name tomcat --target 192.168.1.10 +''' + + self.arg_parser = PupyArgumentParser(prog="changeme", description=header + self.__doc__, epilog=example) + self.arg_parser.add_argument('--protocol', choices=['ftp', 'http' , 'mssql', 'ssh', 'telnet'], help='Protocol of default creds to scan for', default=None) + self.arg_parser.add_argument('--category', '-c', choices=['webcam', 'web', 'phone', 'printer'], help='Category of default creds to scan for', default=None) + self.arg_parser.add_argument('--name', '-n', type=str, help='Narrow testing to the supplied credential name', default=None) + + # Targets to launch scan + self.arg_parser.add_argument('--target', type=str, help='Subnet or IP to scan') + self.arg_parser.add_argument('--targets', type=str, help='File of targets to scan (IP or IP:PORT)', default=None) + self.arg_parser.add_argument('--port', type=int, help='Custom port to connect', default=None) + self.arg_parser.add_argument('--ssl', action='store_true', help='Use ssl', default=None) + self.arg_parser.add_argument('--creds', type=str, help='File of custom credentials to check (login/password)', default=None) + + # Log and output + self.arg_parser.add_argument('--proxy', '-p', type=str, help='HTTP(S) Proxy', default=None) + self.arg_parser.add_argument('--log', '-l', type=str, help='Write logs to logfile', default=None) + # self.arg_parser.add_argument('--output', '-o', type=str, help='Name of file to write CSV results', default=None) + + # Verbosity + self.arg_parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output') + self.arg_parser.add_argument('--debug', '-d', action='store_true', help='Debug output') + + # Advanced options + self.arg_parser.add_argument('--timeout', type=int, help='Timeout in seconds for a request, default=10', default=10) + self.arg_parser.add_argument('--useragent', '-ua', type=str, help="User agent string to use") + self.arg_parser.add_argument('--delay', '-dl', type=int, help="Specify a delay in milliseconds to avoid 429 status codes default=500", default=500) + + def run(self, args): + + proxy = None + if args.proxy and re.match('^https?://[0-9\.]+:[0-9]{1,5}$', args.proxy): + proxy = { + 'http': args.proxy, + 'https': args.proxy + } + elif args.proxy: + print '[!] Invalid proxy, must be http(s)://x.x.x.x:8080' + return + + custom_cred = None + if args.creds: + custom_cred = self.get_custom_creds(args.creds) + if not custom_cred: + return + + targets_list = self.build_targets_list(target=args.target, file=args.targets) + if targets_list: + # Load credential locally from filesystem + root = os.path.join(ROOT, "external", "changeme") + creds = changeme_creds().load_creds(root, args.protocol, args.name, args.category) + # Run main function + pwd_found = self.client.conn.modules["changeme.core"].run_changeme( + protocol=args.protocol, + category=args.category, + name=args.name, + targets=targets_list, + port=args.port, + ssl=args.ssl, + proxy=proxy, + log=args.log, + verbose=args.verbose, + debug=args.debug, + timeout=args.timeout, + useragent=args.useragent, + delay=args.delay, + creds=creds, + custom_creds=custom_cred + ) + + if pwd_found: + db = Credentials() + + clean_creds = [] + for pwd in pwd_found: + self.success('%s' % pwd['name']) + self.success('URL: %s' % pwd['url']) + self.success('%s/%s' % (pwd['username'], pwd['password'])) + + clean_cred = {} + clean_cred['Category'] = '%s' % pwd['name'] + clean_cred['uid']=self.client.short_name() + clean_cred['CredType'] = 'plaintext' + clean_cred['URL'] = pwd['url'] + clean_cred['Login'] = pwd['username'] + clean_cred['Password'] = pwd['password'] + clean_creds.append(clean_cred) + + print + + try: + db.add(clean_creds) + self.success("Passwords stored on the database") + except Exception, e: + print e + else: + self.warning('passwords not found') + else: + self.error('target not defined') + + def get_custom_creds(self, file): + if not os.path.isfile(file): + self.error("file %s not found" % file) + return False + + custom_creds = list() + with open(file, 'r') as fin: + for x in fin.readlines(): + username, password = x.strip('\n').split('/', 1) + custom_creds.append( + { + 'username' : username, + 'password' : password + } + ) + return custom_creds + + + def build_targets_list(self, target, file): + targets_list = list() + if target: + for ip in IPNetwork(target).iter_hosts(): + targets_list.append(ip) + + if file: + if not os.path.isfile(file): + self.error("file %s not found" % file) + return False + with open(file, 'r') as fin: + targets_list = [x.strip('\n') for x in fin.readlines()] + + return targets_list \ No newline at end of file diff --git a/pupy/packages/all/changeme b/pupy/packages/all/changeme new file mode 120000 index 00000000..49ad3290 --- /dev/null +++ b/pupy/packages/all/changeme @@ -0,0 +1 @@ +../../external/changeme/changeme \ No newline at end of file diff --git a/pupy/pupylib/utils/changeme/__init__.py b/pupy/pupylib/utils/changeme/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pupy/pupylib/utils/changeme/load_creds.py b/pupy/pupylib/utils/changeme/load_creds.py new file mode 120000 index 00000000..0b598fc0 --- /dev/null +++ b/pupy/pupylib/utils/changeme/load_creds.py @@ -0,0 +1 @@ +../../../external/changeme/load_creds.py \ No newline at end of file diff --git a/pupy/pupylib/utils/changeme/schema.py b/pupy/pupylib/utils/changeme/schema.py new file mode 120000 index 00000000..0da07818 --- /dev/null +++ b/pupy/pupylib/utils/changeme/schema.py @@ -0,0 +1 @@ +../../../external/changeme/schema.py \ No newline at end of file