A Logo

Feel free to include my content in your page via my
RSS feed

Help Irongeek.com pay for
bandwidth and research equipment:

Subscribestar or Patreon

Search Irongeek.com:

Affiliates:
Irongeek Button
Social-engineer-training Button

Help Irongeek.com pay for bandwidth and research equipment:

paypalpixle


Security and Software Defined Networking: Practical Possibilities and Potential Pitfalls

Security and Software Defined Networking:
Practical Possibilities and Potential Pitfalls

Adrian Crenshaw

Abstract

Software Defined Networking (SDN) is a set of technologies for allowing greater control of how networks operate. Rather than a fairly static network that can only be controlled by proprietary vendor specific protocols, with sometimes limited visibility into the internals of layer 2 devices like switches, SDN allows for experimentation in optimizing and configuring how the network functions. Additionally, SDN can be controlled using commodity server hardware, which can add to the practicality and cost savings. This flexibility is network design is in part accomplished by separating the switch’s control plane from the data plane. Having this new level of control can be of great benefit to security engineers, and we will cover some potential use cases for SDN and information security. With this flexibility comes potential pitfalls as well, the logic in the code that runs the controller may have faults of its own that could be taken advantage of by attackers, or a given rule may have unintended side effects because of bugs in its implementation. This paper will cover a few potential possibilities, both negative and positive, of how SDN can be related to security. As OpenFlow seems to be the prevalent SDN architecture in research, this paper will focus on it.

Software Defined Networking vs. the Autonomous Systems model

Most traditional networks follow the Autonomous Systems (AS) model. In the AS model things are hierarchical, and its routing design has been compared to the post office, with set paths and delegation of who routes to whom on a fairly static basis based on simple communication protocols between the neighboring components. Each part of the network does its job, with little “awareness” of the other equipment’s duties. While the AS model is very scalable, it is not necessarily flexible. Let us say a given network node has been moved from one section of the network to another, it may take some time for the new location to be figured out and adjusted for by traditional Autonomous Systems, and some interruption of service is to be expected. In cloud architectures where servers are expected to be spun up and down as demand changes, perhaps even moved from data center to data center, and still be transparent to the end user, greater levels of control are needed for controlling the path of network communications. Another common use case for SDN is mobile data to devices such as cell phones and tablets. Mobile devices frequently change their location, but data flows to the ever-changing endpoints have to be managed in a reliable manner. One intent of SDN is to allow for this sort of flexibility and control.

Standards vs. Roll Your Own

In a way, network administrators and security engineers have had some of the power of SDN in the past. If desired, and the hardware in place had the functionality to support it, administrators could script something to detect changes and automate configuration adjustments on the network. The author of this paper has implemented similar things to this in the past to manage Cisco ASAs using the Python scripting language and the Pexpect library. The problem with some of these homebrew solutions is that they are not very standardized, may be specific to one vendor’s hardware, and may be incomprehensible to someone else trying to follow the collections of scripts, detection probes and improvised ways of making changes. APIs and standardized systems like OpenFlow can potentially help with this by allowing for a common interface amongst different switch vendors and allowing network admins who inherit a network from a predecessor a better understanding of what is being done on the controller side of a network.

OpenFlow, Oversimplified

                Most security practitioners the author has conversed with have never heard of OpenFlow or Software Defined Networking. This small section of the paper will be somewhat of a massive oversimplification of how OpenFlow works. We hope Software Defined Networking experts will excuse the diversion, but we would like to relay some of the concepts and core ideas of SDN to those who are unfamiliar with the concept so that they might better understand the model before we continue on to the more security related concepts. At its core, OpenFlow lets a network administrator define rules on a switch that allow them specify “if these conditions apply, send the packet out this port on the switch”. There are many variables that can be matched on, and some OpenFlow frameworks (like POX) have their own short-hands and extensions, but in OpenFlow version 1.3 [1] a controller can set a switch to make matches on at least the following required fields from the OpenFlow spec:

Field

Description

OXM_OF_IN_PORT

Ingress port. This may be a physical or switch-defined logical port.

OXM_OF_ETH_DST

Ethernet source address. Can use arbitrary bitmask.

OXM_OF_ETH_SRC

Ethernet destination address. Can use arbitrary bitmask.

OXM_OF_ETH_TYPE

Ethernet type of the OpenFlow packet payload, after VLAN tags.

OXM_OF_IP_PROTO

IPv4 or IPv6 protocol number.

OXM_OF_IPV4_SRC

IPv4 source address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV4_DST

IPv4 destination address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV6_SRC

IPv6 source address. Can use subnet mask or arbitrary bitmask.

