802.1X · · 10 min read

MAB with FreeRADIUS on Cisco: MAC Authentication Bypass Without ISE

MAB with no ISE node and no licence: FreeRADIUS 3.2.7 against a Catalyst 9000v, with the freeradius -X trail that proves MAB is bare PAP, the MAC format everyone gets wrong, and a real Method: mab / Authc Success on a live switchport.

Terminal card showing a Catalyst 9000v port authorized by MAB alongside the FreeRADIUS debug output that accepted the MAC

Every 802.1X rollout stalls on the same list of devices: the label printer in the warehouse, the ceiling cameras, the badge readers at the door, the ancient PLC nobody is allowed to touch. None of them run a supplicant, none of them will ever send an EAPOL frame, and on a port with authentication port-control auto they are simply dead. MAC Authentication Bypass is the escape hatch, and if you understand how 802.1X port authentication works on Cisco switches you are already 90 percent of the way to understanding MAB, because MAB is what happens when that process gives up.

There is already a detailed article on this site covering MAB with Cisco ISE, endpoint identity groups and profiling. If you have ISE, read that one instead: profiling and endpoint groups are genuinely better tooling than anything below. This article owns the other path, the one most engineers actually have available on a Tuesday afternoon, which is FreeRADIUS on a Debian box and a switch. No licence, no appliance, no ISE node. Everything here was built and captured that way.

The captures come from a CML lab: a Catalyst 9000v (cat9000v-uadp) running IOS XE 17.18.02 as the authenticator, FreeRADIUS 3.2.7 on a Debian 13 VM at 192.168.99.100 running in the foreground with freeradius -X, and a headless net-tools container as the endpoint with no supplicant at all. The payoff is a real Method: mab / Authc Success on a real switchport, and the RADIUS transaction that produced it, printed from both ends.

MAB is not 802.1X, it is PAP with a MAC in it

The single most useful thing to internalise about MAB is that it does not use EAP. 802.1X is an EAP transport: the supplicant and the RADIUS server run a full EAP method between them and the switch just relays EAPOL frames. MAB has no supplicant to talk to, so the switch fabricates an ordinary username and password request out of the source MAC address it saw on the wire, and sends that. Plain PAP.

You do not have to take that on faith, because the server prints its own decision. This is the policy trail from freeradius -X as it handles a MAB request:

(4) Received Access-Request Id 176 ... User-Name = "525400031ace"
(4)     [preprocess] = ok
(4) suffix: No '@' in User-Name = "525400031ace", looking up realm NULL
(4) eap: No EAP-Message, not doing EAP          <-- MAB is NOT EAP: bare PAP, no supplicant
(4) files: users: Matched entry 525400031ace at line 222
(4) Found Auth-Type = PAP
(4) pap: Comparing with "known good" Cleartext-Password

eap: No EAP-Message, not doing EAP followed immediately by Found Auth-Type = PAP is the whole distinction in two lines. For contrast, the same server handling a genuine 802.1X supplicant (driven with eapol_test, since the lab endpoint cannot do EAP) negotiates a full tunnelled method:

CTRL-EVENT-EAP-STARTED EAP authentication started
CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=4 -> NAK
CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=25
CTRL-EVENT-EAP-METHOD EAP vendor 0 method 25 (PEAP) selected
EAP: Status notification: remote certificate verification (param=success)
CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
MPPE keys OK: 1  mismatch: 0
SUCCESS

Same server, same users file, two completely different authentication paths. PEAP builds a TLS tunnel, verifies a server certificate and derives keying material. MAB compares a string to another string. Hold that thought for the security section at the end.

The FreeRADIUS side: two files

This is where the open-source path is genuinely simpler than the ISE path. There is no endpoint database, no identity group, no profiling engine. There is clients.conf, which says who is allowed to ask, and users, which says who is allowed in.

! ---- /etc/freeradius/3.0/clients.conf : who may send RADIUS ----
client pinglabz-lab-subnet {
    ipaddr = 192.168.99.0/24
    secret = PingLabzRAD123
    nas_type = cisco
    shortname = pinglabz-lab
}

