MAC address randomization script

We’ve discussed this before and this approach leverages the wifi kill switch and there’s nothing here that macchanger doesn’t already do, but sometimes it’s better to have a script that “just does it” rather than a GUI (and a program that you might not want to install for whatever reason).

Before I introduce the script itself, allow me to recommend how to use it with your Librem laptop. Unfortunately, it doesn’t seem to work in crontab @reboot, so it’s kinda manual, unless you actually want to run it on a periodic basis (which might offend your router and cause problems).

  1. Open a terminal and run ifconfig. Your wifi device will likely start with “w”, and your LAN connection with “l”. Probably you want to connect to a public wifi, so copy its name to the clipboard. Let’s assume that you want to change the MAC address of the device called DEVICE.

  2. Arrive at the coffee shop with your laptop powered off and the wifi kill set to disable wifi (and Bluetooth, for that matter).

  3. Boot up.

  4. Open up a terminal and run the script:

macrandom.sh DEVICE

(This assumes that the script was marked as executable by chmod. If not, you can always prefix it with “/bin/bash” followed by a space.)

  1. It will ask you for your password because it needs to run “ip” as root. Enter your password, but don’t hit Enter yet.

  2. Turn on your radios via the kill switch. (I’m not sure how risky it is to have Bluetooth power back up, but by default, I don’t like it. Turn it back off, but not just yet, as you have more urgent matters to attend to.)

  3. As fast as possible, so as to minimize the expression window for your hardcoded default MAC, hit Enter. If you get an error, hit the up arrow to repeat the script command and run it again. (It won’t ask for your password the second time because you’re already within the root authentication time window.) Do this again and again until the script succeeds (silently). (The retries are necessary because there’s a delay between the time that you enable the radios in hardware and the time that the devices become available.) You can verify the MAC change by doing ifconfig again.

The best method would be to this in a manner so as to ensure that the MAC is randomized before expression of the default is even possible, but I don’t know how to do that without digging into the driver layer.

In order to run the script in the first place, you need xxd, which is a hex dump utility for prettyprinting binary files. Try:

xxd --version

If that doesn’t work, then just install it in the usual way that you’d install any other package, for example:

sudo apt get install xxd

Finally, here’s the script. Note that it ensures that bits 40 and 41 of the 48-bit MAC are set to 0 because they’re special flags. The logic is awkward, but that’s business as usual in bash.


