def rssPoller(sender, receivers, password, smtpsrv, port, tls, language, rss_feeds): print("------------------------------------") summary, url = "", "" new = False con = sqlite3.connect(dir_path + 'secmon.db') cur = con.cursor() for rss_feed in rss_feeds: current_feed = feedparser.parse(rss_feed) for entries in current_feed.entries: url = entries.link cur.execute("SELECT RSS_URL FROM RSS_DATA WHERE RSS_URL = (?);", (url, )) db_result_list = cur.fetchall() db_result_str = "" for db_result_tuple in db_result_list: for result in db_result_tuple: db_result_str += result if url not in db_result_str: new = True title = entries.title summary = entries.summary if summary == "": summary += "Empty summary." dest_language = getUserLanguage() try: title = translateText(dest_language, title) summary = translateText(dest_language, summary) except: pass cur.execute( "INSERT INTO RSS_DATA (RSS_URL, title, rss_f, summary) VALUES (?,?,?,?);", (url, title, rss_feed, summary)) con.commit() try: sendAlert(sender, password, smtpsrv, port, tls, receivers, title, summary, url, language, rss_feed) mail = "sent" except: mail = "unsent" writeNewRssNewLog("rss_poller", parseRSSSource(rss_feed), rss_feed, title, url, mail) if new == True: print(bcolors.OKGREEN + "Database was updated. Goodbye." + bcolors.ENDC) else: print(bcolors.OKGREEN + "No news. Goodbye." + bcolors.ENDC) print("------------------------------------") timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") print(bcolors.OKBLUE + "Finished at : " + timestamp + bcolors.ENDC) print("------------------------------------")
def sendAlert(sender, password, smtpsrv, port, tls, receivers, cve_id, cve_sources, cve_score, cve_status, cve_cpe, cve_date, cve_description, language, key_match): body = "" nvd_url = "https://nvd.nist.gov/vuln/detail/" + cve_id for receiver in receivers: with open(dir_path + 'cve_template.html', 'r') as template: html_code = template.read() if "cpe" in key_match: key_match = getParsedCpe(key_match) if language == "fr" or language == "FR": html_code = html_code.replace( "Responsive HTML email templates", "Alerte") html_code = html_code.replace( "$CVE", cve_id + " (Produit : {})".format(key_match)) try: cve_description = translateText(dest_language, cve_description) except: pass html_code = html_code.replace( "$DESCRIPTION", cve_description.replace("u'", "").replace("u ``", "")) if not "/" in cve_date: cve_date = cve_date.split("-")[2] + "/" + cve_date.split( "-")[1] + "/" + cve_date.split("-")[0] html_code = html_code.replace("$DATE", cve_date) html_code = html_code.replace("$SCORE", str(cve_score)) try: cve_status = translateText(dest_language, cve_status).replace( "nouveau", "une nouvelle").replace( "évalué", "évaluée") except: pass html_code = html_code.replace("$STATUS", cve_status) html_code = html_code.replace("$CPE", cve_cpe) html_code = html_code.replace("$SOURCES", cve_sources) html_code = html_code.replace("See more details", "Voir plus de détails") html_code = html_code.replace("Publication date", "Date de publication") html_code = html_code.replace("Status", "Statut") html_code = html_code.replace("CPE/Product", "CPE/Produit") html_code = html_code.replace("CVSS Score (V3)", "Score CVSS (V3)") html_code = html_code.replace("This email was sent by", "Cet email a été envoyé par") html_code = html_code.replace("CVE module", "Module CVE") body = html_code.replace("URL OF THE NEWS", nvd_url) else: html_code = html_code.replace( "Responsive HTML email templates", "Alert") html_code = html_code.replace( "$CVE", cve_id + " (Product : {})".format(key_match)) html_code = html_code.replace( "$DESCRIPTION", cve_description.replace("u'", "").replace("u ``", "")) html_code = html_code.replace("$DATE", cve_date) html_code = html_code.replace("$SCORE", str(cve_score)) html_code = html_code.replace("$STATUS", cve_status) html_code = html_code.replace("$CPE", cve_cpe) html_code = html_code.replace("$SOURCES", cve_sources) body = html_code.replace("URL OF THE NEWS", nvd_url) print(bcolors.HEADER + "Sending alert at {}...".format(receiver) + bcolors.ENDC) try: smtpserver = smtplib.SMTP(smtpsrv, port) msg = MIMEMultipart() msg['Subject'] = 'SECMON - CVE' msg['From'] = sender msg['To'] = receiver msg.attach(MIMEText(body, 'html')) except: print(bcolors.FAIL + "Failed to send alert at {}.".format(receiver) + bcolors.ENDC) exit() try: if tls == "yes": smtpserver.ehlo() smtpserver.starttls() smtpserver.login(sender, password) smtpserver.sendmail(sender, receiver, msg.as_string()) print(bcolors.HEADER + "Alert was sent at {}\n".format(receiver) + bcolors.ENDC) elif tls == "no": smtpserver.login(sender, password) smtpserver.sendmail(sender, receiver, msg.as_string()) print(bcolors.HEADER + "Alert was sent at {}\n".format(receiver) + bcolors.ENDC) except Exception as e: print( bcolors.FAIL + "An error occurred during authentication with the SMTP server. Check the configuration and try again." + bcolors.ENDC) exit()
def cvePoller(sender, receivers, password, smtpsrv, port, tls, keywords, language): print("------------------------------------") summary, url = "", "" cve_rss = [] current_feed = feedparser.parse( "https://nvd.nist.gov/feeds/xml/cve/misc/nvd-rss.xml") for entries in current_feed.entries: title = entries.title summary = entries.summary cve_id = re.findall('CVE-\d{4}-\d{4,7}', title) cve_id = cve_id[0] cve_rss.append(cve_id + "(=)" + summary) summary, url = "", "" con = sqlite3.connect(dir_path + 'secmon.db') cur = con.cursor() dest_language = getUserLanguage() now_date = str(datetime.now()).split(" ")[0].split("-") idx_date = now_date[2] + "/" + now_date[1] + "/" + now_date[0] # CPE new_cve_list = [] cur.execute("SELECT cpe FROM cpe_list") db_result_list = cur.fetchall() cpes = getCpeList() if cpes != []: print(bcolors.HEADER + "Polling NVD related to your product list (CPE based search)." + bcolors.ENDC + "\n") for cpe in cpes: cve_ids = pollCveIdFromCpe(cpe) cpe = cpe.replace("\n", "").replace(" ", "") for cve_id in cve_ids: cur.execute("SELECT CVE_ID FROM CVE_DATA WHERE CVE_ID = (?);", (cve_id, )) db_result_list = cur.fetchall() db_result_str = "" for db_result_tuple in db_result_list: for result in db_result_tuple: db_result_str += result if cve_id not in db_result_str: nvd_base_url = "https://services.nvd.nist.gov/rest/json/cve/1.0/" nvd_query = nvd_base_url + cve_id nvd_response = requests.get(url=nvd_query) nvd_data = nvd_response.json() if 'result' in nvd_data.keys(): if 'reference_data' in nvd_data['result']['CVE_Items'][ 0]['cve']['references']: nvd_links = nvd_data['result']['CVE_Items'][0][ 'cve']['references']['reference_data'] cve_sources = "" for link in nvd_links: cve_sources += (link['url'] + "\n") else: cve_sources = "N/A" key_match = cpe cve_score = "" fr_society = " (constructeur/éditeur)" en_society = " (builder/publisher)" if not "impact" in nvd_data['result']['CVE_Items'][ 0].keys(): cve_score = "N/A" try: webpage = requests.get( "https://nvd.nist.gov/vuln/detail/{}". format(cve_id)) soup = BeautifulSoup(webpage.content, 'html.parser') cve_cna_score = soup.find( id='Cvss3CnaCalculatorAnchor') cve_cna_score = cve_cna_score.get_text() if cve_cna_score != "": if language == "fr" or language == "FR": cve_score = cve_cna_score + fr_society else: cve_score = cve_cna_score + en_society else: cve_score = "N/A" except: cve_score = "N/A" else: try: cve_score = nvd_data['result']['CVE_Items'][0][ 'impact']['baseMetricV3']['cvssV3'][ 'baseScore'] except: cve_score = "N/A" published_date = nvd_data['result']['CVE_Items'][0][ 'publishedDate'] formatted_date = published_date.split("T")[0] formatted_date = datetime.strptime( formatted_date, "%Y-%m-%d") date_2_days_ago = datetime.now() - timedelta(days=7) if (date_2_days_ago <= formatted_date) and ( formatted_date <= datetime.now()): cve_date_status = "Potentially new CVE" else: cve_date_status = "Potentially an update" cve_date = published_date.split("T")[0] if language == "fr" or language == "FR": fr_date = cve_date.split("-") cve_date = fr_date[2] + "/" + fr_date[ 1] + "/" + fr_date[0] if (cve_score == "N/A"): cve_status = cve_date_status + " - " + "Not yet rated (No score, no CPE)" else: cve_status = cve_date_status + " - " + "Valid evaluation" cve_cpe = "" if "configurations" in nvd_data['result']['CVE_Items'][ 0].keys(): if nvd_data['result']['CVE_Items'][0][ 'configurations']['nodes']: for node in nvd_data['result']['CVE_Items'][0][ 'configurations']['nodes']: if 'cpe_match' in node.keys(): for cpe_node in node['cpe_match']: cve_cpe += (cpe_node['cpe23Uri'] + '\n') if cve_cpe == "": cve_cpe = "N/A" cve_description = nvd_data['result']['CVE_Items'][0][ 'cve']['description']['description_data'][0][ 'value'] if language == "fr" or language == "FR": try: cve_description = translateText( dest_language, cve_description) except: pass if not "/" in cve_date: cve_date = cve_date.split( "-")[2] + "/" + cve_date.split( "-")[1] + "/" + cve_date.split("-")[0] try: cve_status = translateText( dest_language, cve_status).replace( "nouveau", "une nouvelle").replace( "évalué", "évaluée") except: pass nvd_link = 'https://nvd.nist.gov/vuln/detail/' + cve_id else: cve_description = "N/A" date = str(datetime.now()).split(" ")[0].split("-") cve_date = date[2] + "/" + date[1] + "/" + date[0] cve_score = "N/A" cve_sources = "N/A" cve_status = "N/A" cve_cpe = "N/A" nvd_link = 'https://nvd.nist.gov/vuln/detail/' + cve_id status = "Unread" if cve_id not in new_cve_list: cur.execute( "INSERT INTO CVE_DATA (CVE_ID,KEYWORD,STATUS,CVE_SCORE,CVE_DATE,CVE_DESCRIPTION,CVE_EVAL,CVE_CPE,CVE_SOURCES,EXPLOIT_FIND,INDEXING_DATE) VALUES (?,?,?,?,?,?,?,?,?,?,?);", (cve_id, key_match, status, str(cve_score).split(" ")[0], cve_date, cve_description, cve_status, cve_cpe, cve_sources, "False", idx_date)) con.commit() report = "updated" print("New CVE detected -> " + cve_id) try: sendAlert(sender, password, smtpsrv, port, tls, receivers, cve_id, cve_sources, str(cve_score).split(" ")[0], cve_status, cve_cpe, cve_date, cve_description, language, key_match) alert = "sent" except: alert = "unsent" new_cve_list.append(cve_id) writeCveTypeLog("cve_poller", cve_id, "new", key_match, str(cve_score).split(" ")[0], cve_date, cve_cpe, report, alert) if not new_cve_list: print(bcolors.HEADER + "No new CVE related to your (CPE) product list." + bcolors.ENDC) print("------------------------------------") else: timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") print(bcolors.OKGREEN + "Database was updated with new CVE." + bcolors.ENDC) print("------------------------------------") # RSS new_cve_list = [] print(bcolors.HEADER + "Polling NVD RSS feed (keyword based search)." + bcolors.ENDC + "\n") for cve in cve_rss: cve_id = cve.split("(=)")[0] summary = cve.split("(=)")[1] for key in keywords: if key in summary: key_match = key cur.execute("SELECT CVE_ID FROM CVE_DATA WHERE CVE_ID = (?);", (cve_id, )) db_result_list = cur.fetchall() db_result_str = "" for db_result_tuple in db_result_list: for result in db_result_tuple: db_result_str += result if cve_id not in db_result_str: nvd_base_url = "https://services.nvd.nist.gov/rest/json/cve/1.0/" nvd_query = nvd_base_url + cve_id nvd_response = requests.get(url=nvd_query) nvd_data = nvd_response.json() if 'result' in nvd_data.keys(): if 'reference_data' in nvd_data['result']['CVE_Items'][ 0]['cve']['references']: nvd_links = nvd_data['result']['CVE_Items'][0][ 'cve']['references']['reference_data'] cve_sources = "" for link in nvd_links: cve_sources += (link['url'] + "\n") else: cve_sources = "N/A" cve_score = "" fr_society = " (constructeur/éditeur)" en_society = " (builder/publisher)" if not "impact" in nvd_data['result']['CVE_Items'][ 0].keys(): cve_score = "N/A" try: webpage = requests.get( "https://nvd.nist.gov/vuln/detail/{}". format(cve_id)) soup = BeautifulSoup(webpage.content, 'html.parser') cve_cna_score = soup.find( id='Cvss3CnaCalculatorAnchor') cve_cna_score = cve_cna_score.get_text() if cve_cna_score != "": if language == "fr" or language == "FR": cve_score = cve_cna_score + fr_society else: cve_score = cve_cna_score + en_society else: cve_score = "N/A" except: cve_score = "N/A" else: try: cve_score = nvd_data['result']['CVE_Items'][0][ 'impact']['baseMetricV3']['cvssV3'][ 'baseScore'] except: cve_score = "N/A" published_date = nvd_data['result']['CVE_Items'][0][ 'publishedDate'] formatted_date = published_date.split("T")[0] formatted_date = datetime.strptime( formatted_date, "%Y-%m-%d") date_2_days_ago = datetime.now() - timedelta(days=7) if (date_2_days_ago <= formatted_date) and ( formatted_date <= datetime.now()): cve_date_status = "Potentially new CVE" else: cve_date_status = "Potentially an update" cve_date = published_date.split("T")[0] if language == "fr" or language == "FR": fr_date = cve_date.split("-") cve_date = fr_date[2] + "/" + fr_date[ 1] + "/" + fr_date[0] if (cve_score == "N/A"): cve_status = cve_date_status + " - " + "Not yet rated (No score, no CPE)" else: cve_status = cve_date_status + " - " + "Valid evaluation" cve_cpe = "" if "configurations" in nvd_data['result']['CVE_Items'][ 0].keys(): if nvd_data['result']['CVE_Items'][0][ 'configurations']['nodes']: for node in nvd_data['result']['CVE_Items'][0][ 'configurations']['nodes']: if 'cpe_match' in node.keys(): for cpe_node in node['cpe_match']: cve_cpe += (cpe_node['cpe23Uri'] + '\n') if cve_cpe == "": cve_cpe = "N/A" cve_description = nvd_data['result']['CVE_Items'][0][ 'cve']['description']['description_data'][0][ 'value'] nvd_link = 'https://nvd.nist.gov/vuln/detail/' + cve_id if language == "fr" or language == "FR": try: cve_description = translateText( dest_language, cve_description) except: pass if not "/" in cve_date: cve_date = cve_date.split( "-")[2] + "/" + cve_date.split( "-")[1] + "/" + cve_date.split("-")[0] try: cve_status = translateText( dest_language, cve_status).replace( "nouveau", "une nouvelle").replace( "évalué", "évaluée") except: pass else: cve_description = summary date = str(datetime.now()).split(" ")[0].split("-") cve_date = date[2] + "/" + date[1] + "/" + date[0] cve_score = "N/A" cve_sources = "N/A" cve_status = "N/A" cve_cpe = "N/A" nvd_link = 'https://nvd.nist.gov/vuln/detail/' + cve_id status = "Unread" if cve_id not in new_cve_list: cur.execute( "INSERT INTO CVE_DATA (CVE_ID,KEYWORD,STATUS,CVE_SCORE,CVE_DATE,CVE_DESCRIPTION,CVE_EVAL,CVE_CPE,CVE_SOURCES,EXPLOIT_FIND,INDEXING_DATE) VALUES (?,?,?,?,?,?,?,?,?,?,?);", (cve_id, key_match, status, str(cve_score).split(" ")[0], cve_date, cve_description, cve_status, cve_cpe, cve_sources, "False", idx_date)) con.commit() print("New CVE detected -> " + cve_id) report = "updated" try: sendAlert(sender, password, smtpsrv, port, tls, receivers, cve_id, cve_sources, str(cve_score).split(" ")[0], cve_status, cve_cpe, cve_date, cve_description, language, key_match) alert = "sent" except: alert = "unsent" new_cve_list.append(cve_id) writeCveTypeLog("cve_poller", cve_id, "new", key_match, str(cve_score).split(" ")[0], cve_date, cve_cpe, report, alert) if not new_cve_list: print(bcolors.HEADER + "No new CVE matched with your keyword list." + bcolors.ENDC) print("------------------------------------") timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") print(bcolors.OKBLUE + "Finished at : " + timestamp + bcolors.ENDC) print("------------------------------------") else: print(bcolors.OKGREEN + "Database was updated with new CVE." + bcolors.ENDC) timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") print("------------------------------------") print(bcolors.OKBLUE + "Finished at : " + timestamp + bcolors.ENDC) print("------------------------------------")