That secret has to match the switch's key byte for byte. A mismatch shows up server-side as Received packet from ... with invalid Message-Authenticator, which is the most common failure in any RADIUS build and worth recognising on sight. If your switch is not getting answers at all, the diagnostic path for a RADIUS server that the switch thinks is dead is the same whether the server is ISE or FreeRADIUS.

Then the identity itself. For MAB, the identity is the MAC address, used as both username and password:

! ---- /etc/freeradius/3.0/users : the identities ----
! 802.1X supplicant:
alice   Cleartext-Password := "PingLabz1x"
        Reply-Message = "RES-0004 dot1x accept for alice"
! MAB - Cisco sends the endpoint MAC as BOTH username and password,
! lowercase, no separators (default 'ietf' MAB format on IOS):
525400031ace  Cleartext-Password := "525400031ace"
        Reply-Message = "RES-0015 MAB accept for H1 by MAC"

That is the entire MAB "policy". One line per device you are willing to let on. It is crude next to ISE, and it is also the reason you can stand this up in ten minutes.

Before touching the switch, prove the server in isolation with radtest, passing the MAC as both credentials exactly as the switch will:

! Command: radtest 525400031ace 525400031ace 127.0.0.1 0 testing123
Sent Access-Request Id 176 ...
	User-Name = "525400031ace"
	User-Password = "525400031ace"
Received Access-Accept Id 176 from 127.0.0.1:1812 to 127.0.0.1:59405 length 73
	Reply-Message = "RES-0015 MAB accept for H1 by MAC"

The MAC format gotcha that bites everyone

Look closely at that username: 525400031ace. The endpoint's MAC is 52:54:00:03:1a:ce. Cisco's default MAB attribute format on IOS XE (the ietf format) is lowercase hex with no separators at all, and it is sent as the User-Name and the User-Password. If your users entry says 52:54:00:03:1a:ce, or 5254.0003.1ace, or 525400031ACE, the file module simply does not match it and the server rejects. You will get an Access-Reject with no obvious reason, because nothing is wrong except capitalisation.

What makes this genuinely confusing is that a single MAB Access-Request carries the same MAC in two different formats at once. Here is the request the switch actually sent for the live endpoint, MAC 52:54:00:f0:93:48:

(2) Received Access-Request Id 3 from 192.168.99.12:61802 to 192.168.99.100:1812 length 291
(2)   User-Name = "525400f09348"
(2)   User-Password = "525400f09348"
(2)   Service-Type = Call-Check                       <-- the MAB service-type
(2)   Cisco-AVPair = "service-type=Call Check"
(2)   Cisco-AVPair = "method=mab"                      <-- switch declares it is MAB
(2)   Cisco-AVPair = "audit-session-id=0C63A8C00000000E969AF179"
(2)   NAS-IP-Address = 192.168.99.12
(2)   NAS-Port-Id = "GigabitEthernet1/0/2"
(2)   Calling-Station-Id = "52-54-00-F0-93-48"         <-- the endpoint MAC
(2)   Called-Station-Id  = "52-54-00-13-E9-16"         <-- the switch port MAC

The User-Name is lowercase and unseparated. The Calling-Station-Id, in the very same packet, is uppercase and hyphen-separated. People copy the MAC out of a debug, paste it into the users file, and pick the wrong one of the two. Match the User-Name format, always, because that is the attribute the files module keys on. (Other vendors default to different formats, and IOS XE can be told to send others, which is exactly why "the MAC format" is a support ticket generator across every NAC product.)

Two other things in that packet are worth knowing by heart: Service-Type = Call-Check and the Cisco AVPair method=mab. Those are the on-wire fingerprint of MAB. If you ever need to write a server-side policy that treats MAB differently from 802.1X, Call-Check is what you key on.

The switch side: MAB is a fallback, and that is why it feels slow

On the port, MAB is one command. The interesting part is the method order around it:

interface GigabitEthernet1/0/2
 switchport mode access
 authentication port-control auto
 authentication order dot1x mab          ! try 802.1X first, then MAB
 authentication priority dot1x mab
 authentication host-mode single-host
 authentication violation restrict
 dot1x pae authenticator
 dot1x timeout tx-period 5               ! how long each 802.1X probe waits
 dot1x max-reauth-req 2                  ! probes before falling back to MAB
 mab
 spanning-tree portfast

