############# Computest security advisory CT-2016-1110 ############### Summary: Unauthenticated remote command execution as root Affected software: Observium Reference URL: https://www.computest.nl/advisories/ CT-2016-1110_Observium.txt Affected versions: Versions downloaded before 26-10-2016. (First affected version is not known) Credit: Ronald Volgers (rvolgers@computest.nl) Date of publication: 2016-11-10 During a recent penetration test Computest found and exploited various issues in Observium, going from unauthenticated user to full shell access as root. We reported these issues to the Observium project for the benefit of our customer and other members of the community. This was not a full audit and further issues may or may not be present. ( Note about affected versions: The Observium project does not provide a way to download older releases for non-paying users, so there was no way to check whether these problems exist in older versions. All information given here applies to the latest Community Edition as of 2016-10-05. ) About Observium --------------- "Observium is a low-maintenance auto-discovering network monitoring platform supporting a wide range of device types, platforms and operating systems including Cisco, Windows, Linux, HP, Juniper, Dell, FreeBSD, Brocade, Netscaler, NetApp and many more." - observium.org Issue #1: Deserialization of untrusted data ------------------------------------------- Observium uses the get_vars() function in various places to parse the user-supplied GET, POST and COOKIE values. This function will attempt to unserialize data from any of the requested fields using the PHP unserialize() function. Deserialization of untrusted data is in general considered a very bad idea, but the practical impact of such issues can vary. Various memory corruption issues have been identified in the PHP unserialize() function in the past, which can lead directly to remote code execution. On patched versions of PHP exploitability depends on the application. In the case of Observium the issue can be exploited to write mostly user-controlled data to an arbitrary file, such as a PHP session file. Computest was able to exploit this issue to create a valid Observium admin session. The function get_vars() eventually calls var_decode(), which unserializes the user input. ./includes/common.inc.php: function var_decode($string, $method = 'serialize') { $value = base64_decode($string, TRUE); if ($value === FALSE) { // This is not base64 string, return original var return $string; } switch ($method) { case 'json': if ($string === 'bnVsbA==') { return NULL; }; $decoded = @json_decode($value, TRUE); if ($decoded !== NULL) { // JSON encoded string detected return $decoded; } break; default: if ($value === 'b:0;') { return FALSE; }; $decoded = @unserialize($value); if ($decoded !== FALSE) { // Serialized encoded string detected return $decoded; } } Issue #2: Admins can inject shell commands, possibly as root ------------------------------------------------------------ Admin users can change the path of various system utilities used by Observium. These paths are directly used as shell commands, and there is no restriction on their contents. This is not considered a bug by the Observium project, as Admin users are considered to be trusted. [1] The Observium installation guide recommends running various Observium scripts from cron. The instructions given in the installation guide will result in these scripts being run as root, and invoking the user- controllable shell commands as root. Since this functionality resulted in an escalation of privilege from web application user to system root user it is included in this advisory despite the fact that it appears to involve no unintended behavior in Observium. Even if the Observium system is not used for anything else, privileged users log into this system (and may reuse passwords elsewhere), and the system as a whole may have a privileged network position due to its use as a monitoring tool. Various other credentials (SNMP etc) may also be of interest to an attacker. The function rrdtool_pipe_open() uses the Admin-supplied config variable to build and run a command: ./includes/rrdtool.inc.php: function rrdtool_pipe_open(&$rrd_process, &$rrd_pipes) { global $config; $command = $config['rrdtool'] . " -"; // Waits for input via standard input (STDIN) $descriptorspec = array( 0 => array("pipe", "r"), // stdin 1 => array("pipe", "w"), // stdout 2 => array("pipe", "w") // stderr ); $cwd = $config['rrd_dir']; $env = array(); $rrd_process = proc_open($command, $descriptorspec, $rrd_pipes, $cwd, $env); Issue #3: Incorrect use of cryptography in event feed authentication -------------------------------------------------------------------- Observium contains an RSS event feed functionality. Users can generate an RSS URL that displays the events that they have access to. Since RSS viewers may not have access to the user's session cookies, the user is authenticated with a user-specific token in the feed URL. This token consists of encrypted data, and the integrity of this data is not verified. This allows a user to inject essentially random data that the Observium code will treat as trusted. By sending arbitrary random tokens a user has at least a 1/65536 chance of viewing the feed with full admin permissions, since admin privileges are granted if the decryption of this random token happens to start with the two-character string "1|" (1 being the user id of the admin account). In general a brute force attack will gain access to the feed with admin privileges in about half an hour. ./html/feed.php: if (isset($_GET['hash']) && is_numeric($_GET['id'])) { $key = get_user_pref($_GET['id'], 'atom_key'); $data = explode('|', decrypt($_GET['hash'], $key)); // user_id|user_level|auth_mechanism $user_id = $data[0]; $user_level = $data[1]; // FIXME, need new way for check userlevel, because it can be changed if (count($data) == 3) { $check_auth_mechanism = $config['auth_mechanism'] == $data[2]; } else { $check_auth_mechanism = TRUE; // Old way } if ($user_id == $_GET['id'] && $check_auth_mechanism) { session_start(); $_SESSION['user_id'] = $user_id; $_SESSION['userlevel'] = $user_level; ( Note: this session is destroyed at the end of the page ) Issue #4: Authenticated SQL injection ------------------------------------- One of the graphs supported by Observium contains a SQL injection problem. This code is only reachable if unauthenticated users are permitted to view this graph, or if the user is authenticated. The problem lies in the "port_mac_acc_total" graph. When the 'stat' parameter is set to a non-empty value that is not 'bits' or 'pkts' the 'sort' parameter will be used in a SQL statement without escaping or validation. The 'id' parameter can be set to an arbitary numeric value, the SQL is executed regardless of whether this is a valid identifier. This can be exploited to leak various configuration details including the password hashes of Observium users. ./html/includes/graphs/port/mac_acc_total.inc.php: $port = (int)$_GET['id']; if ($_GET['stat']) { $stat = $_GET['stat']; } else { $stat = "bits"; } $sort = $_GET['sort']; if (is_numeric($_GET['topn'])) { $topn = $_GET['topn']; } else { $topn = '10'; } include_once($config['html_dir']."/includes/graphs/common.inc.php"); if ($stat == "pkts") { $units='pps'; $unit = 'p'; $multiplier = '1'; $colours_in = 'purples'; $colours_out = 'oranges'; $prefix = "P"; if ($sort == "in") { $sort = "pkts_input_rate"; } elseif ($sort == "out") { $sort = "pkts_output_rate"; } else { $sort = "bps"; } } elseif ($stat == "bits") { $units='bps'; $unit='B'; $multiplier='8'; $colours_in = 'greens'; $colours_out = 'blues'; if ($sort == "in") { $sort = "bytes_input_rate"; } elseif ($sort == "out") { $sort = "bytes_output_rate"; } else { $sort = "bps"; } } $mas = dbFetchRows("SELECT *, (bytes_input_rate + bytes_output_rate) AS bps, (pkts_input_rate + pkts_output_rate) AS pps FROM `mac_accounting` LEFT JOIN `mac_accounting-state` ON `mac_accounting`.ma_id = `mac_accounting-state`.ma_id WHERE `mac_accounting`.port_id = ? ORDER BY $sort DESC LIMIT 0," . $topn, array($port)); Mitigation ---------- The Observium web application can be placed behind a firewall or protected with an additional layer of authentication. Even then, admin users should be treated with care as they are able to execute commands (probably as root) until the issues are patched. The various cron jobs needed by Observium can be run as the website user (e.g. www-data) or a user created specifically for that purpose instead of as root. Resolution ---------- Observium has released a new Community Edition to resolve these issues. The Observium project does not provide changelogs or version numbers for community releases. Timeline -------- 2016-09 Issue discovered during penetration test 2016-10-21 Vendor contacted 2016-10-21 Vendor responds that they are working on a fix 2016-10-26 Vendor publishes new version on website 2016-10-28 Vendor asks Computest to comment on changes 2016-10-31 Computest responds with quick review of changes 2016-11-10 Advisory published About Computest --------------- Computest helps customers build reliable software, mainly by expertise in the areas of security, performance and functional testing. Computest acquired Pine Digital Security in 2015 and continues Pine's tradition of security testing based on sound technical understanding. [1] Response to another bug report stating that Admin command injection is not a bug: http://jira.observium.org/browse/OBSERVIUM-1821