OXM_OF_IPV6_DST

IPv6 destination address. Can use subnet mask or arbitrary bitmask.

OXM_OF_TCP_SRC

TCP source port

OXM_OF_TCP_DST

TCP destination port

OXM_OF_UDP_SRC

UDP source port

OXM_OF_UDP_DST

UDP destination port

 

                The administrator can set rules so that if certain matches are found, different actions can be taken. The most common are: Forward the packet out a given port or ports, encapsulate the packet and forward to the controller so a decision can be made about it, drop the packet, or just send the packet to the normal switch processing pipeline. Commonly, if the packet matches no rules in the flow table, the packet can be sent to the controller and the controller can decide what to do with it. The controller can then send new flow rules to the switch to tell it how to handle those sorts of packets in the future. This forwarding to the controller for instruction does cause a slow down in performance initially, but once a flow rule has been placed in the TCAM (Ternary Content-Addressable Memory) future packets should be sent on with little delay. Statistics about the switch can also be collected via the OpenFlow protocol which may be of interest to a security engineer.

Potential uses and potential pitfalls

The flexibility of Software Defined Networks/OpenFlow allows for a security engineer to try things they have not been able to easily do before. If they have an idea for how to rearrange data paths on the network to get better visibility on the network, or how to detect and shape possibly malicious traffic on the network, they can now experiment with these ideas. Of course, flexibility of control also allows for mistakes and unintended outcomes in configuration. For a comparison most security practitioners would recognize, consider stacks in memory and SQL backbends. Stacks and SQL are not necessarily insecure technologies in and of themselves, but badly designed applications that use them can bring with them vulnerabilities like buffer overflows and SQL Injection. Is it possible to find similar implementation issues in how people might configure OpenFlow? Currently looking for logic flaws or security possibilities in OpenFlow/SDN implementations would be a very niche endeavor, but over time, as more equipment supports it and more networks utilize it, it could become quite an interesting security topic for researchers.

With greater control of the network though standardized means, a security engineer has new possibilities and pitfalls to face. If a researcher wants to experiment with ways to mitigate against common layer 2 attacks like ARP poisoning, they now have the hooks into the networking equipment to do it. An idea in a similar vein is ELK [2], which is intended to cut back on unneeded ARP congestion by having a more centrally managed ARP table for the network. As a proof of concept the author has put together a simple implementation of a switch that is hardened again ARP poisoning as a proof of concept for this paper. The Anti-Arp-Poisoning switch demo is implemented in POX [2], a Python based framework for creating OpenFlow controllers, and is based on the GPLed POX code from the OpenFlow Tutorial page at OpenFlow.org [4]. The Anti-Arp-Poisoning switch actually had to be opened up somewhat from the switch made in the original OpenFlow tutorial to allow for more flexibility. As the tutorial switch was written originally, flows did not time out so the first MAC address to seize a set of identifiers would always have them. This is not very flexible if the same IP has to be reassigned later to a different MAC address, or moved to a different port on the switch. In the Anti-Arp-Poisoning switch POX implementation OpenFlow’s idle time option for flows is used, along with a table in memory to track IP to MAC address mappings. If a device continues to use the same MAC address and IP within the idle timeout period, the switch continues to consider it as having the rights to use those setting. If an attempt is seen to map a MAC address to an IP that is already taken according to the IP to MAC address table, the switch can detect this and take counter measures. These countermeasures could be anything from the shunning of connections from the (perceived) spoofing MAC address, forwarding to a switch port that has an IDS on it, scanning the host for malware using tools on the controller box initiated by the Python/POX script, or just simply alerting the controller’s admin. For the demo, the Anti-Arp-Poisoning switch simply alerts the console and sets up a shun flow to port 100 (unused) that takes twice as long to time out as a normal flow. If the controller receives a message that a given flow has timed out because it was idle for too long, the IP to MAC address mapping in the table for that IP is removed so that the IP is free to be used by another device attached to the switch. Of course, there are potential race conditions where an unintended MAC address could claim an IP first, but the logic of the switch makes this hard for ongoing connections that do not idle out. As mentioned, shunning of a perceived attacker is possible, but this should be done cautiously. Spoofed packets and bad logic in the controller’s code could lead to legitimate network devices being shunned and cause potential Denial of Service (DoS) problems to occur.

ARP Poisoning protection is just one possibility, and is used only as an example that’s relatively easy to demonstrate in code. If a network administrator wants to control how switch ports are mirrored to Intrusion Detection Systems, Intrusion Prevention Systems or Data Loss Prevention Systems, OpenFlow can potentially be used to implement this sort of functionality as well. Some work has already been done in controlling flows to these sorts of monitoring devices in the form of CloudwWatcher [3]. There is also the possibility of having systems that seem to be behaving oddly automatically quarantined via OpenFlow, or perhaps placed in a sandboxed honeynet for further study of what the host is doing.

