Block Ransomware botnet C&C traffic with a Mikrotik router

March 14, 2016

In my last blog post I wrote about blocking, detecting and mitigating the Locky Ransomware. I’ve referenced to a earlier blog post of mine which allows to block traffic to/from the Tor network. This blog post combines both – a way to block Ransomware botnet C&C traffic on a Mikrotik router. The base are the block lists from Abuse.ch, which also provide a nice statistic. Locky is not the most common Ransomware today.

ransomware

Linux part

You need also a small Linux/Unix server to help. This server needs to be trustworthy one as the router executes a script this server generates. This is required as RouterOS is only able to parse text files up to 4096 by itself, and the IP address and domain list is longer.

So first we create the script /usr/local/sbin/generateMalwareBlockScripts.py on the Linux server by downloading following Python script. Open the file and change the paths to your liking. The filename path works on CentOS, on Ubuntu you need to remove the html directory. Now make the file executable

chmod 755 /usr/local/sbin/generateMalwareBlockScripts.py

and execute it

/usr/local/sbin/generateMalwareBlockScripts.py

No output is good. Make sure that the file is reachable via HTTP (e.g. install httpd on CentOS) from the router. If everything works make sure that the script is called once every hour to update the list. e.g. place a symlink in /etc/cron.hourly:

ln -s /usr/local/sbin/generateMalwareBlockScripts.py /etc/cron.hourly/generateMalwareBlockScripts.py

Mikrotik part

Copy and paste following to get the IP address script onto the router:

/system script
add name=scriptUpdateMalwareIPs owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive source="# Script which will download a script which adds the malware IP addresses to an address-list\
\n# Using a script to add this is required as RouterOS can only parse 4096 byte files, and the list is longer\
\n# Written by Robert Penz <[email protected]> \
\n# Released under GPL version 3\
\n\
\n# get the \"add script\"\
\n/tool fetch url=\"http://10.xxx.xxx.xxx/addMalwareIPs.rsc\" mode=http\
\n:log info \"Downloaded addMalwareIPs.rsc\"\
\n\
\n# remove the old entries\
\n/ip firewall address-list remove [/ip firewall address-list find list=addressListMalware]\
\n\
\n# import the new entries\
\n/import file-name=addMalwareIPs.rsc\
\n:log info \"Removed old IP addresses and added new ones\"\
\n"

and copy and paste following for the DNS filtering script – surely you can combine them … I let them separated as maybe someone needs only one part:

/system script
add name=scriptUpdateMalwareDomains owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive source="# Script which will download a script which adds the malware domains as static DNS entry\
\n# Using a script to add this is required as RouterOS can only parse 4096 byte files, and the list is longer\
\n# Written by Robert Penz <[email protected]> \
\n# Released under GPL version 3\
\n\
\n# get the \"add script\"\
\n/tool fetch url=\"http://10.xxx.xxx.xxx/addMalwareDomains.rsc\" mode=http\
\n:log info \"Downloaded addMalwareDomains.rsc\"\
\n\
\n# remove the old entries\
\n/ip dns static remove [/ip dns static find comment~\"addMalwareDomains\"]\
\n\
\n# import the new entries\
\n/import file-name=addMalwareDomains.rsc\
\n:log info \"Removed old domains and added new ones\"\
\n"

To make the first try run use following command

/system script run scriptUpdateMalwareIPs

and

/system script run scriptUpdateMalwareDomains

if you didn’t get an error

/ip firewall address-list print

and

/ip dns static print

should show many entries. Now you only need to run the script once a hour which following command does:

/system scheduler add interval=1h name=schedulerUpdateMalwareIPs on-event=scriptUpdateMalwareIPs start-date=nov/30/2014 start-time=00:05:00

and

/system scheduler add interval=1h name=schedulerUpdateMalwareDomains on-event=scriptUpdateMalwareDomains start-date=nov/30/2014 start-time=00:10:00

You can use the address list and DNS blacklist now in various ways .. the simplest is following

/ip firewall filter
add chain=forward comment="just the answer packets --> pass" connection-state=established
add chain=forward comment="just the answer packets --> pass" connection-state=related
add action=reject chain=forward comment="no Traffic to malware IP addresses" dst-address-list=addressListMalware log=yes log-prefix=malwareIP out-interface=pppoeDslInternet
add action=reject chain=forward comment="report Traffic to DNS fake IP address" dst-address=10.255.255.255 log=yes log-prefix=malwareDNS out-interface=pppoeDslInternet
add chain=forward comment="everything from internal is ok --> pass" in-interface=InternalInterface

If a clients generates traffic to such DNS names or IP address you’ll get following in your log (and the traffic gets blocked):

20:07:16 firewall,info malwareIP forward: in:xxx out:pppoeDslInternet, src-mac xx:xx:xx:xx:xx:xx, proto ICMP (type 8, code 0), 10.xxx.xxx.xxx->104.xxx.xxx.xxx, len 84

or

