def report_download(uuid): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.reports.details(uuid=uuid) if "error" in results: return render_template("error.html", msg="Unable to fetch report details: {}".format( results["error"])) content = results["content"] if content.strip() == "": return render_template("error.html", msg="The fetched message seems empty") mem = io.BytesIO() mem.write(content.encode("utf-8")) mem.seek(0) if results["type"] == "email": mimetype = "message/rfc822" filename = "{}.eml".format(results["uuid"]) else: mimetype = "text/plain" filename = "{}.txt".format(results["uuid"]) return send_file(mem, mimetype=mimetype, as_attachment=True, attachment_filename=filename)
def indicators(): if not session.__node__: return redirect(url_for("node")) # Get the form to add indicators. if request.method == "GET": ioc = request.args.get("ioc", None) if ioc: ioc = extract_domain(ioc) return render_template("indicators.html", indicators=ioc, page="Indicators") # Process new indicators to be added. elif request.method == "POST": indicators_string = request.form.get("indicators", "") tags_string = request.form.get("tags", "") indicators_string = indicators_string.strip() tags_string = tags_string.strip() if indicators_string == "": return render_template( "indicators.html", page="Indicators", error="You didn't provide a valid list of indicators") if tags_string == "": tags = [] else: tags = [t.lower().strip() for t in tags_string.split(",")] indicators = [i.lower().strip() for i in indicators_string.split()] try: pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.indicators.add(indicators, tags) except Exception as e: return render_template( "error.html", msg="The connection to the PhishDetect Node failed: {}".format( e)) if "error" in results: return render_template("indicators.html", page="Indicators", error=results["error"], tags=tags_string, indicators=indicators_string) msg = "Added {} new indicators successfully!".format( results["counter"]) return render_template("success.html", msg=msg)
def alerts(): if not session.__node__: return redirect(url_for("node")) try: pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.alerts.fetch() except Exception as e: return render_template( "error.html", msg="The connection to the PhishDetect Node failed: {}".format(e), current_node=session.__node__) if results and "error" in results: return render_template("error.html", msg="Unable to fetch alerts: {}".format( results["error"]), current_node=session.__node__) archived = request.args.get("archived", None) archived_alerts = load_archived_alerts() final = [] for result in results: patterns = [ "%Y-%m-%dT%H:%M:%S.%f%z", "%Y-%m-%dT%H:%M:%S.%f%Z", "%Y-%m-%dT%H:%M:%S.%fZ", ] date = None for pattern in patterns: try: date = datetime.datetime.strptime(result["datetime"], pattern) except ValueError: continue else: break if date: result["datetime"] = date.strftime("%Y-%m-%d %H:%M:%S %Z") if archived: if result["uuid"] in archived_alerts: final.append(result) else: if result["uuid"] not in archived_alerts: final.append(result) return render_template("alerts.html", page="Alerts", alerts=final, archived=archived, current_node=session.__node__)
def indicator(sha256): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) details = pd.indicators.details(sha256) return render_template("indicator.html", node=session.__node__["host"], page="Indicator Details", details=details)
def indicators_disable(): content = request.json iocs = content["iocs"] pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) result = pd.indicators.disable(iocs) if "error" in result: return abort(Response(result["error"])) return ("", 200)
def main(): parser = argparse.ArgumentParser(description="Fetch events from the PhishDetect Node") parser.add_argument('--node', default=os.getenv('PDNODE', 'http://127.0.0.1:7856'), help="URL to the PhishDetect Node (default env PDNODE)") parser.add_argument('--key', default=os.getenv('PDKEY', None), help="The API key for your PhishDetect Node user (default env PDKEY)") parser.add_argument('--misp', default=os.getenv('MISPURL', None), help="URL to the MISP instance (default env MISPURL)") parser.add_argument('--token', default=os.getenv('MISPTOKEN', None), help="The MISP api token (default env MISPTOKEN)") args = parser.parse_args() if (not args.node or not args.key or not args.misp or not args.token): parser.print_help() sys.exit(-1) seen_reports = load_data(raw_path) pd = phishdetect.PhishDetect(host=args.node, api_key=args.key) limit = 100 offset = 0 delay = 0 print("Syncing email reports from {} to {}".format(args.node, args.misp)) while True: time.sleep(delay) delay = 60 print("Fetching email reports from offset {}".format(offset)) try: reports = pd.reports.fetch(limit=limit, offset=offset, report_type='email') if not reports: print("Response is empty (nothing to do)") continue except: print("ERROR: Unable to connect to PhishDetect") continue if 'error' in reports: print("ERROR: {}".format(reports['error'])) else: for report in reports: offset = offset + 1 if report['uuid'] not in seen_reports: print("Got a new email report with ID {}".format(report['uuid'])) res = send_misp_event(args.token, args.misp, report['content'], report['user_contact']) if res.status_code == 200: seen_reports.append(report['uuid']) with open(raw_path, 'a') as handle: handle.write('{}\n'.format(report['uuid'])) else: print(res)
def indicators_disabled(): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) iocs = pd.indicators.get_disabled() return render_template("indicators_list.html", iocs=iocs, status="disabled", page="Disabled Indicators", current_node=session.__node__)
def users_deactivate(): if not session.__node__: return redirect(url_for("node")) uuid = request.form.get("uuid", "") # First we get the pending users (before activating the current). pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.users.get_active() if "error" in results: return render_template("error.html", msg="Unable to fetch pending users: {}".format( users["error"]), current_node=session.__node__) # Then we activate the user. result = pd.users.deactivate(uuid) if "error" in result: return render_template("error.html", msg="Unable to deactivate user: {}".format( result["error"]), current_node=session.__node__) email = None for user in results: if uuid == user["uuid"]: email = user["email"] break if not email: return render_template("error.html", msg="User not found", current_node=session.__node__) message = "Your PhishDetect secret token has been deactivated!\n\ If you have any questions, please contact your PhishDetect Node administrator." try: send_email(email, "Your PhishDetect secret token has been deactivated", message) except Exception as e: return render_template( "error.html", msg="Failed to send email to user: {}".format(e), current_node=session.__node__) return render_template("success.html", msg="The user has been deactivated successfully", current_node=session.__node__)
def users_active(): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.users.get_active() if "error" in results: return render_template("error.html", msg="Unable to fetch users: {}".format( results["error"])) return render_template("users_active.html", node=session.__node__["host"], page="Users", users=results)
def report(uuid): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.reports.details(uuid=uuid) if "error" in results: return render_template("error.html", msg="Unable to fetch report details: {}".format( results["error"])) return render_template("report.html", node=session.__node__["host"], page="Report", message=results)
def indicators_add_confirm(): # Process new indicators to be added. enabled = bool(request.form.get("enabled", False)) indicators_string = request.form.get("indicators", "") tags_string = request.form.get("tags", "") indicators_string = indicators_string.strip() tags_string = tags_string.strip() if indicators_string == "": return render_template( "indicators_add.html", page="Add Indicators", error="You didn't provide a valid list of indicators", current_node=session.__node__) if tags_string == "": tags = [] else: tags = [t.lower().strip() for t in tags_string.split(",")] indicators = [i.lower().strip() for i in indicators_string.split()] try: pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.indicators.add(indicators=indicators, tags=tags, enabled=enabled) except Exception as e: return render_template( "error.html", msg="The connection to the PhishDetect Node failed: {}".format(e), current_node=session.__node__) if "error" in results: return render_template("indicators_add.html", page="Indicators", error=results["error"], tags=tags_string, indicators=indicators_string, current_node=session.__node__) msg = "Added {} new indicators successfully!".format(results["counter"]) return render_template("success.html", msg=msg)
def reports(): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.reports.fetch() if "error" in results: return render_template("error.html", msg="Unable to fetch reports: {}".format( results["error"]), current_node=session.__node__) return render_template("reports.html", page="Reports", reports=results, current_node=session.__node__)
def indicators_view(sha256): if not session.__node__: return redirect(url_for("node")) pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.indicators.details(sha256) if "error" in results: return render_template( "error.html", msg="Unable to fetch indicator details: {}".format( results["error"]), current_node=session.__node__) return render_template("indicators_view.html", page="Indicator Details", ioc=results, current_node=session.__node__)
def users_activate(api_key): if not session.__node__: return redirect(url_for("node")) # First we get the pending users (before activating the current). pd = phishdetect.PhishDetect(host=session.__node__["host"], api_key=session.__node__["key"]) results = pd.users.get_pending() if "error" in results: return render_template("error.html", msg="Unable to fetch pending users: {}".format( users["error"])) # Then we activate the user. result = pd.users.activate(api_key) if "error" in result: return render_template("error.html", msg="Unable to activate user: {}".format( result["error"])) email = None for user in results: if api_key == user["key"]: email = user["email"] break if not email: return render_template("error.html", msg="User not found") message = "Your PhishDetect secret token has been activated!" try: send_email(email, "Your PhishDetect secret token has been activated", message) except Exception as e: return render_template( "error.html", msg="Failed to send email to user: {}".format(e)) return render_template("success.html", msg="The user has been activated successfully")
def main(): parser = argparse.ArgumentParser( description="Send indicators to the PhishDetect Node") parser.add_argument("--node", default="http://127.0.0.1:7856", help="URL to the PhishDetect Node") parser.add_argument("--key", required=True, help="The API key for your PhishDetect Node user") parser.add_argument("--type", required=True, help="The type of indicator (\"domain\" or \"email\")") parser.add_argument( "--tags", help="Comma separated list of tags to to mark the indicator") parser.add_argument("--single", metavar="IOC", help="Send this single indicator to PhishDetect Node") parser.add_argument( "--file", metavar="FILE", help="Send all indicators contained in this file to PhishDetect Node") args = parser.parse_args() if (not args.single and not args.file) or (args.single and args.file): parser.print_help() print("\nERROR: You need to specify either --single or --file") sys.exit(-1) indicators = [] if args.file: if not os.path.exists(args.file): print("ERROR: The file you specified at path {} does not exist.". format(args.file)) sys.exit(-1) with open(args.file, "r") as handle: for line in handle: line = line.strip() if line == "": continue if line not in indicators: print("Adding indicator: {}".format(line)) indicators.append(line) elif args.single: ioc = args.single.strip() if ioc not in indicators: print("Adding indicator: {}".format(ioc)) indicators.append(ioc) if len(indicators) == 0: print("ERROR: Somehow there are no indicators to submit") sys.exit(-1) tags = [] if args.tags: for tag in args.tags.split(","): tag = tag.strip() if tag == "": continue if tag not in tags: tags.append(tag) pd = phishdetect.PhishDetect(host=args.node, api_key=args.key) result = pd.indicators.add(indicators=indicators, indicators_type=args.type, tags=tags) print(result)
def main(): parser = argparse.ArgumentParser( description="Synchronize your PhishDetect Node with another") parser.add_argument( "--node", default=os.getenv("PDNODE", "http://127.0.0.1:7856"), help="URL to the target PhishDetect Node (default env PDNODE)") parser.add_argument( "--key", default=os.getenv("PDKEY", None), help="The API key for your PhishDetect Node user (default env PDKEY)") parser.add_argument( "--source-node", required=True, help="URL to the PhishDetect Node you want to fetch indicators from") parser.add_argument( "--source-key", default=os.getenv("PDSRCKEY", None), help="API key for the source PhishDetect Node, if needed") parser.add_argument( "--recent", action="store_true", help="Flag to fetch only indicators from last 24 hours") parser.add_argument("--enabled", action="store_true", default=False, help="Flag to submit synced indicators as enabled") parser.add_argument("--tags", help="Comma-separated list of tags to add to the synced indicators. " \ "If none is specified, one will be generated from the source node address") parser.add_argument( "--batch-size", type=int, default=500, help="Size of batches of indicators to add to target node") args = parser.parse_args() # We create a connection to the source PhishDetect Node. src_pd = phishdetect.PhishDetect(host=args.source_node, api_key=args.source_key) # We generate the list of tags to use. tags = generate_tags(args.tags, args.source_node) if args.recent: # Obtain only the most recent indicators. print("Fetching the list of recent indicators from source node...") src_iocs = src_pd.indicators.fetch_recent() else: # Obtain the list of all "active" indicators from the source node. # Should be all added in the last 6 months. print( "Fetching the full list of active indicators from the source node..." ) src_iocs = src_pd.indicators.fetch() # We create a connection to the target PhishDetect Node. print("Fetching the current list of indicators from the target node...") dst_pd = phishdetect.PhishDetect(host=args.node, api_key=args.key) current_iocs = dst_pd.indicators.fetch() # We loop through the indicators fetched from the source node. for iocs_type, indicators in src_iocs.items(): # We first we clean the list from the source node by comparing it to # the list we got from the target node. cleaned_list = [] for ioc in indicators: if ioc not in current_iocs[iocs_type]: cleaned_list.append(ioc) if len(cleaned_list) == 0: print(f"No new indicators of type {iocs_type} to add. Skip.") continue print(f"From a list of {len(indicators)} of type {iocs_type} " \ f"submitting a filtered list of {len(cleaned_list)}...") # Now we loop through the cleaned list of indicators. for i in range(0, len(cleaned_list), args.batch_size): batch = cleaned_list[i:i + args.batch_size] result = dst_pd.indicators.add( indicators=batch, tags=tags, indicators_type=iocs_type.rstrip("s"), enabled=args.enabled) if "error" in result: print( f"ERROR: Failed to add indicators to target node: {result['error']}" ) else: print(result["msg"])
def main(): parser = argparse.ArgumentParser(description="Fetch alerts from the PhishDetect Node") parser.add_argument("--node", default=os.getenv("PDNODE", "http://127.0.0.1:7856"), help="URL to the PhishDetect Node (default env PDNODE)") parser.add_argument("--key", default=os.getenv("PDKEY", None), help="The API key for your PhishDetect Node user (default env PDKEY)") parser.add_argument("--token", default=os.getenv("POTOKEN", None), help="The Pushover token (default env POTOKEN)") parser.add_argument("--user", default=os.getenv("POUSER", None), help="The Pushover user (default env POUSER)") parser.add_argument("--delay", type=int, default=300, help="Define a delay in seconds between checks") args = parser.parse_args() if (not args.node or not args.key or not args.token or not args.user): parser.print_help() sys.exit(-1) seen_alerts = load_data(alerts_path) seen_reports = load_data(reports_path) seen_users = load_data(users_path) pd = phishdetect.PhishDetect(host=args.node, api_key=args.key) while True: print("Checking for new records to report...") try: alerts = pd.alerts.fetch() if not alerts: raise NothingToReport except NothingToReport: pass except Exception as e: traceback.print_stack() else: if "error" in alerts: print("ERROR: {}".format(alerts["error"])) else: for alert in alerts: if alert["uuid"] not in seen_alerts: print("Got a new alert with ID {}".format(alert["uuid"])) msg = "" user = alert["user_contact"].strip() if user: msg += "User \"{}\"".format(alert["user_contact"]) else: msg += "Unknown user" match = alert["match"].replace("http", "hxxp") match = match.replace(".", "[.]") match = match.replace("@", "[@]") msg += " triggered a {} alert for {}".format(alert["type"], match) send_notification(args.token, args.user, msg) seen_alerts.append(alert["uuid"]) add_to_data(alerts_path, alert["uuid"]) try: reports = pd.reports.fetch() if not reports: raise NothingToReport except NothingToReport: pass except Exception as e: traceback.print_stack() else: if "error" in reports: print("ERROR: {}".format(reports["error"])) else: for report in reports: if report["uuid"] not in seen_reports: print("Got a new report with ID {}".format(report["uuid"])) msg = "" user = report["user_contact"].strip() if user: msg += "User \"{}\"".format(report["user_contact"]) else: msg += "Unknown user" msg += " shared a report of type \"{}\" with UUID {}".format(report["type"], report["uuid"]) send_notification(args.token, args.user, msg) seen_reports.append(report["uuid"]) add_to_data(reports_path, report["uuid"]) try: users = pd.users.get_pending() if not users: raise NothingToReport except NothingToReport: pass except Exception as e: traceback.print_stack() else: if "error" in users: print("ERROR: {}".format(users["error"])) else: for user in users: if user["key"] not in seen_users: print("Got a new user request for {}".format(user["email"])) msg = "Received a users request for \"{}\" with email {}".format(user["name"], user["email"]) send_notification(args.token, args.user, msg) seen_users.append(user["key"]) add_to_data(users_path, user["key"]) time.sleep(args.delay)