Is it possible to emulate a NIC with software? - python

Introduction
I'm trying to emulate a NIC using software (python), to do so I'm responding to ARP and ICMP packages. Is that even possible?
Environment
I'm using Kali (2021.3) within VMWare (bridge NIC), python (3.9.7) and scapy (2.4.5).
eth0 interface:
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.176.139 netmask 255.255.255.0 broadcast 192.168.176.255
inet6 fe80::20c:29ff:fee3:84e prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:e3:08:4e txqueuelen 1000 (Ethernet)
RX packets 14614 bytes 19043293 (18.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2504 bytes 259386 (253.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
What I've tried
First started just by simply responding to ARP requests:
from scapy.all import *
# Your network broadcast address
broadcastNet = "192.168.176.255"
macDict = { "192.168.176.182" : "60:01:94:98:97:c6",
"192.168.176.183" : "68:c6:3a:a7:d3:40"}
# Use MAC address of this machine as source. If not eth0, change this:
myMAC = get_if_hwaddr('eth0')
def handle_arp(packet):
who_has = 1
is_at = 2
if packet[ARP].op == who_has:
print(packet.summary())
if packet.pdst in macDict:
print("Sending ARP response for " + packet.pdst)
reply = ARP(op=is_at, hwsrc=macDict[packet.pdst], psrc=packet.pdst, hwdst="ff:ff:ff:ff:ff:ff", pdst=broadcastNet)
go = Ether(dst="ff:ff:ff:ff:ff:ff", src=myMAC) / reply
print(go.summary())
sendp(go)
return
def handle_icmp(packet):
print(packet)
return
# Sniff for packets.
sniff(filter="arp",prn=handle_arp)
Used ping -c 1 192.168.176.183 to force an ARP, used wireshark and the ARP reply fired successfully:
However if I list my arp table it stands as incomplete:
arp -a
? (192.168.176.183) at <incomplete> on eth0
? (192.168.176.2) at 00:50:56:e6:c2:47 [ether] on eth0
? (192.168.176.254) at 00:50:56:ea:77:59 [ether] on eth0
At this point thought of using the same MAC address of eth0 changing the macDict in the script:
macDict = { "192.168.176.182" : "00:0c:29:e3:08:4e",
"192.168.176.183" : "00:0c:29:e3:08:4e"}
However my arp table wasn't updated at all.
Finally tried creating a link interface:
sudo ip link add link eth0 address 56:8A:C0:DD:EE:FA eth0.1 type dummy
sudo ifconfig eth0.1 up
sudo ifconfig eth0.1
eth0.1: flags=195<UP,BROADCAST,RUNNING,NOARP> mtu 1500
inet6 fe80::548a:c0ff:fedd:eefa prefixlen 64 scopeid 0x20<link>
ether 56:8a:c0:dd:ee:fa txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6 bytes 420 (420.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
This last approach didn't work either.

Related

Python/Scapy - Sniff, & Store source IP address and source MAC address

Trying to create a function that does the following:
Uses sniff() function to listen for traffic at the en0ps3 interface
Handle traffic picked up by the sniff() function
Store the source IP address and source MAC address.
If an IP address has already been stored, but a different MAC address is seen then the script should also store this additional MAC address
The user should see a list of hosts appear in the terminal while the script is running
(I have another separate sample script that generates ARP traffic for testing functionality)
Output I'm getting is below - can anyone confirm if its correct? I'm new, and struggling with Scapy to validate my work:
^CEther / ARP who has 192.168.1.10 says 192.168.1.1
Ether / ARP is at 10:11:12:ab:ab:ab says 192.168.1.10
Ether / ARP who has 192.168.1.11 says 192.168.1.2
Ether / ARP is at 10:11:12:bc:bc:bc says 192.168.1.11
Ether / ARP who has 192.168.1.12 says 192.168.1.3
Ether / ARP is at 10:11:12:cd:cd:cd says 192.168.1.12
Ether / ARP who has 192.168.1.13 says 192.168.1.4
Ether / ARP is at 10:11:12:de:de:de says 192.168.1.13
Ether / ARP who has 192.168.1.14 says 192.168.1.5
Ether / ARP is at 10:11:12:ef:ef:ef says 192.168.1.14
Ether / ARP who has 192.168.1.15 says 192.168.1.6
Ether / ARP is at 10:11:12:f0:f0:f0 says 192.168.1.15
Ether / ARP is at de:ad:be:ef:de:ad says 192.168.1.10
My code is
from scapy.all import *
ethernetHeader = Ether()
ipHeader = IP()
icmpHeader = ICMP()
pkt = ethernetHeader/ipHeader/icmpHeader ##filtering out ARP traffic with an op code of 2 or "is-at"
def filter_packets(packets):
def packet_handler(pkt):
packets.append(pkt)
return packet_handler
def main():
packets = []
sniff(iface="enp0s3", prn=filter_packets(packets))
for p in packets:
print(p.summary(ipHeader))
main()

Python sending package through wrong interface

I have a raspberry pi that is both connected to the internet via Wlan and a local device via Ethernet. So it has two IPs; one for each endpoint.
This is how it looks like simplified when running ifconfig; with different IPs for privacy
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 189.168.200.110 netmask 0.0.0.0 broadcast 255.255.255.255
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 189.168.201.79 netmask 255.255.255.0 broadcast 192.168.1.255
This is the code that python is using to send a message to the device through the Ethernet with that gateway's ip
TCP_PORT = 3001
SERVER_IP_AD = "189.168.200.110"
CLIENT_IP_AD = "189.168.200.155"
BROADCAST_IP = "255.255.255.255"
def sendMessage(self, file_path, client_ip=CLIENT_IP_AD):
print('message en route')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((self.SERVER_IP_AD, 0))
s.connect((client_ip, self.TCP_PORT)) #**ERROR IS HERE**
MESSAGE = self.openFile(file_path)
s.send(MESSAGE.encode())
data = s.recv(self.BUFFER_SIZE)
s.close()
return data
Using wireshark I can see that the package is being sent through the Wlan interface instead of the Ethernet interface with the correct IP source and IP destination.
How do I tell python to use the correct interface when sending out the package?
In my opinion, you can establish Tcp connection with Ethernet, cause there isn't shaking hands by Ethernet
And, you shouldn't use s.bind() and s.connect() at the same time. Because the former is for UDP client, and the later is for TCP client. Have a try with only s.bind().

How can I capture raw IP packets under Ubuntu?

I want to use Python to capture all the IP packets on an Ubuntu’s network. By using the below code, I’ve got all the packet with the Ethernet header. How can I get rid of the Ethernet header and directly get only the IP packets?
s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
while True:
packet = s.recvfrom(65565)
I would suggest you have a look at scapy, a tool that enables the user to send, sniff, dissect and forge network packets. the sniffing paragraph is a probably what you're looking for. Here's an example where I capture 10 IP packets, show a summary of their information, then store them to a pcap file:
$ scapy
Welcome to Scapy (2.3.2)
>>> pkts = sniff(filter='ip', count=10)
>>> print len(pkts)
10
>>> pkts.nsummary()
0000 Ether / IP / TCP 31.13.90.2:https > 192.168.1.14:63748 PA / Raw
0001 Ether / IP / TCP 192.168.1.14:63748 > 31.13.90.2:https A
0002 Ether / IP / TCP 192.168.1.14:63748 > 31.13.90.2:https PA / Raw
0003 Ether / IP / TCP 31.13.90.2:https > 192.168.1.14:63748 PA / Raw
0004 Ether / IP / TCP 192.168.1.14:63748 > 31.13.90.2:https A
0005 Ether / IP / UDP 192.168.1.21:48007 > 192.168.1.255:32412 / Raw
0006 Ether / IP / UDP 192.168.1.21:49808 > 192.168.1.255:32414 / Raw
0007 Ether / IP / UDP 192.168.1.11:64817 > 192.168.1.255:32412 / Raw
0008 Ether / IP / UDP 192.168.1.11:64819 > 192.168.1.255:32414 / Raw
0009 Ether / IP / UDP 192.168.1.11:49670 > 239.255.255.250:ssdp / Raw
>>> wrpcap("temp.cap",pkts)
>>>
socket (AF_INET, SOCK_RAW, IPPROTO_RAW) will get you an IP Layer raw socket
socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL)) will get you a layer 2 raw socket`
There are examples (in C) here.

How can I put mac os x en1 interface into monitor mode to use with python3 scapy?

On my mac the wireless interface is the en1 interface. I can put the interface into monitor mode using mac's airport application but then it doesn't work with the scapy module when i use python 3. How can i make this work?
Thanks in advance
ifconfig output
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=3<RXCSUM,TXCSUM>
inet6 ::1 prefixlen 128
inet 127.0.0.1 netmask 0xff000000
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
nd6 options=1<PERFORMNUD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
nd6 options=1<PERFORMNUD>
media: autoselect (none)
status: inactive
fw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 4078
lladdr 00:3e:e1:ff:fe:0f:0a:4a
nd6 options=1<PERFORMNUD>
media: autoselect <full-duplex>
status: inactive
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
inet6 fe80::7ed1:c3ff:fe6e:eeda%en1 prefixlen 64 scopeid 0x6
inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255
nd6 options=1<PERFORMNUD>
media: autoselect
status: active
en2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
options=60<TSO4,TSO6>
media: autoselect <full-duplex>
status: inactive
p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
media: autoselect
status: inactive
awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1452
inet6 fe80::18b8:64ff:fec8:85%awdl0 prefixlen 64 scopeid 0x9
nd6 options=1<PERFORMNUD>
media: autoselect
status: active
bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
Configuration:
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
ipfilter disabled flags 0x2
member: en2 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 7 priority 0 path cost 0
nd6 options=1<PERFORMNUD>
media: <unknown type>
status: inactive
Python Script To Detected Packets (After putting en1 into mon mode using airport)
from scapy.all import *
def pktIdentifier(pkt):
if pkt.haslayer(Dot11Beacon):
print ("[+] Detected 802.11 Beacon Frame")
elif pkt.haslayer(Dot11ProbeReq):
print ("[+] Detected 802.11 Probe Frame")
elif pkt.haslayer(TCP):
print ("[+] Detected TCP Packet")
elif pky.haslayer(UDP):
print ("[+] Detected UDP Packet")
conf.iface = 'en1'
sniff(prn=pktIdentifier)
Output of conf.route
Network Netmask Gateway Iface Output IP
0.0.0.0 0.0.0.0 192.168.0.1 en1 192.168.0.7
127.0.0.0 255.0.0.0 0.0.0.0 lo0 127.0.0.1
127.0.0.1 255.255.255.255 0.0.0.0 lo0 127.0.0.1
169.254.0.0 255.255.0.0 0.0.0.0 en1 192.168.0.7
192.168.0.0 255.255.255.0 0.0.0.0 en1 192.168.0.7
192.168.0.1 255.255.255.255 0.0.0.0 en1 192.168.0.7
192.168.0.1 255.255.255.255 0.0.0.0 en1 192.168.0.7
192.168.0.7 255.255.255.255 0.0.0.0 en1 192.168.0.7
192.168.0.255 255.255.255.255 0.0.0.0 en1 192.168.0.7
Short Answer: You could MonkeyPatch the _PcapWrapper_pypcap class. An example Code is provided below.
Slightly Longer Answer: On Mac OS X scapy sniffs on interfaces through libpcap. Instead of calling pcap_open_live we call pcap_create, pcap_set_rfmon and pcap_activate (in this order). This will set the interface in monitor mode and start capturing. I tested the following MonkeyPatch under scapy-python3 (0.21) and macOS Sierra 10.12.6. Make sure you run this Code with admin rights.
from scapy.all import *
import scapy.arch.pcapdnet
from ctypes import POINTER, byref, create_string_buffer
from ctypes.util import find_library
class _PcapWrapper_pypcap_monkeypatched(scapy.arch.pcapdnet._PcapWrapper_pypcap):
def __init__(self, device, snaplen, promisc, to_ms):
self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE)
self.iface = create_string_buffer(device.encode('ascii'))
#self.pcap = pcap_open_live(self.iface, snaplen, promisc, to_ms, self.errbuf)
STRING = c_char_p
_lib_name = find_library("pcap")
if not _lib_name:
raise OSError("Cannot fine libpcap.so library")
_lib=CDLL(_lib_name)
pcap_create = _lib.pcap_create
pcap_create.restype = POINTER(pcap_t)
pcap_create.argtypes = [STRING, STRING]
pcap_set_rfmon = _lib.pcap_set_rfmon
pcap_set_rfmon.restype = c_int
pcap_set_rfmon.argtypes = [POINTER(pcap_t), c_int]
pcap_activate = _lib.pcap_activate
pcap_activate.restype = c_int
pcap_activate.argtypes = [POINTER(pcap_t)]
self.pcap = pcap_create(self.iface, self.errbuf)
pcap_set_rfmon(self.pcap, 1)
pcap_activate(self.pcap)
self.header = POINTER(pcap_pkthdr)()
self.pkt_data = POINTER(c_ubyte)()
self.bpf_program = bpf_program()
scapy.arch.pcapdnet._PcapWrapper_pypcap = _PcapWrapper_pypcap_monkeypatched
def pktIdentifier(pkt):
if pkt.haslayer(Dot11Beacon):
print("[+] Detected 802.11 Beacon Frame")
elif pkt.haslayer(Dot11ProbeReq):
print("[+] Detected 802.11 Probe Frame")
sniff(iface="en0", prn=pktIdentifier)
When using the sniff function setting monitor=True on Mac OS Catalina always works for me. Example: scapy.all.sniff(iface='en0, monitor=True) then obviously what ever other functions you want.
This is a possible answer: http://www.cqure.net/wp/2014/04/scapy-with-wifi-monitor-rfmon-mode-on-os-x/
If you will file a bug on http://github.com/phaethon/scapy I will assist with patching part.

Receiving multicast data on specific interface

tcmpdump can view all the multicast traffic to specific group and port on eth2, but my Python program cannot. The Python program, running on Ubuntu 12.04:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Multicast port is 52122
sock.bind(('', 52122))
# Interface eth2 IP is 1.2.3.4, multicast group is 6.7.8.9
mreq = socket.inet_aton('6.7.8.9')+socket.inet_aton('1.2.3.4')
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print '\nwaiting to receive message'
data, address = sock.recvfrom(1024)
print data
When I use another program to send a multicast packet to eth2, it works and prints the packet. But it fails to see all the current multicast traffic. If I run tcpdump on eth2 on the same port and group as the above program:
sudo tcpdump -i eth2 host 6.7.8.9 and port 52122
it sees both the packets I send from another program AND all the current multicast traffic. It's output looks likes this...
# Packet sent from my other program
09:52:51.952714 IP 1.2.3.4.57940 > 6.7.8.9.52122: UDP, length 19
# Packet send from the outside world
09:52:52.143339 IP 9.9.9.9.39295 > 6.7.8.9.52122: UDP, length 62
Why can't my program see the packets from the outside world? How can I modify it (or something else) to fix this?
Edit:
I should have mentioned, the interface this going over is not eth2 but eth2.200 a VLAN. (The local IP and the tcpdump commands are all run with eth2.200, I just changed that in this question to make it simpler.) Based on this answer that could be the problem?
Edit #2:
netstat -ng when the program is running shows eth2.200 subscribed to 224.0.0.1 and 6.7.8.9`.
tshark -i eth2.200 igmp shows three repeated 1.2.3.4 -> 6.7.8.9 IGMP 46 V2 Membership Report / Join group 6.7.8.9 when the program first starts. When the program process is killed, it shows 1.2.3.4 -> 224.0.0.2 IGMP 46 V2 Leave group 6.7.8.9. There is also an infrequent 1.2.3.1 -> 224.0.0.1 IGMP 60 V2 Membership Query, general, where 1.2.3.1 is 1.2.3.4's gateway.
Not sure if it will help, but the routing table looks like:
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 1.2.5.6 0.0.0.0 UG 0 0 0 eth1
1.2.3.0 0.0.0.0 255.255.255.240 U 0 0 0 eth2.200
Thank you!
Finally! Found this question on ServerFault that addresses the same thing. Basically the kernel was not forwarding on / was filtering out the packets because it thought the sourced address was spoofed.
Changed the settings in /etc/sysctl.conf to match:
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.ip_forward = 1
Rebooted and everything works.

Categories