authentication order dot1x mab means the switch tries 802.1X first every single time, even on a port where you know full well the device is a printer. It sends EAP-Request/Identity, waits tx-period seconds, retransmits, waits again, and only when the silence has gone on for tx-period multiplied by max-reauth-req does it give up and start MAB. With the values above that is about 15 seconds; with the IOS defaults it is closer to 30. This is the answer to the most common MAB complaint, which is "the camera takes half a minute to come online after a reboot". Nothing is broken. You are watching 802.1X time out.

authentication priority dot1x mab is the other half and it is a security control, not a timer. It says that if an EAPOL frame ever shows up on a port that MAB has already authorised, 802.1X wins and preempts the MAB session. Without it, someone who unplugs the printer and plugs in a laptop inherits the printer's authorisation.

If a port is dedicated to a non-supplicant device forever, you can flip to authentication order mab dot1x and cut the wait to a couple of seconds. Keep priority dot1x mab when you do.

The payoff: Method mab, Authc Success

With the endpoint plugged into Gi1/0/2 and its MAC in the users file, this is what the switch reports. This is the output the whole article is built toward, and it tells the story in four lines:

SW9K# show authentication sessions interface GigabitEthernet1/0/2 details
            Interface:  GigabitEthernet1/0/2
          MAC Address:  5254.00f0.9348
            User-Name:  525400f09348
               Status:  Authorized
               Domain:  DATA
       Oper host mode:  single-host
    Common Session ID:  0C63A8C00000000E969AF179
Method status list:
       Method           State
        dot1x           Stopped          <-- 802.1X tried first, no supplicant, timed out
          mab           Authc Success    <-- fell back to MAB, RADIUS said yes

The method status list is the part to read. dot1x Stopped then mab Authc Success is authentication order dot1x mab doing exactly what it was told: 802.1X was attempted, the endpoint stayed silent, MAB took over and the RADIUS server said yes. If you are ever handed a port that will not come up, this list is the first thing to look at, and the wider technique for reading show authentication sessions when a port stays unauthorized applies unchanged to MAB.

Server-side, the same transaction completes like this:

(2) eap: No EAP-Message, not doing EAP
(2) files: users: Matched entry 525400f09348 at line 226
(2) Found Auth-Type = PAP
(2) pap: User authenticated successfully
(2) Sent Access-Accept Id 3 from 192.168.99.100:1812 to 192.168.99.12:61802 length 83
(2)   Reply-Message = "RES-0015 live MAB accept for H9 on cat9000v"

And once the port is authorised, the switch pins the endpoint's MAC:

SW9K# show mac address-table interface GigabitEthernet1/0/2
   1    5254.00f0.9348    STATIC      Gi1/0/2      <-- pinned STATIC once authorized

A ping from the endpoint to the switch SVI came back 3 of 3 after authorisation. Before it, the port was unauthorised and the endpoint was cut off entirely. That is the control working.

Which switch image actually implements MAB

This cost more lab time than the entire FreeRADIUS build, so take it as given. Three common virtual switch images, three different answers, and only one of them is usable:

ioll2-xe
CLI acceptedNo
Behaviour% Invalid input
VerdictHonest rejection
iosvl2
CLI acceptedYes
BehaviourNever authenticates
VerdictSilent no-op, avoid
cat9000v-uadp
CLI acceptedYes
BehaviourFull authenticator
VerdictUse this one

The iosvl2 case is the nasty one, because the configuration is accepted into the running config without a murmur and then nothing happens. No Access-Request ever leaves the box. debug dot1x events during an interface bounce explains why:

dot1x-ev:[Gi0/1] Interface state changed to UP
dot1x-ev:DOT1X Supplicant not enabled on GigabitEthernet0/1
dot1x-ev:[Gi0/1] No DOT1X subblock found for port down

