Firewalling your SMTP traffic

So I get this email the other day from my ISP

From - Tue Jan 08 05:17:43 2013
X-Account-Key: account10
X-UIDL: 1248218041.44
X-Mozilla-Status: 0001
X-Mozilla-Status2: 00000000
X-Mozilla-Keys:                                                                                 
Return-Path: <abuse@tpg.com.au>
Received: from deliver ([unix socket])
	 by spool-host12.tpgi.com.au (Cyrus v2.4.13) with LMTPA;
	 Tue, 08 Jan 2013 06:11:43 +1100
X-TPG-Junk-Status: Message not scanned
X-TPG-Antivirus: Passed
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tpg.com.au; s=alpha;
	t=1357585903; bh=Binl+Wy3iNrYGxLTRbEtR5Kenr1Q3xVjfrcukV9hL4M=;
	h=X-TPG-Abuse:From:To:Subject:Content-Type;
	b=if3MycnUd9LEJ/32hgIAMiXd6KySEwo85mvj5ohFbq8G1WJ4BPKddnqkLkh9cYfaH
	 IAAE7MgKGd3zOLBSIH6jMr5psWv4++aHR0feZ3siJ5wAyJOy5zwxTxPZG999s56WX8
	 7L0mEJbKgLFwKWROrt1qDybuVVyNPXIfwRc8s8Ok=
X-TPG-Abuse: host=eve-new.tpgi.com.au; ip=203.12.160.183; date=Tue, 8 Jan 2013 06:11:43 +1100
Received: from eve-new.tpgi.com.au (eve-new.tpgi.com.au [203.12.160.183])
	by mail15.tpgi.com.au (envelope-from abuse@tpg.com.au) (8.14.3/8.14.3) with ESMTP id r07JBfcB011666;
	Tue, 8 Jan 2013 06:11:43 +1100
Date: Tue, 8 Jan 2013 06:11:41 +1100
Message-Id: <201301071911.r07JBfcB011666@mail15.tpg.com.au>
From: Internet Abuse Team <abuse@tpg.com.au>
Sender: Internet Abuse Team <abuse@tpg.com.au>
Organization: TPG Internet
Reply-To: Internet Postmaster Team <postmaster@tpg.com.au>
To: knoxg@tpg.com.au
Subject: [knoxg@tpg.com.au] AISI reported activities
User-Agent: TPG-IRS/1.2
X-Mailer: Help stamp out HTML
X-No-Archive: Yes
Content-Type: multipart/mixed; boundary="message_boundary"

--message_boundary
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Dear Customer (knoxg@tpg.com.au),

We have received reports from the ACMA's Australian Internet Security
Initiative (AISI) that a machine accessing the Internet using your TPG
Service is causing unwanted traffic to be transmitted, such as spam
and viruses.

A summary of the last few complaints have been provided below:

[2013-01-05 09:59:54] [123.243.191.198] Trojan: Generic 
[2013-01-04 09:59:55] [123.243.191.198] Trojan: Generic 
[2013-01-03 09:59:39] [123.243.191.198] Trojan: Generic 


It may be that your equipment has been compromised by a hacker or some
other malicious software has been installed onto your system. Please
obtain an up to date antivirus software and ensure that all your
machines are cleaned as a matter of urgency. If you fail to do so and
the malicious traffic persists, TPG may take steps to limit it by
suspending your service.

For more information about how to protect your computer, please visit
the following websites below:

http://www.acma.gov.au/WEB/STANDARD/pc=PC_310316
http://www.staysmartonline.gov.au/home_internet_users/secure_your_computer

If you have any questions about this email or our Terms and Conditions,
please contact Customer Service on customer_service@tpg.com.au or
13 14 23.

Thank you.


Kind Regards,

Internet Abuse Team
TPG Internet

E-mail:	abuse@tpg.com.au
Phone:	13 14 23
Fax:	02 9850 0813


--message_boundary--

Which, long story short, means that my ISP thinks that trojan emails are escaping from within my prophylactic fortress.

I generate a fair number of emails automatically, which judging by the timestamps they supplied:

[2013-01-05 09:59:54] [123.243.191.198] Trojan: Generic 
[2013-01-04 09:59:55] [123.243.191.198] Trojan: Generic 
[2013-01-03 09:59:39] [123.243.191.198] Trojan: Generic

seem to line up with a cron job that I run every 9am every morning (Sydney timezones notwithstanding), which sends an email to my own email account on a google server somewhere.

So even though I was pretty sure this was a fairly innocuous event, and in lieu of actually getting some useful information from either TPG or AISA as to what these ‘Trojan’ emails contained, I thought I’d better disable my outgoing emails whilst I removed myself from the CBL and looked under the hood.

