Skip to content

Commit

Permalink
Adding initial source commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mikechabot committed Aug 2, 2018
1 parent 3e0bc6d commit 9bcf712
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ venv.bak/

# mypy
.mypy_cache/
.idea
/*.iml
3 changes: 3 additions & 0 deletions message-body.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This is an e-mail message to be sent in HTML format
<b>This is HTML message.</b>
<h1>This is headline.</h1>
61 changes: 61 additions & 0 deletions raw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import util

PORT_OUT_OF_RANGE = 'SMTP port is out-of-range (0-65535)'
PORT_NAN = 'SMTP port must be a number'


def get_port():
while True:
try:
port = int(util.get_required_prompt('SMTP port: '))
if port < 0:
print(PORT_OUT_OF_RANGE)
elif port > 65535:
print(PORT_OUT_OF_RANGE)
else:
return str(port)
except ValueError:
print(PORT_NAN)


def get_debug_level():
is_debug = util.get_optional_prompt('Enable debug (y/n)?: ', 'n')
return util.convert_answer_to_int(is_debug)


def get_from_address():
return util.get_required_prompt('\nEnter FROM address: ')


def get_from_name():
return util.get_required_prompt('Enter FROM name (e.g. John Smith): ')


def get_subject():
return util.get_required_prompt('Enter SUBJECT line: ')


def get_to_addresses():
to_address = util.get_required_prompt('Enter TO address: ')
to_addresses = [to_address]
if is_multi_address():
while to_address:
to_address = util.get_optional_prompt('Enter TO address (blank to continue): ', None)
if to_address:
to_addresses.append(to_address)
return to_addresses


def is_multi_address():
is_multi = util.get_optional_prompt('Enter more TO addresses (y/n)?: ', 'n')
return util.convert_answer_to_int(is_multi)


def load_body_from_file():
load_from_file = util.get_optional_prompt('Load message body from file (y/n)?: ', 'n')
return util.convert_answer_to_int(load_from_file)


def get_body_filename():
return util.get_required_prompt('Filename: ')

3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
colorama==0.3.9
freeze==1.0.10
six==1.11.0
90 changes: 90 additions & 0 deletions smtp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import smtplib
import util as u
from socket import gaierror

AUTH = 'auth'
STARTTLS = 'STARTTLS'
SUPPORTED_AUTH_TYPES = {'PLAIN', 'LOGIN'}

# Error strings
TLS_NOT_SUPPORTED = u.generate_fatal('SMTP server does not support TLS.')
NO_SMTP_FEATURES = u.generate_fatal('No SMTP features detected.')
NO_AUTH_FEATURES = u.generate_fatal('No AUTH types detected.')
NO_PLAIN_OR_LOGIN_FEATURE = u.generate_fatal('SMTP server does not support AUTH PLAIN or AUTH LOGIN.')
UNABLE_TO_CONNECT = u.generate_fatal('Unable to connect to SMTP server. Check hostname and port.')

INVALID_CREDENTIALS = 'Error: The server did not accept the username/password combination'
AUTH_NOT_SUPPORTED = 'Error: The AUTH command is not supported by the server'
GENERIC_AUTHENTICATION_EXCEPTION = 'Error: Encountered an error during authentication'


def connect(host, port, debug):
socket = host + ':' + port
print('\nAttempting connection to socket (' + socket + ')...')
try:
server = smtplib.SMTP(host, port)
server.set_debuglevel(debug)
print('--> Successfully connected to SMTP server!\n')
return server
except (gaierror, OSError):
print(UNABLE_TO_CONNECT)
exit(1)


def start_tls(server):
server.ehlo()
if not server.has_extn(STARTTLS):
print(TLS_NOT_SUPPORTED)
exit(1)
else:
server.starttls()


def verify_auth_feature(features):
if not features:
print(NO_SMTP_FEATURES)
exit(1)
elif not features[AUTH]:
print(NO_AUTH_FEATURES)
exit(1)


def get_supported_server_auth_types(features):
server_auth_types = features[AUTH].strip().split()

auth_types = []
for auth_type in SUPPORTED_AUTH_TYPES:
if auth_type in server_auth_types:
auth_types.append(auth_type)

if not auth_types:
print(NO_PLAIN_OR_LOGIN_FEATURE)
exit(1)
else:
return auth_types


def evaluate_server(server):
server.ehlo()
verify_auth_feature(server.esmtp_features)

print('Listing supported AUTH types...')
for auth_type in get_supported_server_auth_types(server.esmtp_features):
print("--> " + auth_type)


def login(server):
login_attempt = None
while not login_attempt:
try:
username = u.get_required_prompt('\nEnter username: ')
password = u.get_required_prompt('Enter password: ')
login_attempt = server.login(username, password)
except smtplib.SMTPAuthenticationError:
print(INVALID_CREDENTIALS)
except smtplib.SMTPNotSupportedError:
print(AUTH_NOT_SUPPORTED)
except smtplib.SMTPException:
print(GENERIC_AUTHENTICATION_EXCEPTION)

print('--> ' + login_attempt[1].decode())
59 changes: 59 additions & 0 deletions spoofer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import smtplib
import util as u
import raw as r
import smtp as s

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

smtp_host = u.get_required_prompt('SMTP server: ')
smtp_port = r.get_port()
debug_level = r.get_debug_level()

server = s.connect(smtp_host, smtp_port, debug_level)

s.start_tls(server)
s.evaluate_server(server)
s.login(server)

from_address = r.get_from_address()
from_name = r.get_from_name()
to_addresses = r.get_to_addresses()
subject = r.get_subject()

msg = MIMEMultipart('alternative')
msg.set_charset("utf-8")

msg["From"] = from_name + "<" + from_address + ">"
msg['Subject'] = subject
msg["To"] = u.COMMASPACE.join(to_addresses)

load_body_from_file = r.load_body_from_file()

if load_body_from_file:
filename = r.get_body_filename()
with open(filename) as file:
body = MIMEText(file.read(), 'html')
msg.attach(body)
else:
print('--> Enter HTML line by line.')
print('--> Press CTRL+D on an empty line to finish.)')
html = u.EMPTY_STRING
while True:
try:
line = input("> ")
html += line + "\n"
except EOFError:
print('HTML captured.')
break
body = MIMEText(html, 'html')
msg.attach(body)

print('\n--> Spoofing from ' + from_address + ' (' + from_name + ')')
print('--> Sending to ' + u.COMMASPACE.join(to_addresses))

try:
server.sendmail(from_address, to_addresses, msg.as_string())
print('\nSuccessfully sent email')
except smtplib.SMTPException:
print('\nError sending email. Check FROM/TO and MESSAGE body.')
37 changes: 37 additions & 0 deletions util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from colorama import init
init()

AFFIRMATIVE_RESPONSES = {'y', 'ye', 'yes'}
ERROR = 'Error: '
EXITING = ' Exiting...'
EMPTY_STRING = ''
COMMASPACE = ', '


def prompt(text):
return input(text).strip()


def get_required_prompt(text):
var = None
while not var:
var = prompt(text)
return var


def get_optional_prompt(text, default_value):
var = prompt(text)
if var:
return var
else:
return default_value


def convert_answer_to_int(answer):
if answer in AFFIRMATIVE_RESPONSES:
return 1
return 0


def generate_fatal(message):
return ERROR + message + EXITING

0 comments on commit 9bcf712

Please sign in to comment.