Motorola MM1000 MoCA Adapter

The Problem

Every other year or so, our home internet and cable bill goes up and we have to call our ISP to renegotiate our terms. This happened recently, and I asked my wife to give Verizon (our ISP) a call and threaten to switch to Comcast (competitor) if we couldn’t get a better deal. After several calls and hours on hold, my wife was able to get our bill knocked down about 10% and our internet speed doubled from 100Mbps to 200Mbps. She said the only issue is that if we wanted to actually make use of the full 200Mbps, we’d have to switchover to using Ethernet at the ONT (Optical Network Terminal) instead of coax, because the ONT can only operate up to 100Mbps over coax.

Ironically, even with five people in our house, and all using the internet concurrently for work or school during the COVID-19 lockdown, we didn’t have much complaints about our 100Mbps throughput limit. BUT now that we had access to 200Mbps, I couldn’t not take advantage of that! And so began my investigations into MoCA.

MoCA

So what’s MoCA? MoCA stands for Multimedia over Coax Alliance, and it’s simply an alternative physical layer implementation of Ethernet that goes over your coax line instead of something more familiar, like CAT6 cabling. Many people are using MoCA these days as a way to overcome the limitations of WiFi and bring gigabit speeds to each room of their house without re-wiring their entire house with CAT6. While WiFi speeds are improving with each new release of the standard, they still lag wired solutions and probably always will. If your house is already wired with a coax cable connection in each room, why not take advantage of it?

Here’s how my house was setup before making any changes. The Verizon Fios fiber line comes to the ONT mounted outside my house. A coax line connects from the ONT to my home’s coax network. Inside the house, I had a splitter that connected from a coax port in my basement to both my G1100 FiOS Quantum Gateway, which I bought from Verizon, and the Verizon Set Top Box (STB). I do have some parts of my house wired with CAT6, and so I connect my home’s CAT6 Ethernet network to the Ethernet LAN port of the Quantum Gateway.

Configuration Before Any Modifications

The ONT at my house is an Alcatel I-211M-H and was installed at our house around 2010. As explained by Verizon, this ONT only supports up to 100Mbps over the MoCA interface. I have some doubts about the actual throughput of the ONT, because the MoCA 1.0 standard, which was ratified in 2006, supports speeds of up to 135Mbps, and MoCA 1.1, which was released in 2007, supports up to 175Mbps. At any rate, my older ONT certainly couldn’t support the gigabit speeds of MoCA 2.0, because it’s just too old.

Now the easiest solution would be to simply switchover to the ethernet port at the ONT and connect this up to the rest of my house, which is partially wired for CAT6 Ethernet anyway. The problem was that I couldn’t easily do this, because the ONT is on the far outside corner of my house, and there isn’t an easy way to run Ethernet from there to the network in my house. Verizon wanted to sell me the Fios Network Extender and charge me monthly to rent the device, but I didn’t want an additional monthly charge on my bill, so I decided to buy my own device.

The second easiest solution would be to buy two MoCA adapters and connect one of them to the ONT’s Ethernet port and the other one to my router’s WAN Ethernet port, with the coax line between the two MoCA adapters. But that would require purchasing two MoCA adapters, and that seemed like a waste of good money when my router already had a coax port that supported MoCA.

Goal: Simulate the ONT’s WAN MoCA Interface

There are several popular MoCA adapters on the market that are sold in pairs, but like I said, I only wanted to buy a single MoCA adapter to place at the ONT between the Ethernet port of the ONT and coax line going into my house. I settled on purchase the Motorola MM1000 MoCA 2.0 adapter for about $70 on Amazon. My plan was to place the MM1000 in my garage by the ONT, and connect the Ethernet port to the ONT and the coax to the line going to my house. The MM1000 would effectively simulate the ONT’s MoCA interface in my original setup. Here’s a diagram of what I envisioned.

Desired Configuration to Support 200Mbps

