Unauthenticated RCE in Motorola's MR2600 Router

MrBruh3 pts0 comments

Unauthenticated RCE in Motorola's MR2600 Router | MrBruh's Epic Blog

Unauthenticated RCE in Motorola’s MR2600 Router

I’m currently on a quest to find at least one Remote Code Execution vulnerability per router vendor. This is the story of how I found an unauthenticated RCE in Motorola’s MR2600 router.

The first problem I had to solve was acquiring the firmware. For the majority of their routers, Motorola doesn’t distribute the firmware publicly; instead, it is only ever distributed via over-the-air updates.

The first exception to this rule that I could find was the Motorola MR2600. It was a Wi-Fi 5 router, with the last firmware update (v1.0.22) released in mid-2024.

https://help.motorolanetwork.com/kb/mr2600/mr2600-software-updates

With the firmware downloaded, I extracted the filesystem and started digging through the router’s CGI scripts and SOAP handlers to see how a legitimate manual update actually worked.

It turned out to be a two-step process: upload the image, then trigger a validation-and-flash routine. As I’d soon discover, both steps were supposed to require authentication but didn’t.

Uploading the Malicious Firmware

In the router firmware, you can find a reference to a fwupload endpoint:

POST /WEBCGI1/prog.fcgi?method=/cgi-bin/fwupload.cgi

In particular, the UpdateFirmware_Moto.html firmware update page contains:

form id="fwupload"<br>action="/WEBCGI1/prog.fcgi?method=/cgi-bin/fwupload.cgi"<br>method="post"<br>enctype="multipart/form-data">

This allows the administrator to manually upgrade the Motorola firmware. An internal handler validates that the uploaded file is a valid SEAMA image by checking that the first four bytes of the file are 0x27 0x05 0x19 0x56.

Unfortunately, the developers made a crucial mistake when parsing the SEAMA image from the multipart form. They try to validate that the raw multipart data is prefixed with the magic numbers instead of first extracting the relevant field from the form.

------WebKitFormBoundaryXXXXXXXX\r\n<br>Content-Disposition: form-data; name="file"; filename="firmware.img"\r\n<br>Content-Type: application/octet-stream\r\n<br>\r\n<br>[actual SEAMA bytes start here]<br>------WebKitFormBoundaryXXXXXXXX--\r\n

This results in any legitimate use of the API failing because the first four characters will always be &ldquo;----&rdquo;, which does not equal 0x27 0x05 0x19 0x56.

As an attacker, we can bypass this requirement by claiming we are sending a multipart form, but instead sending the raw firmware image without any multipart boundary at all.

The developers likely anticipated that this API endpoint could be abused if it were called by an unauthenticated attacker on the LAN, so they added a check to reject the request if the user was not authenticated.

However, the authentication check only triggers after the firmware has been uploaded and written to /tmp/firmware.img on the router, and it does nothing to delete the file if the check fails.

Flashing the Firmware

To flash the firmware, we need to make the following network request:

POST /WEBCGI1/ HTTP/1.1<br>Content-Type: text/xml; charset=utf-8<br>SOAPAction: "http://purenetworks.com/WEBCGI1/LoadFirmwareValidation"

In layman&rsquo;s terms, this just triggers an internal function within the router called LoadFirmwareValidation. It tries to validate that the file in /tmp/firmware.img is a valid SEAMA image and checks a CRC32 checksum to ensure that there is no risk of corruption in the file.

Anyone can generate a valid firmware image, since doing so doesn&rsquo;t require any cryptographic signing to prove it was created by Motorola.

Once the firmware has been validated, the internal function calls a command called mtd_write:

mtd_write -r -w write /tmp/firmware.img Kernel &

This flashes the new firmware and then reboots the router (signified by the -r flag).

This API endpoint also requires authentication; however, it too can be bypassed.

The code for checking if a given endpoint should require authentication is implemented incorrectly.

It first checks if the URI contains any of the allowlisted or denylisted paths. In the dictionary shown below, only the last value, /WEBCGI1/, is considered denylisted.

Once a match is found, the function explicitly checks if the URI is exactly equal to /WEBCGI1/. As long as the URI doesn&rsquo;t perfectly match /WEBCGI1/, the system assumes the requested endpoint is allowed and lets the traffic through.

The core vulnerability stems from the application using inconsistent comparison operators.

It checks for allowlisted endpoints using a basic substring match but checks for the denylisted endpoint (/WEBCGI1/) by checking the entire URI for an exact match.

Because of this flaw, an attacker can call the API and simply append an allowlisted string as an HTTP parameter, tricking the system into bypassing authentication completely.

e.g., POST /WEBCGI1/?Login.html

/WEBCGI1/?Login.html contains "Login.html" --> YES (Allowlist check...

firmware router rsquo webcgi1 motorola mr2600

Related Articles