Skip to content

Commit 437fa5b

Browse files
author
Till Brehm
committed
Merge branch 'patch-cron' into 'stable-3.1'
Fixed authenticated user privilegue escalation vulnerability in cron module, fixes #4869 See merge request ispconfig/ispconfig3!680
2 parents eee06e1 + 63b51e3 commit 437fa5b

File tree

5 files changed

+89
-21
lines changed

5 files changed

+89
-21
lines changed

install/patches/upd_0085.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
if(!defined('INSTALLER_RUN')) die('Patch update file access violation.');
4+
5+
/*
6+
Example installer patch update class. the classname must match
7+
the php and the sql patch update filename. The php patches are
8+
only executed when a corresponding sql patch exists.
9+
*/
10+
11+
class upd_0085 extends installer_patch_update {
12+
13+
public function onAfterSQL() {
14+
global $inst, $conf;
15+
16+
$cron_files = $conf['cron']['crontab_dir'] . '/ispc_*';
17+
$check_suffix = '';
18+
if (file_exists('/etc/gentoo-release')) {
19+
$cron_files .= '.cron';
20+
$check_suffix = '.cron';
21+
}
22+
23+
$file_list = glob($cron_files);
24+
if(is_array($file_list) && !empty($file_list)) {
25+
for($f = 0; $f < count($file_list); $f++) {
26+
$cron_file = $file_list[$f];
27+
$fp = fopen($cron_file, 'r');
28+
while($fp && !feof($fp)) {
29+
$line = trim(fgets($fp));
30+
if($line == '') continue;
31+
elseif(substr($line, 0, 1) === '#') continue; // commented out
32+
33+
$fields = preg_split('/\s+/', $line);
34+
if(trim($fields[0]) == '') {
35+
// invalid line
36+
swriteln($inst->lng('[INFO] Invalid cron line in file ' . $cron_file));
37+
} elseif(preg_match('/^\w+=/', $line)) {
38+
if(preg_match('/\s/', $line)) {
39+
// warning line with env var and space!
40+
swriteln($inst->lng("\n" . '[WARNING] Cron line in file ' . $cron_file . ' contains environment variable.' . "\n"));
41+
}
42+
} elseif(!isset($fields[5])) {
43+
// invalid line (missing user)
44+
swriteln($inst->lng("\n" . '[WARNING] Cron line in file ' . $cron_file . ' misses user field.' . "\n"));
45+
} else {
46+
$check_filename = trim($fields[5]) . $check_suffix;
47+
if(substr($cron_file, -strlen($check_filename)) != $check_filename) {
48+
// warning user not equal to file name
49+
swriteln($inst->lng("\n" . '[WARNING] SUSPECT USER IN CRON FILE ' . $cron_file . '! CHECK CRON FILE FOR MALICIOUS ENTRIES!' . "\n"));
50+
}
51+
}
52+
}
53+
fclose($fp);
54+
}
55+
}
56+
}
57+
}
58+
59+
?>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ALTER TABLE `web_domain` CHANGE `folder_directive_snippets` `folder_directive_snippets` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL;
2+
ALTER TABLE `web_domain` ADD `log_retention` INT NOT NULL DEFAULT '30' AFTER `https_port`;
3+
ALTER TABLE `web_domain` CHANGE `stats_type` `stats_type` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'awstats';
4+
ALTER TABLE `spamfilter_policy`
5+
CHANGE `virus_lover` `virus_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
6+
CHANGE `spam_lover` `spam_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
7+
CHANGE `banned_files_lover` `banned_files_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
8+
CHANGE `bad_header_lover` `bad_header_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
9+
CHANGE `bypass_virus_checks` `bypass_virus_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
10+
CHANGE `bypass_spam_checks` `bypass_spam_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
11+
CHANGE `bypass_banned_checks` `bypass_banned_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
12+
CHANGE `bypass_header_checks` `bypass_header_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
13+
CHANGE `spam_modifies_subj` `spam_modifies_subj` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
14+
CHANGE `warnvirusrecip` `warnvirusrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
15+
CHANGE `warnbannedrecip` `warnbannedrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
16+
CHANGE `warnbadhrecip` `warnbadhrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N';
17+
ALTER TABLE `dns_rr` CHANGE `data` `data` TEXT NOT NULL;
18+
ALTER TABLE `web_database` CHANGE `database_quota` `database_quota` INT(11) NULL DEFAULT NULL;
19+
ALTER TABLE `web_domain` ADD `log_retention` INT NOT NULL DEFAULT '30' ;
20+
ALTER TABLE spamfilter_policy CHANGE spam_tag_level spam_tag_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_tag2_level spam_tag2_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_kill_level spam_kill_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_dsn_cutoff_level spam_dsn_cutoff_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_quarantine_cutoff_level spam_quarantine_cutoff_level DECIMAL(5,2) NULL DEFAULT NULL;
21+
UPDATE `web_database` as d LEFT JOIN `web_domain` as w ON (w.domain_id = d.parent_domain_id) SET d.parent_domain_id = 0 WHERE w.domain_id IS NULL AND d.parent_domain_id != 0;
Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1 @@
1-
ALTER TABLE `web_domain` CHANGE `folder_directive_snippets` `folder_directive_snippets` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL;
2-
ALTER TABLE `web_domain` ADD `log_retention` INT NOT NULL DEFAULT '30' AFTER `https_port`;
3-
ALTER TABLE `web_domain` CHANGE `stats_type` `stats_type` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'awstats';
4-
ALTER TABLE `spamfilter_policy`
5-
CHANGE `virus_lover` `virus_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
6-
CHANGE `spam_lover` `spam_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
7-
CHANGE `banned_files_lover` `banned_files_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
8-
CHANGE `bad_header_lover` `bad_header_lover` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
9-
CHANGE `bypass_virus_checks` `bypass_virus_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
10-
CHANGE `bypass_spam_checks` `bypass_spam_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
11-
CHANGE `bypass_banned_checks` `bypass_banned_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
12-
CHANGE `bypass_header_checks` `bypass_header_checks` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
13-
CHANGE `spam_modifies_subj` `spam_modifies_subj` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
14-
CHANGE `warnvirusrecip` `warnvirusrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
15-
CHANGE `warnbannedrecip` `warnbannedrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
16-
CHANGE `warnbadhrecip` `warnbadhrecip` ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N';
17-
ALTER TABLE `dns_rr` CHANGE `data` `data` TEXT NOT NULL;
18-
ALTER TABLE `web_database` CHANGE `database_quota` `database_quota` INT(11) NULL DEFAULT NULL;
19-
ALTER TABLE `web_domain` ADD `log_retention` INT NOT NULL DEFAULT '30' ;
20-
ALTER TABLE spamfilter_policy CHANGE spam_tag_level spam_tag_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_tag2_level spam_tag2_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_kill_level spam_kill_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_dsn_cutoff_level spam_dsn_cutoff_level DECIMAL(5,2) NULL DEFAULT NULL, CHANGE spam_quarantine_cutoff_level spam_quarantine_cutoff_level DECIMAL(5,2) NULL DEFAULT NULL;
21-
UPDATE `web_database` as d LEFT JOIN `web_domain` as w ON (w.domain_id = d.parent_domain_id) SET d.parent_domain_id = 0 WHERE w.domain_id IS NULL AND d.parent_domain_id != 0;
1+

interface/lib/classes/validate_cron.inc.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ function command_format($field_name, $field_value, $validator) {
5454

5555
if(preg_match("'^([a-z0-9][a-z0-9\-]{0,62}\.)+([A-Za-z0-9\-]{2,30})$'i", $parsed["host"]) == false) return $this->get_error($validator['errmsg']);
5656
}
57+
if(strpos($field_value, "\n") !== false || strpos($field_value, "\r") !== false || strpos($field_value, chr(0)) !== false) {
58+
return $this->get_error($validator['errmsg']);
59+
}
5760
}
5861

5962
function run_month_format($field_name, $field_value, $validator) {

server/plugins-available/cron_plugin.inc.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ function _write_crontab() {
239239
if($job['type'] == 'url') {
240240
$command .= "\t{$cron_config['wget']} --user-agent='Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0' -q -t 1 -T 7200 -O " . $log_wget_target . " " . escapeshellarg($job['command']) . " " . $log_target;
241241
} else {
242+
if(strpos($job['command'], "\n") !== false || strpos($job['command'], "\r") !== false || strpos($job['command'], chr(0)) !== false) {
243+
$app->log("Insecure Cron job SKIPPED: " . $job['command'], LOGLEVEL_WARN);
244+
continue;
245+
}
246+
242247
$web_root = '';
243248
if($job['type'] == 'chrooted') {
244249
if(substr($job['command'], 0, strlen($this->parent_domain['document_root'])) == $this->parent_domain['document_root']) {

0 commit comments

Comments
 (0)