This post is about MAC Addresses and dissecting their common randomization format.

metal

What is a MAC Address

A MAC address1 is an identifier for a network interface. This is sometimes referred to as a layer 2 address2 or physical address.

Note some networked devices may have multiple interfaces. This means a device could be identified by multiple MAC addresses.

There are different types of MAC addresses. Some are universally unique. Some are locally assigned (not universally unique). Some kinds have different lengths.

This post will only cover at the surface unicast 48-bit MAC addresses (with a 24-bit OUI), Universally Administered (UAA), and Locally Administered (LAA).

Governance and Definition

There are two standards bodies involved in MAC Addresses. The concept and definition is a recognized international standard.

The International Organization for Standardization (ISO) body defines MAC Address definition and representation. ISO legitimizes the format internationally. The original standard is ISO/IEC 10039 published first in 1991, but I was unable to find an available copy to review for this post.

The Institute of Electrical and Electronics Engineers (IEEE) is the authority, assigner, and maintainer of unique identifiers.

Representation of a MAC Address

A binary3 representation of a 48-bit MAC address is 6 octets of binary digits (bits):

octet 0 1 2 3 4 5
  01110000 10000001 11101011 11001010 10101001 10110001

The human readable format of a 48-bit MAC address is represented by 12 hexadecimal (hex) characters. There are 16 characters in a single hex digit which represent values 0 to 9 and a to f. This means hex is known as a base 16 numerical system. Each octet is represented by 2 hexadecimal characters.

A 48-bit MAC address represented in hexadecimal:

  • 70:81:eb:ca:a9:b1

You might see hexadecimal representation in a few different formats. Here are some common ones:

  • 70:81:eb:ca:a9:b1 - colon delimiter
  • 70-81-eb-ca-a9-b1 - hyphen delimiter
  • 7081.ebca.a9b1 - dot delimiter in groups of 4 hex digits
  • 7081ebcaa9b1 - no delimiter

Stacked binary and hexadecimal representation:

octet 0 1 2 3 4 5
hex 7 0 8 1 e b c a a 9 b 1
binary 0111 0000 1000 0001 1110 1011 1100 1010 1010 1001 1011 0001

Universal MAC Addresses

Companies like Apple, Google, Microsoft, etc. request and purchase blocks of identifiers (MAC addresses) from the IEEE. The IEEE will assign them a 24-bit Organizationally Unique Identifier (OUI). The remaining bits are assigned however the company wishes given uniqueness is maintained.

When a network interface is using the MAC address assigned by the company, this is known as the burned-in address. This particular identifier is known as a Universal MAC Address.

Example representation of an OUI:

octet 0 1 2
24-bit OUI
hex 7 0 8 1 e b
binary 0111 0000 1000 0001 1110 1011

70:81:eb is an OUI assigned to Apple by the IEEE. You can see for yourself by plugging in the hex representation into an OUI lookup tool4.

MAC Randomization

MAC randomization is not a new concept. It has been used for years by certain clients to randomize the address sent in probe requests. When associated, the client would send the real MAC address.

Many client manufacturers now enable MAC randomization by default. For both the probe request and association. This includes iOS 14, Android 10, and Windows 10. Jim Palmer has a great article on this.

This means the MAC randomization feature changes the burned-in address to a randomized MAC address. The idea of MAC randomization is to help protect end users from tracking or profiling across Wi-Fi networks. It is a privacy feature for consumers.

The common private MAC address format leveraged is technically known as the Locally Administered Address (LAA) format.

Local MAC Addresses

MAC address can either be universally or locally assigned. We already covered that universal addresses are assigned by the manufacturer. A locally administered address is not assigned from an IEEE block of MAC addresses.

We can determine whether a MAC address is universal or local by looking at a specific bit in the first octet. The distinguishment is by setting the second least significant bit (LSB) of the first octet.

If the second LSB is set (1), the address is administered locally. If the second LSB is unset (0), the address is administered universally:

  • 0: intended to be globally unique (OUI enforced)
  • 1: locally administered

For a local example, the first octet of d6:70:b6:a2:b8:8a contains d6 (2 hex digits). The binary representation of the first octet is 11010110. Where bits 7-4 = 1101 or hex digit d, and then bits 3-0 = 0110 or hex digit 6. The second LSB is set. This is a local MAC address.

