By Jason Marcello
Why Layer 2?
Layer 2 security is often overlooked when thinking about how to secure an environment. Many neglect the fact that security vulnerabilities exist on all seven layers of the OSI model, and layer 2 is just as important as any other layer. Attacks such as spanning tree protocol attacks, ARP attacks, MAC address table flooding, VLAN hopping etc. are just a few examples of things that can go wrong on layer 2. For example, if port-security isn’t configured anywhere on the LAN, then anyone could plug into a wall Ethernet port, and perform a mac-address table flood, essentially turning a switch into a hub (not good). Misconfigurations in switches can cause dangerous attacks that could lead to DOS, interception of traffic, spoofing, etc. For this reason and several others, it is important to have a quick and efficient way to audit layer 2 and search for these vulnerabilities.
How can we audit layer 2?
In a huge business, with switches located across hundreds of different datacenters and offices, how can we ensure that every switch is configured in a secure manner? Having thousands of switches to manage makes it nearly impossible to manually log into each switch to perform a security audit. Luckily for us, Ansible comes with several network modules that can aid us in designing automation software to do the hard work for us. Ansible supports many networking platforms such as Cisco, HP, Juniper, etc. A long list of all the modules can be found here https://docs.ansible.com/ansible/latest/modules/list_of_network_modules.html. I will be focusing on the HP Aruba modules, most notably the “aruba_command” module which allows us to run commands on HP Aruba switches from Ansible. I began to run different show commands with the aruba_commad module, testing on my HP 2530 home lab switch to see how this could possibly be used to perform a security audit on layer 2 switches. I found that there are many configuration options that you can see with certain show commands to determine if a switch will be vulnerable to a specific attack. The attacks I currently check for in my code are mac-address table flooding attacks, ARP attacks and DHCP snooping attacks.
Let’s take a look at the code
I wrote an Ansible playbook and a custom python module to perform an audit on my HP 2530 switch to see if my switch is currently vulnerable to mac-address table flooding attacks, ARP attacks, or DHCP snooping attacks. Essentially, my code logs into my switch, runs a few show commands using the aruba_command module, and sends the output of those commands to my python module where the output is analyzed and an audit report is generated based on the current configuration. My repository can be found here https://github.com/jmarcello97/HP-Aruba-Switch-Security-Audit.
Above you can see my playbook. 10.0.0.5 is the private IP of my switch (this would obviously change if you were to test this on your own network). Overall, the playbook is pretty straightforward. I run the commands “show port-security”, “show arp-protect”, and “show dhcp-snooping | i DHCP Snooping” and send the stdout of these commands to my python module (security_audit.py). This currently will only work on one switch, but can easily be modified to loop a hosts file and hit multiple switches in one run-through, which would be ideal in a large business. Now let’s dissect the python module.
I’m going to break the code down by analyzing each function separately, so it only makes sense to start with main(). The “fields” dictionary will define the output of the commands that ran in the Ansible playbook. Under that we have “module = AnsibleModule(argument_spec=fields)”, which passes the fields variable made to the AnsibleModule function to gather the output of the commands ran. Now since the output of the “show” commands were successfully put into python; we can parse through the output and determine the security of the current switch configuration. The rest of main just runs the necessary functions to analyze the output and exits the module at the end.
“show port-security” sample output
Port security check function
Now let’s take a look at the first function called in main(). Above we can see the output of the “show-port security” command and below that we can see the port_security_audit function which parses through this output and puts the “Learn Mode”, “Action”, and “Eavesdrop Prevention” configuration settings for each interface into a dictionary called “port_security_config”. This function essentially loops through each line in the output (each interface), splits the line to extract the configuration setting of each option, and puts that information into a dictionary that is returned to main(). We will discuss the security of all these options later on when we take a look at the generate_audit_report function.
“show arp-protect” and “show dhcp-snooping” sample output
Arp_protect_audit and dhcp_snooping_audit functions
These functions are very straightforward and have a lot of room for further functionality. At the moment, they simply just grab the line in the output of each command that specifies whether the option is enabled or not. So in the “show arp-protect” command output, the line that says “ARP Protection Enabled: “ is grabbed and returned to main and in the “show dhcp-snooping” command output, the line that says “DHCP Snooping: “ is grabbed and sent to main(). I also plan on adding functionality to add checks for trusted/untrusted interfaces, protected vlans, untrusted policy, etc. but unfortunately have not gotten to that yet as of this post.
This final function is the one that actually analyzes the output parsed in the other functions, and generates an audit report specifying how secure the current configuration options for these commands are. The first output I analyze is the output of the port-security. I check to see if the “Learn Mode” option is set to “Continuous”. If this is set to continuous, the interface will not restrict the number of mac-addresses that can be learned from that interface making it vulnerable to mac-address table flooding. This is when an attacker can send a ton of different/bogus mac-addresses to an interface so that they will be added to the switches mac-address table. This table will eventually overflow and the switch will begin to forward all traffic out of all of its interfaces (acting as a hub). The next option checked is the “Action” option, which specifies the action that will be taken if a rule is broken. For example, if the “Learn Mode” is set to static (only the specified number of mac-addresses can be learned) and 5 addresses are allowed to be learned, then if a sixth is attempted to be learned, it will trigger the “Action” option. The “Action” option can be set to “None”, “Send-Alarm”, or “Send-disable”. “None” will do nothing if the learn mode is violated, “Send-Alarm” will send an alarm, and “Send-Disable” will send an alarm and completely disable the port. Here I simply check if there is no action taken (“None”), which is a security risk. The final option I check is the “Eavesdrop Prevention” option. If this option is set to “Disabled”, it means that a unicast flooding attack could occur. So, the recommended configuration is to set this to “Enabled”. I then simply check if the arp-protect option is set to “Yes”. If it’s set to yes, then arp-requests/responses will be verified for legitimacy. If this option was set to “No”, a man in the middle attack could occur where an attacker binds his mac-address to a legitimate IP address so that he receives traffic that is meant to go to a different user. The final output I analyze is the dhcp-snooping command. Again, I simply check to see if this option is set to “Yes”. If not, then the switch is vulnerable to DHCP snooping attacks where a rogue DHCP server is placed into the LAN which would allow an attacker to log traffic and forward it to the correct gateway, making this attack nearly invisible.
Let’s run the playbook
The playbook can get executed with the following command where hosts is the file that contains the IP address of the target switch and “manager” is the user account we use to log in. The –ask-pass option will prompt you for a password upon execution:
ansible-playbook -i hosts -u manager get_info.yml –ask-pass
Upon execution, the output should look like the one above if no verbosity is set. This should generate a file called “audit_report.txt”.
Audit report output for first four interfaces (port-security check)
If you refer back to the “show port-security” command output screenshot above, you can see that ports 1, 3, and 4 have a continuous learn mode and have action set to “None”. This is a security risk and the results will tell you that. Port 2 on the other hand is labeled as “seems secure” which is because it has a static learn-mode and the action is set to send-disable which will send an alarm and disable the port if the static address rule is violated.
Audit report output for dhcp-snooping and arp-protect check
The next bit of output in the audit report are the results for the dhcp-snooping and arp-protect checks. In this example, arp protection was set to “No” (disabled) and triggers a warning. On the other hand, dhcp-snooping is enabled globally and notifies the user that the configuration looks good.
Final thoughts and possible future work:
This report clearly does not get into the nitty gritty details of security flaws in switch configurations. However, it serves as a starting point for an idea that can make the lives of auditors much easier and ensure that layer 2 is always as secure as possible. Some possible future work would be to check for more attacks and attempt to fix these security flaws automatically. However, this would be tricky in a business environment because any sudden change to a switch configuration could possibly break something and lead to a network outage. I have also tested the Cisco Nexus Ansible modules in the past and they seem to work great. With Ansible supporting so many different platforms, network automation can be deployed on a large scale and make the lives of networking and security professionals much easier.