2

I am trying to write a program that will display all wifi networks and their clients. The code looks as follows:

import os
import socket
import struct

def unpack_ethernet_frame(data):
    dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])  # Big-endian, 6bytes, 6bytes, 1 short
    return format_mac(dest_mac), format_mac(src_mac), socket.htons(proto), data[14:]

def format_mac(bytes_addr):
    bytes_s = map('{:02x}'.format, bytes_addr) 
    return ":".join(bytes_s).upper()

def format_ip(bytes_addr):
    return ".".join(map(str, bytes_addr))

def unpack_ipv4_frame(data):
    header_len = (data[0] & 15) * 4  # last 4 bits of first byte * 4
    ttl, prot, src, dst = struct.unpack("! 8x B B 2x 4s 4s", data[:20])
    return format_ip(src), format_ip(dst), prot, data[header_len:]

def unpack_tcp(data):
    src_port, dst_port = struct.unpack("! H H", data[:4])
    return src_port, dst_port

def unpack_udp(data):
    src_port, dst_port, size = struct.unpack("! H H 2x H", data[:8])
    return src_port, dst_port, size, data[8:]

if __name__ == '__main__':
    conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))

    while True:
        raw_data, addr = conn.recvfrom(65536)
        dest, src, prot, data = unpack_ethernet_frame(raw_data)
        if prot == 8:  # IP
            print("Dest {0}, Src {1}, prot {2}".format(dest, src, prot))
            src_id, dst_ip, prot, data = unpack_ipv4_frame(data)
            print("From {0} to {1}".format(src_id, dst_ip))
            if prot == 6:  # TCP
                src_port, dst_port = unpack_tcp(data)
                print("src_port {0} dst_port {1}".format(src_port, dst_port))
            if prot == 17:  # UdP
                src_port, dst_port, size, data = unpack_udp(data)
                print("src_port {0} dst_port {1} size {2}".format(src_port, dst_port, size))

I do not know why, but "unpack_ethernet_frame" function receives Ethernet frames, not MAC frames. If I receive wireless frames then I should rather get MAC frames? Mostly what I am trying to do is to find out which frames are beacon frames and get their SSID.

EDIT: I am trying this code with Monitor mode enabled

Alicja Głowacka
  • 401
  • 5
  • 11

2 Answers2

3

When you are getting your data from a socket, the IEEE802-frame has already been unpacked and the Ethernet-frame within it is what you see. There are historical reasons why this is done and the whole IEEE802-layer is usually not visible even to the OS. In order to receive unmodified IEEE802-frames, you need to

  • turn your WiFi-device into "Monitor mode", which not all devices support. This will cause all Ethernet- (and by extension, all TCP/UDP-) connections to get dropped.
  • receive your data not from a socket but directly from the device, e.g. via libpcap.

Also see this answer and this link regarding scapy.

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • Hi but there is also similiar code used there: http://hackoftheday.securitytube.net/2013/04/wi-fi-ssid-sniffer-in-12-lines-of.html – Alicja Głowacka May 12 '18 at 14:14
  • So is it not possible to do this with sockets? – Alicja Głowacka May 12 '18 at 14:16
  • 1
    I don't know for sure, yet I would not count on it. Be aware that the code on securitytube.net receives packets from `mon0`, which on some driver stacks is a special device in monitor mode that receives `IEEE802`-frames while `eth0` and similar get the strapped `Ethernet`-frames. – user2722968 May 12 '18 at 15:06
  • but actually I am also using a monitor mode interface and get Ethernet-frames :/ – Alicja Głowacka May 12 '18 at 18:17
  • So where should be the beacon frame? I assume that I receive eth frame that has got IP frame, that has got TCP frame and so on. In which layer should I get beacons? – Alicja Głowacka May 12 '18 at 18:36
  • In the `IEEE802`-part. See [here](https://github.com/JPaulMora/Pyrit/blob/master/cpyrit/pckttools.py#L717) for a full example (it's using libpcap and scapy under the hood) – user2722968 May 12 '18 at 19:04
  • So how this huy here: https://stackoverflow.com/questions/39472943/python-broadcast-802-11-frames-using-the-socket-module knows that it is a beacon when "packet[30] == "\x80""? He receives 802 parts? – Alicja Głowacka May 12 '18 at 19:35
0

You've connected to the wrong interface. You shall specify which network interface you want to connect.

if __name__ == '__main__':
    conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
    conn.bind(("wlan0mon", 0x0003))
    while True:
        raw_data, addr = conn.recvfrom(65536)

This worked for me