What I’d normally do at this point is turn off outgoing SMTP connections at my router, so that I can check what connections are being made by various virtual machines I’ve got running here. This involves delving into the administrative console of my ADSL router (which for me, is a DSL-G604T and looks like this):

Login via the supersecret username/password, then go to Advanced -> Filters -> whatever
Login via the supersecret username/password, then go to Advanced -> Filters -> whatever

Which is all fine and dandy until I actually want to send an email, in which case I need to log into the thing again, re-enable SMTP, send the email, and then turn it off again.

So I wrote a script, which I’m posting up here in case anyone else needs to do something similar.

When you go to the script in your web browser, it shows you list of all the firewall rules that it knows about, and the state of that rule (‘open’, ‘closed’ or ‘partial’). This is what it looks like, when web traffic (HTTP) is open, outgoing email traffic (SMTP) is open, and the FISHMONGER is closed:

(I’ve taken the liberty of cleaning it up a bit and added some CSS and javascript for the purposes of this blog entry)

And this is what it looks like when you decide to prevent any outgoing SMTP connections, by clicking on the ‘SMTP’ rule on the page:

The user could then reopen SMTP trafic by clicking on the ‘SMTP’ rule in the ‘Current state’ section of the page.

If you like, try clicking on the ‘Click to view commands sent to router’ or the ‘Click to view iptables rules’ to see the iptables commands, and the previous and current iptables rules that were captured when I cut and pasted these samples into this blog.

It runs as a perl CGI script, and telnets into a router and runs some iptables commands to turn on and off outgoing connections to SMTP servers. This will only work if your router has a telnet interface (and it’s enabled); most routers these days seem to have busybox installed on them somewhere, which you should be enable by googling up your router brand/model and the words “telnet” or “shell access”.

A quick introduction to iptables

iptables allows you to define what internet traffic is allowed into and out of your local network. Every packet that enters or leaves a machine goes through it. We’re interested in packets that go through our router, and we’re only interested in the ‘FORWARD’ rule sets, since these govern the internet packets that leave the router.

The rules that are displayed are configured using the firewall.json file, which sits in the same directory as the script.

