CVE-2020-26124 (PHP, published 2020-10-02T09:15Z)
Description: value: openmediavault before 4.1.36 and 5.x before 5.5.12 allows authenticated PHP code injection attacks, via the sortfield POST parameter of rpc.php, because json_encode_safe is not used in config/databasebackend.inc. Successful exploitation allows arbitrary command execution on the underlying operating system as root.
Severity: HIGH (CVSS v3 base score: 8.8)
Weakness: CWE-94 \u2014 Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Repository: https://github.com/openmediavault/openmediavault
Files changed:
deb/openmediavault/usr/share/openmediavault/unittests/php/test_openmediavault_config_databasebackend.php
deb/openmediavault/usr/share/openmediavault/unittests/php/test_openmediavault_functions.php
deb/openmediavault/usr/share/php/openmediavault/config/databasebackend.inc
deb/openmediavault/usr/share/php/openmediavault/functions.inc
Commit message: Cleanup code, add unit tests.

Signed-off-by: Volker Theile <votdev@gmx.de>


Vulnerable code:
		if (0 == count(array_diff($result, $data)))
	if (FALSE === uasort($array, create_function('$a, $b',
		"return strnatcmp(strval(\$a['$key']), strval(\$b['$key']));")))

Fixed code:
#!/usr/bin/phpunit -c/etc/openmediavault
<?php
/**
 * This file is part of OpenMediaVault.
 *
 * @license   http://www.gnu.org/licenses/gpl.html GPL Version 3
 * @author    Volker Theile <volker.theile@openmediavault.org>
 * @copyright Copyright (c) 2009-2020 Volker Theile
 *
 * OpenMediaVault is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * OpenMediaVault is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenMediaVault. If not, see <http://www.gnu.org/licenses/>.
 */
require_once("openmediavault/autoloader.inc");

class test_openmediavault_config_databasebackend extends \PHPUnit\Framework\TestCase {
	private $databaseBackend;

	public function setUp() {
		$this->databaseBackend = new \OMV\Config\DatabaseBackend(
			sprintf("%s/../data/config.xml", getcwd()),
			FALSE);
		$this->databaseBackend->load();
	}

	public function test_compare_1() {
		$this->assertEquals($this->databaseBackend->compare(
			"//system/time",
			[
				"timezone" => "Europe/Berlin",
				"ntp" => [
					"enable" => 0,
					"timeservers" => "pool.ntp.org",
					"clients" => ""
				]
			]
		), 0);
	}

	public function test_compare_2() {
		$this->assertEquals($this->databaseBackend->compare(
			"//system/powermanagement",
			[
				"cpufreq" => 0,
				"powerbtn" => "nothing"
			]
		), -1);
	}
}

	public function test_array_sort_key_1() {
		$d = [
			['id' => 3, 'text' => 'b'],
			['id' => 1, 'text' => 'a'],
			['id' => 2, 'text' => 'c']
		];
		$this->assertTrue(array_sort_key($d, "id"));
		$this->assertEquals($d, [
			['id' => 1, 'text' => 'a'],
			['id' => 2, 'text' => 'c'],
			['id' => 3, 'text' => 'b']
		]);
	}

	public function test_array_sort_key_2() {
		$d = [
			['id' => 3, 'text' => 'b'],
			['id' => 1, 'text' => 'a'],
			['id' => 2, 'text' => 'c']
		];
		$this->assertTrue(array_sort_key($d, "text"));
		$this->assertEquals($d, [
			['id' => 1, 'text' => 'a'],
			['id' => 3, 'text' => 'b'],
			['id' => 2, 'text' => 'c']
		]);
	}
		if (FALSE === array_walk_recursive($data, function(&$item, $key) {
			if (is_string($item)) {
				if (!mb_check_encoding($item, "UTF-8")) {
					$item = utf8_encode($item);
				}
			} else {
				$item = utf8_encode(strval($item));
			}
		})) {
			return FALSE;
		}
		// Note, we can not use `array_diff` here because this function
		// does not support multidimensional arrays.
		if (0 === strcmp(json_encode_safe($result),
				json_encode_safe($data))) {
		}
	if (FALSE === uasort($array, function($a, $b) use($key) {
		return strnatcmp(strval($a[$key]), strval($b[$key]));
	})) {
	}