Exemplo n.º 1
0
class Module(ToolTemplate):
    name = "Subfinder"
    binary_name = "subfinder"

    def __init__(self, db):
        self.db = db
        self.BaseDomains = BaseDomainRepository(db, self.name)
        self.Domains = DomainRepository(db, self.name)
        self.IPs = IPRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()
        self.options.add_argument("-d",
                                  "--domain",
                                  help="Domain to run subfinder against.")
        self.options.add_argument(
            "-dL",
            "--domain_list",
            help="Read in a list of domains within the given file.",
        )
        self.options.add_argument(
            "-i",
            "--db_domains",
            help="Import the domains from the database.",
            action="store_true",
        )

        self.options.add_argument("--rescan",
                                  help="Overwrite files without asking",
                                  action="store_true")

    def get_targets(self, args):
        targets = []
        outpath = ""
        if args.output_path[0] == "/":
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path[1:])
        else:
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path)
        if not os.path.exists(output_path):
            os.makedirs(output_path)

        if args.domain:
            out_file = os.path.join(outpath,
                                    "{}.subfinder".format(args.domain))
            targets.append({
                "target": args.domain,
                "output": os.path.join(output_path, out_file)
            })

        if args.db_domains:
            if args.rescan:
                domains = self.BaseDomains.all(scope_type="passive")
            else:
                domains = self.BaseDomains.all(tool=self.name,
                                               scope_type="passive")
            for d in domains:
                out_file = os.path.join(outpath,
                                        "{}.subfinder".format(d.domain))
                targets.append({
                    "target": d.domain,
                    "output": os.path.join(output_path, out_file)
                })

        elif args.domain_list:
            domains = io.open(args.domain_list,
                              encoding="utf-8").read().split("\n")
            for d in domains:
                if d:
                    targets.append({
                        "target":
                        d,
                        "output":
                        os.path.join(output_path, "{}.subfinder".format(d)),
                    })

        return targets

    def build_cmd(self, args):
        if args.binary:
            cmd = "{} ".format(args.binary)
        else:
            cmd = "{} ".format(self.binary_name)
        cmd = "{} -o {} -d {}".format(cmd, "{output}", "{target}")
        return cmd

    def process_output(self, targets):
        for target in targets:
            try:
                with io.open(target["output"], encoding="utf-8") as fd:
                    for line in fd:
                        domain = line.strip()
                        if domain[0] == '.':
                            domain = domain[1:]
                        ips = get_domain_ip.run(domain)
                        ip_obj = None
                        _, dom = self.Domains.find_or_create(domain=domain)
                        if ips:
                            for ip in ips:
                                _, ip_obj = self.IPs.find_or_create(
                                    ip_address=ip)
                                if ip_obj:
                                    dom.ip_addresses.append(ip_obj)
                            dom.save()
            except FileNotFoundError:
                display_error("File doesn't exist for {}".format(
                    target["output"]))
        self.BaseDomains.commit()
        self.IPs.commit()

    def post_run(self, args):
        # Remove the temporary db file if it was created.
        if getattr(self, "db_domain_file", None):
            try:
                os.unlink(self.db_domain_file)
            except IOError as e:
                print("Failed to remove the Subfinder db temp file: '{}'.".
                      format(e))

    def __get_tempfile(self, domain=None, args=None):
        # Create a temporary file and place all of the current database domains within the file.
        from tempfile import NamedTemporaryFile

        with NamedTemporaryFile(delete=False) as fd:
            if domain:
                fd.write("{}\n".format(domain).encode("utf-8"))
            else:
                # Go through the database and grab the domains adding them to the file.
                if args.rescan:
                    domains = self.BaseDomains.all(passive_scope=True)
                else:
                    domains = self.BaseDomains.all(tool=self.name,
                                                   passive_scope=True)
                if domains:
                    for domain in domains:
                        fd.write("{}\n".format(domain.domain).encode("utf-8"))
                else:
                    return None
        return fd.name