We have covered some of the positive possibilities of SDN for network security, so let us consider some of the potential negative ramifications of bad implementations as well. As an example of an unintended consequence of badly implemented SDN control, let us consider an application that can control an OpenFlow switch. Because of how OpenFlow works, this would likely be a piece of shim software on the application’s client host that communicates to the controller, which then communicates to the switch. With OpenFlow and flow tables, a firewall of sorts can be made out of an OpenFlow enabled switch by telling it to drop or pass packets depending on which ports and protocols are in use. A dynamic firewall based on OpenFlow messages could be seen as a little like a SOHO (Small Office/Home Office) router with UPnP (Universal Plug and Play) support, but with hopefully more authentication (depending on the implementation). Let us continue to use UPnP as a comparison technology to explain how things could potential go wrong security wise. Many SOHO wireless routers seem to leave UPnP on, which allows a client to set up a port-forwarding rule on the NAT device dynamically. The problem here is that a piece of malicious code, or a malicious user, may open up ports for things that are not wanted by the network administrator, like ports for back doors and file sharing. For example, BitTorrent may be slow at someone’s local coffee shop without forwarding a port for ratio reasons, and the user at the coffee shop likely doesn't have the admin password to set up a static port forward on the wireless router’s interface directly, but if UPnP is enabled the user can open the port anyway using the UPnP protocol. Can a similar situation appear in SDN? It depends greatly on the intended users.

With the subject of intended users comes the subject of authentication, and OpenFlow has a fairly simple model. Communication between a switch and the controller happens over TCP on port 6633 (canonical, this could be configured differently). The option is there for using TLS (Transport Layer Security) and mutual authentication via certs on both the switch and the controller. However, based on what the author has read from the documents of  one controller implementer, it seems that many switch vendors do not support the TLS options yet, so the control channel would be in the clear. Without TLS, what security features are in place to keep just anyone from configuring an OpenFlow enabled switch? The author’s understanding is the switch starts the communications, so it has to know the IP/host name of the controller. An important question for security is how is this configured? If it's by host name an attacker could think of doing some DNS shenanigans to have the host name map to their IP, thus gaining control of the switches. Or the attacker may knock out the real controller, and if the attacker is on the same LAN, take its IP for themselves. TLS with signed certs on each switch and controller would prevent these attacks, though having the communications between the controller and the switches be on its own channel/out of band would also help.

Some potential pitfalls also exist depending on how the Software Defined Networking system is configured, where it is allowed to be configured from, and who is allowed to do the configuring. As a mental exercise, let’s say an application is given the power to decide on its own network path via some extensions/shims that communicate with an OpenFlow controller. This could be usefully in many situations. For example, let’s say the data is of an immediate nature, like Voice over IP. An application like VoIP may choose a path that is not as reliable but has lower latency and a lot of bandwidth. If the data is important from a confidentiality standpoint, but time is not of the essence, then a slower set of links routed around the more exposed sections of the network could be preferred. However, a user/application being able to choose the network path data takes could lead to potential problems depending on how authentication and repudiation are handled. As a comparison from history, IP4 has source routing options that allow for the host to specify the path of the packet though routers on the Internet. Let’s say Mallory wants to talk to Bob while pretending she is Alice, and use a connection based protocol. Normally spoofing IP packets is easy (Scapy, Nemesis, HPing are common tools for this task) if the attacker does not care if they get a response back. Mallory sending a packet with Alice's IP is not a problem using raw sockets, but when Bob gets the message he will reply in a way that is routed to Alice’s host, not Mallory’s, so Mallory never sees the return traffic and is blind as to what to send next (Sometimes not so blind as with vulnerable services like the R tools suite). If Mallory controls a router in the path however, and uses source routing to specify the path for the connection to return though, she can see the returning traffic and keep spoofing a session-based connection as Alice. The author wonders if something similar can be done with applications that use OpenFlow to choose their own path. For security reasons many modern Internet routers are configure to drop source routed packets to avoid these sorts of spoofing attacks. Can there be a modern equivalent?