if (($# != 1)); then
  echo Syntax: macrandomize.sh link-name-from-ifconfig
  exit
fi
mac=""
flag=0
for i in {0..5}; do
  byte=$(cat /dev/urandom|head -c1|xxd -i|tail -c3|head -c2)
  if (( $i == 0 )); then
    firstdigit=${byte:1:1}
    bits=$(cat /dev/urandom|head -c1|xxd -b|cut -c-12|tail -c3|head -c2)
    if (( $bits == "00" )); then
      seconddigit="0"
    elif (( $bits == "01" )); then
      seconddigit="4"
    elif (( $bits == "10" )); then
      seconddigit="8"
    else
      seconddigit="c"
    fi
    byte="${firstdigit}${seconddigit}"
  fi
  mac="${mac}${byte}"
  if (( $i < 5 )); then
    mac="${mac}:"
  fi
done
sudo ip link set dev $1 down
sudo ip link set $1 address ${mac}
sudo ip link set dev $1 up

The line containing “address” sets the actual address in the hardware. If you want to set a specific MAC address instead of a random one, you could do that in the same way.

4 Likes

Maybe less awkward for the MAC address generation …

# get raw random 48 bit number from uuidgen
macr=`uuidgen -r | cut -d - -f 5`
# clear bits 40 and 41
topb=`printf '%02x' "$(( 0x${macr:0:2} & 0xFC ))"`
macr=$topb${macr:2}

# format as a MAC address
mac=
for b in `echo $macr | fold -w 2` ; do
    mac=$mac$b:
done
mac=${mac:0:17}

I didn’t check the masking of the bits very carefully so e.&o.e. :wink:

2 Likes

Thanks for the simplification. Seems to work.

hey, I add the same idea a long time ago, never did it by lazyness

I took your code, and completely re-writted it

I add the following fonctionnalities

  • The script loop until it verify the mac has changed
  • Making it works with ‘ifconfig’ or ‘ip’
  • Random choose of the first 3 bytes from a manufacturer table
    One or many incoherent MAC can be tracked and trigger some warnings, it’s better to use a real manufacturer MAC, but random
    Take a look at your own devices around you, to add int the table the first bytes of your WIFI cards (from laptop, usb wifi cards, phones, tablets, etc …)
    Don’t put ethernet MAC or printer MAC, or other incoherent MACs
#!/bin/bash                                                                                                                         

# -------------------- Verifications ------------------------                                                                       
[ "$1" == "" ] && echo -e "\e[1;31mSyntax error\e[0m: $0 <link-name-from-ifconfig-or-ip>" && exit 1
device=$1

SUDO=""
if [ "$(whoami)" != "root" ] ; then
    which sudo >/dev/null
    [ $? -ne 0 ] && echo -e "\e[1;31mError\e[0m: 'sudo' is required, or execute in root" && exit 1
    SUDO="sudo"
fi

# Is ifconfig available ?                                                                                                           
flg_ifconfig=0
which ifconfig >/dev/null
[ $? -eq 0 ] && flg_ifconfig=1

# Is ip available ?                                                                                                                 
flg_ip=0
which ip >/dev/null
[ $? -eq 0 ] && flg_ip=1

[ ${flg_ifconfig} -ne 1 ] && [ ${flg_ip} -ne 1 ] && echo -e "\e[1;31mError\e[0m: 'ifconfig' or 'ip' command is required" && exit 1

# -------------------- Initialisations ------------------------                                                                     
# Manufacturer table IDs                                                                                                            
declare -a manu_table=(
"78:54:2e" #(d-link)                                                                                                                
"48:af:72" #(intel)                                                                                                                 
"48:5d:60" #(broadcom - AzureWave Technology)                                                                                       
"08:d4:0c" #(intel)                                                                                                                 
"0c:7a:15" #(intel)                                                                                                                 
# Add your known 3 first bytes of WIFI cards                                                                                        
)
nb_id=${#manu_table[@]}

# Get 6 random hexa from uuidgen for the last 3 bytes of the MAC                                                                    
mac=$(uuidgen -r)

# Random choos of the manufacturer                                                                                                  
id_manu=$(( $RANDOM % ${nb_id} ))

mac="${manu_table[${id_manu}]}:${mac:0:2}:${mac:2:2}:${mac:4:2}"

# -------------------- Setting the MAC ------------------------                                                                     
flg_ok=0
while [ $flg_ok -ne 1 ] ; do
    # Priority on 'ip', because 'ifconfig' is being replaced                                                                        
    if [ ${flg_ip} -eq 1 ] ; then
        ${SUDO} ip link set dev ${device} down
        ${SUDO} ip link set ${device} address ${mac}
        ${SUDO} ip link set dev ${device} up
    else
        ${SUDO} ifconfig ${device} down
        ${SUDO} ifconfig ${device} hw ether ${mac}
        ${SUDO} ifconfig ${device} up
    fi

    # Verify with ifconfig                                                                                                          
    if [ ${flg_ifconfig} -eq 1 ] ; then
        current_mac=$(ifconfig ${device} | grep -m1 ether | awk '{ print $2 }')
        [ "${current_mac}" == "${mac}" ] && flg_ok=1
    fi

    # Verify with ip                                                                                                                
    if [ ${flg_ip} -eq 1 ] ; then
        current_mac=$(ip link | grep -m1 ${device} -A1 | grep -m1 ether | awk '{ print $2 }')
        [ "${current_mac}" == "${mac}" ] && flg_ok=1
    fi
done

echo "New MAC : '${mac}'"

exit 0

I think there is one pending problem with my script is : if the device doesn’t support the operation, it willl loop forever
I don’t know how to catch this, I have no device rejecting it, better test before automating the use of this script :wink:

edit: seems like I forgot the thing about the bits 40 and 41

4 Likes

I dont understand the thing with the bits 40 and 41, seems to me that the flags are in the first byte (from the manufacturer)
see scheme : https://en.wikipedia.org/wiki/MAC_address#/media/File:MAC-48_Address.svg

Network transmission order for MAC addresses in 802.3 is most significant byte first i.e. high/low i.e. big endian - in the sense that most people when interpreting a MAC address as a 48 bit number would think of the OUI part of the MAC address as being more significant. (Weirdly though, the bits within the byte are transmitted low/high. The net effect is that the control bits are available to the receiving PHY layer first and immediately, so that if any hardware logic depends on those bits, that logic can be triggered before even the rest of the MAC address is received)

Since you are dealing with the MAC address purely as a string, you can ignore all that and just make sure that the second hex digit is always 0, 4, 8 or C i.e. the first byte is divisible by 4 i.e. the bottom two bits of the first byte are always 0 - whatever works for you.

I think this needs greater exploration. For sure, having a table of valid OUIs is a maintenance hassle. I would wonder better whether you can download that table from the IEEE or someone like that, if you are going down that road.

The question that I would put is: when a mainstream phone like the spiPhone randomizes the MAC address, does it

a) keep the first 3 bytes completely fixed
b) ensure the first 3 bytes are registered as a known OUI but otherwise random
c) first 3 bytes are fully random (other than clearing bits 40 and 41)