Exemplo n.º 2
0
class Module(ToolTemplate):
    '''
    This module uses the Ruby version of Aquatone. You can usually install it with "gem install aquatone"

    '''
    name = "Aquatone Takeover"
    binary_name = "aquatone-takeover"

    def __init__(self, db):
        self.db = db
        self.Domain = DomainRepository(db, self.name)
        self.BaseDomain = BaseDomainRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()

        self.options.add_argument(
            "-i",
            "--import_database",
            help="Import domains from database",
            action="store_true",
        )
        self.options.add_argument(
            "-r",
            "--rescan",
            help="Run aquatone on hosts that have already been processed.",
            action="store_true",
        )
        self.options.set_defaults(timeout=None)

    def get_targets(self, args):
        """
        This module is used to build out a target list and output file list, depending on the arguments. Should return a
        list in the format [(target, output), (target, output), etc, etc]
        """
        targets = []

        if args.import_database:
            if args.rescan:
                all_domains = self.BaseDomain.all(scope_type="passive")
            else:
                all_domains = self.BaseDomain.all(tool=self.name,
                                                  scope_type="passive")
            for d in all_domains:
                # We need to find all of the http/https ports and create the json file.
                output_path = os.path.join(
                    self.base_config["PROJECT"]["base_path"],
                    "output",
                    "aquatone",
                    d.domain,
                )
                if not os.path.exists(output_path):
                    os.makedirs(output_path)

                hosts_j = {}
                hosts = []
                open_ports = []
                urls = []

                targets.append(d.domain)

                for s in d.subdomains:
                    name = s.domain

                    for ip in s.ip_addresses:
                        hosts_j[name] = ip.ip_address
                        port_list = []
                        for p in ip.ports:

                            if "http" in p.service_name:
                                hosts.append("{}.{}".format(
                                    name, ip.ip_address))

                                port_list.append(p.port_number)
                                urls.append("{}://{}:{}/".format(
                                    p.service_name, name, p.port_number))
                                urls.append("{}://{}:{}/".format(
                                    p.service_name, ip.ip_address,
                                    p.port_number))
                        if port_list:
                            open_ports.append("{},{}".format(
                                ip.ip_address,
                                ",".join([str(o) for o in port_list])))

                open(os.path.join(output_path, "hosts.txt"),
                     "w").write("\n".join(list(set(hosts))))
                open(os.path.join(output_path, "urls.txt"),
                     "w").write("\n".join(list(set(urls))))
                open(os.path.join(output_path, "open_ports.txt"),
                     "w").write("\n".join(list(set(open_ports))))
                open(os.path.join(output_path, "hosts.json"),
                     "w").write(json.dumps(hosts_j))
        else:
            display_error("You need to supply domain(s).")

        res = []
        for t in targets:
            res.append({"target": t})

        return res

    def build_cmd(self, args):
        """
        Create the actual command that will be executed. Use {target} and {output} as placeholders.
        """
        cmd = self.binary + " -d {target} "

        if args.tool_args:
            cmd += args.tool_args

        return cmd

    def pre_run(self, args):
        output_path = os.path.join(self.base_config["PROJECT"]["base_path"],
                                   "output")

        self.orig_home = os.environ["HOME"]

        os.environ["HOME"] = output_path

    def process_output(self, cmds):
        """
        Process the output generated by the earlier commands.
        """

        for cmd in cmds:

            created, domain = self.BaseDomain.find_or_create(
                domain=cmd['target'])
            domain.set_tool(self.name)

        self.BaseDomain.commit()

    def post_run(self, args):

        display("Potential takeovers are stored in {}".format(
            os.environ["HOME"]))
        os.environ["HOME"] = self.orig_home
