Changing the MAC Address using Python
Tags: python, cybersecurity, tutorial, security
Published: 2024-02-27 00:48:12

>  **Note:** The following tutorial works on any Linux Distributions, but you will need the root access to execute the system commands necessary in order to change the MAC Address successfully.


### What is MAC Address?

To learn how to change the MAC Address, it is essential to learn about what a MAC Address is. We all use various electronic gadgets such as smartphones, desktops, laptops, tablets, and many more to browse the internet, for entertainment, among other tasks. Each of these electronic gadgets has its own MAC Address. **MAC** stands for **Media Access Control** Address. Every device needs a MAC Address to be uniquely identified by others on the network. For instance, when you watch a video on Youtube, you send a request for the video via your device, and this request is transmitted through your default gateway (usually through a router for a private home network). The router then sends this request to the World Wide Web and receives a response. Now, the **World Wide Web (which is public)** does not know who you are because your device has a **private IP Address** that is only recognized by your router, so the response received by the router is then given to you because the router knows your MAC Address associated with the private IP Address of your device.

MAC Address is a 48-bits hardware address that is hardcoded on the **Network Interface Card (NIC)** at the time of manufacturing. MAC Address is also known as the **Physical Address** of a device. MAC Addresses are used to transmit packets within the network.

For example, **00:1A:3F:F1:4C:C6** is a MAC Address. The first three octets (00:1A:3F) of the address identifies the manufacturer and is known as **OUI (Organizational Unique Identifier). **The last three octets are **Network Interface Controller Specific**.