XSP [4] (eXtensible Session Protocol) is a framework that leverages OpenFlow to allow an application to adjust its own path through the network. The author’s understanding is that XSP has mitigations in place to thwart these sorts of attacks, but what if someone designs a similar system and is not as careful? There are plenty of implementation details that could have security consequences, and as with cryptographic implementations, even seemingly minor details can matter. A few potential security related questions include: Is each application responsible for having credentials to make calls for path changes, or is the authentication built into the OS and any application the user is running has to make an OS call to use the SDN features?
How and where are these credentials stored? If each application has to be configured with credentials separately it would be a burden to the user, but if the credentials are handled by the OS and all applications use them, the author can also envision some unintended consequences happening as well if the credentials can be obtained by malicious applications and used in unanticipated ways. Much of the security impact of SDN aware applications may depend on the intended users. Giving the keys to the kingdom to control network switches to an application only network administrators themselves can access is not so bad, access by end users could be catastrophic.

Conclusion

                This paper has attempted to spur an interest in Software Defined Networking amongst security practitioners. We have tried to provide a few ideas, and some practical examples, of how SDN can be both used and misused. Many of the ideas put forth in this paper are just conceptual for the time being, but we hope it inspires others to research these topics in more depth. We highly recommend going to the OpenFlow tutorial site [4], downloading the VM, and working though the projects just to get a feel for the possibilities. After that, experiment with how OpenFlow/Software Define Networking can be used in securing your network environment.

Thanks to my professor/advisor Martin Swany for feedback on this paper, and Brent Salisbury for answering some of my OpenFlow/SDN questions.

Bibliography

[1]

Open Networking Foundation, "OpenFlow Switch Specification 1.3.0," 25 June 2012. [Online]. Available: https://www.opennetworking.org/images/stories/downloads/specification/openflow-spec-v1.3.0.pdf .

[2]

"POX Wiki," Stanford University, [Online]. Available: https://openflow.stanford.edu/display/ONL/POX+Wiki . [Accessed 12 12 2012].

[3]

I. Aggarwal, Implementation and Evaluation of ELK, an ARP scalability enhancement, 2011.

[4]

S. Shin and G. Gu, CloudWatcher: Network Security Monitoring Using OpenFlow in Dynamic Cloud Networks (or: How to Provide Security Monitoring as a Service in Clouds?), 2012.

[5]

E. Kissel, G. Fernandes, M. Jaffee and M. Swany, Driving Software Defined Networks with XSP, 2012.

[6]

G. Yao, J. Bi and P. Xiao, Source Address Validation Solution with OpenFlow/NOX Architecture, 2011.

[7]

N. McKeown, T. Anderson, H. Balakrishnan, G. Parulkar, L. Peterson, J. Rexford, S. Shenker and J. Turner, OpenFlow: Enabling Innovation in Campus Networks, 2008.

 

Anti-Arp-Poisoning Switch Demo Using OpenFlow & POX