In researching the MM1000 before placing the order, I was unsure of whether it would be able to simulate the ONT’s MoCA interface. MoCA supports several different frequency bands as shown in the tables below. My ONT was using the C4 Band at 1000 MHz to connect to the WAN interface of my G1100 Gateway, but from the G1100’s User Manual, I noticed that it can use either the 975-1025 MHz range or the 1350-1675 MHz range for the WAN port, so I was hopeful it would work out.

MoCA ChannelFrequency (center), MHz
E1500
E2525
E3550
E4575
E5600
A1875
B1900
C1925
C2950
C3975
C41000
MoCA E, A, B, and C Bands
MoCA ChannelFrequency (center), MHz
D11150
D21200
D31250
D41300
D51350
D61400
D71450
D81500
MoCA D Bands

 

When the MM1000 finally arrived, I was excited to test it out and see if I could get it to simulate the ONT’s WAN connection to the G1100. When I connected them together, the MM1000 immediately established a link with the G1100, but it was on the MoCA LAN band of 1150MHz instead of the MoCA C4 Band used for the WAN interface on my router. I didn’t realize until starting this project that you can have MoCA operating on multiple bands at the same time. For the past 10 years that I’ve had Fios, the ONT was talking to my G1100 router over the C4 band on the router’s WAN interface and it was also using the D1 band via the LAN interface. This is in fact how the Set Top Box is connected to the internet to get out-of-band info like the Channel Guide menu. The STB gets its internet connection via the LAN MoCA interface on my router, and it gets an IP address via DHCP using the same DHCP IP range as the rest of my home’s network.

Now the MM1000 advertises that it supports the entire D band (1125 – 1675 MHz), so I was still hopeful that there was some way to have the MM1000 simulate the ONT’s MoCA interface on the WAN interface of the G1100 using the 1350-1675 MHz range as mentioned in the G1100’s User Manual.

G1100 User Manual, Page 198

Clearly I needed some way to configure the MM1000, but there was no documentation provided for configuring the thing. After some digging around, I realized that the MM1000 has a web interface used for upgrading the device’s firmware and doing some very minimal configuration. The default IP address of the device is 192.168.0.2 and there is no authentication required to access the site. One of the pages of the web interface allows you to select the MoCA operating band as shown below.

MM1000 Configuration Web Page

Finding the Backdoor Shell

I tried all kinds of combinations of these settings to get the MM1000 to simulate the ONT’s WAN connection to the router on the 1350-1675 MHz range (Band D High), but nothing worked. Setting the RF Band to C4 or Band D High both failed to establish a connection with the G1100 on the WAN interface. What was even more frustrating was that the RF Switch setting would keep reverting back to Hi even after I selected Low (for the C4 Band) and submitted the setting. While trying to troubleshoot the buggy interface, I opened Wireshark to see what the API looked like when interacting with the web UI. Here’s what I saw when I sniffed a refresh of the Configuration page.

What immediately stood out to me was the GET request to /cmd.sh?mocap&get&–link. This looked like some kind of engineering shell, and after playing with the URLs a bit (hmmm…what kind of nefarious commands can I inject?!), I wrote a simple Python script to wrap this backdoor shell and provide full access to the device’s configuration.

#!/usr/bin/env python3
import cmd
from urllib.request import urlopen

IP="192.168.0.2"

class MM1000Shell(cmd.Cmd):
    intro = "MM1000 Command Shell\n"
    prompt = "(mm1000) "
    file = None

    def default(self, line):
        args = line.split()
        url = "http://%s/cmd.sh?%s%s%s" % (IP, args[0], "&" if len(args) > 1 else "", '&'.join(args[1:]))
        with urlopen(url) as response:
            output = response.read().decode("UTF-8")
            print(output)

    def do_help(self, arg):
        self.default("help")

    def do_exit(self, arg):
        return True

    def do_quit(self, arg):
        return True

if __name__ == "__main__":
    MM1000Shell().cmdloop()

