Running a Fake SMTP Server and Sending Emails to Gmail

in DevTalk4 years ago (edited)

Disclaimer: This post was created for learning purposes only. If you are not fully knowledgable on the technology discussed in this post and/or the security vulnerability risks it might entail, do not try to replicate anything stated on this post. Do not store or share emails or passwords on your source codes or any manner that is not secure.

Sending and receiving emails is normally easy, but when you work in the background, handling transfers can be a bit complicated.

TL;DR

Due to my Work-from-Home status, my local devices (except the laptop) can't connect to test servers. So, I made a Python script to listen to test emails and relay it to Gmail.

Emails and SMTP

The email concept is just a digitalization of the snail mail. The underlying technology can be oversimplified into the following:


Person #1 (Sender) > Postman #1 (SMTP) > Post Office (Mail Server) > Postman #2 (IMAP/POP3) > Person #2 (Receiver)

SMTP (Simple Mail Transfer Protocol) is a connection-oriented, text-based protocol in which a mail sender communicates with a mail receiver by issuing command strings and supplying necessary data over a reliable ordered data stream channel.
WikiPedia

SMTP only handles outgoing emails from your email application and sends it to one or more mail servers (send/push email). The common port is 25. For receiving emails, it is handled by IMAP or POP3.

IMAP is short for Internet Message Access Protocol. With IMAP, the message does not remain on the local device, such as a computer, it remains on the server.
Name.com

IMAP allows emails to be safely saved in the email server and copies the email to the receiver's connected devices.

POP3 is short for Post Office Protocol. With POP3 mail, it will connect and attempt to keep the mail located on the local device (computer or mobile).
Name.com

POP3 will download the email from the server to the receiver's device, once downloaded, the email will be no longer available in the server.

IMAP and POP3 has it pros and cons, usage should be determined depending on the needs of the end-users, organization, or company.

In the next sections of this post, I will be focusing more on SMTP since my project was about on the sending process.

Work from Home Setup

For security reasons, any off-site devices (except laptops) shouldn't be connected to corporate VPN. With this, I need to devise my own plan on how I can send test emails using Gmail's SMTP server.

Good thing is I have Python and it has built-in modules to handle SMTP: smtpd and smtplib. I just needed a working Gmail account and with a few tweaks, I was able to send emails to any email account.

Managing up Gmail Account

Note: This involves security vulnerability risks, please create and use test emails for your projects.

You need to enable "Less secure app access" so that your SMTP will not return a 5xx error. This process requires you to log in to your test Gmail account, and make sure you are in the right account before continuing.

  1. On your browser, go to google.com
  2. Click on your account avatar and click "Manage your Google"
  3. On left pane, select "Security"
  4. Scroll down to "Less secure app access"
  5. Click "Turn on access (not recommended)"
  6. Enable "Allow less secure apps: OFF"

Python Script

I have a device that broadcast emails and what I needed was listener to forward it to Gmail. Your setup might be different and you might tweak this a bit depending on your needs.

Because I needed it ASAP, I wasn't able to check that smtpd is already deprecated, but for this tutorial, I will still be using smtpd for my fake server.

The aiosmtpd package is a recommended replacement for this module. It is based on asyncio and provides a more straightforward API. smtpd should be considered deprecated.
Python.org

To see how aiosmtpd works, check Migrating from smtpd to aiosmtpd for more info.

import smtpd
import asyncore


class CustomSMTPServer(smtpd.SMTPServer):

  # https://github.com/MISP/mail_to_misp/issues/13#issuecomment-577987466
  def process_message(self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None):
    print("Receiving message from:", peer)
    print("Message addressed from:", mailfrom)
    print("Message addressed to  :", rcpttos)
    print("Message length    :", len(data))

server = CustomSMTPServer(("0.0.0.0", 1025), None)
asyncore.loop()

The SMTP Server (smtpd) module doesn't really send your email to the SMTP server, it just read the headers and disposes it. This script is blocking and will not be able to listen to keyboard events.

In order to forward the email, I needed to use the SMTP protocol client (smtplib) module to formally send it to Gmail.

This is the generic setup for SMTP server:

  • SMTP server: smtp.gmail.com
  • SMTP port: 465 (SSL) / 587 (TLS)
  • SMTP username: [email protected]
  • SMTP password: ******
  • TLS/SSL: Required.

The smtplib module defines an SMTP client session object that can be used to send mail to any Internet machine with an SMTP or ESMTP listener daemon.
Python.org

smtp.py

import sys
import smtpd
import socket
import getpass
import smtplib
import asyncore
import threading