?

Because if you don’t want to stand out from the crowd / self-fingerprint then you would do what Apple and Android do.

However the article you link to notes that there are other ways of fingerprinting WiFi advertisements by the client that do not depend on the (randomized) MAC address.

Okay thanks for the confirmation !

I don’t think it’s a maintenance hassle, once a range of MAC can contains WIFI cards, it doesn’t have to be removed
You can add some ranges time to time, it’s better, but it’s not an absolute requirement

I found this list, it contains every range possible, but adding routers or priinters whould be kind of strange: https://gist.github.com/aallan/b4bb86db86079509e6159810ae9bd3e4
Keeping it up to date is a maintenance hassle, some are missing :wink:

It would really surprise me if Apple or Android don’t use a fixed MAC

Actually, looking at the spiPhone, it seems as if the MAC address you get when you choose a “private address” i.e. a randomized address, is in fact a Locally Administered address (i.e. bit 41 set!), which means that OUIs are probably irrelevant and you shouldn’t use them at all.

So that means that the second hex digit is always 2, 6, A or E. The bottom two bits of the first byte are always ‘10’ and perhaps the remaining 46 bits are freely random.

It looks like the spiPhone is doing something funny with DHCP though - so that it picks up the IP address that I have mapped for its real MAC address. This seems to be within the DHCP spec, since the (real) MAC address occurs within the IP layer DHCP message in addition to the (randomized) MAC address that occurs in the L2 header - and hence the two can be different.

MAC address randomization can be configured on a per SSID basis and it may tentatively be the case that the MAC address used with a given SSID does not change.

1 Like

Trying to take up the pace I do not succeed. Could anyone of you discussing this topic briefly summarize what you are talking about? Why would I want to randomize my MAC address? What are the benefits? Privacy (my assumption), but what exactly?

Tracking. Imagine yourself moving by the street and your phone roaming across XXX FreeWifi (replace XXX with local telco). That will give very precise trace of your movement, and then you come home and your trace is fully mapped to your identity. If your phone though changes mac on each wifi reassoc - you blend with the crowd.

2 Likes

will this be a default behavior with the L5 ? i assume this is legal so why shouldn’t it be ?

Thank you, now understood the use case. Is this “script” already ready to use for non-developers? Meaning is there a “how to” somewhere? My assumtion is that I cannot simply install it via PureOS Store, right?

1 Like

@kieran : okay so, the bit is set to 1 to tell the sytem , the MAC address was changed manually, so it may prevent a duplicate MAC (1 unlucky chance over 16777216 possibilities by manufacturer)
I have an opposite view on the OUI, because setting the bit to 1 does exactly the contrary to what we want : blending to the crowd, because the crowd don’t change their MAC, they have this flag set to 0
Maybe you can try to look at the actual MAC on your android/IOS device if you have one.

It remind me that funny (april fool) security flag in the IP protocol called ‘the evil bit’ : https://ietf.org/rfc/rfc3514.txt :smiley:

@ruff: to prevent what you say here, you’ll have to change your MAC regurarly (like, for exemple, every X minutes) but every time the MAC changes, any request network request sent before the change will not be answered, and you will have to reconnect

@aircan : it also prevent potential attacks on known range of product that may have a security flaw
Using the MAC can theorically help me identify what hardware you use
By randomizing it you, makes it more difficult
Actually, my script changes the MAC only one time when you execute it.
If you want to automatise it (with crontab for example) you have to be aware of the consequences :

  • loss of connections every time it’s changed
  • possible duplicate in local network (I think the consequence here are less problematic with WIFI, in a wired network it’s a very blocking matter)