Source Code

 
# ARP Poison Resistant Switch Demo # Created by Adrian Crenshaw # Heavily based on switch/hub demo code by James McCauley # Copyright 2012 James McCauley # # This file is part of POX. # # POX is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # POX is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with POX. If not, see <http://www.gnu.org/licenses/>. from pox.core import core import pox.openflow.libopenflow_01 as of from pox.lib.recoco import Timer from pox.lib.util import dpidToStr from pox.lib.addresses import IPAddr, EthAddr log = core.getLogger() #The variable below are timer setting. #With this number, itemes are set to timeout on idle, and timer set to run, ever 10 sec. TimerQuantumDuration = 10 class AntiARPPoisonSwitch (object): """ A AntiARPPoisonSwitch object is created for each switch that connects. A Connection object for that switch is passed to the __init__ function. """ def __init__ (self, connection): # Keep track of the connection to the switch so that we can # send it messages! self.connection = connection # This binds our PacketIn event listener connection.addListeners(self) # Use this table to keep track of which ethernet address is on # which switch port (keys are MACs, values are ports). self.mac_to_port = {} self.ip_to_mac = {} self.ip_to_mac_time_track = {} Timer(TimerQuantumDuration, self.Clean_Tables, recurring = True) def send_packet (self, buffer_id, raw_data, out_port, in_port): """ Sends a packet out of the specified switch port. If buffer_id is a valid buffer on the switch, use that. Otherwise, send the raw data in raw_data. The "in_port" is the port number that packet arrived on. Use OFPP_NONE if you're generating this packet. """ msg = of.ofp_packet_out() msg.in_port = in_port if buffer_id != -1 and buffer_id is not None: # We got a buffer ID from the switch; use that msg.buffer_id = buffer_id else: # No buffer ID from switch -- we got the raw data if raw_data is None: # No raw_data specified -- nothing to send! return msg.data = raw_data # Add an action to send to the specified port action = of.ofp_action_output(port = out_port) msg.actions.append(action) # Send message to switch self.connection.send(msg) #Checks to see if a IP to MAC mapping is already there. def arp_spoof_detected(self, packet, packet_in): ipsrc=self.int_of_ip(packet, packet_in) arp = packet.find('arp') if arp is not None: macsrc=str(arp.hwsrc) ip = packet.find('ipv4') if ip is not None: macsrc=str(packet.src) if ipsrc in self.ip_to_mac: if self.ip_to_mac[ipsrc] != macsrc: return True #If it seems to be a new IP/MAC mapping, add it to are table, but make it timeout #for network flexibility. Also, new packets restart the clock for IP/MAC time outs. #Let's note the IP to MAC address mapping. self.ip_to_mac[ipsrc] = macsrc return False #General function for checking if something looks like ARP poisoning def check_arp_spoof(self, packet, packet_in): if self.arp_spoof_detected(packet, packet_in): #Here we can put code that runs if ARP Poisoning is detected. In this case I am just alerting. arp = packet.find('arp') if arp is not None: tempipstring=str(self.ip_to_mac[arp.protosrc.toUnsignedN()]) print "Dup IP to MAC!!! " + str(arp.protosrc) + " is already taken by " + \ tempipstring + ". " + str(arp.hwsrc) + " may be spoofing!" #I need an index for my dictonary I can also use as an OpenFlow cookie, so I make the IP an integer def int_of_ip (self, packet, packet_in): arp = packet.find('arp') if arp is not None: ipsrc=arp.protosrc.toUnsignedN() ip = packet.find('ipv4') if ip is not None: ipsrc=ip.srcip.toUnsignedN() return ipsrc def act_like_switch (self, packet, packet_in): # Learn the port for the source MAC self.mac_to_port[str(packet.src)] = packet_in.in_port self.check_arp_spoof(packet, packet_in) if self.arp_spoof_detected(packet, packet_in): log.debug("Installing drop flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod() # Set fields to match received packet source msg.match=of.ofp_match(dl_src=packet.src) #We don't want the flow to last forever, so we set it to time out #Make the blocked, potential ARP poisoner wait twice as long as other flows to time out msg.idle_timeout = TimerQuantumDuration * 2 #Output to nowhere, or in this case port 100 that is unused. msg.actions.append(of.ofp_action_output(port=100)) self.connection.send(msg) elif (str(packet.dst) in self.mac_to_port): log.debug('MAC '+str(packet.src)+' on port '+str(packet_in.in_port)) # Send packet out the associated port self.send_packet(packet_in.buffer_id, packet_in.data, self.mac_to_port[str(packet.dst)], packet_in.in_port) log.debug("Installing flow...") # Maybe the log statement should have source/destination/port? msg = of.ofp_flow_mod(cookie=self.int_of_ip(packet, packet_in),flags=of.OFPFF_SEND_FLOW_REM) # Set fields to match received packet source msg.match.dl_dst=packet.dst #Uncomment line below to lock on more fields #msg.match = of.ofp_match.from_packet(packet) #We don't want the flow to last forever, so we set it to time out msg.idle_timeout = TimerQuantumDuration msg.actions.append(of.ofp_action_output(port = self.mac_to_port[str(packet.dst)])) self.connection.send(msg) else: # Flood the packet out everything but the input port # This part looks familiar, right? self.send_packet(packet_in.buffer_id, packet_in.data, of.OFPP_FLOOD, packet_in.in_port) def _handle_PacketIn (self, event): """ Handles packet in messages from the switch. """ packet = event.parsed # This is the parsed packet data. if not packet.parsed: log.warning("Ignoring incomplete packet") return packet_in = event.ofp # The actual ofp_packet_in message. self.act_like_switch(packet, packet_in) #If the flow times out, we assume the IP to MAC address mapping is not longer in use def _handle_FlowRemoved (self, event): log.debug("Flow removed on switch %s", dpidToStr(event.dpid)) #If I get message back that a flowhas timed out/been remove, I also remove it from the IP/MAC table if event.ofp.cookie in self.ip_to_mac: del self.ip_to_mac[event.ofp.cookie] #Just used for debugging, lets us show out IP/MAC table def Clean_Tables(self): print "Show Table" print self.ip_to_mac def launch (): """ Starts the component """ def start_switch (event): log.debug("Controlling %s" % (event.connection,)) AntiARPPoisonSwitch(event.connection) core.openflow.addListenerByName("ConnectionUp", start_switch)

Printable version of this article

15 most recent posts on Irongeek.com:


If you would like to republish one of the articles from this site on your webpage or print journal please contact IronGeek.

Copyright 2020, IronGeek
Louisville / Kentuckiana Information Security Enthusiast