The second LSB is 1. It’s bit position 1 in the table below (the value two from the far right).

bit position 7 6 5 4 3 2 1 0
1 1 0 1 0 1 1 0
MSB LSB

For a universal example, the first octet of 70:81:eb:ca:a9:b1 contains 70 (2 hex digits). The binary representation of the first octet is 01110000. Where bits 7-4 = 0111 or hex digit 7, and then bits 3-0 = 0000 or hex digit 0. The second LSB is unset. This is a universal MAC address.

The second LSB is 0. It is represented by bit position 1 in the table below.

bit position 7 6 5 4 3 2 1 0
0 1 1 1 0 0 0 0
MSB LSB

Structure of Local MAC Addresses

We can now look at the 2nd LSB of the first octet of a MAC address to determine if it is following the MAC randomization format.

Here are the possible unicast values for the 2nd digit of a MAC address:

bit position 7-4 3 2 1 0 hex type
.... 0 0 0 0 x0 universal
.... 0 1 0 0 x4 universal
.... 1 0 0 0 x8 universal
.... 1 1 0 0 xC universal
.... 0 0 1 0 x2 local
.... 0 1 1 0 x6 local
.... 1 0 1 0 xA local
.... 1 1 1 0 xE local
MSB LSB

This means we can look at the second character of a MAC address for 2, 6, a, or e to determine if it is a randomized MAC:

  • x2:xx:xx:xx:xx:xx
  • x6:xx:xx:xx:xx:xx
  • xA:xx:xx:xx:xx:xx
  • xE:xx:xx:xx:xx:xx

Detection Using Python

Example Function

Here is an example using a Python function to test if a string representation of a MAC is a randomized MAC:

def is_randomized(mac: str) -> bool:
    return any(local == mac[1].lower() for local in ["2", "6", "a", "e"])

Example:

>>> def is_randomized(mac: str) -> bool:
...     return any(local == mac[1].lower() for local in ["2", "6", "a", "e"])
...
>>>
>>> macs = [
...     "9a:38:2b:54:f3:7b",
...     "b2:fd:70:62:b0:d9",
...     "16:08:61:6f:a4:97",
...     "5e:b8:22:43:61:6b",
...     "00:0b:86:a3:71:22",
...     "fc:ec:da:16:50:fb",
...     "d4:20:b0:09:1c:bf",
...     "a8:8e:24:a3:38:bc",
... ]
>>>
>>> for mac in macs:
...     print(f"mac: {mac}, random? {is_randomized(mac)}")
...
mac: 9a:38:2b:54:f3:7b, random? True
mac: b2:fd:70:62:b0:d9, random? True
mac: 16:08:61:6f:a4:97, random? True
mac: 5e:b8:22:43:61:6b, random? True
mac: 00:0b:86:a3:71:22, random? False
mac: fc:ec:da:16:50:fb, random? False
mac: d4:20:b0:09:1c:bf, random? False
mac: a8:8e:24:a3:38:bc, random? False

Example Regular Expression

Here is a Python example using a regular expression to do the same thing:

>>> macs = [
...     "9a:38:2b:54:f3:7b",
...     "b2:fd:70:62:b0:d9",
...     "16:08:61:6f:a4:97",
...     "5e:b8:22:43:61:6b",
...     "00:0b:86:a3:71:22",
...     "fc:ec:da:16:50:fb",
...     "d4:20:b0:09:1c:bf",
...     "a8:8e:24:a3:38:bc",
... ]
>>>
>>> import re
>>>
>>> for mac in macs:
...     match = re.search("^.[ae26].*", mac.lower())
...     if match:
...         print(mac)
...
9a:38:2b:54:f3:7b
b2:fd:70:62:b0:d9
16:08:61:6f:a4:97
5e:b8:22:43:61:6b

Changelog

  • 2020/10/03: clarifications, add link to a post by Jim Palmer

Thanks to Keith Miller for reading drafts of this.


  1. The Wikipedia MAC address article is a good reference for more in depth reading. The IEEE tutorial on Standard Group MAC Addresses is also essential. 

  2. Layer 2 is known as the Data Link layer of the OSI Model

  3. When referring to bit order in networks we use network byte order or big-endian byte order. This means the most significant bit is first. See endianness for the gory details. 

  4. I use the Wireshark OUI lookup tool