This shell gave me access to every possible setting available on the device. What’s more, it gave me arbitrary read/write access to memory, flash, and NVRAM parameters, so I could make any possible change I could dream up.

HTTP Wrapped Shell Interface

Reverse Engineering the Firmware

After playing around with the interface a little, I realized that the C4 Band would likely not be usable. Anytime I tried to set the RF Switch to Low, it would revert back to Hi like I saw before. Later I would confirm that the board only supports the high frequency range. I wanted to know more about all the configuration settings of the device, so I decided to reverse engineer the firmware.

While I could have used the backdoor shell interface to pull down the contents of RAM and flash, I ended up just using the firmware image that is available for download at Motorola’s support site for the MM1000. The latest firmware image is MM1000_1.0.0.8.bin. The first step was to figure out how the image is constructed. Looking at a hex dump of the file revealed a fairly obvious pattern. The header of the firmware contains a list of file entry records, each 32 bytes long. Each record contains the file’s length (4 bytes), the file’s starting offset within the firmware image (4 bytes), and the file’s path (24 bytes).

Firmware Header

I wrote a simple Python script to extract the contents of the firmware image into its individual file components.

#!/usr/bin/env python3
import sys
import os
from struct import unpack, calcsize
from collections import namedtuple

def extract_file(file, path, offset, length):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(file, "rb") as inf:
        inf.seek(offset)
        with open(path, "wb") as outf:
            outf.write(inf.read(length))

def extract(file):
    with open(file, "rb") as f:

        format = ">II24s"

        # skip first entry
        f.read(calcsize(format))

        # read all valid entries
        while True:
            length, offset, path = unpack(format, f.read(calcsize(format)))
            if 0 == offset:
                break
            
            path = path.decode("UTF-8").rstrip('\x00')
            print("0x%08x 0x%08x %s" % (offset, length, path))
            extract_file(file, os.path.join("extracted", path), offset, length)

if __name__ == "__main__":
    extract(sys.argv[1])

The astute reader might notice a subtle vulnerability in the Python code above. Take another read and ask yourself how a clever firmware developer could compromise a security researcher’s dev box by using malicious paths for the files in the firmware bundle.

Here’s what I ended up with:

anthony:~/workspace/moca/extracted$ ls -lah
total 5784
drwxr-xr-x   8 anthony  staff   256B Feb 13 15:38 ./
drwxr-xr-x  17 anthony  staff   544B Feb 28 15:40 ../
-rw-r--r--   1 anthony  staff   1.9M Feb 12 15:59 flashapp.bin
drwxr-xr-x  22 anthony  staff   704B Feb 12 15:59 html/
-rw-r--r--   1 anthony  staff   728K Feb 12 15:59 moca20core-gen22.bin
-rw-r--r--   1 anthony  staff   213K Feb 12 15:59 ramimage.bin
-rw-r--r--   1 anthony  staff    93B Feb 12 15:59 runonce.sh
-rw-r--r--   1 anthony  staff   626B Feb 12 15:59 startup.sh

The processor on the board is the Broadcom BCM6802, and true to form, Broadcom does not make the chipset documentation available to the public. Their 2013 product sheet lists this chipset as Ethernet/MoCA 2.0 Bridge, which is no surprise. While there isn’t any documentation available for the BCM6802, I figured out it is a MIPS-based processor by finding a few key strings in the firmware image (“mipsel”, which stands for little-endian MIPS, and “MIPS_EXCEPTION”).

I loaded the flashapp.bin file into Ghidra and selected MIPS:LE:32:default as the processor family. The hardcoded references in the image made it clear that the flashapp.bin file executes from a base address of 0x1fc14000, and I found that the ramimage.bin executes from a base address of 0x00000000. By loading both of these files into the same Ghidra project, I had a fairly complete view of the firmware image.

Ghidra Memory Map
Firmware Image in Ghidra

Finding the Serial Shell