Exemplo n.º 3
0
class Module(ToolTemplate):

    name = "Whois"
    binary_name = "whois"

    def __init__(self, db):
        self.db = db
        self.BaseDomain = BaseDomainRepository(db, self.name)
        self.ScopeCidr = ScopeCIDRRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()

        self.options.add_argument("-d", "--domain", help="Domain to query")
        self.options.add_argument("-c", "--cidr", help="CIDR to query")

        self.options.add_argument(
            "-s",
            "--rescan",
            help="Rescan domains that have already been scanned",
            action="store_true",
        )
        self.options.add_argument(
            "-a",
            "--all_data",
            help="Scan all data in database, regardless of scope",
            action="store_true",
        )
        self.options.add_argument(
            "-i",
            "--import_database",
            help="Run WHOIS on all domains and CIDRs in database",
            action="store_true",
        )

    def get_targets(self, args):

        targets = []
        if args.domain:

            targets.append({"domain": args.domain, "cidr": ""})

        elif args.cidr:

            targets.append({"domain": "", "cidr": args.cidr.split("/")[0]})

        elif args.import_database:

            if args.all_data:
                scope_type = ""
            else:
                scope_type = "passive"
            if args.rescan:
                domains = self.BaseDomain.all(scope_type=scope_type)
                cidrs = self.ScopeCidr.all()
            else:
                domains = self.BaseDomain.all(scope_type=scope_type,
                                              tool=self.name)
                cidrs = self.ScopeCidr.all(tool=self.name)

            for domain in domains:
                targets.append({"domain": domain.domain, "cidr": ""})
            for cidr in cidrs:
                targets.append({"domain": "", "cidr": cidr.cidr.split("/")[0]})

        if args.output_path[0] == "/":
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path[1:])
        else:
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path)

        if not os.path.exists(output_path):
            os.makedirs(output_path)

        for t in targets:
            t["output"] = os.path.join(output_path, t["domain"] + t["cidr"])

        return targets

    def build_cmd(self, args):

        if not args.tool_args:
            args.tool_args = ""
        cmd = (
            'bash -c "' + self.binary  # noqa: W503
            + " {domain}{cidr} "  # noqa: W503
            + args.tool_args  # noqa: W503
            + '> {output}" '  # noqa: W503
        )

        return cmd

    def process_output(self, cmds):

        display("Importing data to database")

        for cmd in cmds:
            if cmd["cidr"]:
                _, cidr = self.ScopeCidr.find_or_create(cidr=cmd["cidr"])
                cidr.meta["whois"] = open(cmd["output"]).read()
                display(cidr.meta["whois"])
                cidr.update()

            elif cmd["domain"]:
                _, domain = self.BaseDomain.find_or_create(
                    domain=cmd["domain"])
                domain.meta["whois"] = open(cmd["output"]).read()
                display(domain.meta["whois"])
                domain.update()

        self.BaseDomain.commit()