It looks like this (there’s a description that follows):

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
/*
 
  firewall.json
 
  List of firewall rules, for the firewall.pl CGI script
 
  See http://www.randomnoun.com/wp/2013/02/25/firewalling-your-smtp-traffic/
  for a brief description of the syntax of this file.
 
*/
{
  "syntax" : "firewall.pl-1.0",
  "revision" : "$Id: firewall.json,v 1.6 2013-02-22 02:28:54 knoxg Exp $",
  "config" : {
    "logfile" : "/var/log/firewall.log",
    "path" : "/usr/bin:/bin",
    "machine" : "192.168.0.1",
    "connection" : "telnet-busybox",
    "username" : "admin",
    "password" : "admin",
    "timeout" : "10",
    "rules" : [ 
      {
        "name" : "HTTP",
        "type" : "close",
        "image" : "pc2globe",
        "comment" : "HTTP traffic is considered to be any tcp traffic on port 80.",
        "iptable" : [
           { "target" : "DROP", "prot" : "tcp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "80", "specAsServiceName" : "www" }
         ]
      }, {
        "name" : "SMTP",
        "type" : "close",
        "image" : "pc2globe",
        "comment" : "SMTP traffic is considered to be any tcp or udp traffic on ports 25 (SMTP), 465 (SMTP over SSL; e.g. google mail), and 587 (SMTP over SSL; e.g. hotmail)",
        "iptable" : [
           { "target" : "DROP", "prot" : "udp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "587" },
           { "target" : "DROP", "prot" : "tcp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "587" },
           { "target" : "DROP", "prot" : "udp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "465" },
           { "target" : "DROP", "prot" : "tcp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "465" },
           { "target" : "DROP", "prot" : "udp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "25" },
           { "target" : "DROP", "prot" : "tcp", "opt" : "--", "source" : "anywhere", "dest" : "anywhere", "spec" : "25", "specAsServiceName" : "smtp" }
        ]
      }, {
        "name" : "FISHMONGER",
        "type" : "close",
        "image" : "pc2globe",
        "comment" : "test rule with no ipRules",
        "iptable" : [ ]
      } 
    ]
  }
}

It is structured as a JSON Object, with the following keys:

  • syntax: this value defines the version of the syntax of the configuration file. This is currently set to ‘firewall.pl-1.0’, but may change if I ever decide to make it a bit more fancy (e.g. adding multiple routers, that sort of thing).
  • config: the main configuration object; this is a hashmap with the following keys:
    • logfile: the name of the logfile that records any operations made to the firewall. This file must be writable by the www-data user (or whatever user your web server is running as)
    • path: the value of the PATH environment variable (this PATH needs to be able to access the telnet binary)
    • machine: the machine holding the iptables rules (in my case, this is my ADSL router)
    • connection: the type of connection used to communicate with the machine (there is only one connection type currently implemented, which is “telnet-busybox“)
    • username: the username used to authenticate the telnet connection
    • password: the password used to authenticate the telnet connection
    • timeout: a timeout (in seconds) for the router to respond to commands
    • rules: the list of rules for this router; the list is a JSON array, with each element containing a hashmap representing a single rule (represented as a block in the web UI). The hashmap has following keys:
      • name: the name of the rule
      • type: either ‘open‘ or ‘closed‘; this is the state of the rule when the iptables records are active
      • image: either ‘globe2pc‘ ( ) or ‘pc2globe‘ ( ); this is the image displayed to the user to represent whether the rule represents incoming or outgoing network traffic
      • comment: tooltip text which is displayed when the user hovers their mouse over a rule on the webpage
      • iptable: a list of iptables rules; the list is a JSON array, with each element containing a hashmap with the following keys:
        • target: what the iptables filter will do if the rule matches; to prevent a packet from being sent, this should be set to DROP
        • prot: the protocol for this packet; either udp or tcp
        • opt: iptables options; normally set to --
        • source: the source of the packet; normally set to anywhere
        • dest: the destination of the packet; normally set to anywhere
        • spec: the match specification, which is normally set to the port number of the TCP packet; e.g. for HTTP this would be set to 80
        • specAsServiceName: to handle the case when iptables displays port numbers as service names (from /etc/services), this is an alternate string to match against; e.g. for HTTP on my router this is set to www

Any other keys (e.g. the comment and revision keys at the top of the file) are ignored.

In order to run the script, you’ll need the perl ‘Expect’ package, and the Javascript minifier package which you can install on an Ubuntu or other debian-based system using the commands

sudo apt-get install libexpect-perl
sudo apt-get install libjavascript-minifier-perl

Copy it into a directory with executable permissions in your webserver (e.g. cgi-bin), and bob’s your uncle.

For those people who actually look at the code, you might notice that the script has a few interesting features:

  • All the javascript, css, and image resources for the script are contained in the script. The images are encoded as base64, and decoded before being sent back to the browser
  • The getTemplateAsPerl() function converts an ASP/JSP-like script into it’s perl equivalent. It understand <%= ... %>-style substitutions and <% ... %>-style code blocks. See the getFirewallHtmlTemplate() function for the sort of input it handles.

You can download the files from here, if you like:

Download script
firewall.pl

Download config
firewall.json

A few caveats:

  • The JSON configuration is minified before it is parsed, in order to remove comments in the source file. This has the the benefit of allowing comments in the file, but also means that any parsing errors encountered will have the wrong line/offset reported. So be wary of that
  • The type value in the JSON configuration file doesn’t actually do anything, but I’ve kept it there for future expansion
  • The script currently only performs telnet logins to authenticate to a router (the telnet-busybox connection type); if you add any more connection types (e.g. via ssh, or running on the localhost), let me know and I’ll add them to the script on this site.
  • The username/password credentials for the telnet-busybox connection type are kept in plaintext, but this is probably OK, since the credentials should only work if you’re already inside the ‘trusted’ side of your network, and anyone who has access to the script probably has access to your router anyway.
  • The rules set by this script aren’t persisted or stored anywhere; so when the router running the iptables commands loses and regains power (or is turned off and on again or reset manually), all the rules will revert to their initial state.
  • A rule is considered ‘closed’ if all the iptables records defined for that rule are active, and ‘open’ if none of the iptables records defined for that rule are active. If some (but not all) of the iptables records defined for that rule are active, then the rule is displayed as being partially open: (image of that). Clicking on such a rule will close it completely.
  • Before running this for the first time, you might want to set the DEBUG_ECHO constant defined at the top of the script; this converts any iptables commands sent to the router to echo commands (which just relays them back to you without doing anything). That way you can verify that the commands it sends are what you want, since sending the wrong commands could end up with you preventing all network traffic to and from your router. (If this does happen, just turn the thing off and on again).
  • There’s no locking implemented in this script, so if two people attempt to change the iptables rules at the same time, then strange and unusual things will probably happen.

Enjoy.

Update 12/5/2013: If you notice a whole bunch of iptables rules of the form ‘ACCEPT tcp/udp anywhere (your IP) dpt:31280‘ (or port numbers near there), then it’s probably Skype trying to tear holes in your firewall. I’m not a huge fan of software being able to circumvent security policy, so if you disable UPnP on your router and restart it, they should disappear.

Add a Comment

Your email address will not be published. Required fields are marked *