Having the complete firmware image in Ghidra allowed me to easily search through the firmware for strings and functionality relevant to my attempts to get the MM1000 to simulate the ONT’s WAN interface over MoCA. I found that there were lots of debug messages throughout the firmware image, and I wanted a way to see those debug messages during runtime, but that wasn’t possible over the HTTP shell I had setup. What I really wanted was a serial based shell that didn’t require a network connection. I figured there was a serial UART on the board and it was just a matter of finding the right pins to connect to.

Along the edge of the board there is a row of 8 pin headers, and I guessed that the RX and TX UART pins were among them. To figure out exactly which pins they were, I first grabbed my multimeter and probed out which pins were ground. I then hooked up my Saleae Logic Pro 8 logic analyzer to the remaining pins, booted up the board, and captured the activity using the Saleae Logic2 app. Once I had a capture of the pin activity during bootup, it was fairly obvious which pin was likely the TX line of the UART. On this pin, I configured the data to be interpreted as ASCII serial data. It took me a few guesses, but once I configured the baud rate as 115200, the data came to life and I confirmed I had a shell!

Saleae Logic2 Serial Data Capture

I connected a USB to TTL Serial Cable I had laying around from Adafruit and was up and running with a full engineering shell on the MM1000. Here’s how the pins are mapped:

Pin (on the J401 Header)Description
2Ground
3TX (output from the MM1000)
4RX (input to the MM1000)
UART Pinout on the MM1000
MM1000 UART Pins

Here’s what I saw when I booted up the MM1000.

$ screen /dev/tty.SLAB_USBtoUART 115200
Bootloader: Dec  9 2015 11:19:56

Last Image: (0x00410000):
IMG 0x2FC10000 (3847972)
SHA1SUM   : 4C3A4B22 73FA73E7 2CFADF11 BB7337D7 0A5FC0BD
Calculated: 4C3A4B22 73FA73E7 2CFADF11 BB7337D7 0A5FC0BD

Booting from ramimage 0x2FDF7BF4 (len=217972)
RESET_HISTORY=0x00000003
Detected 8MB flash
DRAM freelist wasted 16
No symbols found
IDLE task created
ISR Stack: 0007CE18-0007DE48
AVS Predicted Voltage: 777 mVolt
AVS Activated Voltage: 850 mVolt
CORE1: Using 1400 Mhz for LOF
CORE1: HiRF board detected
CORE1: Found valid MPS Data file: Paired 1 Privacy 1 Password 18 bytes

BRCM MoCA 2.0.  g_EndText=000205A8 g_EndBss=00058A08
Flash page=0x00400000
Compiled by hudson on Thu Aug  4 10:09:07 EDT 2016

Running startup.sh
Note: Parameter will take effect after next MoCA core initialization.
#

Establishing the WAN Connection

The whole goal of this exercise was to get the MM1000 to connect to the G1100 via the WAN interface. Now that I had a shell, I could enable all the debug messages I had seen in the firmware image in Ghidra and try to figure out why the link wasn’t being established. The command to enable the additional debug messages from the MoCA Core process is:

# mocap set --trace 1

The terminal gets spammed with lots of debug messages after enabling the tracing, but all that debug info proved to be very helpful.

Knowing that the G1100 supports the 1350-1675 MHz range (Band D High) for the WAN interface, the first thing I did was to set the RF band of the MM1000 to the D-High range as shown below.

 # mocap set --rf_band help
 Defines one or multiple bands or sub-bands of operation of the Node among all the supported bands and  
 sub-bands.
          
 mocap set --rf_band <uint32 val> 
 val:
 ====         
 
 Values:
 0 = D-Low, support all MoCA channels in sub-band D-Low
 1 = D-High, support all MoCA channels in sub-band D-High
 2 = ExD, support all MoCA channels in band D
 3 = E, support all MoCA channels in band E 
 4 = F, support all MoCA channels in band F
 5 = C4, support single MoCA channel C4 (1000 MHz) 
 6 = H, support all MoCA channels in band H
 7 = Generic, support all MoCA channels in single channel mode only
 Note: Parameter will take effect after next MoCA core initialization.

 # mocap set --rf_band 1
 Note: Parameter will take effect after next MoCA core initialization. 