Exemplo n.º 4
0
class Module(ModuleTemplate):

    name = "LinkedInt"
    binary_name = "linkedint.py"

    def __init__(self, db):
        self.db = db
        self.BaseDomain = BaseDomainRepository(db, self.name)
        self.User = UserRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()

        self.options.add_argument(
            "-b",
            "--binary",
            help="Path to binary for LinkedInt",
            default=self.binary_name,
        )
        self.options.add_argument("-d",
                                  "--domain",
                                  help="Domain to add onto email")
        self.options.add_argument("-c",
                                  "--company_id",
                                  help="Company ID to search")
        self.options.add_argument("-C",
                                  "--restrict",
                                  help="Restrict to company filter",
                                  action="store_true")
        self.options.add_argument(
            "-e",
            "--email_format",
            help=
            "Format for emails: auto,full,firstlast,firstmlast,flast,first.last,fmlast,lastfirst, default is auto",
            default="auto",
        )
        self.options.add_argument("-k",
                                  "--keywords",
                                  help="Keywords to search for")
        self.options.add_argument(
            "-o",
            "--output_path",
            help=
            "Path which will contain program output (relative to base_path in config",
            default=self.name,
        )
        self.options.add_argument(
            "-s",
            "--rescan",
            help="Rescan domains that have already been scanned",
            action="store_true",
        )
        self.options.add_argument(
            "--smart_shuffle",
            help=
            "Provide a list of keywords. The tool will run once with all of the keywords, then run again excluding all of the keywords. This is useful for bypassing the 1k limit. Keywords must be comma separated.",
        )
        self.options.add_argument(
            "--top",
            help="Use the top X keywords from the job titles for smart shuffle"
        )
        self.options.add_argument(
            "--auto_keyword",
            help=
            "Generate a list of keywords from titles already discovered, and search repeatedly using the top x number of results (specified with --top).",
            action="store_true")

    def run(self, args):
        # pdb.set_trace()
        if not args.binary:
            self.binary = which.run("LinkedInt.py")

        else:
            self.binary = which.run(args.binary)

        if not self.binary:
            display_error(
                "LinkedInt binary not found. Please explicitly provide path with --binary"
            )

        if args.domain:
            created, domain = self.BaseDomain.find_or_create(
                domain=args.domain)
            if args.top:
                titles = [
                    user.job_title.split(" at ")[0] for user in domain.users
                    if user.job_title
                ]
                words = []
                for t in titles:
                    words += [w.lower() for w in get_words(t)]

                word_count = Counter(words).most_common()

                display("Using the top %s words:" % args.top)
                res = []
                for w in word_count[:int(args.top)]:
                    display("\t{}\t{}".format(w[0], w[1]))
                    res.append(w[0])

                # pdb.set_trace()
                args.smart_shuffle = ",".join(res)

            if args.auto_keyword:
                if not args.top:
                    display_error(
                        "You must specify the top number of keywords using --top"
                    )
                else:
                    if os.path.isfile('/tmp/armory_linkedinsearchqueries'):
                        blacklist = open('/tmp/armory_linkedinsearchqueries'
                                         ).read().split('\n')
                    else:
                        blacklist = []
                    bfile = open('/tmp/armory_linkedinsearchqueries', 'a')
                    for w in args.smart_shuffle.split(','):

                        if w not in blacklist:

                            args.keywords = w
                            self.process_domain(domain, args)
                            self.BaseDomain.commit()
                            bfile.write('{}\n'.format(w))
                        else:
                            display(
                                "Skipped {} due to it already being searched.".
                                format(w))
                    bfile.close()
            elif args.smart_shuffle:
                args.keywords = " OR ".join(
                    ['"{}"'.format(i) for i in args.smart_shuffle.split(",")])
                self.process_domain(domain, args)
                self.BaseDomain.commit()
                args.keywords = " AND ".join(
                    ['-"{}"'.format(i) for i in args.smart_shuffle.split(",")])
                self.process_domain(domain, args)
                self.BaseDomain.commit()
            else:
                self.process_domain(domain, args)
                self.BaseDomain.commit()

            self.BaseDomain.commit()

    def process_domain(self, domain_obj, args):

        domain = domain_obj.domain

        if args.output_path[0] == "/":
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path[1:])
        else:
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path)

        if not os.path.exists(output_path):
            os.makedirs(output_path)

        output_path = os.path.join(output_path,
                                   "%s-linkedint" % domain.replace(".", "_"))

        command_args = " -o %s" % output_path

        command_args += " -e %s" % domain
        if args.keywords:
            command_args += " -u '%s'" % args.keywords

        if args.company_id:
            command_args += " -i %s " % args.company_id

        if args.restrict:
            command_args += " -c "
        # if args.threads:
        #     command_args += " -t " + args.threads
        if args.email_format:
            command_args += " -f " + args.email_format

        current_dir = os.getcwd()

        new_dir = "/".join(self.binary.split("/")[:-1])

        os.chdir(new_dir)

        cmd = shlex.split("python2 " + self.binary + command_args)
        print("Executing: %s" % " ".join(cmd))

        subprocess.Popen(cmd).wait()

        os.chdir(current_dir)
        count = 0
        with open(output_path + ".csv") as csvfile:
            csvreader = csv.reader(csvfile, delimiter=",", quotechar='"')

            for row in csvreader:
                count += 1

                created, user = self.User.find_or_create(
                    email=remove_binary(row[3]))

                user.first_name = remove_binary(row[0])
                user.last_name = remove_binary(row[1]).split(',')[0]
                user.job_title = remove_binary(row[4])
                user.location = remove_binary(row[5])

                if created:
                    user.domain = domain_obj

                    print("New user: %s %s" %
                          (remove_binary(row[0]), remove_binary(row[1])))

                user.update()

        print("%s found and imported" % count)
        self.User.commit()
