-
Notifications
You must be signed in to change notification settings - Fork 13
/
core.py
188 lines (165 loc) · 6.42 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/python
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Checks database for new phishing entries and executes alerts."""
import os
import sys
import smtplib
import logging
from dnslib import dnslib #dnslib.py
import gfyp_db #gfyp_db.py
from common import pretty_print, log #common.py
#SET EMAIL SETTINGS HERE IF NOT USING ENVIRONMENT VARIABLES
EMAIL_USERNAME = None
EMAIL_PASSWORD = None
EMAIL_SMTPSERVER = None
def send_email(smtp_auth, recipient, subject, body):
"""Send email via SMTP.
Args:
smtp_auth (dict): Contains 'username' (str), 'password' (str), and
'server' (str).
recipient (str): The email address to send to
subject (str)
body (str)
http://stackoverflow.com/questions/10147455/trying-to-send-email-gmail-as-mail-provider-using-python
"""
email_to = [recipient]
#Sending message, first construct actual message
message = ("From: %s\nTo: %s\nSubject: %s\n\n%s" %
(smtp_auth['username'], ", ".join(email_to), subject, body))
try:
server_ssl = smtplib.SMTP_SSL(smtp_auth['server'], 465)
server_ssl.ehlo()
server_ssl.login(smtp_auth['username'], smtp_auth['password'])
server_ssl.sendmail(smtp_auth['username'], email_to, message)
server_ssl.close()
except Exception as err:
msg = "Failed to send mail: %s" % str(err)
log(msg, logging.ERROR)
sys.exit(msg)
msg = "Email sent to %s." % recipient
print(msg)
log(msg)
def check_and_send_alert(smtp_auth, alert_email, domain, escape_alert=False,
db_con=None):
"""Consult DB whether an alert needs to be sent for domain, and send one.
Args:
smtp_auth (dict): Credentials for SMTP server, including 'username',
'password', and 'server'.
alert_email (str)
domain (str)
escape_alert (bool): Whether or not to escape periods in the email body
in order to avoid spam filtering. (Default: False)
db_con (None or `gfyp_db.DatabaseConnection`): This can optionally
provide a database connection to reuse. Otherwise, a new one will
be created.
"""
msg = "Now checking %s - %s" % (alert_email, domain)
print(msg)
log(msg)
close_db = False
if db_con is None:
db_con = gfyp_db.DatabaseConnection()
close_db = True
body = ""
dns_check = dnslib()
entries = dns_check.checkDomain(domain)
msg = "DNSTwist found %d variant domains from %s." % (len(entries), domain)
print(msg)
log(msg)
num_new_entries = 0
for domain_found, domain_info in entries:
found_entries = db_con.get_matching_found_domains(domain_found)
entries_iter = found_entries.fetchall()
if len(entries_iter) == 0:
db_con.add_discovered_domain(domain_found, domain_info)
body += "\r\n\r\n%s - %s" % (domain_found, domain_info)
num_new_entries += 1
if body != "":
recipient = alert_email
subject = 'GFYP - New Entries for %s' % domain
if escape_alert:
body = body.replace('.', '[.]')
send_email(smtp_auth, recipient, subject, body)
msg = "Found %d new domain variants from %s" % (num_new_entries, domain)
print(msg)
log(msg)
if close_db:
db_con.conn.close()
def main():
"""Description: Search for new domain variants and email alerts for new ones.
"""
args = get_args()
#Get configuration from env variables or fallback to hard-coded values
smtp_auth = dict()
smtp_auth['username'] = os.getenv('GFYP_EMAIL_USERNAME', EMAIL_USERNAME)
smtp_auth['password'] = os.getenv('GFYP_EMAIL_PASSWORD', EMAIL_PASSWORD)
smtp_auth['server'] = os.getenv('GFYP_EMAIL_SMTPSERVER', EMAIL_SMTPSERVER)
for key, value in list(smtp_auth.items()):
if value is None:
msg = "Fatal error: Email setting '%s' has not been set." % key
log(msg, logging.ERROR)
sys.exit(msg)
if any([EMAIL_USERNAME, EMAIL_PASSWORD, EMAIL_SMTPSERVER]):
msg = ("WARNING: You have hard-coded credentials into a code file. Do "
"not commit it to a public Git repo!")
print(msg)
log(msg, logging.WARNING)
with gfyp_db.DatabaseConnection() as db_con:
if db_con.is_db_current():
domain_entries = db_con.get_watch_entries()
if len(domain_entries) == 0:
msg = ("No domains have been added for watching/alerts. Use "
"util.py to add domains.")
print(msg)
log(msg)
for row in domain_entries:
alert_email = row[0]
domain = row[1]
check_and_send_alert(
smtp_auth, alert_email, domain,
escape_alert=args['escape_alert'], db_con=db_con)
else:
msg = "GFYP database is not current. Please run 'python util.py migrate' to update to the current schema"
print(msg)
log(msg,logging.ERROR)
def usage():
"""Print usage info."""
usage_str = (
"GFYP Core - Find domain variants and send alerts\n"
"usage: python core.py [$BOLD$-escapealert$END$]\n"
"Options:\n"
" $BOLD$-escapealert$END$ - Escape periods in email alert to avoid "
"spam filter")
pretty_print(usage_str)
sys.exit()
def get_args():
"""Get command line arguments.
Current arguments:
* escape_alert (bool): Whether to escape periods in alert email.
"""
args = dict()
args['escape_alert'] = False
if len(sys.argv) == 1:
return args
elif len(sys.argv) == 2:
if sys.argv[1] == '-escapealert':
args['escape_alert'] = True
else:
log("Invalid arguments: %s" % sys.argv, logging.ERROR)
usage()
else:
log("Invalid arguments: %s" % sys.argv, logging.ERROR)
usage()
return args
if __name__ == "__main__":
main()