Read more about **NIC** [here](https://www.tutorialspoint.com/what-is-network-interface-card-nic).

### Why do we need to change the MAC Address?

There are several reasons why you would want to change the MAC Address of your device. Let’s look at them one by one:

* **Increase Anonymity:** When on a public network, you might want to change the MAC Address to stay anonymous, if you don’t then there are chances that the network you are on might log your MAC Address and thus leaving behind a trail.

* **Impersonate other devices:** When you want to carry out an attack (for testing purposes), changing the MAC Address can be useful to impersonate any legitimate device.

* **Bypassing filters:** If a MAC Address is blacklisted to use the network, and to overcome this is to change the MAC Address and fool the filters.

### How to change the MAC Address?

To change the MAC Address, we need to execute a few system commands. The first step is to identify the interface whose MAC Address you want to change and you can do this using the following command in Linux.

    dcode@dcode-pop-os:~$ ifconfig

The above command produces the following output.

    **enp0s3:** flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 10.0.2.5  netmask 255.255.255.0  broadcast 10.0.2.255
            **ether 08:00:27:35:21:ff**  txqueuelen 1000  (Ethernet)
            RX packets 13962  bytes 17732985 (17.7 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 7969  bytes 975852 (975.8 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

First of all, to change the MAC Address we need to know the **interface name** and the existing **MAC Address. **This information can be obtained by executing the **ifconfig** command. We can get the interface name and the existing MAC Address from the output produced by **ifconfig**. For instance, from the above output, **enp0s3 **is the name of the **interface **and the **ether** field on the third line has the MAC Address (**08:00:27:35:21:ff** in our case).

Now that we know the interface name whose MAC Address we want to change, we can move to the next command.

    dcode@dcode-pop-os:~$ sudo ifconfig enp0s3 down

The above command when executed, takes the interface down. What that means is you will no longer be able to connect to the internet. We need to take the interface down **(disabling it)** first if we need to change the MAC Address. After this, the next command to be executed will change the MAC Address.

    dcode@dcode-pop-os:~$ sudo ifconfig enps03 hw ether 00:11:22:33:44:55

The above command changes the existing MAC Address **(08:00:27:35:21:FF)** to a new MAC Address **(00:11:22:33:44:55)** specified in the command itself. Now the MAC Address of the interface enp0s3 has been changed but to use it, we need to enable the interface again using the following command.

    dcode@dcode-pop-os:~$ sudo ifconfig enp0s3 up

After executing the above command, you enable the interface again so that you can access the internet. To check whether the MAC Address was changed to what we specified, execute the **ifconfig** command.

    dcode@dcode-pop-os:~$ ifconfig
    **enp0s3:** flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 10.0.2.5  netmask 255.255.255.0  broadcast 10.0.2.255
            **ether 00:11:22:33:44:55**  txqueuelen 1000  (Ethernet)
            RX packets 13997  bytes 17737735 (17.7 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 8892  bytes 1059117 (1.0 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

From the output, we can see that the MAC Address was changed to **00:11:22:33:44:55** from **08:00:27:35:21:ff**.

### Using Python to change the MAC Address

Up until now, we manually typed the commands and executed them but we can automate the entire process using Python so that we just have to provide the script with the interface name and the new MAC Address.

**Modules Used:**

* [subprocess](https://docs.python.org/3.4/library/subprocess.html)

* [argparse](https://docs.python.org/3/library/argparse.html)

* [re](https://docs.python.org/3/library/re.html)

The **subprocess** module is used to execute system commands using Python. It contains several functions that can be used. We are going to focus on one particular function that is used to execute the system commands. It is called the **call() **function.

**Usage**

    subprocess.call([‘ifconfig’])

This instruction executes the ifconfig command using Python.

The **argparse **module makes it convenient to write user-friendly command-line interfaces. The program defines the arguments it requires and **argparse **will figure out how to parse these arguments. It also generates help and usage messages and issues errors when the user enters any invalid arguments.

**Usage**

    dcode@dcode-pop-os:~$ python3 programs_name.py -op1 option -op2 option

With the help of argparse we can provide the user with the ability to pass the input values using command-line arguments. Even if you don’t understand now, it will be clear once we start coding 😃.

Next, the **re **module provides **Regular Expression** matching operations. You’ll know for what we use the **re** module as we progress ahead.

### Python Script Coding

This is where we start coding in Python.

**Step 1:** **Import the modules discussed above.**
{% gist https://gist.github.com/dharmil18/1e1459b9df4617dbbab722ae14950127.js %}
**Step 2:** **Implementing the functionality to allow the users to pass command line arguments.**

To add this feature to our script, we’ll need to make use of the **argparse** module that we imported in Step 1.
{% gist https://gist.github.com/dharmil18/9447246358be9359156c2e40bedd9661.js %}
The above code allows the user to provide the input for **interface** value and **new MAC Address** as follows:

    dcode@dcode-pop-os:~$ python3 mac_changer.py -i interface_name -m new_mac_address

**OR**

    dcode@dcode-pop-os:~$ python3 mac_changer.py --interface interface_name --mac new_mac_address

interface_name = Name of the interface whose MAC Address you want to change.

new_mac_address = MAC Address that you want to replace in place of the old one.

The **options **stores the values provided by the user. When you print it the output is as follow:

    print(options)

    **Output:** 
    Namespace(interface='enp0s3', new_mac='00:55:22:33:44:11')

Individually accessing the **two **arguments is done as follows:

    print(options.interface)
    print(options.new_mac)

    **Output:** 
    enp0s3
    00:11:22:33:44:55
>  Note: The **dest **argument in the **add_argument() **method provides the name according to which the **parse_args()** stores its corresponding values in the **options **variable.

The last thing in Step 2 is we created a variable **command_args **and invoked the function **get_args()**.

    command_args = get_args()

The above statement will store the **options **returned by the **get_args() **hence giving us access to the argument values outside of the scope of the function.

**Step 3: Writing a function that changes the MAC Address.**

Now that we have the two values that we require to change the MAC Address **(interface and new MAC Address), **we’ll write a function that changes the MAC Address using system commands.
{% gist https://gist.github.com/dharmil18/9bd86d1ff2db94acd4a697a3bd0f8753.js %}
Look at the function that we just wrote, it accepts **two **arguments **(interface and new_mac). **The two values are provided to **change_mac()** function by **command_args** variable. It also checks whether the MAC Address provided is valid or not. After that, using the **subprocess **module we execute the system commands. Note: You require root access to execute these commands.

Now, at this point, we have completed a Python script that changes the MAC Address, but we still need to be sure that the new MAC Address was applied. And for that, we need a write another function that returns the current MAC Address of the interface.

**Step 4: Writing a function to get the current MAC Address**

This function takes interface as an input and returns the current MAC Address of that interface. We are writing this function to check whether the new MAC address was applied or not.
{% gist https://gist.github.com/dharmil18/77af041cea502a28a07b0c9542b1650b.js %}
So, the **get_current_mac()** function extracts the current MAC Address of the interface provided. Now, let’s break down this function.

    output = sub.check_output(['ifconfig', interface], universal_newllines = True)

The above statement is similar to **sub.call()** in the **change_mac()** function but there is one difference. The **sub.check_output()** functions returns the output of the command whereas **sub.call()** does not return anything. The returned output is stored in the variable **output.**

Also, the argument **universal_newlines = True **is used because if this value is set to **False** then the output returned is in **bytes.**
>  Official Docs -> By default, this function will return the data as encoded bytes. The actual encoding of the output data may depend on the command being invoked, so the decoding to text will often need to be handled at the application level. This behavior may be overridden by setting *universal_newlines* to True

The next statement is,

    search_mac = re.search(r"\w\w:\w\w:\w\w:\w\w:\w\w:\w\w", output)

This is where the **re** module is being used. **re** stands for Regular Expressions and these are often used in string matching applications. The **re.search()** function accepts the **regular expression** and the **string** in which the pattern is supposed to be matched. In our case, the string is stored inside the variable **output. **The **\w\w:\w\w:\w\w:\w\w:\w\w:\w\w **expression matches the MAC Address in the output variable which has the output of the **ifconfig** command. See the image below to understand it better.

![Regex Demonstration](https://cdn-images-1.medium.com/max/2124/1*6kJTE176C7GJTuPsi2A8AA.png)

As you can see that the regular expression matches the MAC Address perfectly (Highlighted in green in the Match result box). [Regular Expression Cheatsheet](https://pythex.org/)

Furthermore, there are chances that **re.search()** might find more than one string that matches the regex (regular expression). Therefore, the below statement will make sure to return the first matched element only. What if it matches more than one string and returns the string which is not desired? In the output returned by **ifconfig,** the chances of a getting string other than the desired one is next to zero.

    search_mac.group(0)

This sums up the **get_current_mac()** function.

Now, the only thing remaining is to check whether the MAC Address provided by the user was applied. For this, you can see that we have called **get_current_mac()** function twice, once before calling **change_mac()** function and once after calling the **change_mac()** function. This ensures that we capture both the MAC Addresses (before and after change). We then compare **changed_mac** and **command_args.new_mac **and if both matches then the MAC Address has been changed successfully.

    if changed_mac == command_args.new_mac:
        print('Successfully Changed the MAC Address')
    else:
        print('Could not change the MAC Address')

This where the script ends and to execute the script do the following: This was covered in **Step 2.**

    dcode@dcode-pop-os:~$ python3 mac_changer.py -i interface_name -m new_mac_address

    **OR**

    dcode@dcode-pop-os:~$ python3 mac_changer.py --interface interface_name --mac new_mac_address

The entire code can be found on my [Github](https://github.com/dharmil18/MAChanger).