Moderate
CVE-2018-20434 - LibreNMS Addhost Command Injection
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
Moderate
(1 user assessed)Moderate
(1 user assessed)Unknown
Unknown
Unknown
CVE-2018-20434 - LibreNMS Addhost Command Injection
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
LibreNMS 1.46 allows remote attackers to execute arbitrary OS commands by using the $_POST['community']
parameter to html/pages/addhost.inc.php
during creation of a new device, and then making a /ajax_output.php?id=capture&format=text&type=snmpwalk&hostname=localhost request that triggers html/includes/output/capture.inc.php
command mishandling.
Add Assessment
Ratings
-
Attacker ValueMedium
-
ExploitabilityMedium
Technical Analysis
Useful exploit with a caveat. This exploit takes more effort to execute given that authentication is required first.
According to the CVE listing on NVD, shell commands can be passed through the _POST['community']
parameter to html/pages/addhost.inc.php
, which deals with the creation of new devices. After successfully creating a device, a request can be sent to ajax_output.php
, which triggers the actual execution of code through html/includes/output/capture.inc.php
.
If the community
parameter is set when a request is made to addhost.inc.php
, then community
is passed to the clean()
function with the second argument set to false
.
if ($_POST['community']) { $config['snmp']['community'] = array(clean($_POST['community'], false)); }
The clean()
function is located in includes/common.php
. Here’s what it looks like in version 1.46
:
function clean($value, $strip_tags = true) { if ($strip_tags === true) { return strip_tags(mres($value)); } else { return mres($value); } }
In this particular call to clean()
, the $strip_tags
value is set to false
, meaning that the community
parameter is acted upon by the mres()
function, then returned. The mres()
function:
function mres($string) { return $string; global $database_link; return mysqli_real_escape_string($database_link, $string); }
The community
parameter is simply returned without any modifications.
From here, we can see that the community
parameter is set through a POST request to addhost.inc.php
, and it is unsanitized. Assuming that unwanted input is passed into the community
parameter, now the goal is to see how a request to ajax_output.php
will trigger code execution.
In ajax_output.php
, the id
is checked and is used to require another file. In this case, that would be capture.inc.php
.
if (isset($id)) { require $config['install_dir'] . "/html/includes/output/$id.inc.php"; }
The functionality in capture.inc.php
runs a command that is determined by the type
parameter and either prints the output of the command or saves the output to a file. Initially, the type
parameter is checked against three different values. If the type
parameter is snmp
walk, the command becomes the output of the gen_snmp_walk()
function.
$type = $_REQUEST['type']; switch ($type) { case 'poller': ... case 'snmpwalk': $device = device_by_name(mres($hostname)); $cmd = gen_snmpwalk_cmd($device, '.', ' -OUneb'); ... case 'discovery': ... default: ... }
The gen_snmp_walk()
function is located in the snmp.inc.php
file. gen_snmp_walk()
first checks the version that was passed in the addhost
POST request earlier and then returns the result of calling gen_snmp_cmd()
. The gen_snmp_cmd()
also resides in the snmp.inc.php
file, and this function is where the bulk of the command used in capture.inc.php
is created.
The first addition to $cmd
is set to the result of calling the snmp_gen_auth()
function.
function gen_snmp_cmd($cmd, $device, $oids, $options = null, $mib = null, $mibdir = null) { ... $cmd .= snmp_gen_auth($device); ... }
snmp_gen_auth()
further builds the $cmd
variable by checking snmpver
. If that value is either v2c
or v1
, then the unsanitized community
parameter is added to the command and then returned.
} elseif ($device['snmpver'] === 'v2c' or $device['snmpver'] === 'v1') { $cmd = " -".$device['snmpver']; $cmd .= " -c '".$device['community']."'"; ... return $cmd;
Now that there is a command that contains unsanitized input, code execution is the last step.
As was stated previously, the functionality in capture.inc.php
generates a command to run and either prints the output of that command or saves the output to a file. The functionality that runs the command checks the format
parameter passed in the request made to ajax_output.php
described earlier. If the format
parameter is set to text
, then this code block will be executed:
if ($_GET['format'] == 'text') { header("Content-type: text/plain"); header('X-Accel-Buffering: no'); if (($fp = popen($cmd, "r"))) { while (!feof($fp)) { $line = stream_get_line($fp, 1024, PHP_EOL); echo preg_replace('/\033\[[\d;]+m/', '', $line) . PHP_EOL; ob_flush(); flush(); // you have to flush buffer } fclose($fp); }
The $cmd
variable that now contains the unsanitized community
parameter gets passed to the popen()
function and executed.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportGeneral Information
References
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: