def view(actor_id): logging_prefix = logger_prefix + "view({}) - ".format(actor_id) log.info(logging_prefix + "Starting") editors = None try: populate_simple_choices() form, editors = es_to_form(actor_id) search_form = forms.searchForm() except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) form = forms.actorForm() #render the template, passing the variables we need # templates live in the templates folder return render_template("actor.html", page_title="View Actor", role="VIEW", actor_id=actor_id, form=form, editors=editors, search_form = search_form )
def view(report_id): logging_prefix = logger_prefix + "view({}) - ".format(report_id) log.info(logging_prefix + "Starting") editors = None try: form, editors = es_to_form(report_id) search_form = forms.searchForm() except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error, "danger") log.exception(logging_prefix + error) form = forms.reportForm() # render the template, passing the variables we need # templates live in the templates folder return render_template( "report.html", page_title="View Report", role="VIEW", report_id=report_id, form=form, editors=editors, search_form=search_form, )
def add(template=None): logging_prefix = logger_prefix + "add({}) - ".format(template) log.info(logging_prefix + "Starting") error = None try: populate_simple_choices() form = forms.actorForm(request.form) search_form = forms.searchForm() if request.method == 'POST': #trick the form validation into working with our dynamic drop downs for sub_form in form.actor_class: sub_form.a_id.choices = fetch_child_data('tpx_classification',sub_form.a_family.data) #convert the field that lists the related_element_choices #choices = [] #rec = json.loads(form.related_element_choices.data) #for k,v in rec.items(): # choices.append((v,k)) if form.validate(): log.info(logging_prefix + "Add Detected") #create an actor id actor_id = str(uuid.uuid4()) #convert the form to ES format form_to_es(form, actor_id) #rebuild the form from ES #flash('Not requerying ES, this should change', 'warning') form, editors = es_to_form(actor_id) flash(Markup('<a href="/actor/view/'+actor_id+'" style="text-decoration:none; color:#3c763d;">New Actor Successfully Added. Click here to view this actor</a>') , "success") #return redirect("/add/{}/".format(actor_id), code=302) else: #if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) elif template: form, editors = es_to_form(template) else: #populate certain fields with default data form.actor_class[0].a_family.data = 'Actors' form.actor_class[0].a_id.choices = fetch_child_data('tpx_classification','Actors') except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) return render_template("actor.html", page_title="Add New Actor", role="ADD", form=form, search_form = search_form )
def issues(): search_form = forms.searchForm() return render_template("issues.html", page_title="Trouble Logging In", search_form=search_form )
def password_reset(user_id=None, expiration_ts=None, password_reset_hash=None): logging_prefix = logger_prefix + "password_reset({},{},{}) - ".format(user_id, expiration_ts, password_reset_hash) log.info(logging_prefix + "Starting") try: if sha256(SALTS['user'],user_id+expiration_ts) != password_reset_hash: flash("There was an error completing your request", 'danger') return redirect("/user/password/request_reset/") form = forms.passwordPerformResetForm(request.form) search_form = forms.searchForm() now = int(time.time()) if now > int(expiration_ts): # passworD1! flash("This link has expired", 'danger') return redirect("/user/password/request_reset/") conn=get_mysql().cursor(DictCursor) conn.execute("SELECT COUNT(*) as count FROM password_reset_hashes WHERE hash = %s AND user_id = %s", (password_reset_hash,user_id)) result = conn.fetchone() conn.close() if not result: flash("This link has expired", 'danger') return redirect("/user/password/request_reset/") if result['count'] == 0: flash("This link has expired", 'danger') return redirect("/user/password/request_reset/") if request.method == 'POST': if form.validate(): password = form.user_password.data salted_password = sha256(SALTS['user'],password) conn=get_mysql().cursor() conn.execute("UPDATE users SET password = %s WHERE id = %s", (salted_password,user_id)) conn.execute("DELETE FROM password_reset_hashes WHERE user_id = %s", (user_id,)) get_mysql().commit() conn.close() flash("Your password has been updated", "success") return redirect("/user/login") else: print(form.errors) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) log.info(logging_prefix + "Rendering") return render_template("password_reset.html", page_title="Reset Your Password", page_type="PERFORM", form=form, search_form=search_form )
def add(template=None): logging_prefix = logger_prefix + "add({}) - ".format(template) log.info(logging_prefix + "Starting") error = None try: form = forms.reportForm(request.form) search_form = forms.searchForm() if request.method == "POST": # trick the form validation into working with our dynamic drop downs for sub_form in form.report_class: sub_form.a_id.choices = fetch_child_data("tpx_classification", sub_form.a_family.data) # convert the field that lists the related_element_choices # choices = [] # rec = json.loads(form.related_element_choices.data) # for k,v in rec.items(): # choices.append((v,k)) if form.validate(): log.info(logging_prefix + "Add Detected") # create a ttp id report_id = str(uuid.uuid4()) # convert the form to ES format form_to_es(form, report_id) # rebuild the form from ES form, editors = es_to_form(report_id) flash( Markup( '<a href="/report/view/' + report_id + '" style="text-decoration:none; color:#3c763d;">New Report Successfully Added. Click here to view this Report</a>' ), "success", ) else: # if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) elif template: form, editors = es_to_form(template) else: # populate certain fields with default data form.report_class[0].a_family.data = "Actors" form.report_class[0].a_id.choices = fetch_child_data("tpx_classification", "Actors") except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error, "danger") log.exception(logging_prefix + error) form = forms.reportForm() return render_template("report.html", page_title="Add New Report", role="ADD", form=form, search_form=search_form)
def edit(ttp_id): logging_prefix = logger_prefix + "edit({}) - ".format(ttp_id) log.info(logging_prefix + "Starting") error = None try: editors = None search_form = forms.searchForm() if request.method == 'POST': form = forms.ttpForm(request.form) #trick the form validation into working with our dynamic drop downs for sub_form in form.ttp_class: sub_form.a_id.choices = fetch_child_data( 'tpx_classification', sub_form.a_family.data) #convert the field that lists the related_element_choices #choices = [] #rec = json.loads(form.related_element_choices.data) #for k,v in rec.items(): #choices.append((v,k)) if form.validate(): log.info(logging_prefix + "Edit Detected") #convert the form to ES format form_to_es(form, ttp_id) #rebuild the form from ES form, editors = es_to_form(ttp_id) flash("TTP Update Successful!", "success") else: #if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) else: form, editors = es_to_form(ttp_id) except Exception as e: error = "There was an error completing your request. Details: {}".format( e) flash(error, 'danger') log.exception(logging_prefix + error) form = forms.ttpForm() #render the template, passing the variables we need # templates live in the templates folder return render_template("ttp.html", page_title="Edit TTP", role="EDIT", ttp_id=ttp_id, form=form, editors=editors, search_form=search_form)
def edit(report_id): logging_prefix = logger_prefix + "edit({}) - ".format(report_id) log.info(logging_prefix + "Starting") error = None try: search_form = forms.searchForm() editors = None if request.method == "POST": form = forms.reportForm(request.form) # trick the form validation into working with our dynamic drop downs for sub_form in form.report_class: sub_form.a_id.choices = fetch_child_data("tpx_classification", sub_form.a_family.data) # convert the field that lists the related_element_choices # choices = [] # rec = json.loads(form.related_element_choices.data) # for k,v in rec.items(): # choices.append((v,k)) if form.validate(): log.info(logging_prefix + "Edit Detected") # convert the form to ES format form_to_es(form, report_id) # rebuild the form from ES form, editors = es_to_form(report_id) flash("Report Update Successful!", "success") else: # if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) else: form, editors = es_to_form(report_id) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error, "danger") log.exception(logging_prefix + error) form = forms.reportForm() # render the template, passing the variables we need # templates live in the templates folder return render_template( "report.html", page_title="Edit Report", role="EDIT", report_id=report_id, form=form, editors=editors, search_form=search_form, )
def password_request_reset(): logging_prefix = logger_prefix + "password_request_reset() - " log.info(logging_prefix + "Starting") try: form = forms.userEmailForm(request.form) search_form = forms.searchForm() if request.method == 'POST': if form.validate(): email = form.user_email.data #make sure the email exists in the system conn=get_mysql().cursor(DictCursor) conn.execute("SELECT id FROM users WHERE email = %s", (email,)) user = conn.fetchone() conn.close() if user: expiration_ts = int(time.time()) + 3600 password_reset_hash = sha256(SALTS['user'],str(user['id']) + str(expiration_ts)) #set this value in the database, so it can be expired after the password is changed conn=get_mysql().cursor(DictCursor) conn.execute("INSERT INTO password_reset_hashes (user_id,hash) VALUES (%s,%s)", (user['id'],password_reset_hash)) get_mysql().commit() conn.close() password_reset_link = "/user/password/reset/{}/{}/{}/".format(user['id'],expiration_ts,password_reset_hash) sendPasswordResetEmail(email, password_reset_link) flash("An email has been sent to '{}' with a link to reset your password. This link expires in 1 hour.".format(email),'success') else: flash("The email you entered was not found", "danger") else: print(form.errors) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) return render_template("password_reset.html", page_title="Reset Your Password", page_type="REQUEST", form=form, search_form=search_form )
def edit(actor_id): logging_prefix = logger_prefix + "edit({}) - ".format(actor_id) log.info(logging_prefix + "Starting") try: populate_simple_choices() form = forms.actorForm(request.form) editors = None search_form = forms.searchForm() if request.method == 'POST': #trick the form validation into working with our dynamic drop downs for sub_form in form.actor_class: sub_form.a_id.choices = fetch_child_data('tpx_classification',sub_form.a_family.data) if form.validate(): log.info(logging_prefix + "Edit Detected") #convert the form to ES format form_to_es(form, actor_id) #rebuild the form from ES form, editors = es_to_form(actor_id) flash("Actor Update Successful!" , "success") #return redirect("/edit/{}/".format(actor_id), code=302) else: #if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) else: form, editors = es_to_form(actor_id) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) #render the template, passing the variables we need # templates live in the templates folder return render_template("actor.html", page_title="Edit Actor", role="EDIT", actor_id=actor_id, form=form, editors=editors, search_form = search_form )
def reverify(): logging_prefix = logger_prefix + "reverify() - " log.info(logging_prefix + "Starting") try: form = forms.userEmailForm(request.form) search_form = forms.searchForm() if request.method == 'POST': if form.validate(): email = form.user_email.data #make sure the email exists in the system conn=get_mysql().cursor(DictCursor) conn.execute("SELECT verification_hash FROM users WHERE email = %s", (email,)) user = conn.fetchone() conn.close() if user: sendAccountVerificationEmail(email, user['verification_hash']) flash("A verification email has been sent to '{}' with a link to verify your account.".format(email),'success') else: flash("The email you entered was not found", "danger") else: print(form.errors) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) return render_template("account_reverify.html", page_title="Verify Account", form=form, search_form=search_form )
def view_all(t, page=1): if t == 'favicon.ico': return jsonify({}), 404 page = int(page) logging_prefix = logger_prefix + "view_all() - " log.info(logging_prefix + "Loading view all page {} for {}".format(page, t)) form = forms.searchForm(request.form) error = None page_size = 50 offset = (page - 1) * page_size url = "/{}/{}/".format(t, page) search_url = "" results_text = "" try: #this is the default query for actors in ES, i'd imagine this will be recently added/modified actors es_query = { "query": { "match_all": {} }, "size": page_size, "from": offset, "sort": { "last_updated_s": { "order": "desc" } } } #pull the query out of the url query_string = request.args.get("q") #someone is searching for something if request.method == 'POST' and not query_string: if form.validate(): print("VALID SEARCH OPERATION DETECTED, redirecting...") #get the value value = form.query.data log.info(value) #redirect to this same page, but setting the query value in the url return redirect("/{}/1/?q={}".format(t, quote_plus(value)), code=307) else: #if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) elif query_string: #now that the query_string is provided as ?q=, perform the search print("VALID SEARCH OPERATION DETECTED") #do some searching... es_query = { "query": { "query_string": { "query": query_string } }, "size": page_size, "from": offset } search_url = "?q=" + query_string #set the form query value to what the user is searching for form.query.data = query_string ''' Fetch the data from ES ''' data = {} data['hits'] = {} data['hits']['hits'] = [] if t == 'actor': index = ES_PREFIX + 'threat_actors' doc_type = 'actor' salt = SALTS['actor'] link_prefix = 'actor' data_header = 'Actors' field_header = 'Actor Name' elif t == 'report': index = ES_PREFIX + 'threat_reports' doc_type = 'report' salt = SALTS['report'] link_prefix = 'report' data_header = 'Reports' field_header = 'Report Title' elif t == 'ttp': index = ES_PREFIX + 'threat_ttps' doc_type = 'ttp' salt = SALTS['ttp'] link_prefix = 'ttp' data_header = 'TTPs' field_header = 'TTP Name' else: raise Exception("Unknown type {}".format(t)) try: data = get_es().search(index, doc_type, es_query) num_hits = len(data['hits']['hits']) #set up previous link if page == 1: prev_url = None else: prev_url = "/{}/{}/{}".format(t, (page - 1), search_url) if ((page - 1) * page_size) + num_hits < data['hits']['total']: next_url = "/{}/{}/{}".format(t, (page + 1), search_url) else: next_url = None url += search_url for d in data['hits']['hits']: s = salt + d['_id'] hash_object = hashlib.sha256(s.encode('utf-8')) hex_dig = hash_object.hexdigest() d["_source"]['id_hash'] = hex_dig if num_hits == 0: results_text = "" else: f = ((page - 1) * page_size) + 1 l = f + (num_hits - 1) results_text = "Showing {} to {} of {} total results".format( f, l, data['hits']['total']) except TransportError as te: #if the index was not found, this is most likely becuase theres no data there if te.status_code == 404: log.warning("Index '{}' was not found".format(index)) else: error = "There was an error fetching {}. Details: {}".format( t, te) flash(error, 'danger') log.exception(logging_prefix + error) except Exception as e: error = "The was an error fetching {}. Error: {}".format(t, e) log.exception(error) flash(error, "danger") except Exception as e: error = "There was an error completing your request. Details: {}".format( e) log.exception(error) flash(error, "danger") return redirect("/") return render_template("view_all.html", page_title="View All", form=form, data_header=data_header, results_text=results_text, field_header=field_header, data=data, link_prefix=link_prefix, prev_url=prev_url, next_url=next_url, url=quote_plus(url))
def contact(): search_form = forms.searchForm() return render_template("contact.html", page_title="Contact", search_form=search_form)
def index(): logging_prefix = logger_prefix + "index() - " log.info(logging_prefix + "Loading home page") form = forms.searchForm(request.form) error = None url = "/" query_string_url = "" try: #this is the default query for actors in ES, i'd imagine this will be recently added/modified actors es_query = { "query": { "match_all": {} }, "size": 10, "sort": { "last_updated_s": { "order": "desc" } } } #pull the query out of the url query_string = request.args.get("q") #someone is searching for something if request.method == 'POST' and not query_string: if form.validate(): #get the value value = form.query.data #redirect to this same page, but setting the query value in the url return redirect("/?q={}".format(quote_plus(value)), code=307) else: #if there was an error print the error dictionary to the console # temporary help, these should also appear under the form field print(form.errors) elif query_string: #now that the query_string is provided as ?q=, perform the search print("VALID SEARCH OPERATION DETECTED") #do some searching... es_query = { "query": { "query_string": { "query": query_string } }, "size": 10 } url += "?q=" + query_string query_string_url = "?q=" + query_string #set the form query value to what the user is searching for form.query.data = query_string ''' Fetch the data from ES ''' actors = {} actors['hits'] = {} actors['hits']['hits'] = [] reports = dict(actors) ttps = dict(actors) try: actors = get_es().search(ES_PREFIX + 'threat_actors', 'actor', es_query) except TransportError as te: #if the index was not found, this is most likely becuase theres no data there if te.status_code == 404: log.warning("Index 'threat_actors' was not found") else: error = "There was an error fetching actors. Details: {}".format( te) flash(error, 'danger') log.exception(logging_prefix + error) except Exception as e: error = "The was an error fetching Actors. Error: {}".format(e) log.exception(error) flash(error, "danger") try: reports = get_es().search(ES_PREFIX + 'threat_reports', 'report', es_query) except TransportError as te: #if the index was not found, this is most likely becuase theres no data there if te.status_code == 404: log.warning("Index 'threat_reports' was not found") else: error = "There was an error fetching reports. Details: {}".format( te) flash(error, 'danger') log.exception(logging_prefix + error) except Exception as e: error = "The was an error fetching Reports. Error: {}".format(e) log.exception(error) flash(error, "danger") try: ttps = get_es().search(ES_PREFIX + 'threat_ttps', 'ttp', es_query) except TransportError as te: #if the index was not found, this is most likely becuase theres no data there if te.status_code == 404: log.warning("Index 'threat_ttps' was not found") else: error = "There was an error ttps. Details: {}".format(te) flash(error, 'danger') log.exception(logging_prefix + error) except Exception as e: error = "The was an error fetching TTPs. Error: {}".format(e) log.exception(error) flash(error, "danger") ''' Modify the data as needed ''' for actor in actors['hits']['hits']: s = SALTS['actor'] + actor['_id'] hash_object = hashlib.sha256(s.encode('utf-8')) hex_dig = hash_object.hexdigest() actor["_source"]['id_hash'] = hex_dig for report in reports['hits']['hits']: s = SALTS['report'] + report['_id'] hash_object = hashlib.sha256(s.encode('utf-8')) hex_dig = hash_object.hexdigest() report["_source"]['id_hash'] = hex_dig for ttp in ttps['hits']['hits']: s = SALTS['ttp'] + ttp['_id'] hash_object = hashlib.sha256(s.encode('utf-8')) hex_dig = hash_object.hexdigest() ttp["_source"]['id_hash'] = hex_dig except Exception as e: error = "There was an error completing your request. Details: {}".format( e) log.exception(error) flash(error, "danger") #render the template, passing the variables we need # templates live in the templates folder return render_template("index.html", page_title="ActorTrackr", form=form, query_string_url=query_string_url, actors=actors, reports=reports, ttps=ttps, url=quote_plus(url))
def register(): logging_prefix = logger_prefix + "register() - " log.info(logging_prefix + "Starting") try: form = forms.registerForm(request.form) search_form = forms.searchForm() if request.method == 'POST': #verify captcha perform_validate = True try: if RECAPTCHA_ENABLED: captcha_value = request.form['g-recaptcha-response'] r = requests.post("https://www.google.com/recaptcha/api/siteverify", data={ 'secret': RECAPTCHA_PRIVATE_KEY, 'response': request.form['g-recaptcha-response'] }) r_json = json.loads(r.text) if not r_json['success']: flash("Bots are not allowed",'danger') perform_validate = False except Exception as s: log.exception(logging_prefix + "Error preforming captcha validation") raise s if perform_validate and form.validate(): log.info(logging_prefix + "Sign up Detected") #get data from form name = form.user_name.data email = form.user_email.data password = form.user_password.data company = form.user_company.data reason = form.user_reason.data #create the necessary hashes salted_password = sha256(SALTS['user'],password) verification_hash = sha256(SALTS['email_verification'], email) #insert data into mysql insert = "INSERT INTO users (email, password, name, company, justification, email_verified, verification_hash) VALUES (%s,%s,%s,%s,%s,%s,%s)" values = (email, salted_password, name, company, reason, 0, verification_hash) log.info(logging_prefix + "Adding new user {}".format(values)) try: conn=get_mysql().cursor() conn.execute(insert, values) get_mysql().commit() conn.close() except Exception as s: conn.close() raise s #email the user to verify email try: sendAccountVerificationEmail(email, verification_hash) except Exception as s: log.exception(logging_prefix + "Unable to send email to {} with verification_hash {}".format(email, verification_hash)) raise s flash("You have been registered. A verification email with an actviation link has been sent to {} from [email protected]".format(email),'success') else: print(form.errors) else: pass except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) return render_template("register.html", page_title="Register", form=form, recaptcha_enabled=RECAPTCHA_ENABLED, recaptcha_key=RECAPTCHA_PUBLIC_KEY, search_form=search_form )
def login(): logging_prefix = logger_prefix + "login() - " log.info(logging_prefix + "Starting") try: form = forms.loginForm(request.form) search_form = forms.searchForm() if request.method == 'POST': if form.validate(): email = form.user_email.data password = form.user_password.data hashed_password = sha256(SALTS['user'], password) conn=get_mysql().cursor(DictCursor) #since email is unique conn.execute("SELECT id, password, name, email_verified, approved, write_permission, delete_permission, admin FROM users WHERE email = %s", (email,)) user = conn.fetchone() if user: if user['password'] == hashed_password: #we have found a valid user if user['email_verified']==0: #the user has not verified their email address flash("Your email address has not been verified. Click here if you did not receive a verification email", "danger") elif user['approved']==0: #the user has not been approved, or their access has been disapproved flash("Your account has been approved yet, you will receive an email when your account has been approved", "danger") else: #woohoo successful login, set up session variables session['logged_in'] = True session['id'] = user['id'] session['name'] = user['name'] session['email'] = email session['approved'] = (user['approved'] == 1) session['write'] = (user['write_permission'] == 1) session['delete'] = (user['delete_permission'] == 1) session['admin'] = (user['admin'] == 1) session['expires'] = math.ceil(time.time()) + SESSION_EXPIRE #now + 10 minutes of inactivity #each time this user loads a page #the expiration time gets now + 10m #update last login timestamp conn=get_mysql().cursor(DictCursor) conn.execute("UPDATE users SET last_login=%s WHERE id = %s", (datetime.now(), user['id'])) get_mysql().commit() conn.close() flash("You have been logged in", "success") if request.args.get("r"): return redirect(request.args.get("r")) else: return redirect("/") else: log.warning(logging_prefix + "Invalid login attempt for {}".format(email)) flash("The username or password is incorrect", "danger") else: log.warning(logging_prefix + "Invalid login attempt for {}".format(email)) flash("The username or password is incorrect", "danger") else: print(form.errors) except Exception as e: error = "There was an error completing your request. Details: {}".format(e) flash(error,'danger') log.exception(logging_prefix + error) return render_template("login.html", page_title="Login", form=form, search_form=search_form )
def about(): search_form = forms.searchForm() return render_template("about.html", page_title="About", search_form=search_form)