In the tracing output, I saw that there was a problem with the encryption:

CORE1: MoCA_W: 15608  W_INFO: NAL: No adm res RX for N4 Beacons after adm req TX. Check your Privacy settings.

MoCA 2.0 supports 128-bit AES encryption, and the pre-shared key must be configured on each side. Looking at the configuration of the G1100, I noticed that the default privacy key of the G1100 is 66947388374966, so I enabled the privacy setting on the MM1000 and configured the key as follows:

# mocap set --password 66947388374966
CORE1: Saving new MPS Data: Paired 0 Privacy -1 Password -1 bytes

# mocap set --privacy_en 1
CORE1: Saving new MPS Data: Paired 0 Privacy -1 Password -1 bytes
Note: Parameter will take effect after next MoCA core initialization.

After setting the appropriate RF band and configuring the privacy settings, I was still unable to get the MM1000 to connect to the G1100 over the WAN coax interface, so I once again turned to the debug trace output. This is what I saw:

CORE1: Beacon from new NC! (0), Txm=0x45e2ee20, NC=0, MoCAVer=0x20, DisCap=1, Bch=56, PriCh=56, SecCh=61 NCVer=0x1
CORE1: Clk-Correction on Beacon - BeaconTxm=0x45e2ee20, BeaconReceivedAt = 0x45e2ee20 , BeaconExpectedAt = 0x45dda274 , diff = 347052
CORE1: Clk-Correction on Beacon - BeaconTxm=0x45ea8f40, BeaconReceivedAt = 0x45ea8f40 , BeaconExpectedAt = 0x45ea8f40 , diff = 0
CORE1: MLME: Sending ACF frame type 9 subtype 0 to node 63 length 24 at mac clk 0x45f9ef73
CORE1: MLME: Tx DiscoveryPreAdmReq: Nc=0, Options=1, BrcmVendorInfo=1, MAC_CLK=45f9f193
CORE1: MLME: Rx DiscoveryRsp: TurboMode=1 PermSalt=0x44565f06, 0x26581ea2, 0x33be5a04 Code=0 VendorID=BRCM MAC_CLK=46021f65
CORE1: MoCA_W: 15610  W_INFO: The NC's and NN's turbo modes are different. Aborting admission
CORE1: Admission failed as NN. Might be caused by non-matching Privacy settings, full network or link problems.

It turns out that MoCA has a “Turbo Mode”, which provides greater data rates but only works when there are only two nodes in the MoCA network, i.e., a point-to-point link. When operating in the 1400MHz band, the G1100 expects Turbo Mode to be enabled on the link. That makes sense when you consider the use case of the G1100, where the only thing it connects to on the WAN band is the ONT. I easily found the turbo setting on the MM1000 and enabled turbo mode as follows:

 mocap set --turbo_en 1
 Note: Parameter will take effect after next MoCA core initialization.

 mocap set --restart
 CORE1: Stopping MoCA interfac
 CORE1: Starting MoCA interface
 CORE1: Loading Moca Core image…(15)
 CORE1: Loading Moca Core image done.
 CORE1: MPS Session Init: Start generating MPS Public key pair…
 CORE1: THIS Node MAC address: 38:80:df:fe:90:39
 CORE1: Last Operational Frequency = 1400 Mhz
 CORE1: MoCA Startup Successful.
 CORE1: MoCA Version
 CORE1: -----------------------
 CORE1: firmware version  : 2.12.0.4
 CORE1: mocad version     : 2.12.0.4
 CORE1: HW version        : 0x680200c0
 CORE1: bmoca version     : 2.0.0
 CORE1: MoCA self version : 0x20
 CORE1: -----------------------
 MPS Session Init: Delayed New MPS Session, creating new keys
 MPS Session Init: Delayed key creation done!
 CORE1: MPS Session Init: Delayed New MPS Session returned to mocad, set MPS data, ready for new MPS 
 Session

 CORE1: MoCA link is up after 55s of downtime
 CORE1: Saving new MPS Data: Paired 1 Privacy 1 Password 32 bytes
 IP link up
 IP: 192.168.0.2 MASK: 255.255.254.0 GW: 192.168.0.1
 DNS: 208.67.222.222