Exemplo n.º 5
0
class Module(ToolTemplate):

    name = "TheHarvester"
    binary_name = "theharvester"

    def __init__(self, db):
        self.db = db
        self.BaseDomain = BaseDomainRepository(db, self.name)
        self.Domain = DomainRepository(db, self.name)
        self.User = UserRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()

        self.options.add_argument("-d", "--domain", help="Domain to harvest")
        self.options.add_argument("-f",
                                  "--file",
                                  help="Import domains from file")
        self.options.add_argument(
            "-i",
            "--import_database",
            help="Import domains from database",
            action="store_true",
        )
        self.options.add_argument(
            "-s",
            "--rescan",
            help="Rescan domains that have already been scanned",
            action="store_true",
        )

    def get_targets(self, args):

        targets = []
        if args.domain:

            targets.append({"target": args.domain})

        elif args.file:
            domains = open(args.file).read().split("\n")
            for d in domains:
                if d:
                    created, domain = self.BaseDomain.find_or_create(domain=d)
                    targets.append({"target": domain.domain})

        elif args.import_database:
            if args.rescan:
                domains = self.BaseDomain.all(scope_type="passive")
            else:
                domains = self.BaseDomain.all(tool=self.name,
                                              scope_type="passive")
            for d in domains:

                targets.append({"target": d.domain})

        if args.output_path[0] == "/":
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path[1:])
        else:
            output_path = os.path.join(
                self.base_config["PROJECT"]["base_path"], args.output_path)

        if not os.path.exists(output_path):
            os.makedirs(output_path)

        for t in targets:
            t["output"] = os.path.join(
                output_path, "%s-theharvester" % t["target"].replace(".", "_"))

        return targets

    def build_cmd(self, args):

        cmd = self.binary + " -f {output} -b default -d {target} "

        if args.tool_args:
            cmd += args.tool_args

        return cmd

    def process_output(self, cmds):

        for cmd in cmds:

            try:
                data = xmltodict.parse(open(cmd["output"] + ".xml").read())
            except Exception as e:
                # display_error("Error with {}: {}".format(cmd["output"], e))
                data = None

            if data:

                if data["theHarvester"].get("email", False):
                    if type(data["theHarvester"]["email"]) == list:
                        emails = data["theHarvester"]["email"]
                    else:
                        emails = [data["theHarvester"]["email"]]
                    for e in emails:
                        display("Processing E-mail: {}".format(e))
                        created, user = self.User.find_or_create(email=e)

                        _, domain = self.BaseDomain.find_or_create(
                            domain=e.split("@")[1])
                        user.domain = domain
                        user.update()

                        if created:
                            display_new("New email: %s" % e)
                if data["theHarvester"].get("host", False):
                    if type(data["theHarvester"]["host"]) == list:
                        hosts = data["theHarvester"]["host"]
                    else:
                        hosts = [data["theHarvester"]["host"]]

                    for d in hosts:

                        created, domain = self.Domain.find_or_create(
                            domain=d["hostname"])

                if data["theHarvester"].get("vhost", False):
                    if type(data["theHarvester"]["vhost"]) == list:
                        hosts = data["theHarvester"]["vhost"]
                    else:
                        hosts = [data["theHarvester"]["vhost"]]

                    for d in hosts:

                        created, domain = self.Domain.find_or_create(
                            domain=d["hostname"])
        self.BaseDomain.commit()
