Tapping 802.1x Links with Marvin

While testing fat clients and appliances for resistance against man-in-the-middle attack I always had to mess with iptables/ebtables/socat to divert network connections. It is enough in most cases, but sometimes the setup gets too elaborate. To make my life easier, I have decided to write a tool, capable to divert and re-inject a network connections while preserving the original network addresses, including layer 2 ones. The tool is not complete yet, but it already can be used to tap into a wired network protected with 802.1x, so I've decided to publish it anyway.

Update 15/01/2011: Initially the tool has been published under the name of Mallory, but it turned out there is a TCP/UDP mitm tool with this name, so I had to change the name to Marvin.

Update 27/01/2011: New version of the tool, Marvin 0.91, can be downloaded from here. It supports connection interception and includes numerous GUI improvements. Unfortunately as of now it is not extensively tested nor documented. It should be reasonably straightforward though -- just click on 'Intercept' button in 'Conversations' table. If you have any questions or problems feel free to comment here.

Intended Use

I have to say upfront that Marvin has nothing to do with ARP poisoning or similar techniques. It will not impact any remote traffic flow. The assumption is that the pen-tester cuts the link and connect two cables to the host running Marvin. The diagram below illustrates the tapping:
Links Before and After Tapping

The tool will bridge the traffic between the first and the second interface and inject traffic it receives from the third interface into the first two. The pen-tester is expected to connect another host to the third network interface to enjoy ICMP/TCP/UDP connectivity to the bridged network. The source addresses of the injected frames/packets will be masqueraded (on IP and Ethernet levels), as configured by the user.

Step 1. Install and Run

The simplest way to run Marvin is on a box with three physical network interfaces. It must be Linux with Java JRE, and you must have root privileges there. The tool is self-contained, all libraries it needs are bundled with it.

The latest build can be downloaded from here. The tarball contains a sample config file marvin.properties, and a script called marvin.sh which should be used to launch the tool.

Marvin uses JPcap (wrapper around libpcap) to receive and send Ethernet frames, so you have to run it as root. Run it with '-t' option to disable GUI, add '-d' option to increase the logging level, set '-l marvin.log' to redirect all logging output into a file. Use '-f configfile' to specify path to a config file explicitly (marvin.properties from the same directory as marvin.sh is the default choice).

I will assume you wire the first interface (eth1) to the legitimate client, the second (eth2) -- to the switch, and the third (eth3) -- to the tap client. (Alternatively you can also run Marvin in VM and use host OS as tap client. This way you will need only one computer with two physical network interfaces. I can write the instructions for this use case if someone is interested).

The configuration file bundled with the tool is relevant to my testbed only. Unless you have very similar setup, you will have to reconfigure to fit your environment before it does anything useful. You can do it by manually editing the config file or via GUI.

Just as an example, the IP addressing scheme of my testbed (which corresponds to the default config bundled with Marvin) looks like following:
Marvin ip-before-after-tapping.png

Step 2: Make Bridging Work

On startup Marvin reads the config and, if the config was correct, starts working right away. Unless you run Marvin with '-t' flag, the following GUI will appear:
Marvin main-screen.png
There you can change any parameter in runtime and click 'Apply' button.

At the very minimum you have to select the interfaces Marvin will send and receive network traffic. For example:

  • Set br1if.interface parameter to the interface name facing the client (eth1)
  • Set br2if.interface parameter to the interface name facing the switch (eth2)
  • Set tapif.interface parameter to the interface name facing the tap client (eth3)

Now the bridge between eth1 and eth2 is active, all traffic being forwarded transparently. The legitimate 802.1x client should be able to authenticate towards the network and have network access as usual.

Step 3. Configure Tap Interface and Masquerading

Now let's finish off the config file and set parameters necessary for tap clients to access the bridged network. If you use GUI, you can click on 'Select ...' button to access the list of suitable candidates to use for masquerading addresses. For the first interface Marvin will suggest to use stations connected to the second interface, and visa versa. The list of candidates may appear empty if Marvin has not seen any ARP messages yet.

The following parameters have to be set, via GUI or in the config filie:

  • MAC and IP address of the legitimate client. Marvin will use it to masquerade the tap client when it accesses the network. Set br2if.smac and br2if.saddr accordingly.
  • MAC and IP address of some host on the network (i.e. default gateway). Marvin will use it to masquerade the tap client when it accesses the legitimate client. Default gateway seems to be an adequate choice here, but it can be absolutely anything as long as the legitimate client swallows it. Set br1if.smac and br1if.saddr accordingly.
  • IP address of the default gateway and the netmask. Have a look at the content of DHCP Offer message, if any. Marvin needs this information to route traffic to non-directly connected hosts. Set br.netmask and br.gateway accordingly.

