Recreating a 90s Dial-Up ISP and Finding a RADIUS Bug in a US.S. Robotics NetServer/16

Recreating a 90s Dial-Up ISP and Finding a RADIUS Bug in a US.S. Robotics NetServer/16

I've been working on a personal side project that only makes sense if you grew up around modems, dial tones, and busy signals. I'm recreating a late-90s dial-up ISP. Not as a joke. Not as a museum piece. Just because I always wanted to understand how it really worked.

Back in the 90s I was obsessed with ISPs. I wanted to work at one. I loved the idea of racks of modems, blinking lights, terminal screens, and phone lines humming away in the background. That never happened back then, so I figured I'd build one now.

This post is about one specific and unexpected problem I ran into while doing that: a real firmware bug in a U.S. Robotics NetServer/16 Plus that breaks RADIUS authentication.

The Hardware

The core of this setup is a U.S. Robotics NetServer/16 Plus, a rack-mount modem pool unit from the late 1990s. This is the kind of gear small ISPs actually used.

Mine came to me with:

  • Firmware: V4.2.3, build 85
  • Approximate era: 1999
  • 16 internal modems
  • Ethernet uplink
  • Built-in support for PPP and RADIUS authentication

The goal was simple and period-accurate:

  • Incoming calls hit the NetServer
  • Modems answer
  • PPP negotiates
  • Authentication is handed off to a modern FreeRADIUS server running on Linux (turns out findind old, free, radius servers is not that simple)

At least, that was the plan.

Everything works... until RADIUS

From the outside, things looked good. Modems answered calls, PPP negotiation completed, Usernames and passwords were sent, but authentication always failed.?

FreeRADIUS wasn't logging rejects. It wasn't logging errors. It was acting like the packets didn't exist at all.

That was the first red flag.

The Bug

After spending more time than I want to admit staring at tcpdump output, I finally noticed something subtle but very wrong. The NetServer was sending malformed RADIUS packets. More specifically the RADIUS length field was set to 70 bytes (0x0046). The actual packet size was only 64 bytes (0x0040) that's an exact 6-byte mismatch. FreeRADIUS reads the length field, waits for 70 bytes, only receives 64, and silently drops the packet. No error. No log entry. Just gone.

From FreeRADIUS' point of view, this packet is invalid and unsafe to process.

Example: bad packet (firmware V4.2.3)

0000   bc 24 11 32 ac 55 00 c0 49 11 36 1f 08 00 45 00   .$.2.U..I.6...E.
0010   00 5c 00 d9 00 00 ff 11 a6 9b 0a 0a 00 03 0a 0a   .\..............
0020   00 06 06 6d 07 14 00 48 93 9e 01 01 00 46 1e 74   ...m...H.....F.t
0030   cc 64 e5 cc 02 28 00 99 93 64 5f b8 e3 17 01 07   .d...(...d_.....
0040   77 69 6e 39 38 03 13 04 9f 28 44 9e 15 e5 bc aa   win98....(D.....
0050   a1 3c dc 29 b8 de 66 d1 07 06 00 00 00 01 04 06   .<.)..f.........
0060   0a 0a 00 03 05 06 00 00 00 01                     ..........

Note the length field 00 46 even though only 64 bytes are present.

Tracking down older firmware

At this point I had two options. Write a packet mangler to fix the length field on the fly or see if older firmware behaved correctly.

I got lucky, very lucky. I found an old .nac firmware file on a 1.44 MB floppy containing U.S. Robotics NetServer/16 Version V4.1.7 Built March 16, 1998. I flashed the NetServer back to this version. Immediately, RADIUS authentication started working. No config changes. No hacks. Just correct packets.

Example: good packet (firmware V4.1.7)

0000   00 00 00 01 00 06 00 c0 49 11 36 1f 00 00 08 00   ........I.6.....
0010   45 00 00 5c 01 d9 00 00 ff 11 a5 9b 0a 0a 00 03   E..\............
0020   0a 0a 00 06 06 6d 06 6d 00 48 71 0c 01 01 00 40   .....m.m.Hq....@
0030   2c 7a ee be 9b af 84 e1 3e a0 46 b4 85 ca eb 2d   ,z......>.F....-
0040   01 07 77 69 6e 39 38 03 13 05 79 59 ab 1f c4 6b   ..win98...yY...k
0050   93 15 f9 72 5a 08 8d 23 91 97 07 06 00 00 00 01   ...rZ..#........
0060   04 06 0a 0a 00 03 05 06 00 00 00 00               ............

Here the length field is correctly set to 00 40.

A side note about ports

You might notice another difference in ports. Firmware V4.2.3 allowed changing the RADIUS port, Firmware V4.1.7 is locked to UDP 1645. That's the old pre-standard RADIUS port, before 1812 became common. This suggests active development was happening between these versions. Which makes what happened next even more interesting.

The real cause

After comparing notes and digging further, I'm still not sure if this was a known bug or not however my best guess is when U.S. Robotics was acquired and rebranded as 3Com, someone did a global search-and-replace in the source code USRobotics -> 3Com. That string is exactly 6 characters shorter. The RADIUS packet builder used a hardcoded or pre-computed size that included the old string length. When the name changed, the packet size shrank, but the length field calculation didn't.

Result Payload is 6 bytes shorter, length field is unchanged, every RADIUS packet is malformed.

It's a perfect late-90s firmware bug.

Final thoughts

Rolling this NetServer back one firmware version fixed everything and made the setup fully functional. Dial-up users authenticate cleanly against FreeRADIUS, just like they would have in the late 90s. There's something deeply satisfying about finding a real bug in real hardware that once lived in real ISPs. This isn't emulation. This isn't theory. This is the actual code people depended on.

If there's interest, I may still do a write-up on fixing this in-flight with nftables and NFQUEUE, because it's a fun problem. But honestly, flashing older firmware felt more authentic anyway. If you're also playing with vintage networking gear, modem pools, terminal servers, or retro ISPs, I'd love to hear about it.

My only regret in this entire project was not being able to save the bad firmware V4.2.3 or even be able to find this firmware in it's origional .nac format as I would really like to take a stab at reverse engineering it, in an attempt to fix that one issue.

And yes, I know it's 2025.

Cheers.