{"id":741,"date":"2022-11-20T16:06:21","date_gmt":"2022-11-21T02:06:21","guid":{"rendered":"https:\/\/wroberts.me\/?p=741"},"modified":"2022-12-14T12:45:31","modified_gmt":"2022-12-14T22:45:31","slug":"create-a-firewall-using-python-and-suricata","status":"publish","type":"post","link":"https:\/\/wroberts.me\/?p=741","title":{"rendered":"Creating a Firewall Using Python and Suricata"},"content":{"rendered":"\n<p>In this post, I&#8217;m going to show how to make an ip-blocking firewall using Python and Suricata. I&#8217;ve recently started learning Python and this was a project to help solidify what I&#8217;ve learned so far while applying it to cybersecurity. This program operates as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Runs in the background as a linux daemon<\/li>\n\n\n\n<li>Scans the Suricata eve.json file to find certain alerts and blocks the related IP addresses in iptables<\/li>\n\n\n\n<li>Can also block IPs based on pcap files fed to Suricata<\/li>\n<\/ul>\n\n\n\n<p>In practice, this program would run on a linux box set up as a transparent bridge in order to monitor network traffic. Suricata on this machine would trigger alerts and this program would block the IPs for the entire network.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Requirements\"><\/span>Requirements<span class=\"ez-toc-section-end\"><\/span><\/h2><div id=\"ez-toc-container\" class=\"ez-toc-v2_0_83 counter-hierarchy ez-toc-counter ez-toc-transparent ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/wroberts.me\/?p=741\/#Requirements\" >Requirements<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/wroberts.me\/?p=741\/#Create_Ipset\" >Create Ipset<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/wroberts.me\/?p=741\/#Code_Explanation\" >Code Explanation<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/wroberts.me\/?p=741\/#Function_Explanations\" >Function Explanations<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/wroberts.me\/?p=741\/#Log_Formatter\" >Log Formatter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/wroberts.me\/?p=741\/#IP_List\" >IP List<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/wroberts.me\/?p=741\/#Alert_Count\" >Alert Count<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/wroberts.me\/?p=741\/#Log_Parser\" >Log Parser<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/wroberts.me\/?p=741\/#Main_Loop\" >Main Loop<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/wroberts.me\/?p=741\/#Daemon_Setup\" >Daemon Setup<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/wroberts.me\/?p=741\/#Add_Program_to_Cron\" >Add Program to Cron<\/a><\/li><\/ul><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/wroberts.me\/?p=741\/#Firewall_Demonstration\" >Firewall Demonstration<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/wroberts.me\/?p=741\/#Block_IPs_from_SSH_Brute_Force\" >Block IPs from SSH Brute Force<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/wroberts.me\/?p=741\/#Blocking_Malicious_IPs\" >Blocking Malicious IPs<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/wroberts.me\/?p=741\/#Conclusion\" >Conclusion<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n\n<p>Since this program uses pcaps that contain malware, this demonstration is taking place on virtual machines on an isolated network without internet access. The necessary packages were installed before isolation.<\/p>\n\n\n\n<p>Besides Suricata, the machine running this script also needs ipset installed. Supervisor is not necessary to run the script but is used to manage it as a daemon. On Ubuntu\/Debian they can be installed with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt ipset supervisor -y<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Create_Ipset\"><\/span>Create Ipset <span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Ipset allows us to efficiently block IPs with iptables. Rather than adding a massive amount of IP addresses to block into iptables directly, we can instead add the IPs to an ipset list, then add that ipset list to iptables. Not only does it make our iptables look cleaner but more importantly makes it easier to manage. Create a new ipset list with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ipset create blocklist hash:ip hashsize 4096<\/code><\/pre>\n\n\n\n<p>If you run the command: sudo ipset list, you&#8217;ll see the created.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-1.png\" alt=\"\" class=\"wp-image-768\" width=\"818\" height=\"187\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-1.png 818w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-1-300x69.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-1-768x176.png 768w\" sizes=\"auto, (max-width: 818px) 100vw, 818px\" \/><\/figure>\n<\/div>\n\n\n<p>Next, we need to add this list to iptables. Run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo iptables -I INPUT -m set --match-set blocklist src -j DROP<\/code><\/pre>\n\n\n\n<p>Now, any IP added to our ipset list will be blocked by iptables.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"822\" height=\"95\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-2.png\" alt=\"\" class=\"wp-image-771\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-2.png 822w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-2-300x35.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-2-768x89.png 768w\" sizes=\"auto, (max-width: 822px) 100vw, 822px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Code_Explanation\"><\/span>Code Explanation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The code can be downloaded from my github here:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/willjroberts\/Suricata-Firewall\/blob\/main\/suricata_firewall.py\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/willjroberts\/Suricata-Firewall\/blob\/main\/suricata_firewall.py<\/a><\/p>\n\n\n\n<p>The script is run from the terminal with two arguments. The first is the path of the Suricata eve.json file. The second is the name of the ipset list. The main loop function starts with creating variables for the json file and ipset list.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"727\" height=\"72\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-8.png\" alt=\"\" class=\"wp-image-779\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-8.png 727w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-8-300x30.png 300w\" sizes=\"auto, (max-width: 727px) 100vw, 727px\" \/><\/figure>\n<\/div>\n\n\n<p>The basic flow of the program is as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Stores the eve.json path and ipset list as variables<\/li>\n\n\n\n<li>Scans the provided eve.json file for relevant keywords and adds the IPs of the alerts to the ipset list<\/li>\n\n\n\n<li>Records the number of alerts currently in eve.json<\/li>\n\n\n\n<li>Waits for a short period of time<\/li>\n\n\n\n<li>Gets the current number of alerts in eve.json again. If the number of alerts is higher than last recorded, meaning new alerts were added, scan these new alerts<\/li>\n\n\n\n<li>Wait again and repeat indefinitely<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Function_Explanations\"><\/span>Function Explanations<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Log_Formatter\"><\/span>Log Formatter<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>An essential function. This function is needed to convert the eve.json file into a Python-readable format. This function opens eve.json and uses the json library to create a list of Python dictionaries. This was necessary since despite the format of the eve.json file looking like a Python dictionary, it&#8217;s type is actually a string. The function returns a list with each item as a Suricata alert.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"480\" height=\"266\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-11.png\" alt=\"\" class=\"wp-image-784\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-11.png 480w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-11-300x166.png 300w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"IP_List\"><\/span>IP List<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>This function gets the IPs currently in the provided ipset list. When used with another function, it tells the program to add a new IP to the ipset list if it not in it already.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"773\" height=\"203\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-12.png\" alt=\"\" class=\"wp-image-785\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-12.png 773w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-12-300x79.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-12-768x202.png 768w\" sizes=\"auto, (max-width: 773px) 100vw, 773px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Alert_Count\"><\/span>Alert Count<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>This function is for keeping track the number of alerts in eve.json.   <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"266\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-10.png\" alt=\"\" class=\"wp-image-782\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-10.png 476w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-10-300x168.png 300w\" sizes=\"auto, (max-width: 476px) 100vw, 476px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Log_Parser\"><\/span>Log Parser<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>The main workhorse of the program. This function scans eve.json for relevant keywords and adds the IPs to the ipset list if they&#8217;re not the list already. If there are new IPs added, the IP and time it was added is output to the screen and recorded in a log file.<\/p>\n\n\n\n<p> In this program, there are two lists for keywords. The reason for this is Suricata records two IPs per alert, a source IP and a destination IP. For some alerts such as those related to malware, the IP you want to block is the source IP \/ source of the malware. In others cases, alerts deal with information getting out of your network such as malware trying to reach a C2 server. In this case, you want the destination IP since the source IP will be the machine on your network. The lists in this program, src_keywords and dest_keywords are examples of keywords can be used for scanning eve.json for alerts of interest.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"947\" height=\"742\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-13.png\" alt=\"\" class=\"wp-image-786\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-13.png 947w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-13-300x235.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-13-768x602.png 768w\" sizes=\"auto, (max-width: 947px) 100vw, 947px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Main_Loop\"><\/span>Main Loop<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>The necessary libraries are imported. When the program starts, it stores the path of the provided eve.json file and ipset list as variables.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"668\" height=\"354\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-3.png\" alt=\"\" class=\"wp-image-774\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-3.png 668w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-3-300x159.png 300w\" sizes=\"auto, (max-width: 668px) 100vw, 668px\" \/><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"742\" height=\"30\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-7.png\" alt=\"\" class=\"wp-image-778\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-7.png 742w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-7-300x12.png 300w\" sizes=\"auto, (max-width: 742px) 100vw, 742px\" \/><\/figure>\n<\/div>\n\n\n<p>Then, the function records the current alert count by calling the alert_count function. The alert_count function needs first to use the log_formatter function to make the provided eve.json file readable. A second alert count variable is made and is set equal to the initial alert count. These two variables are used later to keep determine if there new alerts in eve.json. If the current alert count is greater than the initial count, then there are new alerts to scan.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"736\" height=\"434\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-9.png\" alt=\"\" class=\"wp-image-780\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-9.png 736w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-9-300x177.png 300w\" sizes=\"auto, (max-width: 736px) 100vw, 736px\" \/><\/figure>\n<\/div>\n\n\n<p>After getting the alert counts, the program scans eve.json for relevant alerts. Then the programs sleeps.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"550\" height=\"65\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-16.png\" alt=\"\" class=\"wp-image-796\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-16.png 550w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-16-300x35.png 300w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/figure>\n<\/div>\n\n\n<p>After the initial scans are done, a while loop starts. First, the number of current number of alerts is recorded again by calling the alert_count function. If the current number of alerts (currentAlertCount) is  not higher than the inital alert count, the loop sleeps for 5 seconds. If the current number if alerts is higher, the loop continues.<\/p>\n\n\n\n<p>First, it creates an empty list called reduced log. Then, the log formatter function is called on the current eve.json file and stored in the variable updatedLog. A for loop is then performed on updatedLog which appends only the newest eve.json alerts to reducedLog. The loop is able to know the number of alerts to add by using the difference the initial alert count and the current alert count as ranges.<\/p>\n\n\n\n<p>Then, the log_parser function is called using the reducedLog as an argument. If any of the alerts contain the relevant keywords, their IP will be added to the ipset list.<\/p>\n\n\n\n<p>Finally, initialAlertCount is set equal currentAlertCount to reset their difference and the loop sleeps for 5 seconds. <\/p>\n\n\n\n<p>The two exception clauses are included for manually stopping the program with a keyboard interrupt or some other unexpected reason. In both cases, the reason and time of the error are logged to an error file.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"787\" height=\"586\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-15.png\" alt=\"\" class=\"wp-image-795\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-15.png 787w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-15-300x223.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-15-768x572.png 768w\" sizes=\"auto, (max-width: 787px) 100vw, 787px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Daemon_Setup\"><\/span>Daemon Setup<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>While this program can run in the background by default, we can use supervisor to automatically run it at startup. Start by moving the program to the \/opt folder.<\/p>\n\n\n\n<p>Next, we need to create the configuration file in supervisor to manage the firewall program as a daemon. Navigate to the supervisor directory and create the configuration file with the following commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/etc\/supervisor\/conf.d\nsudo vim suricata-firewall.conf<\/code><\/pre>\n\n\n\n<p>Copy the following into the configuration file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n&#91;program:suricata_firewall]\nprocess_name=suricata_firewall\ncommand=python3 \/opt\/suricata_firewall.py \/var\/log\/suricata\/eve.json blocklist\nautostart=true\nautorestart=unexpected\nnumprocs=1\nstartsecs=1\nstartretries=30\nstopsignal=QUIT\nstopwaitsecs=30<\/code><\/pre>\n\n\n\n<p>On the command line, we&#8217;re running this script with the default path of the Suricata eve.json file and the name of the ipset blocklist used earlier. You can change these based on your system&#8217;s setup.<\/p>\n\n\n\n<p>Now, restart supervisor with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo service restart supervisor<\/code><\/pre>\n\n\n\n<p>Then, run supervisor to make sure the firewall program is running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo supervisorctl<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"837\" height=\"84\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-17.png\" alt=\"\" class=\"wp-image-799\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-17.png 837w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-17-300x30.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-17-768x77.png 768w\" sizes=\"auto, (max-width: 837px) 100vw, 837px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Add_Program_to_Cron\"><\/span>Add Program to Cron<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p>The final step in setting up the daemon is using another daemon manager called cron. Cron allows us to run commands at certain intervals for us. In this case, we&#8217;re going to have cron restart supervisor every 24 hours or in other words, reset all of our daemons every day.<\/p>\n\n\n\n<p>Run the command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>crontab -e<\/code><\/pre>\n\n\n\n<p>At the end of the file add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@daily supervisorctl restart all<\/code><\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-18.png\" alt=\"\" class=\"wp-image-800\" width=\"678\" height=\"401\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-18.png 678w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-18-300x177.png 300w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Firewall_Demonstration\"><\/span>Firewall Demonstration<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Now that the firewall is up and running, we&#8217;re going to test it by:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Trying to SSH into this machine from an unauthorized machine<\/li>\n\n\n\n<li>Running a pcap that will trigger alerts related to malware<\/li>\n<\/ul>\n\n\n\n<p>Make sure Suricata is running. You can run it with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl start suricata.service<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Block_IPs_from_SSH_Brute_Force\"><\/span>Block IPs from SSH Brute Force<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>In this scenario, our machine is only allows SSH access from certain machines. In Suricata, we&#8217;ll make a custom rule in the local.rules file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vim \/etc\/suricata\/rules\/local.rules<\/code><\/pre>\n\n\n\n<p>The rule says to alert if any machine that is not 10.80.80.2 that tries to connect to this device over port 22.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"818\" height=\"53\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-23.png\" alt=\"\" class=\"wp-image-817\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-23.png 818w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-23-300x19.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-23-768x50.png 768w\" sizes=\"auto, (max-width: 818px) 100vw, 818px\" \/><\/figure>\n<\/div>\n\n\n<p>From our program, recall that we made one of the keywords &#8216;SSH&#8221;.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"645\" height=\"137\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-20.png\" alt=\"\" class=\"wp-image-814\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-20.png 645w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-20-300x64.png 300w\" sizes=\"auto, (max-width: 645px) 100vw, 645px\" \/><\/figure>\n<\/div>\n\n\n<p>To test this alert and the firewall, we need to attempt to SSH into this machine from another one on the same isolated network. On the other machine, I make an SSH attempt.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"663\" height=\"55\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-24.png\" alt=\"\" class=\"wp-image-818\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-24.png 663w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-24-300x25.png 300w\" sizes=\"auto, (max-width: 663px) 100vw, 663px\" \/><\/figure>\n<\/div>\n\n\n<p>SSH is blocked on the target machine by default but we should still see an alert from Suricata and the IP added to the ipset list.<\/p>\n\n\n\n<p>Checking the fast.log on our target machine, we see the alert.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"843\" height=\"52\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-25.png\" alt=\"\" class=\"wp-image-819\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-25.png 843w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-25-300x19.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-25-768x47.png 768w\" sizes=\"auto, (max-width: 843px) 100vw, 843px\" \/><\/figure>\n<\/div>\n\n\n<p>Now, if the firewall is working properly, the IP of the machine that tried to SSH should be added to the ipset blocklist. We can check the list with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ipset list<\/code><\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"721\" height=\"163\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-26.png\" alt=\"\" class=\"wp-image-820\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-26.png 721w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-26-300x68.png 300w\" sizes=\"auto, (max-width: 721px) 100vw, 721px\" \/><\/figure>\n<\/div>\n\n\n<p>This is exactly what we needed to see. Now this IP is blocked. We can confirm this by enabling the SSH service on the target machine  and trying to SSH into again. We&#8217;ll see the connection refuse again. Re-enable the SSH service with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl start ssh.service<\/code><\/pre>\n\n\n\n<p>When trying to SSH into the target machine, the connection times out.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"637\" height=\"77\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-27.png\" alt=\"\" class=\"wp-image-821\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-27.png 637w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-27-300x36.png 300w\" sizes=\"auto, (max-width: 637px) 100vw, 637px\" \/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Blocking_Malicious_IPs\"><\/span>Blocking Malicious IPs<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>For this next part, we&#8217;re going to replay a pcap file containing malware from https:\/\/www.malware-traffic-analysis.net\/. This file contains packets that downloaded Dridex malware which is a type of malware that harvests credentials and return them to a C2 server. For the firewall, it means it needs to block the IPs that downloaded the malware and the IPs of the C2 servers the malware will try to connect to. Our keywords in our firewall program reflect this.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"645\" height=\"137\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-28.png\" alt=\"\" class=\"wp-image-822\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-28.png 645w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-28-300x64.png 300w\" sizes=\"auto, (max-width: 645px) 100vw, 645px\" \/><\/figure>\n<\/div>\n\n\n<p>This pcap file is going to be replayed in offline mode in Suricata to simulate the firewall reacting the packets in a real scenario. When pcaps are replayed in offline mode, Suricata creates a separate eve.json file so we need to run the firewall program on this new json file. <\/p>\n\n\n\n<p>First, we need to stop supervisor so we can run the program with the new eve.json file.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"456\" height=\"65\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-29.png\" alt=\"\" class=\"wp-image-825\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-29.png 456w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-29-300x43.png 300w\" sizes=\"auto, (max-width: 456px) 100vw, 456px\" \/><\/figure>\n<\/div>\n\n\n<p>Then, we&#8217;re going to run the replay the pcap file in the directory it was downloaded.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-30.png\" alt=\"\" class=\"wp-image-826\" width=\"840\" height=\"28\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-30.png 840w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-30-300x10.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-30-768x26.png 768w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><\/figure>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"952\" height=\"241\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-31.png\" alt=\"\" class=\"wp-image-827\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-31.png 952w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-31-300x76.png 300w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-31-768x194.png 768w\" sizes=\"auto, (max-width: 952px) 100vw, 952px\" \/><\/figure>\n\n\n\n<p>Before running the firewall, let&#8217;s view some of the alerts generated to understand why the keywords chosen for the firewall program.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"493\" height=\"38\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-32.png\" alt=\"\" class=\"wp-image-828\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-32.png 493w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-32-300x23.png 300w\" sizes=\"auto, (max-width: 493px) 100vw, 493px\" \/><\/figure>\n<\/div>\n\n\n<p>Here&#8217;s where the alert generated from downloading the malware. From this alert, we added the keyword DLL and EXE.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"695\" height=\"391\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-33.png\" alt=\"\" class=\"wp-image-829\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-33.png 695w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-33-300x169.png 300w\" sizes=\"auto, (max-width: 695px) 100vw, 695px\" \/><\/figure>\n<\/div>\n\n\n<p>And this is the alert for catching possible dridex traffic.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"692\" height=\"417\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-34.png\" alt=\"\" class=\"wp-image-830\" srcset=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-34.png 692w, https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-34-300x181.png 300w\" sizes=\"auto, (max-width: 692px) 100vw, 692px\" \/><\/figure>\n<\/div>\n\n\n<p>Now, we&#8217;ll run the firewall program on the pcap.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo python3 \/opt\/suricata_firewall.py eve.json blocklist<\/code><\/pre>\n\n\n\n<p>Upon starting the program, the malicious IPs are added to the blocklist.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"241\" height=\"97\" src=\"https:\/\/wroberts.me\/wp-content\/uploads\/2022\/12\/image-35.png\" alt=\"\" class=\"wp-image-831\"\/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>That wraps up the program demonstration. I had to do a fair amount of research to get this program working. Specifically, I had to learn how to run commands from a program using the subprocess library, error handling, and daemon management using supervisor and cron. There are improvements I would like to make including a better method of abstracting the keywords to scan Suricata alerts and a more efficient way to scan the eve.json file. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, I&#8217;m going to show how to make an ip-blocking firewall using Python and Suricata. I&#8217;ve recently started learning Python and this was a project to help solidify what I&#8217;ve learned so far while applying it to cybersecurity. This program operates as follows: In practice, this program would run on a linux box &#8230; <a href=\"https:\/\/wroberts.me\/?p=741\" class=\"more-link\">Read More<span class=\"screen-reader-text\"> &#8220;Creating a Firewall Using Python and Suricata&#8221;<\/span> &raquo;<\/a><\/p>\n","protected":false},"author":1,"featured_media":837,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15],"tags":[],"series":[],"class_list":["post-741","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/posts\/741","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=741"}],"version-history":[{"count":24,"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/posts\/741\/revisions"}],"predecessor-version":[{"id":852,"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/posts\/741\/revisions\/852"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=\/wp\/v2\/media\/837"}],"wp:attachment":[{"href":"https:\/\/wroberts.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=741"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=741"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=741"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/wroberts.me\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=741"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}