Now, connect you tap client host to the third interface and restart Marvin (or apply the config). If you haven't modified the default values, set tap client to use 10.0.1.2 as IP address, 255.255.255.252 as netmask, and 10.0.1.1 as default gateway. Try to ping the hosts on the bridged link, it should work.

Information About the Relayed Connections

Marvin GUI has several tabs which contain information about ARP/ICMP/TCP/UDP flows and conversations it relays/translates (a conversation is a pair of unidirectional flows). You can peek there for troubleshooting purposes, but under high load you can experience inconsistency in the displayed information. I don't have documentation for these screens yet, only a screenshot of Marvin masquerading TAP client traffic. Here tap client (10.0.1.2) is pinging the legitimate client (172.16.208.148). Marvin translates the source IP address of a tap client (10.0.1.2) into IP address of the default gateway (172.16.208.2). MAC addresses get translated too, but you can't see it on this screen.

Performance

The performance is not bad at all. Marvin is multi-thraded and will benefit from multi-core CPUs. It can certainly handle nmap SYN scan in normal mode, but will die soon if you try to run nmap at wire speed (-T insane). Obviously, it has to keep connection states for every TCP SYN and expire them. If you have hundreds thousands of active connections these tasks will start to take quite a bit of memory and CPU. Running in VM with two CPUs and relaying a SYN scan, it will let nmap go up to 3kpps.

Additional Information

I know the explanations could be more clear, I will certainly write better docs in the future. If you have any problems or questions, just post a comment here and I will reply. Again, the download is here.

For now the project is closed source. More likely than not 1.0 release will be open source under GNU or Apache License.

There is a couple of PDF documents which may shed some light on how the tool is working. These slides contain more less the same info as this post. This table explains the packet manipulation process in some more details.

The following features will be added in the future (higher priority first):

  • Make a better package
  • Fix stats display inconsistencies (done in 0.91)
  • NAPT (now ports are not translated)
  • Enqueue IP packets pending ARP resolution (now they are dropped)
  • Connection interception rules (done in 0.91)
  • Automatically prepare connection re-injection rules
  • Save the config, retain the interception rules between restarts
  • 802.1q
  • Full ICMP support (now only ICMP echo is supported)
  • Move to JNetPcap
  • IP packet reassembly (now fragmented packets are dropped)
  • ARP entry expiration (now learned IP-MAC resolution remains forever)

Comments

There is a blog post about Marvin used during a pentest: http://lanmaster53.com/?p=555. Contains clear explanations and nice diagrams.

Very nice tool. Hope you keep developing it!

DiabloHorn

"Alternatively you can also run Marvin in VM and use host OS as tap client. This way you will need only one computer with two physical network interfaces. I can write the instructions for this use case if someone is interested."

Any chance of this? I've got the client and switch connected and bridged with no issues using Marvin. Which interface type do you use in the VM? With Virtualbox Marvin only sees HOST, but with vmplayer Marvin see HOST & NAT. The diagram above shows the tap has a default gateway, but with HOST only there is no DG. If I switch this to NAT the switch port locks out as it sees the MAC from the laptop.

Sorry for the late reply.

The following considerations apply when you use Marvin in VM:

Configure VM with three network interfaces:
* Two in bridged mode, attached to physical network interfaces connected to the victim and the switch
* The third one (tap) in host-only mode
* The interfaces must be allowed to go into promiscuous mode (this depends on your virtualization platform)

In the VM:
* Make sure all interfaces are up, have no IP addresses, are not members of any bridge, etc
* Start Marvin

On the host:
* Make sure bridge interfaces are up, have no IP addresses, are not members of any bridge, etc
* For the host-only interface, assign some IP address/netmask which covers IP address set as IPr parameter in Marvin. Assuming default value 10.0.1.1 is used in Marvin, you can use 10.0.1.2/255.255.255.252 on the host.
* Set default gateway to IPr (10.0.1.1 by default)

This should to the trick.

Interesting slides from the last year BlackHat https://media.defcon.org/dc-19/presentations/Duckwall/DEFCON-19-Duckwall... . The author presents shell-script based approach for doing same stuff Marvin does. He has even tried Marvin and found it working, but then decided to do the same with shell-scripts because Marvin lacks autoconfiguration. The purpose of this exercise was to mount the attack from a small embedded system, perhaps JRE would be a bit too heavy for that purpose anyway. Nice work.