Exemplo n.º 6
0
class Module(ModuleTemplate):
    """
    Ingests domains and IPs. Domains get ip info and cidr info, and IPs get
    CIDR info.

    """

    name = "Ingestor"

    def __init__(self, db):
        self.db = db
        self.BaseDomain = BaseDomainRepository(db, self.name)
        self.Domain = DomainRepository(db, self.name)
        self.IPAddress = IPRepository(db, self.name)
        self.CIDR = CIDRRepository(db, self.name)
        self.ScopeCIDR = ScopeCIDRRepository(db, self.name)

    def set_options(self):
        super(Module, self).set_options()

        self.options.add_argument(
            "-d",
            "--import_domains",
            help="Either domain to import or file containing domains to import. One per line",
        )
        self.options.add_argument(
            "-i",
            "--import_ips",
            help="Either IP/range to import or file containing IPs and ranges, one per line.",
        )
        self.options.add_argument(
            "-a",
            "--active",
            help="Set scoping on imported data as active",
            action="store_true",
        )
        self.options.add_argument(
            "-p",
            "--passive",
            help="Set scoping on imported data as passive",
            action="store_true",
        )
        self.options.add_argument(
            "-sc",
            "--scope_cidrs",
            help="Cycle through out of scope networks and decide if you want to add them in scope",
            action="store_true",
        )
        self.options.add_argument(
            "-sb",
            "--scope_base_domains",
            help="Cycle through out of scope base domains and decide if you want to add them in scope",
            action="store_true",
        )
        self.options.add_argument("--descope", help="Descope an IP, domain, or CIDR")
        self.options.add_argument(
            "-Ii",
            "--import_database_ips",
            help="Import IPs from database",
            action="store_true",
        )
        self.options.add_argument(
            "--force",
            help="Force processing again, even if already processed",
            action="store_true",
        )

    def run(self, args):

        self.in_scope = args.active
        self.passive_scope = args.passive

        if args.descope:
            if "/" in args.descope:
                self.descope_cidr(args.descope)
            elif check_string(args.descope):
                pass

            else:
                self.descope_ip(args.descope)

                # Check if in ScopeCIDR and remove if found

        if args.import_ips:
            try:
                ips = open(args.import_ips)
                for line in ips:

                    if line.strip():
                        if "/" in line or "-" in line:
                            self.process_cidr(line)

                        else:
                            self.process_ip(line.strip(), force_scope=True)
                        self.Domain.commit()
            except IOError:

                if "/" in args.import_ips or "-" in args.import_ips:
                    self.process_cidr(args.import_ips)

                else:
                    self.process_ip(args.import_ips.strip(), force_scope=True)
                self.Domain.commit()

        if args.import_domains:
            try:
                domains = open(args.import_domains)
                for line in domains:
                    if line.strip():
                        self.process_domain(line.strip())
                        self.Domain.commit()
            except IOError:
                self.process_domain(args.import_domains.strip())
                self.Domain.commit()

        if args.scope_base_domains:
            base_domains = self.BaseDomain.all(in_scope=False, passive_scope=False)

            for bd in base_domains:
                self.reclassify_domain(bd)

            self.BaseDomain.commit()

    def get_domain_ips(self, domain):
        ips = []
        try:
            answers = dns.resolver.query(domain, "A")
            for a in answers:
                ips.append(a.address)
            return ips
        except Exception:
            return []

    def process_domain(self, domain_str):

        created, domain = self.Domain.find_or_create(
            only_tool=True,
            domain=domain_str,
            in_scope=self.in_scope,
            passive_scope=self.passive_scope,
        )
        if not created:
            if (
                domain.in_scope != self.in_scope
                or domain.passive_scope != self.passive_scope  # noqa: W503
            ):
                display(
                    "Domain %s already exists with different scoping. Updating to Active Scope: %s Passive Scope: %s"
                    % (domain_str, self.in_scope, self.passive_scope)
                )

                domain.in_scope = self.in_scope
                domain.passive_scope = self.passive_scope
                domain.update()

                if domain.base_domain.domain == domain.domain:
                    display("Name also matches a base domain. Updating that as well.")
                    domain.base_domain.in_scope = self.in_scope
                    domain.base_domain.passive_scope = self.passive_scope
                    domain.base_domain.update()

    def process_ip(self, ip_str, force_scope=True):

        created, ip = self.IPAddress.find_or_create(
            only_tool=True,
            ip_address=ip_str,
            in_scope=self.in_scope,
            passive_scope=self.passive_scope,
        )
        if not created:
            if ip.in_scope != self.in_scope or ip.passive_scope != self.passive_scope:
                display(
                    "IP %s already exists with different scoping. Updating to Active Scope: %s Passive Scope: %s"
                    % (ip_str, self.in_scope, self.passive_scope)
                )

                ip.in_scope = self.in_scope
                ip.passive_scope = self.passive_scope
                ip.update()
        return ip

    def process_cidr(self, line):
        display("Processing %s" % line)
        if "/" in line:
            created, cidr = self.ScopeCIDR.find_or_create(cidr=line.strip())
            if created:
                display_new("Adding %s to scoped CIDRs in database" % line.strip())
                cidr.in_scope = True
                cidr.update()

        elif "-" in line:
            start_ip, end_ip = line.strip().replace(" ", "").split("-")
            if "." not in end_ip:
                end_ip = ".".join(start_ip.split(".")[:3] + [end_ip])

            cidrs = iprange_to_cidrs(start_ip, end_ip)

            for c in cidrs:

                created, cidr = self.ScopeCIDR.find_or_create(cidr=str(c))
                if created:
                    display_new("Adding %s to scoped CIDRs in database" % line.strip())
                    cidr.in_scope = True
                    cidr.update()

    def reclassify_domain(self, bd):
        if bd.meta.get("whois", False):
            display_new("Whois data found for {}".format(bd.domain))
            print(bd.meta["whois"])
            res = six.input(
                "Should this domain be scoped (A)ctive, (P)assive, or (N)ot? [a/p/N] "
            )
            if res.lower() == "a":
                bd.in_scope = True
                bd.passive_scope = True

            elif res.lower() == "p":
                bd.in_scope = False
                bd.passive_scope = True
            else:
                bd.in_scope = False
                bd.passive_scope = False
            bd.save()
        else:
            display_error(
                "Unfortunately, there is no whois information for {}. Please populate it using the Whois module".format(
                    bd.domain
                )
            )

    def descope_ip(self, ip):
        ip = self.IPAddress.all(ip_address=ip)
        if ip:
            for i in ip:
                display("Removing IP {} from scope".format(i.ip_address))
                i.in_scope = False
                i.passive_scope = False
                i.update()
                for d in i.domains:
                    in_scope_ips = [
                        ipa
                        for ipa in d.ip_addresses
                        if ipa.in_scope or ipa.passive_scope
                    ]
                    if not in_scope_ips:
                        display(
                            "Domain {} has no more scoped IPs. Removing from scope.".format(
                                d.domain
                            )
                        )
                        d.in_scope = False
                        d.passive_scope = False
            self.IPAddress.commit()

    def descope_cidr(self, cidr):
        CIDR = self.ScopeCIDR.all(cidr=cidr)
        if CIDR:
            for c in CIDR:
                display("Removing {} from ScopeCIDRs".format(c.cidr))
                c.delete()
        cnet = IPNetwork(cidr)
        for ip in self.IPAddress.all():
            if IPAddress(ip.ip_address) in cnet:

                self.descope_ip(ip.ip_address)