I could easily add an option to make it change the MAC every X secondes or minutes (X given with the option)

@reC I don’t think it will be by default because of what I replied to aircan
I think it’s more a user choice here

But this quote here

Is something different than that one here

right?

So as of now the MAC address changes, when script is executed and not when connecting to new wifi? Or what dows “wifi reassoc” exactly mean?

Not sure what your mean…
You can change your MAC as many time as you want before even connecting the WIFI (that’s what my script does)
When you connect to the WIFI router, you send it your MAC so the router can target his answer to your card (that’s what the Operating System does)
wifi reassoc = wifi reassociation (or reconnection)
Does this answer to your question ?

classic security/usability problem. But don’t over-react. If you want to hide the traces - yes, you sacrifice usability for privacy/confidentiality. But for normal use-case it would suffice to change the mac on disassoc event (eg when network is already broken anyway). After all public wifi does not have such a tight coverage so you won’t be ever roaming smoothly/continuously.

1 Like

Oh it was not my intention
I totally agree with you here
Lauching the script when switching on the WIFI hardware would be great

But that is exactly what I did!

I am telling you what my spiPhone does. My research on the internet is that any recent Android phone will do likewise. Look at https://en.wikipedia.org/wiki/MAC_address#Randomization and other discussions on the internet.

The one thing that I can’t check is in what way Android randomizes.

I believe that MAC randomization is the default for all newly added SSIDs for all recent phones.

So I would tentatively assert: If you fail to randomize your MAC address then you will stand out from the crowd.

If you want to blend in, at least with spiPhones, then you should randomize in the specific way that I am describing.

Yes, but once you set the LA bit I believe that you have 46 bits of entropy in the rest of the MAC address, so the chance of a duplicate is much tinier than the number that you give - and the software will often probe for a duplicate before deciding to use a randomly generated address, and simply retry for a new address in the vanishingly small chance (1 in 70 trillion) that a duplicate occurred the first time.

Not necessarily. I haven’t looked into it in detail but if the IP stack reissues an ARP request when communication looks to have fallen into a hole (e.g. transmission fails to be ACKed, so it is going to retransmit) then it should ride out the change of MAC address. There could be a pause in communication so you probably wouldn’t want to accept that for any real-time or time-critical communication e.g. streaming video. But web and email might not even notice the change.

Of greater concern is how you change the MAC address. If it involves bringing down the interface, does that in turn cause other bad effects? I suspect it might.

I am not personally advocating changing the MAC address every X minutes. A different MAC address for every SSID, and all changed, say, every day at midnight should be good enough for me. But then I plan to have the WiFi killed when in public anyway - so the whole problem goes away.

For sure this is an issue. (If this is the only concern then you don’t need to randomize and you don’t need to change on an ongoing basis. A one-off overriding may be adequate.)

However I think for most people the motivation is avoiding being tracked. If you always use the same MAC address then a network of passive (or active) WiFi receivers can track your movement over large parts of a metropolitan area. See above link.

Not yet. :frowning:

From the wikipedia link

But it doesn’t say if there is randomization when connecting, which is the purpose here
I’ll try to test and :nose: some packets from android/IOS users next time, probably next week :frowning: (nothing better than observation :face_with_monocle: of the real requests)

1 Like

OK, fair cop. I think the Wikipedia article is somewhat out of date, at least as far as the spiPhone is concerned. I believe that randomization now applies beyond just scanning.

I can easily jump on my WAP and see that the spiPhone is associated with the WAP under a fake address that has the LA bit set. The fake address is the specific address that the spiPhone lists as to be used with that SSID. I have multiple SSIDs. If I look on the spiPhone under a different SSID, a different, unrelated random MAC address is given, again with the LA bit set. Neither MAC address equals the actual baked in MAC address that does not have the LA bit set and which uses the OUI and which is presumably never(?) used by the phone, unless there is privacy fail somewhere.

I can also use arp -a on my desktop/laptop after pinging the spiPhone and see that the phone is using its random MAC address. That command also nicely highlights which devices are using a MAC address with an OUI and which devices are using a MAC address with the LA bit set.

However, yes, there is no substitute for a bit of packet sniffing to satisfy yourself as to exactly what is being transmitted.