ahmed@home:~$

Pwn The Network - How to spoof your MAC address

What is a MAC address?

MAC stands for Media Access Control, and also known as Physical address, hardware address which uniquely identifies each device on a given network. If a LAN network has two or more devices with the same MAC address, that network will not work.


Architecture of MAC

Reasons to change (Spoof) the MAC address

  • Some of the free wifi connection filter the MAC addresses to enable only devices that are on the whitelist to connect to the network. You can impersonate one of the allowed MAC addresses to access the network.

  • If you are caught doing some suspicious stuff in a network, your MAC address might be banned, or even to identify you.

  • MAC spoofing is essential in MITM (Man In The Middle) attack to redirect network traffic.

Final product

The final product, we aim to code is a python script that allows us to show the current MAC address provided a network interface, change it to some random MAC or change it to a MAC of our choosing.

First, let’s check our current MAC address using ifconfig command in Linux.


We can achieve the same result using our script, but in a much cleaner way.


Now let’s set a random MAC address.


We can check if we really changed the MAC address of enp0s3 interface using the command ifconfig.


We can also set a MAC address of our choosing. This can be useful in some situations.



Let’s code

This code is a Python script that can be used to change the MAC address of a network interface on a computer. The script takes several command-line arguments that determine which action it will take.
The check_mac function is used to verify that a given MAC address is valid. It does this by using a regular expression to check if the MAC address is in the correct format. If the MAC address is in the correct format, it returns True, otherwise it returns False.
The check_interface function is used to check if a given network interface exists on the computer. It does this by creating a socket using the AF_INET (Internet) and SOCK_DGRAM (datagram) protocols and then using the ioctl method to retrieve information about the given interface. If the interface exists, it returns the interface’s information, otherwise it returns False.

def check_mac(mac):
    # Compile a regular expression pattern to match a MAC address
    pattern = re.compile("\A\w\w:\w\w:\w\w:\w\w:\w\w:\w\w\Z")

    # Use the `match` method to check if the pattern matches the given MAC address
    if pattern.match(mac) != None:
        # If the pattern matches, return True
        return True
    # If the pattern does not match, return False
    return False
def check_interface(ifname):
    try:
        # Create a socket using the AF_INET (Internet) and SOCK_DGRAM (datagram) protocols
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # Use the ioctl method to retrieve information about the given interface
        # The ioctl method takes the file descriptor of the socket, a command (0x8927)
        # and a packed binary string representing the interface name
        info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
        return info
    except OSError:
        return False

Now we define the function that will generate a random MAC address. The random MAC address must respect the LAA (Locally Administered Address) format. A locally administered address is assigned to a device by a network administrator, overriding the burned-in address.


Ranges of group and locally administered addresses (Wikipedia)

The generate_random_mac function is used to generate a random MAC address. It creates a string of characters that can be used in a MAC address and then uses the random module to randomly select characters from this string to create the MAC address.

def generate_random_mac():
    # Create a string of characters that can be used in a MAC address
    char = "abcdef" + string.digits
    return random.choice(char) + random.choice("26ae") + ":" + "".join(random.choice(char) + random.choice(char) + ":"  for _ in range(5))[:-1]

he getHwAddr function is used to retrieve the MAC address of a given network interface. It uses the check_interface function to retrieve the information about the given interface and then returns the MAC address as a string in the correct format.

def getHwAddr(ifname):
    info = check_interface(ifname)
    if info:
        return ':'.join('%02x' % b for b in info[18:24])
    return f"Interface {ifname} does not exist."

The change_mac function is used to change the MAC address of a given network interface. It does this by using the subprocess module to run the ifconfig command with the down, hw, ether, and up options to disable the interface, change its MAC address, and then enable it again.

def change_mac(interface,new_mac):
    # Use the `check_output` method of the `subprocess` module to run the `ifconfig` command
    # with the `down` option to disable the given interface
    subprocess.check_output(["sudo","ifconfig",interface,"down"])

    # Use the `check_output` method of the `subprocess` module to run the `ifconfig` command
    # with the `hw` and `ether` options to change the MAC address of the given interface
    subprocess.check_output(["sudo","ifconfig",interface,"hw","ether",new_mac])

    # Use the `check_output` method of the `subprocess` module to run the `ifconfig` command
    # with the `up` option to enable the given interface
    subprocess.check_output(["sudo","ifconfig",interface,"up"])

The main function is the main entry point of the script. It uses the argparse module to parse the command-line arguments that are passed to the script. Based on the arguments that are passed, it either prints the current MAC address of a given interface, changes the MAC address of a given interface to a specified value, or generates and sets a random MAC address for a given interface.

def main():
    # Create an ArgumentParser object to parse command-line arguments
    parser = argparse.ArgumentParser()

    # Add a `--change` option to the ArgumentParser
    # If this option is specified, the `args.change` variable will be set to `True`
    parser.add_argument('--change', action='store_true')

    # Add a `--show` option to the ArgumentParser
    # This option takes a string argument that represents the name of an interface
    # If this option is specified, the `args.show` variable will be set to the specified string
    parser.add_argument('--show', type=str, required=False)

    # Add an `--interface` option to the ArgumentParser
    # This option takes a string argument that represents the name of an interface
    # If this option is specified, the `args.interface` variable will be set to the specified string
    parser.add_argument('--interface', type=str, required=False)

    # Add a `--fake` option to the ArgumentParser
    # This option takes a string argument that represents a fake MAC address
    # If this option is specified, the `args.fake` variable will be set to the specified string
    parser.add_argument('--fake', type=str, required=False)

    # Add a `--random` option to the ArgumentParser
    # If this option is specified, the `args.random` variable will be set to `True`
    parser.add_argument('--random', action='store_true')

    # Parse the command-line arguments using the ArgumentParser
    args = parser.parse_args()

    # If the `--show` option was specified, retrieve the MAC address of the specified interface
    # and print it to the console
    if args.show:
        print(getHwAddr(args.show))

    # If the `--change` option was specified, change the MAC address of the specified interface
    if args.change:
        # If the `--interface` option was not specified, print an error message and exit
        if not args.interface:
            print("Please specify an interface.")
            exit()

        # If the `--fake` or `--random` option was not specified, print an error message and exit
        if not (args.fake or args.random):
            print("Please specify --random or --fake <fake_mac>.")
            exit()

        # If the `--interface` option was not specified, print an error message and exit
        if not args.interface:
            print("You must select an interface first.")
            exit()

        # Check if the specified interface exists
        if not check_interface(args.interface):
            # If the interface does not exist, print an error message and exit
            print(f"Interface {args.interface} does not exist.")
            exit()

        # If the `--random` option was specified, generate a random MAC address
        if args.random:
            # Generate a random MAC address
            fake_mac = generate_random_mac()

            # Change the MAC address of the specified interface to the random MAC address
            change_mac(args.interface, fake_mac)

            # Print the current MAC address to the console
            print("Current MAC address: ",fake_mac)

        
if __name__ == "__main__":
    main()

Conclusion

Understanding the MAC address and its importance to uniquely identify devices on a network is fundamental knowledge for every hacker/pentester. The ARP protocol is used to associate a logical address with a physical or MAC address. In an upcoming article, we will be conducting a MITM attack by spoofing the ARP protocol and we will encounter MAC addresses again.
Full code in github here.
HAPPY HACKING.