20:09:34 firewall,info malwareDNS forward: in:xxx out:pppoeDslInternet, src-mac xx:xx:xx:xx:xx:xx, proto ICMP (type 8, code 0), 10.xxx.xxx.xxx ->10.255.255.255, len 84

ps: The Python script is done in a way that it easily allows you to add also other block lists … e.g. I added the Feodo blocklist from Abuse.ch.

29 Comments »

RSS feed for comments on this post. TrackBack URI

  1. Great topic, thanks!!!

    Comment by Bernd — March 21, 2016 #

  2. Thanks, just curious why you picked GPLv3 as its the worst license on the planet 😀

    Comment by Richard — January 21, 2017 #

  3. As it doesn’t matter as the script is written in Python/RouterOS-Script, so if you give someone the script you also gave him/her the source. If I didn’t add any license (as many to on github) you couldn’t use it at all.

    Comment by robert — January 21, 2017 #

  4. failure: unable to resolve hostname

    url=\”http://10.xxx.xxx.xxx/addMalwareDomains.rsc is a valid URL?

    Comment by Dhanny — March 3, 2017 #

  5. You need to replace that IP address with the IP address or hostname of your sever that hosts the file. It would be a bad idea to accept scripting commands from an external server.

    Comment by robert — March 5, 2017 #

  6. Where i get that addMalwareDomains.rsc ?

    Comment by addMalwareDomains.rsc — March 28, 2017 #

  7. gets generated by the python script

    Comment by robert — March 28, 2017 #

  8. is there any sample of that script?

    Comment by addMalwareDomains.rsc — March 28, 2017 #

  9. script is linked in the blog article.

    Comment by robert — March 28, 2017 #

  10. yes sorry i see… is there any chance to run this script on IIS?

    Comment by addMalwareDomains.rsc — March 28, 2017 #

  11. sure, python runs also under windows and the file is just a text file for the web server.

    Comment by robert — March 28, 2017 #

  12. Thank You very Much!
    It works perfectly on Synology NAS + Mirkotik RB1100AHx2!

    Comment by Mauri — April 24, 2017 #

  13. I have only 1 question:
    My RB log register only 1 error: “script error:failure:entry already exixst”

    this because obviously it tries to write some entities already present in the address list.

    Is this a problem? Do I need to insert a script in order to delete all the address list before to update it or it’s sufficient to ignore this error?

    Thank You very much!

    Comment by Mauri — April 26, 2017 #

  14. The script does delete the old entries:

    # remove the old entries
    ip firewall address-list remove [/ip firewall address-list find list=addressListMalware]

    please check if you copied the script correctly.

    Comment by robert — April 26, 2017 #

  15. ops…merging the 2 scripts (domains & IPs) togheter I missed this step…
    thank You very much! It works great! 😉

    Comment by Mauri — April 27, 2017 #

  16. Getting this error

    Traceback (most recent call last):
    File “/cmd/generateMalwareBlockScripts.py”, line 146, in
    main()
    File “/cmd/generateMalwareBlockScripts.py”, line 142, in main
    doGenerateScripts()
    File “/cmd/generateMalwareBlockScripts.py”, line 81, in doGenerateScripts
    resp = requests.get(url=entry[“url”])
    File “/usr/lib/python2.6/site-packages/requests/api.py”, line 68, in get
    return request(‘get’, url, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/api.py”, line 50, in request
    response = session.request(method=method, url=url, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/sessions.py”, line 464, in req uest
    resp = self.send(prep, **send_kwargs)
    File “/usr/lib/python2.6/site-packages/requests/sessions.py”, line 576, in sen d
    r = adapter.send(request, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/adapters.py”, line 431, in sen d
    raise SSLError(e, request=request)
    requests.exceptions.SSLError: [Errno 0] _ssl.c:330: error:00000000:lib(0):func(0 ):reason(0)

    Comment by Ben — May 1, 2017 #

  17. Fixed … My System CA certs were corrupted

    Comment by Ben — May 1, 2017 #

  18. Thks! thats coool!

    Comment by Dem — May 15, 2017 #

  19. Thanks! There is a minor problem with the script. If there are duplicate ip addresses in “addMalwareDomains.rsc”, ROS will stop with the address import. What to add into the script to remove duplicates?

    Comment by Patrik — August 3, 2017 #

  20. hmmm …. I need to adapt the script for that, currently it just adds everything. It needs to download the list and put every IP address into a set() which eliminates the duplicates and use that for the routeros script generation.

    Comment by robert — August 3, 2017 #

  21. As a partial solution I changed the output.append for the ip addresses section from “output.append(‘add list=addressListMalware address=”%s” comment=”addMalwareIPs %s”‘ % (ip, entry[“name”]))” to “output.append(‘/do {ip firewall address-list add list=addressListMalware address=%s comment=”addMalwareIPs %s”} on-error={};’ % (ip, entry[“name”]))”.

    This tells ROS to ignore (skip) duplicates. Something similar must be done for domains also.

    More info:
    https://forum.mikrotik.com/viewtopic.php?f=1&t=113540
    https://forum.mikrotik.com/viewtopic.php?f=1&t=113770

    Comment by Patrik — August 3, 2017 #

  22. Traceback (most recent call last):
    File “/cmd/generateMalwareBlockScripts.py”, line 146, in
    main()
    File “/cmd/generateMalwareBlockScripts.py”, line 142, in main
    doGenerateScripts()
    File “/cmd/generateMalwareBlockScripts.py”, line 81, in doGenerateScripts
    resp = requests.get(url=entry[“url”])
    File “/usr/lib/python2.6/site-packages/requests/api.py”, line 68, in get
    return request(‘get’, url, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/api.py”, line 50, in request
    response = session.request(method=method, url=url, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/sessions.py”, line 464, in req uest
    resp = self.send(prep, **send_kwargs)
    File “/usr/lib/python2.6/site-packages/requests/sessions.py”, line 576, in sen d
    r = adapter.send(request, **kwargs)
    File “/usr/lib/python2.6/site-packages/requests/adapters.py”, line 431, in sen d
    raise SSLError(e, request=request)
    requests.exceptions.SSLError: [Errno 0] _ssl.c:330: error:00000000:lib(0):func(0 ):reason(0)

    Solution :
    $ sudo apt-get install libffi-dev
    $ pip install pyOpenSSL ndg-httpsclient pyasn1

    Comment by Abdi Fahri — November 2, 2018 #

  23. Ransomware Tracker has been discontinued on Dec 8th, 2019

    Comment by Tansy Abud — December 21, 2019 #

  24. Hi I modified the script in the following points, now it works

    # Domain block lists

    domainBlockListFile = “/var/www/html/addMalwareDomains.rsc”
    domainBlockListsUrls = [{“name”: “Urlhaus_Host”, “url”: “https://urlhaus.abuse.ch/downloads/hostfile/”},]
    domainBlackListIPaddress = “10.255.255.255”

    # IP address block lists

    ipBlockListFile = “/var/www/html/addMalwareIPs.rsc”

    ipBlockListsUrls = [
    # abuse.ch Feodo Tracker Botnet C2 IP Blocklist (IPs only)
    #{“name”: “Feodo_IPBL”, “url”: “https://feodotracker.abuse.ch/downloads/ipblocklist.txt”},
    # abuse.ch Feodo Tracker Botnet C2 IP Blocklist (IPs only) – Aggressive
    {“name”: “Feodo_IPBL_AGG”, “url”: “https://feodotracker.abuse.ch/downloads/ipblocklist_aggressive.txt”},
    # abuse.ch SSLBL Botnet C2 IP Blacklist (IPs only)
    #{“name”: “SSL_IPBL”, “url”: “https://sslbl.abuse.ch/blacklist/sslipblacklist.txt”},
    # abuse.ch SSLBL Botnet C2 IP Blacklist (IPs only) – Aggressive
    {“name”: “SSL_IPBL_AGG”, “url”: “https://sslbl.abuse.ch/blacklist/sslipblacklist_aggressive.txt”},
    ]

    and

    #### domains
    output = [“# This script adds malware domains to a block list via static dns entries (list created: %s)” % time.asctime(),
    “/ip dns static”]

    for entry in domainBlockListsUrls:
    resp = requests.get(url=entry[“url”])
    for rawLine in resp.text.splitlines():
    domain = rawLine.strip().replace(‘127.0.0.1\t’,”)

    Comment by Angelo — July 5, 2020 #

  25. Hi Angelo,

    I got a syntax error here:

    domainBlockListsUrls = [{“name”: “Urlhaus_Host”, “url”: “https://urlhaus.abuse.ch/downloads/hostfile/”},]

    Comment by Maurizio — December 1, 2020 #

  26. The sytax error is due to ” char…sorry.

    But I think You missed something here:

    domain = rawLine.strip().replace(‘127.0.0.1\t’,”)

    I think It must be:

    domain = rawLine.strip().replace(‘127.0.0.1\t’,””)

    However I got this error:

    line 51, import requests
    ImportError: no module named requests

    Can You help me?
    Thank You very much!

    Maurizio

    Comment by Maurizio — December 1, 2020 #

  27. you need the python module requests installed on your system.

    ps: about the quotes, on my side they are correct, maybe your editor changed then.

    Comment by robert — December 1, 2020 #

  28. Thank You very much Robert!
    I fixed It.
    The only thing…my IP list has got some duplicated IPs (is the actual IP list – 07/12/2020).
    In mikrotik log I found that It give an error and It stop the procedure (“script error:failure:already have such entry”).

    For example, in the list the IP “185.157.162.81” is present 3 times.

    Is there a Way to do a check in the file while importing?

    Thanks!

    Maurizio

    Comment by Maurizio — December 7, 2020 #

  29. I fixed It! Thank You very much!

    But there is a little issue.

    The IP list has got duplicated lines, so the script give an error and stops itself and doesn’t parse the other lines.

    Is there a way to avoid this?

    Thank You very much!
    Maurizio

    Comment by Mauri — December 10, 2020 #

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Powered by WordPress
Entries and comments feeds. Valid XHTML and CSS. 38 queries. 0.050 seconds.