diff --git a/web/ajax/modals/filterdebug.php b/web/ajax/modals/filterdebug.php
index ec8022a10..f0dd63a1e 100644
--- a/web/ajax/modals/filterdebug.php
+++ b/web/ajax/modals/filterdebug.php
@@ -16,7 +16,7 @@
if ($fid) {
$filter = new ZM\Filter($fid);
if (!$filter->Id()) {
- echo '
Filter not found for id '.$_REQUEST['fid'].'
';
+ echo 'Filter not found for id '.$fid.'
';
}
} else {
$filter = new ZM\Filter();
diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php
index e302ca5cc..cf81de629 100644
--- a/web/api/app/Controller/EventsController.php
+++ b/web/api/app/Controller/EventsController.php
@@ -396,7 +396,7 @@ class EventsController extends AppController {
}
$matches = NULL;
$value = preg_replace('/^\s?interval\s?/i', '', $value);
- if (preg_match('/^(?P[ -.:0-9\']+)\s+(?P[_a-z]+)$/i', trim($value), $matches) !== 1) {
+ if (preg_match('/^(?P[ \-.:0-9]+)\s+(?P[_a-z]+)$/i', trim($value), $matches) !== 1) {
throw new Exception('Invalid interval: ' . $value);
}
$expr = trim($matches['expr']);
@@ -429,7 +429,7 @@ class EventsController extends AppController {
$matches = NULL;
// https://dev.mysql.com/doc/refman/5.5/en/expressions.html#temporal-intervals
// Examples: `'1-1' YEAR_MONTH`, `'-1 10' DAY_HOUR`, `'1.999999' SECOND_MICROSECOND`
- if (preg_match('/^(?P[ -.:0-9\']+)\s+(?P[_a-z]+)$/i', trim($interval), $matches) !== 1) {
+ if (preg_match('/^(?P[ \-.:0-9]+)\s+(?P[_a-z]+)$/i', trim($interval), $matches) !== 1) {
throw new Exception('Invalid interval: ' . $interval);
}
$expr = trim($matches['expr']);
diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php
index 58653e8d5..7e469f810 100644
--- a/web/api/app/Controller/HostController.php
+++ b/web/api/app/Controller/HostController.php
@@ -44,7 +44,7 @@ class HostController extends AppController {
return;
}
# To try to prevent abuse here, we are only going to allow certain characters in the daemon and args.
- $safe_daemon = preg_replace('/[^A-Za-z0-9\- \.]/', '', $daemon, -1, $count);
+ $safe_daemon = preg_replace('/[^A-Za-z0-9\-\.]/', '', $daemon, -1, $count);
if ($count) Error("Invalid characters found in daemon string ($daemon). Potential attack?");
$safe_command = preg_replace('/[^a-z]/', '', $command, -1, $count);
if ($count) Error("Invalid characters found in command string ($command). Potential attack?");
@@ -240,8 +240,8 @@ class HostController extends AppController {
if ( $mid ) {
// Get disk usage for $mid
- ZM\Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
- $usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'");
+ ZM\Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print \$1}'");
+ $usage = shell_exec("du -s0 ".escapeshellarg($zm_dir_events.'/'.$mid)." | awk '{print \$1}'");
} else {
$monitors = $this->Monitor->find('all', array(
'fields' => array('Id', 'Name', 'WebColour')
diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php
index 90935fb0f..660fd8c70 100644
--- a/web/api/app/Controller/MonitorsController.php
+++ b/web/api/app/Controller/MonitorsController.php
@@ -259,7 +259,7 @@ class MonitorsController extends AppController {
global $user;
$mToken = $this->request->query('token') ? $this->request->query('token') : $this->request->data('token');;
if ($mToken) {
- $auth = ' -T '.$mToken;
+ $auth = ' -T '.escapeshellarg($mToken);
} else if (ZM_AUTH_RELAY == 'hashed') {
$auth = ' -A '.calculateAuthHash(''); # Can't do REMOTE_IP because zmu doesn't normally have access to it.
} else if (ZM_AUTH_RELAY == 'plain') {
@@ -278,13 +278,17 @@ class MonitorsController extends AppController {
$password = $_SESSION['password'];
}
- $auth = ' -U ' .$user->Username().' -P '.$password;
+ $auth = ' -U '.escapeshellarg($user->Username()).' -P '.escapeshellarg($password);
} else if (ZM_AUTH_RELAY == 'none') {
- $auth = ' -U ' .$user->Username();
+ $auth = ' -U '.escapeshellarg($user->Username());
}
}
-
- $shellcmd = escapeshellcmd(ZM_PATH_BIN."/zmu $verbose -m$id $q $auth");
+
+ $shellcmd = ZM_PATH_BIN.'/zmu'
+ .($verbose ? " $verbose" : '')
+ .' -m'.escapeshellarg($id)
+ ." $q"
+ .$auth;
$status = exec($shellcmd, $output, $rc);
ZM\Debug("Command: $shellcmd output: ".implode(PHP_EOL, $output)." rc: $rc");
if ($rc) {
diff --git a/web/includes/Event.php b/web/includes/Event.php
index 3379b88d1..7b154f705 100644
--- a/web/includes/Event.php
+++ b/web/includes/Event.php
@@ -700,22 +700,20 @@ class Event extends ZM_Object {
}
function createVideo($format, $rate, $scale, $transform, $overwrite=false) {
- $command = ZM_PATH_BIN.'/zmvideo.pl -e '.$this->{'Id'}.' -f '.$format.' -r '.sprintf('%.2F', ($rate/RATE_BASE));
- if (preg_match('/\d+x\d+/', $scale)) {
- $command .= ' -S '.$scale;
+ $command = ZM_PATH_BIN.'/zmvideo.pl -e '.escapeshellarg($this->{'Id'})
+ .' -f '.escapeshellarg(preg_replace('/[^\w]/', '', $format))
+ .' -r '.escapeshellarg(sprintf('%.2F', ($rate/RATE_BASE)));
+ if (preg_match('/^\d+x\d+$/', $scale)) {
+ $command .= ' -S '.escapeshellarg($scale);
} else {
- if ( version_compare(phpversion(), '4.3.10', '>=') )
- $command .= ' -s '.sprintf('%.2F', ($scale/SCALE_BASE));
- else
- $command .= ' -s '.sprintf('%.2f', ($scale/SCALE_BASE));
+ $command .= ' -s '.escapeshellarg(sprintf('%.2F', ($scale/SCALE_BASE)));
}
if ($transform != '') {
$transform = preg_replace('/[^\w=]/', '', $transform);
- $command .= ' -t '.$transform;
+ $command .= ' -t '.escapeshellarg($transform);
}
if ($overwrite)
$command .= ' -o';
- $command = escapeshellcmd($command);
$result = exec($command, $output, $status);
Debug("generating Video $command: result($result outptu:(".implode("\n", $output )." status($status");
return $status ? '' : rtrim($result);
diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php
index c06beb3b2..2465969cf 100644
--- a/web/includes/FilterTerm.php
+++ b/web/includes/FilterTerm.php
@@ -40,6 +40,12 @@ class FilterTerm {
$this->attr = preg_replace('/[^A-Za-z0-9\.]/', '', $this->attr, -1, $count);
if ($count) Error("Invalid characters removed from filter attr {$term['attr']}, possible hacking attempt.");
$this->op = isset($term['op']) ? $term['op'] : '=';
+ $valid_ops = array('=', '!=', '>=', '<=', '>', '<', 'LIKE', 'NOT LIKE', '=~', '!~',
+ '=[]', '![]', 'IN', 'NOT IN', 'EXISTS', 'IS', 'IS NOT');
+ if (!in_array($this->op, $valid_ops)) {
+ Warning('Invalid operator in filter term: ' . $this->op);
+ $this->op = '=';
+ }
$this->val = isset($term['val']) ? $term['val'] : '';
if (is_array($this->val)) $this->val = implode(',', $this->val);
if ( isset($term['cnj']) ) {
@@ -71,7 +77,7 @@ class FilterTerm {
}
$this->cookie = isset($term['cookie']) ? $term['cookie'] : '';
$this->placeholder = isset($term['placeholder']) ? $term['placeholder'] : null;
- $this->collate = isset($term['collate']) ? $term['collate'] : '';
+ $this->collate = isset($term['collate']) ? preg_replace('/[^a-zA-Z0-9_]/', '', $term['collate']) : '';
$this->multiple = isset($term['multiple']) ? $term['multiple'] : '';
$this->chosen = isset($term['chosen']) ? $term['chosen'] : '';
@@ -80,6 +86,21 @@ class FilterTerm {
}
} # end function __construct
+ private function compare($left, $op, $right) {
+ $right = floatval($right);
+ switch ($op) {
+ case '=': return $left == $right;
+ case '!=': return $left != $right;
+ case '>': return $left > $right;
+ case '>=': return $left >= $right;
+ case '<': return $left < $right;
+ case '<=': return $left <= $right;
+ default:
+ Warning("Invalid operator '$op' in compare");
+ return false;
+ }
+ }
+
# Returns an array of values. AS term->value can be a list, we will break it apart, remove quotes etc
public function sql_values() {
$values = array();
@@ -433,16 +454,9 @@ class FilterTerm {
}
} # end foreach Storage Area
} else if ( $this->attr == 'SystemLoad' ) {
- $string_to_eval = 'return getLoad() '.$this->op.' '.$this->val.';';
- try {
- $ret = eval($string_to_eval);
- Debug("Evaled $string_to_eval = $ret");
- if ( $ret )
- return true;
- } catch ( Throwable $t ) {
- Error('Failed evaluating '.$string_to_eval);
- return false;
- }
+ $ret = $this->compare(getLoad(), $this->op, $this->val);
+ Debug("SystemLoad compare: getLoad() {$this->op} {$this->val} = " . ($ret ? 'true' : 'false'));
+ if ($ret) return true;
} else {
Error('testing unsupported pre term ' . $this->attr);
}
@@ -459,27 +473,13 @@ class FilterTerm {
return !file_exists($event->Path());
}
} else if ( $this->attr == 'DiskPercent' ) {
- $string_to_eval = 'return $event->Storage()->disk_usage_percent() '.$this->op.' '.$this->val.';';
- try {
- $ret = eval($string_to_eval);
- Debug("Evalled $string_to_eval = $ret");
- if ( $ret )
- return true;
- } catch ( Throwable $t ) {
- Error('Failed evaluating '.$string_to_eval);
- return false;
- }
+ $ret = $this->compare($event->Storage()->disk_usage_percent(), $this->op, $this->val);
+ Debug("DiskPercent compare: " . ($ret ? 'true' : 'false'));
+ if ($ret) return true;
} else if ( $this->attr == 'DiskBlocks' ) {
- $string_to_eval = 'return $event->Storage()->disk_usage_blocks() '.$this->op.' '.$this->val.';';
- try {
- $ret = eval($string_to_eval);
- Debug("Evalled $string_to_eval = $ret");
- if ( $ret )
- return true;
- } catch ( Throwable $t ) {
- Error('Failed evaluating '.$string_to_eval);
- return false;
- }
+ $ret = $this->compare($event->Storage()->disk_usage_blocks(), $this->op, $this->val);
+ Debug("DiskBlocks compare: " . ($ret ? 'true' : 'false'));
+ if ($ret) return true;
} else if ( $this->attr == 'Tags' ) {
// Debug('TODO: Complete this post_sql_condition for Tags val: ' . $this->val . ' op: ' . $this->op . ' id: ' . $this->id);
// Debug(print_r($this, true));
diff --git a/web/skins/classic/views/onvifprobe.php b/web/skins/classic/views/onvifprobe.php
index 88132a644..d152bb7eb 100644
--- a/web/skins/classic/views/onvifprobe.php
+++ b/web/skins/classic/views/onvifprobe.php
@@ -50,7 +50,7 @@ function execONVIF($cmd) {
function probeCameras($localIp) {
$cameras = array();
- $lines = @execONVIF('probe 1.1,1.2'.(isset($_REQUEST['interface']) ? ' '.$_REQUEST['interface'] : '' ));
+ $lines = @execONVIF('probe 1.1,1.2'.(isset($_REQUEST['interface']) ? ' '.escapeshellarg($_REQUEST['interface']) : '' ));
if ($lines) {
foreach ($lines as $line) {
$line = rtrim($line);
@@ -93,7 +93,7 @@ function probeCameras($localIp) {
function probeProfiles($device_ep, $soapversion, $username, $password) {
$profiles = array();
- if ($lines = @execONVIF("profiles $device_ep $soapversion $username $password")) {
+ if ($lines = @execONVIF('profiles '.escapeshellarg($device_ep).' '.escapeshellarg($soapversion).' '.escapeshellarg($username).' '.escapeshellarg($password))) {
foreach ($lines as $line) {
$line = rtrim( $line );
diff --git a/web/views/image.php b/web/views/image.php
index 9d2c34f85..8b61c3aae 100644
--- a/web/views/image.php
+++ b/web/views/image.php
@@ -60,6 +60,11 @@ if (!empty($_REQUEST['proxy'])) {
}
$url_parts = parse_url($url);
+ if (!$url_parts || !isset($url_parts['scheme']) ||
+ !in_array(strtolower($url_parts['scheme']), array('http', 'https'))) {
+ ZM\Warning('Image proxy only supports http/https URLs');
+ return;
+ }
$username = $url_parts['user'];
$password = isset($url_parts['pass']) ? $url_parts['pass'] : '';