1

How can bridge two different network interfaces (e.g. eth0 and wlan0) while intercepting and modifying the content?

Please let me know if there are nice libraries or tools to do this. If there aren't how would one do it in Python or C++? I would like to do this as high-level as possible.

Can someone provide a minimal working example?

Philipp F
  • 874
  • 9
  • 29

2 Answers2

1

So after a lot of research and trying out other implementations, I finally assembled a version that works for me. It keeps lists of MAC addresses of both interfaces and hence can prevent loops. Also it's faster than other implementations by keeping an L2Socket and reusing it.

There is still a lot one could tune, but here it is:

#!/usr/bin/python2

import signal
from threading import Thread,Lock
from scapy.all import *


def usage():
    print 'Usage: scapy_bridge.py host1_interface host2_interfcae'
    print ''
    print 'Example: sudo python scapy_bridge.py eth1 eth2'
    print '   Sets up a bridge between the hosts connected on eth1 and eth2'


class Sniffer():

    def __init__(self, input_interface, output_interface, sniffer_name):
        self.input_interface = input_interface
        self.output_interface = output_interface
        self.sniffer_name = sniffer_name

        self.output_socket = L2Socket(output_interface)

        self.output_mac = get_if_hwaddr(self.output_interface)

    def add_mac(self, list, mac):
        if mac not in list:
            list.append(mac)

    def process_packet(self, pkt):
        global macs_if1, macs_if2
        handle_packet = True

        if Ether in pkt:
            src_mac = pkt[Ether].src
            dst_mac = pkt[Ether].dst
            if self.sniffer_name == '1to2':

                if src_mac in macs_if2:
                    handle_packet = False
                else:
                    self.add_mac(macs_if1, src_mac)

            else:
                if src_mac in macs_if1:
                    handle_packet = False
                else:
                    self.add_mac(macs_if2, src_mac)

            print 'MAC table 1: ' + str(macs_if1)
            print 'MAC table 2: ' + str(macs_if2)

        if handle_packet:
            p = pkt.copy()

            print 'MSGLEN=%d' % len(p)
            if len(p) > 1400:
                p.show()
                frags = fragment(p)
                for frag in frags:
                    self.output_socket.send(frag)
            else:
                self.output_socket.send(p)

    def stopper_check(self, pkt):
        return not still_running_lock.locked()

    def sniffloop(self):
        sniff(iface=self.input_interface, prn=self.process_packet, stop_filter=self.stopper_check)


# ==================================== MAIN

# global list of running threads
threads = []

# MAC table
macs_if1 = []
macs_if2 = []

# global lock to signal that we're still running
still_running_lock = Lock()

# catch Ctl-c and clean up threads
def signal_handler(signal, frame):
    print 'Cleaning up sniff threads...'
    still_running_lock.release()
    try:
        for t in threads: t.join()
    except:
        pass
    print 'exiting.'
    sys.exit(0)

if __name__ == '__main__':
    if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) != 3:
        usage()
        sys.exit(-1)

    (host1_interface, host2_interface) = sys.argv[1:]

    sniffer1 = Sniffer(host1_interface, host2_interface, '1to2')
    sniffer2 = Sniffer(host2_interface, host1_interface, '2to1')

    threads.append( Thread(target=sniffer1.sniffloop) )
    threads.append( Thread(target=sniffer2.sniffloop) )

    # set our "state" to running by acquiring the lock
    still_running_lock.acquire()

    for t in threads: t.start()

    signal.signal(signal.SIGINT, signal_handler)
    signal.pause()

EDIT: I would like to add, that if you want to mess with the packets, mitmproxy in transparent mode together with iptables seems to be a perfect solution which also does not require any client side configuration. (i.e. it's transparent)

Philipp F
  • 874
  • 9
  • 29
0

Python has the scapy library (available as python-scapy on Debian & Ubuntu) which can ease the retrieval, handling and re-injection of low level network data.

Here you have a simple example that forwards all data from an interface to another, modifying HTTP GET requests to point to example.com domain.

#!/usr/bin/env python

import scapy.all as scapy
import re

OWN_IP_ON_RECV_IFACE="192.168.1.128"
RECV_IFACE="wlan0"
SEND_IFACE="eth0"

def modify_packet(packet):
    # example
    if packet.haslayer(scapy.TCP) and packet[scapy.TCP].dport == 80 and packet[TCP].payload:
        data = str(packet[scapy.TCP].payload)
        modified_data = re.sub("\r\nHost: .+\r\n", "\r\nHost: example.com\r\n", data)
        packet[scapy.TCP].payload = modified_data
        print "Packet modified!"
    return packet

def packet_received(packet):
    if packet.haslayer(scapy.IP) and packet[scapy.IP].dst != OWN_IP_ON_RECV_IFACE and  packet[scapy.IP].src != OWN_IP_ON_RECV_IFACE:
        modified_packet = modify_packet(packet[scapy.IP])
        packet_to_send = scapy.Ether() / modified_packet
        scapy.sendp(packet_to_send, iface=SEND_IFACE)
        print "Packet sent"

scapy.sniff(iface=RECV_IFACE, store=0, prn=packet_received)

You can see more examples here, here or here.

Alvaro Gutierrez Perez
  • 3,669
  • 1
  • 16
  • 24