fix possible privilege escalation from customer to root when specifying custom error documents in directory-options

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann
2023-01-28 20:00:24 +01:00
parent bd5b99dc1c
commit 0034681412
5 changed files with 62 additions and 12 deletions

View File

@@ -157,16 +157,15 @@ class DirOptions extends ApiCommand implements ResourceEntity
* this functions validates a given value as ErrorDocument * this functions validates a given value as ErrorDocument
* refs #267 * refs #267
* *
* @param * @param string $errdoc
* string error-document-string
* @param bool $throw_exception * @param bool $throw_exception
* *
* @return string error-document-string * @return string error-document-string
* *
*/ */
private function correctErrorDocument($errdoc = null, $throw_exception = false) private function correctErrorDocument(string $errdoc, $throw_exception = false)
{ {
if ($errdoc !== null && $errdoc != '') { if (trim($errdoc) != '') {
// not a URL // not a URL
if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) { if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) {
// a file // a file
@@ -176,14 +175,14 @@ class DirOptions extends ApiCommand implements ResourceEntity
if (!substr($errdoc, 0, 1) == '/') { if (!substr($errdoc, 0, 1) == '/') {
$errdoc = '/' . $errdoc; $errdoc = '/' . $errdoc;
} }
} else { } elseif (preg_match('/^"([^\r\n\t\f\0"]+)"$/', $errdoc)) {
// a string (check for ending ") // a string (check for ending ")
// string won't work for lighty // string won't work for lighty
if (Settings::Get('system.webserver') == 'lighttpd') { if (Settings::Get('system.webserver') == 'lighttpd') {
Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception); Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception);
} elseif (substr($errdoc, -1) != '"') {
$errdoc .= '"';
} }
} else {
Response::standardError('invaliderrordocumentvalue', '', $throw_exception);
} }
} else { } else {
if (Settings::Get('system.webserver') == 'lighttpd') { if (Settings::Get('system.webserver') == 'lighttpd') {
@@ -191,7 +190,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
} }
} }
} }
return $errdoc; return trim($errdoc);
} }
/** /**

View File

@@ -147,9 +147,9 @@ class FileDir
*/ */
public static function makeSecurePath($path) public static function makeSecurePath($path)
{ {
// check for bad characters, some are allowed with escaping // check for bad characters, some are allowed with escaping,
// but we generally don't want them in our directory-names, // but we generally don't want them in our directory-names,
// thx to aaronmueller for this snipped // thx to aaronmueller for this snippet
$badchars = [ $badchars = [
':', ':',
';', ';',
@@ -161,7 +161,11 @@ class FileDir
'$', '$',
'~', '~',
'?', '?',
"\0" "\0",
"\n",
"\r",
"\t",
"\f"
]; ];
foreach ($badchars as $bc) { foreach ($badchars as $bc) {
$path = str_replace($bc, "", $path); $path = str_replace($bc, "", $path);
@@ -606,7 +610,7 @@ class FileDir
} }
/** /**
* *
* @return array|false * @return array|false
*/ */
public static function getFilesystemQuota() public static function getFilesystemQuota()

View File

@@ -837,6 +837,7 @@ return [
'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.', 'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',
'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an', 'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an', 'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
'invaliderrordocumentvalue' => 'Der angegebene Wert für das Fehlederdokument ist keine gültige Datei, URL oder Text-Zeile.',
'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld "%s")', 'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld "%s")',
'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld "%s")', 'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld "%s")',
'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren', 'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren',

View File

@@ -905,6 +905,7 @@ return [
'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification', 'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',
'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file', 'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file',
'urlerrordocumentnotvalidforlighty' => 'An URL as ErrorDocument does not work in lighttpd, please specify a path to a file', 'urlerrordocumentnotvalidforlighty' => 'An URL as ErrorDocument does not work in lighttpd, please specify a path to a file',
'invaliderrordocumentvalue' => 'The value given as ErrorDocument does not seem to be a valid file, URL or string.',
'intvaluetoolow' => 'The given number is too low (field %s)', 'intvaluetoolow' => 'The given number is too low (field %s)',
'intvaluetoohigh' => 'The given number is too high (field %s)', 'intvaluetoohigh' => 'The given number is too high (field %s)',
'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID', 'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID',

View File

@@ -191,4 +191,49 @@ class DirOptionsTest extends TestCase
$this->expectExceptionMessage("Directory option with id #1 could not be found"); $this->expectExceptionMessage("Directory option with id #1 could not be found");
DirOptions::getLocal($admin_userdata, $data)->get(); DirOptions::getLocal($admin_userdata, $data)->get();
} }
public function testCustomerDirOptionsAddMalformed()
{
global $admin_userdata;
// get customer
$json_result = Customers::getLocal($admin_userdata, array(
'loginname' => 'test1'
))->get();
$customer_userdata = json_decode($json_result, true)['data'];
$data = [
'path' => '/testmalformed',
'error404path' => '/"'.PHP_EOL.'something/../../../../weird 404.html'.PHP_EOL.'#'
];
$json_result = DirOptions::getLocal($customer_userdata, $data)->add();
$result = json_decode($json_result, true)['data'];
$expected = '/"something/././././weird\ 404.html#';
$this->assertEquals($expected, $result['error404path']);
}
public function testCustomerDirOptionsAddMalformedInvalid()
{
global $admin_userdata;
// get customer
$json_result = Customers::getLocal($admin_userdata, array(
'loginname' => 'test1'
))->get();
$customer_userdata = json_decode($json_result, true)['data'];
$data = [
'path' => '/testmalformed',
'error404path' => '"'.PHP_EOL.'IncludeOptional /something/else/'.PHP_EOL.'#'
];
$this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string.");
DirOptions::getLocal($customer_userdata, $data)->add();
$data = [
'path' => '/testmalformed',
'error404path' => '"something"oh no a quote within the string"'
];
$this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string.");
DirOptions::getLocal($customer_userdata, $data)->add();
}
} }