Build on cat9000v-uadp, or on real 9200/9300 hardware. If you want the full authenticator build (AAA, the RADIUS server block, the global dot1x system-auth-control) rather than just the MAB delta shown above, the complete 802.1X and FreeRADIUS lab build is the prerequisite for this one and covers the base configuration line by line.

Gotchas from the build

  • The port boots err-disabled with a security violation. The default dot1x violation action is shutdown, so the very first frame from an unauthorised endpoint on a single-host port can kill the port before MAB ever runs: %PM-4-ERR_DISABLE: security-violation error detected on Gi1/0/2. Add authentication violation restrict (and errdisable recovery cause security-violation), then bounce the port.
  • Wrong MAC format, silent reject. Lowercase, no separators, matching the User-Name in the Access-Request. Not the Calling-Station-Id form.
  • The first interface on a Catalyst 9000v is not a switchport. Gi0/0 is a management port in Mgmt-vrf. Cabling your external connector there (the obvious first-slot wiring) puts your RADIUS path in the wrong VRF and nothing reaches the server. Cable a real Gi1/0/x switchport instead.
  • After aaa new-model, your priv-15 user is not priv-15. Add aaa authorization exec default local or SSH sessions land at SW> and automation fails to enter enable mode. On the virtual 9000v specifically, do not set an enable secret at all if you are driving the console with PyATS.
  • Do not skip the radtest step. Proving the users entry locally before you involve the switch turns a two-variable problem (server policy, switch config) into two one-variable problems.

The honest part: MAB is inventory control, not authentication

A MAC address is not a secret. It is printed on a sticker on the back of the device, it is broadcast in every frame the device sends, and any laptop can be made to claim it in one command. The captures above show why that matters at the protocol level: 802.1X ran PEAP, built a TLS tunnel, verified a certificate and derived keys. MAB compared 525400f09348 to 525400f09348. Anyone who can read a sticker or run a sniffer for thirty seconds has the credential.

So do not describe MAB to your security team as authentication, because it is not. It is an allow list of known hardware, and its real value is that it forces you to have an inventory at all. Treat every MAB port as a port that a determined attacker owns, and design around that: put MAB endpoints in their own VLAN, give them a restrictive dACL or port ACL, keep authentication priority dot1x mab so a supplicant preempts a spoofed session, and use single-host mode where the device genuinely never changes. Layer 2 controls matter more here than anywhere else, so pair it with the protections that stop a host from poisoning ARP or claiming an IP that is not its own. And if you have ISE, its profiling engine at least cross-checks that the thing claiming to be a printer behaves like a printer, which bare FreeRADIUS cannot do.

Key Takeaways

  • MAB carries no EAP. The switch turns the source MAC into a plain PAP username and password, which is exactly why a device with no supplicant can pass it, and eap: No EAP-Message, not doing EAP in freeradius -X is the proof.
  • The username format is lowercase hex with no separators (525400f09348). The Calling-Station-Id in the same packet is uppercase and hyphenated. Match the User-Name.
  • authentication order dot1x mab means 802.1X is always tried first, so a MAB endpoint waits tx-period times max-reauth-req seconds before it is authorised. That delay is the design, not a fault.
  • Method: mab / Authc Success after dot1x Stopped in show authentication sessions is the confirmation that fallback fired, and Service-Type = Call-Check plus method=mab is the same event seen from the RADIUS server.
  • Lab it on cat9000v-uadp or real hardware. ioll2-xe rejects the commands and iosvl2 accepts them and does nothing.
  • MAB is an allow list of known hardware, not authentication. Scope its access accordingly, and see the rest of the wired port authentication cluster for the controls that go around it.

Read next

Terminal card showing a repeating CDP native VLAN mismatch log naming Ethernet0/0 native VLAN 1 against SW2 Ethernet0/0 native VLAN 99
VLANs ·

Native VLAN Mismatch: Read the CDP Log, Fix the Trunk

%CDP-4-NATIVE_VLAN_MISMATCH hands you both interfaces and both native VLANs in one line. The real damage is underneath it: untagged frames get re-homed at the trunk boundary and two VLANs quietly become one. Captured live on IOS XE 17.18.2 in CML.