That did the trick! I finally saw the WAN interface on the G1100 become active.

G1100 Network Connection Status

To summarize, here are the commands I needed to execute on the MM1000 to get it to simulate the ONT’s WAN MoCA interface.

mocap set --rf_band 1                 http://192.168.1.2/cmd.sh?mocap&set&--rf_band&1
mocap set --password 66947388374966   http://192.168.1.2/cmd.sh?mocap&set&--password&66947388374966 
mocap set --privacy_en 1              http://192.168.1.2/cmd.sh?mocap&set&--privacy_en&1       
mocap set --turbo_en 1                http://192.168.1.2/cmd.sh?mocap&set&--turbo_en&1

As it turns out, all of the settings above, except for enabling Turbo Mode, are exposed via the MM1000 Web interface. If you don’t want to run the Python shell script I wrote to get access to the MM1000’s command line interface, you can simply use the URLS above to execute each command, one at a time, by entering them into your browser. You shirt first visit the main web page at http://192.168.1.2 to ensure you have a connection to the MM1000 (most likely you will need to add a secondary IP address to your desktop computer to be able to access the 192.168.1.0/24 subnet).

Security Considerations

There’s a lot to say with regard to security here, probably enough for a separate article, but let me at least hit on a few noteworthy points.

Use MoCA’s Privacy Mode

If you’re using MoCA, you really should enable MoCA’s privacy mode with 128-bit AES encryption. Most people’s homes have at least some amount of coax cable exposed on the exterior of their home. It would be very easy for an intruder to tap into that coax line and gain access to your MoCA LAN. Privacy mode provides at least some form of protection against that attack vector.

To enable privacy mode on your G1100, click on My Network -> Network Connections -> Broadband Connection -> Settings. You can setup your own password as shown in the highlighted box below, and then click Apply to save the setting.

You’ll have to configure the same password on the MM1000 using the following command:

# mocap set --password 66947388374966

Or via the MM1000 configuration web page:

Disable the Backdoor Shell on the MM1000

If you are going to use the MM1000 in your home’s MoCA network, I strongly suggest that you disable the backdoor shell that is available over HTTP sans authentication. The level of access that this shell provides is equivalent to someone having root access to your home’s internal network. A motivated attacker could use the MM1000 to do all kinds of damage via man-in-the-middle attacks. There’s a simple command to disable shell access via HTTP:

# http-cmd-disable all

Warning! If you disable the HTTP command interface with the command above or below, then you won’t be able to send any more commands until you either factory reset the device or gain access to the serial console on the board.

Instead, I suggest you take it one step further and disable the entire HTTP web server with:

# http-disable

You can always restore web access by factory resetting the MM1000.

Don’t use Verizon’s G1100 Quantum Gateway

Verizon maintains a backdoor connection to the G1100 Quantum Gateway. This backdoor connection is how Verizon makes it possible for you to change your home’s wifi connection from your online Verizon account. While they take measures to protect this backdoor access, I suggest that you shouldn’t trust them and get your own router instead. You can still use MoCA to connect to the Ethernet port at the ONT, but you’ll need to have two MoCA adapters, one at the ONT and the other at your router inside the house, unless your router happens to support MoCA natively.


Anthony DeRosa is a software security researcher with nearly 20 years of experience in static and dynamic reverse engineering of embedded systems, Linux, Windows, and mobile applications. He serves as an expert witness in technology-related litigation and patent infringement cases in the automotive, medical, and mobile industries and teaches a course on software reverse engineering. In his spare time, he likes to take things apart when he’s not getting beat at video games by his kids.