class CustomSMTPServer(smtpd.SMTPServer):

  # https://github.com/MISP/mail_to_misp/issues/13#issuecomment-577987466 
  def process_message(self, peer, mailfrom, rcpttos, data, mail_options=None, rcpt_options=None):
    send(mailfrom, rcpttos[0], data)

# https://stackoverflow.com/a/14496815/4943299
class MyReceiver(object):

  def start(self):
    """Start the listening service"""

    host = 1025

    # show the IP Address of the fake SMTP server
    ip = socket.gethostbyname(socket.gethostname())
    print(f"SMTP Server: {ip}:{host}")

    # note the '0.0.0.0' is required
    # to be accessed in all the network devices
    # using localhost will only be accessible the host only(?)
    self.smtp = CustomSMTPServer(("0.0.0.0", host), None)

    # set timeout to 1 to prevent blocking after SMTP is closed
    self.thread =  threading.Thread(target=asyncore.loop, kwargs = {"timeout": 1} )
    self.thread.start()  

  def stop(self):
    """Stop listening now to port 1025"""

    # close the SMTPserver to ensure
    # no channels connect to asyncore
    # before waiting for the thread to finish
    self.smtp.close()
    self.thread.join()

def send(sender, receiver, data):
  """SMTP send process""""

  # can be manually set
  # but do not store email or passwords
  # on source codes
  email = input("SMTP Email Address: ")
  password = getpass.getpass("SMTP Email Password: ")

  mailserver = smtplib.SMTP("smtp.gmail.com", 587)
  mailserver.ehlo()
  mailserver.starttls()
  mailserver.ehlo()
  mailserver.login(email, password)
  
  print("\nSending email...")
  try:
    mailserver.sendmail(sender, receiver, data.decode("utf-8"))
    print("- Email was sent.")
    print("- Sender:", sender)
    print("- Receiver:", receiver)
    mailserver.quit()
  except Exception as e:
    print("- Unable to send mail.", e)
    sys.exit(1)

# start the listener
MyReceiver().start()

On command line / Terminal, run:

cd C:\path\to\script
C:\path\to\script>python smtp.py

Once email is broadcasted, the listener will proceed to sending and will require the SMTP credentials to be set. Will return errors if incorrect or if "Less secure app access" was not properly set.

This could also used to send emails to non-Gmail accounts as the code merely relay emails to Gmail SMTP server, all email processing are done in the Gmail SMTP servers.

If you're no longer using the SMTP setup, be sure to Turn OFF "Less secure app access" in your test Google Account.

You can re-use the last part and integrate it in your own projects. For standard email data format refer to email module on Python 3 for more tutorials.

Takeaways

This was just a workaround projects, so I didn't expect much quality and thread-safety of the code. As long as it worked, it's OK. For real projects, consider checking the code for security vulnerabilities and whether modules are deprecated or not.

The SMTP modules can be helpful for automations and IoT, but like any other technology, usage should be done ethically.

If you have thoughts, suggestions, or violent reactions, feel free to comment down below.

#CodeRunDebug


PREVIOUS POST
I Used Bitcoin Cash to buy Hive

With a soaring ETH price, I only have few options left to buy Hive—BTC and BCH.


HiveHealth BETA
Check your performance in Hive, find out your top performing post, and more. TRY IT!

HiveHealth Weekly Reports #1
Welcome to HiveHealth's aggregated weekly reports!


Coin Alerts BETA
Check HIVE exchange rate in your currency. TRY IT!


Curious about Crypto?
Buy and Sell on Coinbase
Get Started


About Me

@oniemaniego is a test engineer, but outside work, he experiments in the kitchen, writes poetry and fiction, paints his heart out, or toils under the hot sun.

Onie Maniego was born in Leyte, PH. He grew up in a rural area with a close-knit community and a simple lifestyle, he is often visiting his father's orchards during summer and weekends, which has a great impact on his works.

Don't forget to vote, comment, and follow me.


Not yet on Hive? Earn while blogging.
Sign Up

Sort:  

I have used smtplib and BeatifulSoup to send myself an email with 'the story of the day' ! quite a thing...

That's nice. How's the HTML being handled, do you encode it or just raw text?

I'm planning to use it in my personal projects, maybe for some security alerts 😆

Congratulations @oniemaniego! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :

You got more than 600 replies. Your next target is to reach 700 replies.

You can view your badges on your board And compare to others on the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

To support your work, I also upvoted your post!

Do not miss the last post from @hivebuzz:

HiveBuzz Ranking update - New key indicators