From cb1221013251b4e9cba8e129edc6b8dbd8fd5145 Mon Sep 17 00:00:00 2001 From: Till Brehm <tbrehm@ispconfig.org> Date: Mon, 25 Aug 2014 09:33:09 -0400 Subject: [PATCH] FS#3640 - Add Intrusion Detection System --- interface/lib/classes/IDS/license.txt | 18 interface/lib/classes/IDS/default_filter.xml | 787 +++++++ install/dist/lib/fedora.lib.php | 6 interface/lib/classes/IDS/Caching/MemcachedCache.php | 181 + interface/lib/classes/IDS/Init.php | 174 + security/ids.whitelist | 45 interface/lib/classes/IDS/Converter.php | 779 +++++++ interface/lib/classes/IDS/Monitor.php | 566 +++++ install/lib/installer_base.lib.php | 6 interface/lib/classes/IDS/Filter/Storage.php | 401 ++++ interface/lib/classes/IDS/Caching/DatabaseCache.php | 277 ++ interface/lib/classes/IDS/Filter.php | 175 + interface/lib/classes/IDS/Caching/ApcCache.php | 144 + interface/lib/classes/getconf.inc.php | 18 interface/lib/classes/db_mysql.inc.php | 47 interface/lib/classes/IDS/default_filter.json | 933 +++++++++ interface/lib/classes/IDS/Caching/CacheFactory.php | 85 install/dist/lib/gentoo.lib.php | 6 install/dist/lib/opensuse.lib.php | 6 interface/lib/classes/ids.inc.php | 149 + security/security_settings.ini | 9 interface/lib/classes/IDS/Config.ini.php | 59 interface/lib/classes/IDS/.htaccess | 5 interface/lib/classes/IDS/Caching/CacheInterface.php | 64 interface/lib/classes/IDS/Caching/SessionCache.php | 136 + interface/lib/classes/IDS/Event.php | 221 ++ interface/lib/classes/IDS/Caching/FileCache.php | 189 + interface/lib/classes/IDS/Version.php | 49 security/apache_directives.blacklist | 3 security/ids.htmlfield | 5 interface/lib/app.inc.php | 13 interface/lib/classes/IDS/Report.php | 339 +++ interface/lib/classes/IDS/Config/Config.ini.php | 91 33 files changed, 5,979 insertions(+), 7 deletions(-) diff --git a/install/dist/lib/fedora.lib.php b/install/dist/lib/fedora.lib.php index a7179ef..7dd7d9b 100644 --- a/install/dist/lib/fedora.lib.php +++ b/install/dist/lib/fedora.lib.php @@ -1010,6 +1010,12 @@ caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); $command = 'chown root:ispconfig '.$install_dir.'/security'; caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.whitelist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.htmlfield'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/apache_directives.blacklist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); //* Make the global language file directory group writable exec("chmod -R 770 $install_dir/interface/lib/lang"); diff --git a/install/dist/lib/gentoo.lib.php b/install/dist/lib/gentoo.lib.php index 153a4e0..005a2fc 100644 --- a/install/dist/lib/gentoo.lib.php +++ b/install/dist/lib/gentoo.lib.php @@ -903,6 +903,12 @@ caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); $command = 'chown root:ispconfig '.$install_dir.'/security'; caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.whitelist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.htmlfield'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/apache_directives.blacklist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); //* Make the global language file directory group writable exec("chmod -R 770 $install_dir/interface/lib/lang"); diff --git a/install/dist/lib/opensuse.lib.php b/install/dist/lib/opensuse.lib.php index 935b816..ea13cd2 100644 --- a/install/dist/lib/opensuse.lib.php +++ b/install/dist/lib/opensuse.lib.php @@ -1081,6 +1081,12 @@ caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); $command = 'chown root:ispconfig '.$install_dir.'/security'; caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.whitelist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.htmlfield'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/apache_directives.blacklist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); //* Make the global language file directory group writable exec("chmod -R 770 $install_dir/interface/lib/lang"); diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php index 538f21e..a30a7ec 100644 --- a/install/lib/installer_base.lib.php +++ b/install/lib/installer_base.lib.php @@ -1937,6 +1937,12 @@ caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); $command = 'chown root:ispconfig '.$install_dir.'/security'; caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.whitelist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/ids.htmlfield'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); + $command = 'chown root:ispconfig '.$install_dir.'/security/apache_directives.blacklist'; + caselog($command.' &> /dev/null', __FILE__, __LINE__, "EXECUTED: $command", "Failed to execute the command $command"); //* Make the global language file directory group writable exec("chmod -R 770 $install_dir/interface/lib/lang"); diff --git a/interface/lib/app.inc.php b/interface/lib/app.inc.php index 8832f45..615e390 100755 --- a/interface/lib/app.inc.php +++ b/interface/lib/app.inc.php @@ -48,6 +48,7 @@ private $_wb; private $_loaded_classes = array(); private $_conf; + private $_security_config; public $loaded_plugins = array(); @@ -109,7 +110,8 @@ } $this->uses('functions'); // we need this before all others! - $this->uses('auth,plugin'); + $this->uses('auth,plugin,ini_parser,getconf'); + } public function __get($prop) { @@ -327,4 +329,13 @@ //* possible future = new app($conf); $app = new app(); +// load and enable PHP Intrusion Detection System (PHPIDS) +$ids_security_config = $app->getconf->get_security_config('ids'); + +if(is_dir(ISPC_CLASS_PATH.'/IDS') && $ids_security_config['ids_enabled'] == 'yes') { + $app->uses('ids'); + $app->ids->start(); +} +unset($ids_security_config); + ?> diff --git a/interface/lib/classes/IDS/.htaccess b/interface/lib/classes/IDS/.htaccess new file mode 100644 index 0000000..878d02a --- /dev/null +++ b/interface/lib/classes/IDS/.htaccess @@ -0,0 +1,5 @@ +# in case PHPIDS is placed in the web-root +deny from all + +# silence is golden +php_flag display_errors off \ No newline at end of file diff --git a/interface/lib/classes/IDS/Caching/ApcCache.php b/interface/lib/classes/IDS/Caching/ApcCache.php new file mode 100644 index 0000000..e82cc88 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/ApcCache.php @@ -0,0 +1,144 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ + +namespace IDS\Caching; + +/** + * APC caching wrapper + * + * This class inhabits functionality to get and set cache via memcached. + * + * @category Security + * @package PHPIDS + * @author Yves Berkholz <godzilla80@gmx.net> + * @copyright 2007-2009 The PHPIDS Groupoup + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.6.5 + */ +class ApcCache implements CacheInterface +{ + /** + * Caching type + * + * @var string + */ + private $type = null; + + /** + * Cache configuration + * + * @var array + */ + private $config = null; + + /** + * Flag if the filter storage has been found in memcached + * + * @var boolean + */ + private $isCached = false; + + /** + * Holds an instance of this class + * + * @var object + */ + private static $cachingInstance = null; + + /** + * Constructor + * + * @param string $type caching type + * @param array $init the IDS_Init object + * + * @return void + */ + public function __construct($type, $init) + { + $this->type = $type; + $this->config = $init->config['Caching']; + } + + /** + * Returns an instance of this class + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return object $this + */ + public static function getInstance($type, $init) + { + if (!self::$cachingInstance) { + self::$cachingInstance = new ApcCache($type, $init); + } + + return self::$cachingInstance; + } + + /** + * Writes cache data + * + * @param array $data the caching data + * + * @return object $this + */ + public function setCache(array $data) + { + if (!$this->isCached) { + apc_store( + $this->config['key_prefix'] . '.storage', + $data, + $this->config['expiration_time'] + ); + } + + return $this; + } + + /** + * Returns the cached data + * + * Note that this method returns false if either type or file cache is + * not set + * + * @return mixed cache data or false + */ + public function getCache() + { + $data = apc_fetch($this->config['key_prefix'] . '.storage'); + $this->isCached = !empty($data); + + return $data; + } +} diff --git a/interface/lib/classes/IDS/Caching/CacheFactory.php b/interface/lib/classes/IDS/Caching/CacheFactory.php new file mode 100644 index 0000000..c358332 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/CacheFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Caching; + +/** + * Caching factory + * + * This class is used as a factory to load the correct concrete caching + * implementation. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class CacheFactory +{ + /** + * Factory method + * + * @param object $init the IDS_Init object + * @param string $type the caching type + * + * @return object the caching facility + */ + public static function factory($init, $type) + { + $object = false; + $wrapper = preg_replace( + '/\W+/m', + null, + ucfirst($init->config['Caching']['caching']) + ); + $class = '\\IDS\\Caching\\' . $wrapper . 'Cache'; + $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . $wrapper . 'Cache.php'; + + if (file_exists($path)) { + include_once $path; + + if (class_exists($class)) { + $object = call_user_func( + array('' . $class, 'getInstance'), + $type, + $init + ); + } + } + + return $object; + } +} diff --git a/interface/lib/classes/IDS/Caching/CacheInterface.php b/interface/lib/classes/IDS/Caching/CacheInterface.php new file mode 100644 index 0000000..c1a6a60 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/CacheInterface.php @@ -0,0 +1,64 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Caching; + +/** + * Caching wrapper interface + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @since Version 0.4 + * @link http://php-ids.org/ + */ +interface CacheInterface +{ + /** + * Interface method + * + * @param array $data the cache data + * + * @return void + */ + public function setCache(array $data); + + /** + * Interface method + * + * @return void + */ + public function getCache(); +} diff --git a/interface/lib/classes/IDS/Caching/DatabaseCache.php b/interface/lib/classes/IDS/Caching/DatabaseCache.php new file mode 100644 index 0000000..09c77a8 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/DatabaseCache.php @@ -0,0 +1,277 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Caching; + +/** + * + */ + +/** + * Database caching wrapper + * + * This class inhabits functionality to get and set cache via a database. + * + * Needed SQL: + * + +#create the database + +CREATE DATABASE IF NOT EXISTS `phpids` DEFAULT CHARACTER +SET utf8 COLLATE utf8_general_ci; +DROP TABLE IF EXISTS `cache`; + +#now select the created datbase and create the table + +CREATE TABLE `cache` ( +`type` VARCHAR( 32 ) NOT null , +`data` TEXT NOT null , +`created` DATETIME NOT null , +`modified` DATETIME NOT null +) ENGINE = MYISAM ; + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Groupup + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class DatabaseCache implements CacheInterface +{ + + /** + * Caching type + * + * @var string + */ + private $type = null; + + /** + * Cache configuration + * + * @var array + */ + private $config = null; + + /** + * DBH + * + * @var object + */ + private $handle = null; + + /** + * Holds an instance of this class + * + * @var object + */ + private static $cachingInstance = null; + + /** + * Constructor + * + * Connects to database. + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return void + */ + public function __construct($type, $init) + { + $this->type = $type; + $this->config = $init->config['Caching']; + $this->handle = $this->connect(); + } + + /** + * Returns an instance of this class + * + * @static + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return object $this + */ + public static function getInstance($type, $init) + { + + if (!self::$cachingInstance) { + self::$cachingInstance = new DatabaseCache($type, $init); + } + + return self::$cachingInstance; + } + + /** + * Writes cache data into the database + * + * @param array $data the caching data + * + * @throws PDOException if a db error occurred + * @return object $this + */ + public function setCache(array $data) + { + $handle = $this->handle; + + $rows = $handle->query('SELECT created FROM `' . $this->config['table'].'`'); + + if (!$rows || $rows->rowCount() === 0) { + + $this->write($handle, $data); + } else { + + foreach ($rows as $row) { + + if ((time()-strtotime($row['created'])) > + $this->config['expiration_time']) { + + $this->write($handle, $data); + } + } + } + + return $this; + } + + /** + * Returns the cached data + * + * Note that this method returns false if either type or file cache is + * not set + * + * @throws PDOException if a db error occurred + * @return mixed cache data or false + */ + public function getCache() + { + try { + $handle = $this->handle; + $result = $handle->prepare( + 'SELECT * FROM `' . + $this->config['table'] . + '` where type=?' + ); + $result->execute(array($this->type)); + + foreach ($result as $row) { + return unserialize($row['data']); + } + + } catch (\PDOException $e) { + throw new \PDOException('PDOException: ' . $e->getMessage()); + } + + return false; + } + + /** + * Connect to database and return a handle + * + * @return object PDO + * @throws Exception if connection parameters are faulty + * @throws PDOException if a db error occurred + */ + private function connect() + { + // validate connection parameters + if (!$this->config['wrapper'] + || !$this->config['user'] + || !$this->config['password'] + || !$this->config['table']) { + + throw new \Exception('Insufficient connection parameters'); + } + + // try to connect + try { + $handle = new \PDO( + $this->config['wrapper'], + $this->config['user'], + $this->config['password'] + ); + $handle->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + + } catch (\PDOException $e) { + throw new \PDOException('PDOException: ' . $e->getMessage()); + } + + return $handle; + } + + /** + * Write the cache data to the table + * + * @param object $handle the database handle + * @param array $data the caching data + * + * @return object PDO + * @throws PDOException if a db error occurred + */ + private function write($handle, $data) + { + try { + $handle->query('TRUNCATE ' . $this->config['table'].''); + $statement = $handle->prepare( + 'INSERT INTO `' . + $this->config['table'].'` ( + type, + data, + created, + modified + ) + VALUES ( + :type, + :data, + now(), + now() + )' + ); + + $statement->bindValue( + 'type', + $handle->quote($this->type) + ); + $statement->bindValue('data', serialize($data)); + + if (!$statement->execute()) { + throw new \PDOException($statement->errorCode()); + } + } catch (\PDOException $e) { + throw new \PDOException('PDOException: ' . $e->getMessage()); + } + } +} diff --git a/interface/lib/classes/IDS/Caching/FileCache.php b/interface/lib/classes/IDS/Caching/FileCache.php new file mode 100644 index 0000000..71f01c7 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/FileCache.php @@ -0,0 +1,189 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Caching; + +use IDS\Init; + +/** + * File caching wrapper + * + * This class inhabits functionality to get and set cache via a static flatfile. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class FileCache implements CacheInterface +{ + /** + * Caching type + * + * @var string + */ + private $type; + + /** + * Cache configuration + * + * @var array + */ + private $config; + + /** + * Path to cache file + * + * @var string + */ + private $path; + + /** + * Holds an instance of this class + * + * @var object + */ + private static $cachingInstance; + + /** + * Constructor + * + * @param string $type caching type + * @param object $init the IDS_Init object + * @throws \Exception + * + * @return void + */ + public function __construct($type, Init $init) + { + $this->type = $type; + $this->config = $init->config['Caching']; + $this->path = $init->getBasePath() . $this->config['path']; + + if (file_exists($this->path) && !is_writable($this->path)) { + throw new \Exception( + 'Make sure all files in ' . + htmlspecialchars($this->path, ENT_QUOTES, 'UTF-8') . + 'are writeable!' + ); + } + } + + /** + * Returns an instance of this class + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return object $this + */ + public static function getInstance($type, $init) + { + if (!self::$cachingInstance) { + self::$cachingInstance = new FileCache($type, $init); + } + + return self::$cachingInstance; + } + + /** + * Writes cache data into the file + * + * @param array $data the cache data + * + * @throws Exception if cache file couldn't be created + * @return object $this + */ + public function setCache(array $data) + { + if (!is_writable(preg_replace('/[\/][^\/]+\.[^\/]++$/', null, $this->path))) { + throw new \Exception( + 'Temp directory ' . + htmlspecialchars($this->path, ENT_QUOTES, 'UTF-8') . + ' seems not writable' + ); + } + + if (!$this->isValidFile($this->path)) { + $handle = @fopen($this->path, 'w+'); + + if (!$handle) { + throw new \Exception("Cache file couldn't be created"); + } + + $serialized = @serialize($data); + if (!$serialized) { + throw new \Exception("Cache data couldn't be serialized"); + } + + fwrite($handle, $serialized); + fclose($handle); + } + + return $this; + } + + /** + * Returns the cached data + * + * Note that this method returns false if either type or file cache is + * not set + * + * @return mixed cache data or false + */ + public function getCache() + { + // make sure filters are parsed again if cache expired + if (!$this->isValidFile($this->path)) { + return false; + } + + $data = unserialize(file_get_contents($this->path)); + + return $data; + } + + /** + * Returns true if the cache file is still valid + * + * @param string $file + * @return bool + */ + private function isValidFile($file) + { + return file_exists($file) && time() - filectime($file) <= $this->config['expiration_time']; + } +} diff --git a/interface/lib/classes/IDS/Caching/MemcachedCache.php b/interface/lib/classes/IDS/Caching/MemcachedCache.php new file mode 100644 index 0000000..b3dd5b7 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/MemcachedCache.php @@ -0,0 +1,181 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Caching; + +/** + * File caching wrapper + * + * This class inhabits functionality to get and set cache via memcached. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Groupoup + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class MemcachedCache implements CacheInterface +{ + /** + * Caching type + * + * @var string + */ + private $type = null; + + /** + * Cache configuration + * + * @var array + */ + private $config = null; + + /** + * Flag if the filter storage has been found in memcached + * + * @var boolean + */ + private $isCached = false; + + /** + * Memcache object + * + * @var object + */ + private $memcache = null; + + /** + * Holds an instance of this class + * + * @var object + */ + private static $cachingInstance = null; + + /** + * Constructor + * + * @param string $type caching type + * @param array $init the IDS_Init object + * + * @return void + */ + public function __construct($type, $init) + { + + $this->type = $type; + $this->config = $init->config['Caching']; + + $this->connect(); + } + + /** + * Returns an instance of this class + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return object $this + */ + public static function getInstance($type, $init) + { + if (!self::$cachingInstance) { + self::$cachingInstance = new MemcachedCache($type, $init); + } + + return self::$cachingInstance; + } + + /** + * Writes cache data + * + * @param array $data the caching data + * + * @return object $this + */ + public function setCache(array $data) + { + if (!$this->isCached) { + $this->memcache->set( + $this->config['key_prefix'] . '.storage', + $data, + false, + $this->config['expiration_time'] + ); + } + + return $this; + } + + /** + * Returns the cached data + * + * Note that this method returns false if either type or file cache is + * not set + * + * @return mixed cache data or false + */ + public function getCache() + { + $data = $this->memcache->get( + $this->config['key_prefix'] . + '.storage' + ); + $this->isCached = !empty($data); + + return $data; + } + + /** + * Connect to the memcached server + * + * @throws Exception if connection parameters are insufficient + * @return void + */ + private function connect() + { + + if ($this->config['host'] && $this->config['port']) { + // establish the memcache connection + $this->memcache = new \Memcache; + $this->memcache->pconnect( + $this->config['host'], + $this->config['port'] + ); + + } else { + throw new \Exception('Insufficient connection parameters'); + } + } +} diff --git a/interface/lib/classes/IDS/Caching/SessionCache.php b/interface/lib/classes/IDS/Caching/SessionCache.php new file mode 100644 index 0000000..7168a63 --- /dev/null +++ b/interface/lib/classes/IDS/Caching/SessionCache.php @@ -0,0 +1,136 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ + +namespace IDS\Caching; + +/** + * File caching wrapper + * + * This class inhabits functionality to get and set cache via session. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class SessionCache implements CacheInterface +{ + /** + * Caching type + * + * @var string + */ + private $type = null; + + /** + * Cache configuration + * + * @var array + */ + private $config = null; + + /** + * Holds an instance of this class + * + * @var object + */ + private static $cachingInstance = null; + + /** + * Constructor + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return void + */ + public function __construct($type, $init) + { + $this->type = $type; + $this->config = $init->config['Caching']; + } + + /** + * Returns an instance of this class + * + * @param string $type caching type + * @param object $init the IDS_Init object + * + * @return object $this + */ + public static function getInstance($type, $init) + { + + if (!self::$cachingInstance) { + self::$cachingInstance = new SessionCache($type, $init); + } + + return self::$cachingInstance; + } + + /** + * Writes cache data into the session + * + * @param array $data the caching data + * + * @return object $this + */ + public function setCache(array $data) + { + + $_SESSION['PHPIDS'][$this->type] = $data; + + return $this; + } + + /** + * Returns the cached data + * + * Note that this method returns false if either type or file cache is not set + * + * @return mixed cache data or false + */ + public function getCache() + { + + if ($this->type && $_SESSION['PHPIDS'][$this->type]) { + return $_SESSION['PHPIDS'][$this->type]; + } + + return false; + } +} diff --git a/interface/lib/classes/IDS/Config.ini.php b/interface/lib/classes/IDS/Config.ini.php new file mode 100644 index 0000000..f87ccc8 --- /dev/null +++ b/interface/lib/classes/IDS/Config.ini.php @@ -0,0 +1,59 @@ +; <?php die(); ?> + +; PHPIDS Config.ini + +; General configuration settings + +[General] + + ; basic settings - customize to make the PHPIDS work at all + filter_type = xml + + base_path = /full/path/to/IDS/ + use_base_path = false + + filter_path = default_filter.xml + tmp_path = tmp + scan_keys = false + + ; in case you want to use a different HTMLPurifier source, specify it here + ; By default, those files are used that are being shipped with PHPIDS + HTML_Purifier_Cache = vendors/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer + + ; define which fields contain html and need preparation before + ; hitting the PHPIDS rules (new in PHPIDS 0.5) + ;html[] = POST.__wysiwyg + + ; define which fields contain JSON data and should be treated as such + ; for fewer false positives (new in PHPIDS 0.5.3) + ;json[] = POST.__jsondata + + ; define which fields shouldn't be monitored (a[b]=c should be referenced via a.b) + exceptions[] = GET.__utmz + exceptions[] = GET.__utmc + exceptions[] = POST.maildir_path + + ; you can use regular expressions for wildcard exceptions - example: /.*foo/i + +[Caching] + + ; caching: session|file|database|memcached|apc|none + caching = file + expiration_time = 600 + + ; file cache + path = tmp/default_filter.cache + + ; database cache + wrapper = "mysql:host=localhost;port=3306;dbname=phpids" + user = phpids_user + password = 123456 + table = cache + + ; memcached + ;host = localhost + ;port = 11211 + ;key_prefix = PHPIDS + + ; apc + ;key_prefix = PHPIDS diff --git a/interface/lib/classes/IDS/Config/Config.ini.php b/interface/lib/classes/IDS/Config/Config.ini.php new file mode 100644 index 0000000..0800552 --- /dev/null +++ b/interface/lib/classes/IDS/Config/Config.ini.php @@ -0,0 +1,91 @@ +; <?php die(); ?> + +; PHPIDS Config.ini + +; General configuration settings + +[General] + + ; basic settings - customize to make the PHPIDS work at all + filter_type = xml + + base_path = /full/path/to/IDS/ + use_base_path = false + + filter_path = default_filter.xml + tmp_path = tmp + scan_keys = false + + ; in case you want to use a different HTMLPurifier source, specify it here + ; By default, those files are used that are being shipped with PHPIDS + HTML_Purifier_Cache = vendors/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer + + ; define which fields contain html and need preparation before + ; hitting the PHPIDS rules (new in PHPIDS 0.5) + ;html[] = POST.__wysiwyg + ;html[] = POST.records + ;html[] = REQUEST.records + + ; define which fields contain JSON data and should be treated as such + ; for fewer false positives (new in PHPIDS 0.5.3) + ;json[] = POST.__jsondata + + ; define which fields shouldn't be monitored (a[b]=c should be referenced via a.b) + ; exceptions[] = GET.__utmz + ; exceptions[] = GET.__utmc + ; exceptions[] = POST.maildir_path + ; exceptions[] = REQUEST.maildir_path + ; exceptions[] = REQUEST.website_path + ; exceptions[] = REQUEST.website_symlinks + ; exceptions[] = REQUEST.vhost_conf_dir + ; exceptions[] = REQUEST.vhost_conf_enabled_dir + ; exceptions[] = REQUEST.nginx_vhost_conf_dir + ; exceptions[] = REQUEST.nginx_vhost_conf_enabled_dir + ; exceptions[] = REQUEST.php_open_basedir + ; exceptions[] = REQUEST.awstats_pl + ; exceptions[] = POST.website_path + ; exceptions[] = POST.website_symlinks + ; exceptions[] = POST.vhost_conf_dir + ; exceptions[] = POST.vhost_conf_enabled_dir + ; exceptions[] = POST.nginx_vhost_conf_dir + ; exceptions[] = POST.nginx_vhost_conf_enabled_dir + ; exceptions[] = POST.php_open_basedir + ; exceptions[] = POST.awstats_pl + ; exceptions[] = REQUEST.fastcgi_starter_path + ; exceptions[] = REQUEST.fastcgi_bin + ; exceptions[] = POST.fastcgi_starter_path + ; exceptions[] = POST.fastcgi_bin + ; exceptions[] = REQUEST.jailkit_chroot_home + ; exceptions[] = POST.jailkit_chroot_home + ; exceptions[] = REQUEST.phpmyadmin_url + ; exceptions[] = REQUEST.phpmyadmin_url + ; exceptions[] = REQUEST.records.weak_password_txt + ; exceptions[] = POST.records.weak_password_txt + + + + ; you can use regular expressions for wildcard exceptions - example: /.*foo/i + +[Caching] + + ; caching: session|file|database|memcached|apc|none + caching = file + expiration_time = 600 + + ; file cache + path = tmp/default_filter.cache + + ; database cache + wrapper = "mysql:host=localhost;port=3306;dbname=phpids" + user = phpids_user + password = 123456 + table = cache + + ; memcached + ;host = localhost + ;port = 11211 + ;key_prefix = PHPIDS + + ; apc + ;key_prefix = PHPIDS + diff --git a/interface/lib/classes/IDS/Converter.php b/interface/lib/classes/IDS/Converter.php new file mode 100644 index 0000000..770d1cc --- /dev/null +++ b/interface/lib/classes/IDS/Converter.php @@ -0,0 +1,779 @@ +<?php + +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ + +/** + * PHPIDS specific utility class to convert charsets manually + * + * Note that if you make use of IDS_Converter::runAll(), existing class + * methods will be executed in the same order as they are implemented in the + * class tree! + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ + +namespace IDS; + +class Converter +{ + /** + * Runs all converter functions + * + * Note that if you make use of IDS_Converter::runAll(), existing class + * methods will be executed in the same order as they are implemented in the + * class tree! + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function runAll($value) + { + foreach (get_class_methods(__CLASS__) as $method) { + if (strpos($method, 'run') !== 0) { + $value = self::$method($value); + } + } + + return $value; + } + + /** + * Check for comments and erases them if available + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromCommented($value) + { + // check for existing comments + if (preg_match('/(?:\<!-|-->|\/\*|\*\/|\/\/\W*\w+\s*$)|(?:--[^-]*-)/ms', $value)) { + + $pattern = array( + '/(?:(?:<!)(?:(?:--(?:[^-]*(?:-[^-]+)*)--\s*)*)(?:>))/ms', + '/(?:(?:\/\*\/*[^\/\*]*)+\*\/)/ms', + '/(?:--[^-]*-)/ms' + ); + + $converted = preg_replace($pattern, ';', $value); + $value .= "\n" . $converted; + } + + //make sure inline comments are detected and converted correctly + $value = preg_replace('/(<\w+)\/+(\w+=?)/m', '$1/$2', $value); + $value = preg_replace('/[^\\\:]\/\/(.*)$/m', '/**/$1', $value); + $value = preg_replace('/([^\-&])#.*[\r\n\v\f]/m', '$1', $value); + $value = preg_replace('/([^&\-])#.*\n/m', '$1 ', $value); + $value = preg_replace('/^#.*\n/m', ' ', $value); + + return $value; + } + + /** + * Strip newlines + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromWhiteSpace($value) + { + //check for inline linebreaks + $search = array('\r', '\n', '\f', '\t', '\v'); + $value = str_replace($search, ';', $value); + + // replace replacement characters regular spaces + $value = str_replace('�', ' ', $value); + + //convert real linebreaks + return preg_replace('/(?:\n|\r|\v)/m', ' ', $value); + } + + /** + * Checks for common charcode pattern and decodes them + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromJSCharcode($value) + { + $matches = array(); + + // check if value matches typical charCode pattern + if (preg_match_all('/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)){4,}/ms', $value, $matches)) { + $converted = ''; + $string = implode(',', $matches[0]); + $string = preg_replace('/\s/', '', $string); + $string = preg_replace('/\w+=/', '', $string); + $charcode = explode(',', $string); + + foreach ($charcode as $char) { + $char = preg_replace('/\W0/s', '', $char); + + if (preg_match_all('/\d*[+-\/\* ]\d+/', $char, $matches)) { + $match = preg_split('/(\W?\d+)/', implode('', $matches[0]), null, PREG_SPLIT_DELIM_CAPTURE); + + if (array_sum($match) >= 20 && array_sum($match) <= 127) { + $converted .= chr(array_sum($match)); + } + + } elseif (!empty($char) && $char >= 20 && $char <= 127) { + $converted .= chr($char); + } + } + + $value .= "\n" . $converted; + } + + // check for octal charcode pattern + if (preg_match_all('/(?:(?:[\\\]+\d+[ \t]*){8,})/ims', $value, $matches)) { + $converted = ''; + $charcode = explode('\\', preg_replace('/\s/', '', implode(',', $matches[0]))); + + foreach (array_map('octdec', array_filter($charcode)) as $char) { + if (20 <= $char && $char <= 127) { + $converted .= chr($char); + } + } + $value .= "\n" . $converted; + } + + // check for hexadecimal charcode pattern + if (preg_match_all('/(?:(?:[\\\]+\w+\s*){8,})/ims', $value, $matches)) { + $converted = ''; + $charcode = explode('\\', preg_replace('/[ux]/', '', implode(',', $matches[0]))); + + foreach (array_map('hexdec', array_filter($charcode)) as $char) { + if (20 <= $char && $char <= 127) { + $converted .= chr($char); + } + } + $value .= "\n" . $converted; + } + + return $value; + } + + /** + * Eliminate JS regex modifiers + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertJSRegexModifiers($value) + { + return preg_replace('/\/[gim]+/', '/', $value); + } + + /** + * Converts from hex/dec entities + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertEntities($value) + { + $converted = null; + + //deal with double encoded payload + $value = preg_replace('/&/', '&', $value); + + if (preg_match('/&#x?[\w]+/ms', $value)) { + $converted = preg_replace('/(&#x?[\w]{2}\d?);?/ms', '$1;', $value); + $converted = html_entity_decode($converted, ENT_QUOTES, 'UTF-8'); + $value .= "\n" . str_replace(';;', ';', $converted); + } + + // normalize obfuscated protocol handlers + $value = preg_replace( + '/(?:j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:)|(d\s*a\s*t\s*a\s*:)/ms', + 'javascript:', + $value + ); + + return $value; + } + + /** + * Normalize quotes + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertQuotes($value) + { + // normalize different quotes to " + $pattern = array('\'', '`', '´', '’', '‘'); + $value = str_replace($pattern, '"', $value); + + //make sure harmless quoted strings don't generate false alerts + $value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value); + + return $value; + } + + /** + * Converts SQLHEX to plain text + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromSQLHex($value) + { + $matches = array(); + if (preg_match_all('/(?:(?:\A|[^\d])0x[a-f\d]{3,}[a-f\d]*)+/im', $value, $matches)) { + foreach ($matches[0] as $match) { + $converted = ''; + foreach (str_split($match, 2) as $hex_index) { + if (preg_match('/[a-f\d]{2,3}/i', $hex_index)) { + $converted .= chr(hexdec($hex_index)); + } + } + $value = str_replace($match, $converted, $value); + } + } + // take care of hex encoded ctrl chars + $value = preg_replace('/0x\d+/m', ' 1 ', $value); + + return $value; + } + + /** + * Converts basic SQL keywords and obfuscations + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromSQLKeywords($value) + { + $pattern = array( + '/(?:is\s+null)|(like\s+null)|' . + '(?:(?:^|\W)in[+\s]*\([\s\d"]+[^()]*\))/ims' + ); + $value = preg_replace($pattern, '"=0', $value); + + $value = preg_replace('/[^\w\)]+\s*like\s*[^\w\s]+/ims', '1" OR "1"', $value); + $value = preg_replace('/null([,"\s])/ims', '0$1', $value); + $value = preg_replace('/\d+\./ims', ' 1', $value); + $value = preg_replace('/,null/ims', ',0', $value); + $value = preg_replace('/(?:between)/ims', 'or', $value); + $value = preg_replace('/(?:and\s+\d+\.?\d*)/ims', '', $value); + $value = preg_replace('/(?:\s+and\s+)/ims', ' or ', $value); + + $pattern = array( + '/(?:not\s+between)|(?:is\s+not)|(?:not\s+in)|' . + '(?:xor|<>|rlike(?:\s+binary)?)|' . + '(?:regexp\s+binary)|' . + '(?:sounds\s+like)/ims' + ); + $value = preg_replace($pattern, '!', $value); + $value = preg_replace('/"\s+\d/', '"', $value); + $value = preg_replace('/(\W)div(\W)/ims', '$1 OR $2', $value); + $value = preg_replace('/\/(?:\d+|null)/', null, $value); + + return $value; + } + + /** + * Detects nullbytes and controls chars via ord() + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromControlChars($value) + { + // critical ctrl values + $search = array( + chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), + chr(6), chr(7), chr(8), chr(11), chr(12), chr(14), + chr(15), chr(16), chr(17), chr(18), chr(19), chr(24), + chr(25), chr(192), chr(193), chr(238), chr(255), '\\0' + ); + + $value = str_replace($search, '%00', $value); + + //take care for malicious unicode characters + $value = urldecode( + preg_replace( + '/(?:%E(?:2|3)%8(?:0|1)%(?:A|8|9)\w|%EF%BB%BF|%EF%BF%BD)|(?:&#(?:65|8)\d{3};?)/i', + null, + urlencode($value) + ) + ); + $value = urlencode($value); + $value = preg_replace('/(?:%F0%80%BE)/i', '>', $value); + $value = preg_replace('/(?:%F0%80%BC)/i', '<', $value); + $value = preg_replace('/(?:%F0%80%A2)/i', '"', $value); + $value = preg_replace('/(?:%F0%80%A7)/i', '\'', $value); + $value = urldecode($value); + + $value = preg_replace('/(?:%ff1c)/', '<', $value); + $value = preg_replace('/(?:&[#x]*(200|820|200|820|zwn?j|lrm|rlm)\w?;?)/i', null, $value); + $value = preg_replace( + '/(?:&#(?:65|8)\d{3};?)|' . + '(?:&#(?:56|7)3\d{2};?)|' . + '(?:&#x(?:fe|20)\w{2};?)|' . + '(?:&#x(?:d[c-f])\w{2};?)/i', + null, + $value + ); + + $value = str_replace( + array( + '«', + '〈', + '<', + '‹', + '〈', + '⟨' + ), + '<', + $value + ); + $value = str_replace( + array( + '»', + '〉', + '>', + '›', + '〉', + '⟩' + ), + '>', + $value + ); + + return $value; + } + + /** + * This method matches and translates base64 strings and fragments + * used in data URIs + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromNestedBase64($value) + { + $matches = array(); + preg_match_all('/(?:^|[,&?])\s*([a-z0-9]{50,}=*)(?:\W|$)/im', $value, $matches); + + foreach ($matches[1] as $item) { + if (isset($item) && !preg_match('/[a-f0-9]{32}/i', $item)) { + $base64_item = base64_decode($item); + $value = str_replace($item, $base64_item, $value); + } + } + + return $value; + } + + /** + * Detects nullbytes and controls chars via ord() + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromOutOfRangeChars($value) + { + $values = str_split($value); + foreach ($values as $item) { + if (ord($item) >= 127) { + $value = str_replace($item, ' ', $value); + } + } + + return $value; + } + + /** + * Strip XML patterns + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromXML($value) + { + $converted = strip_tags($value); + if (!$converted || $converted === $value) { + return $value; + } else { + return $value . "\n" . $converted; + } + } + + /** + * This method converts JS unicode code points to + * regular characters + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromJSUnicode($value) + { + $matches = array(); + preg_match_all('/\\\u[0-9a-f]{4}/ims', $value, $matches); + + if (!empty($matches[0])) { + foreach ($matches[0] as $match) { + $chr = chr(hexdec(substr($match, 2, 4))); + $value = str_replace($match, $chr, $value); + } + $value .= "\n\u0001"; + } + + return $value; + } + + /** + * Converts relevant UTF-7 tags to UTF-8 + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromUTF7($value) + { + if (preg_match('/\+A\w+-?/m', $value)) { + if (function_exists('mb_convert_encoding')) { + if (version_compare(PHP_VERSION, '5.2.8', '<')) { + $tmp_chars = str_split($value); + $value = ''; + foreach ($tmp_chars as $char) { + if (ord($char) <= 127) { + $value .= $char; + } + } + } + $value .= "\n" . mb_convert_encoding($value, 'UTF-8', 'UTF-7'); + } else { + //list of all critical UTF7 codepoints + $schemes = array( + '+ACI-' => '"', + '+ADw-' => '<', + '+AD4-' => '>', + '+AFs-' => '[', + '+AF0-' => ']', + '+AHs-' => '{', + '+AH0-' => '}', + '+AFw-' => '\\', + '+ADs-' => ';', + '+ACM-' => '#', + '+ACY-' => '&', + '+ACU-' => '%', + '+ACQ-' => '$', + '+AD0-' => '=', + '+AGA-' => '`', + '+ALQ-' => '"', + '+IBg-' => '"', + '+IBk-' => '"', + '+AHw-' => '|', + '+ACo-' => '*', + '+AF4-' => '^', + '+ACIAPg-' => '">', + '+ACIAPgA8-' => '">' + ); + + $value = str_ireplace( + array_keys($schemes), + array_values($schemes), + $value + ); + } + } + + return $value; + } + + /** + * Converts basic concatenations + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromConcatenated($value) + { + //normalize remaining backslashes + if ($value != preg_replace('/(\w)\\\/', "$1", $value)) { + $value .= preg_replace('/(\w)\\\/', "$1", $value); + } + + $compare = stripslashes($value); + + $pattern = array( + '/(?:<\/\w+>\+<\w+>)/s', + '/(?:":\d+[^"[]+")/s', + '/(?:"?"\+\w+\+")/s', + '/(?:"\s*;[^"]+")|(?:";[^"]+:\s*")/s', + '/(?:"\s*(?:;|\+).{8,18}:\s*")/s', + '/(?:";\w+=)|(?:!""&&")|(?:~)/s', + '/(?:"?"\+""?\+?"?)|(?:;\w+=")|(?:"[|&]{2,})/s', + '/(?:"\s*\W+")/s', + '/(?:";\w\s*\+=\s*\w?\s*")/s', + '/(?:"[|&;]+\s*[^|&\n]*[|&]+\s*"?)/s', + '/(?:";\s*\w+\W+\w*\s*[|&]*")/s', + '/(?:"\s*"\s*\.)/s', + '/(?:\s*new\s+\w+\s*[+",])/', + '/(?:(?:^|\s+)(?:do|else)\s+)/', + '/(?:[{(]\s*new\s+\w+\s*[)}])/', + '/(?:(this|self)\.)/', + '/(?:undefined)/', + '/(?:in\s+)/' + ); + + // strip out concatenations + $converted = preg_replace($pattern, null, $compare); + + //strip object traversal + $converted = preg_replace('/\w(\.\w\()/', "$1", $converted); + + // normalize obfuscated method calls + $converted = preg_replace('/\)\s*\+/', ")", $converted); + + //convert JS special numbers + $converted = preg_replace( + '/(?:\(*[.\d]e[+-]*[^a-z\W]+\)*)|(?:NaN|Infinity)\W/ims', + 1, + $converted + ); + + if ($converted && ($compare != $converted)) { + $value .= "\n" . $converted; + } + + return $value; + } + + /** + * This method collects and decodes proprietary encoding types + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromProprietaryEncodings($value) + { + //Xajax error reportings + $value = preg_replace('/<!\[CDATA\[(\W+)\]\]>/im', '$1', $value); + + //strip false alert triggering apostrophes + $value = preg_replace('/(\w)\"(s)/m', '$1$2', $value); + + //strip quotes within typical search patterns + $value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value); + + //OpenID login tokens + $value = preg_replace('/{[\w-]{8,9}\}(?:\{[\w=]{8}\}){2}/', null, $value); + + //convert Content and \sdo\s to null + $value = preg_replace('/Content|\Wdo\s/', null, $value); + + //strip emoticons + $value = preg_replace( + '/(?:\s[:;]-[)\/PD]+)|(?:\s;[)PD]+)|(?:\s:[)PD]+)|-\.-|\^\^/m', + null, + $value + ); + + //normalize separation char repetion + $value = preg_replace('/([.+~=*_\-;])\1{2,}/m', '$1', $value); + + //normalize multiple single quotes + $value = preg_replace('/"{2,}/m', '"', $value); + + //normalize quoted numerical values and asterisks + $value = preg_replace('/"(\d+)"/m', '$1', $value); + + //normalize pipe separated request parameters + $value = preg_replace('/\|(\w+=\w+)/m', '&$1', $value); + + //normalize ampersand listings + $value = preg_replace('/(\w\s)&\s(\w)/', '$1$2', $value); + + //normalize escaped RegExp modifiers + $value = preg_replace('/\/\\\(\w)/', '/$1', $value); + + return $value; + } + + /** + * This method removes encoded sql # comments + * + * @param string $value the value to convert + * + * @static + * @return string + */ + public static function convertFromUrlencodeSqlComment($value) + { + if (preg_match_all('/(?:\%23.*?\%0a)/im',$value,$matches)){ + $converted = $value; + foreach($matches[0] as $match){ + $converted = str_replace($match,' ',$converted); + } + $value .= "\n" . $converted; + } + return $value; + } + + /** + * This method is the centrifuge prototype + * + * @param string $value the value to convert + * @param Monitor $monitor the monitor object + * + * @static + * @return string + */ + public static function runCentrifuge($value, Monitor $monitor = null) + { + $threshold = 3.49; + if (strlen($value) > 25) { + //strip padding + $tmp_value = preg_replace('/\s{4}|==$/m', null, $value); + $tmp_value = preg_replace( + '/\s{4}|[\p{L}\d\+\-=,.%()]{8,}/m', + 'aaa', + $tmp_value + ); + + // Check for the attack char ratio + $tmp_value = preg_replace('/([*.!?+-])\1{1,}/m', '$1', $tmp_value); + $tmp_value = preg_replace('/"[\p{L}\d\s]+"/m', null, $tmp_value); + + $stripped_length = strlen( + preg_replace( + '/[\d\s\p{L}\.:,%&\/><\-)!|]+/m', + null, + $tmp_value + ) + ); + $overall_length = strlen( + preg_replace( + '/([\d\s\p{L}:,\.]{3,})+/m', + 'aaa', + preg_replace('/\s{2,}/m', null, $tmp_value) + ) + ); + + if ($stripped_length != 0 && $overall_length/$stripped_length <= $threshold) { + $monitor->centrifuge['ratio'] = $overall_length / $stripped_length; + $monitor->centrifuge['threshold'] =$threshold; + + $value .= "\n$[!!!]"; + } + } + + if (strlen($value) > 40) { + // Replace all non-special chars + $converted = preg_replace('/[\w\s\p{L},.:!]/', null, $value); + + // Split string into an array, unify and sort + $array = str_split($converted); + $array = array_unique($array); + asort($array); + + // Normalize certain tokens + $schemes = array( + '~' => '+', + '^' => '+', + '|' => '+', + '*' => '+', + '%' => '+', + '&' => '+', + '/' => '+' + ); + + $converted = implode($array); + + $_keys = array_keys($schemes); + $_values = array_values($schemes); + + $converted = str_replace($_keys, $_values, $converted); + + $converted = preg_replace('/[+-]\s*\d+/', '+', $converted); + $converted = preg_replace('/[()[\]{}]/', '(', $converted); + $converted = preg_replace('/[!?:=]/', ':', $converted); + $converted = preg_replace('/[^:(+]/', null, stripslashes($converted)); + + // Sort again and implode + $array = str_split($converted); + asort($array); + $converted = implode($array); + + if (preg_match('/(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|(?:\({3,}\++:{2,})/', $converted)) { + $monitor->centrifuge['converted'] = $converted; + + return $value . "\n" . $converted; + } + } + + return $value; + } +} diff --git a/interface/lib/classes/IDS/Event.php b/interface/lib/classes/IDS/Event.php new file mode 100644 index 0000000..ebda171 --- /dev/null +++ b/interface/lib/classes/IDS/Event.php @@ -0,0 +1,221 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +/** + * PHPIDS event object + * + * This class represents a certain event that occured while applying the filters + * to the supplied data. It aggregates a bunch of IDS_Filter implementations and + * is a assembled in IDS_Report. + * + * Note that this class implements both Countable and IteratorAggregate + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +class Event implements \Countable, \IteratorAggregate +{ + /** + * Event name + * + * @var string + */ + protected $name = null; + + /** + * Value of the event + * + * @var mixed + */ + protected $value = null; + + /** + * List of filter objects + * + * Filter objects in this array are those that matched the events value + * + * @var Filter[]|array + */ + protected $filters = array(); + + /** + * Calculated impact + * + * Total impact of the event + * + * @var integer + */ + protected $impact = 0; + + /** + * Affecte tags + * + * @var string[]|array + */ + protected $tags = array(); + + /** + * Constructor + * + * Fills event properties + * + * @param string $name the event name + * @param mixed $value the event value + * @param Filter[]|array $filters the corresponding filters + * + * @throws \InvalidArgumentException + * @return \IDS\Event + */ + public function __construct($name, $value, array $filters) + { + if (!is_scalar($name)) { + throw new \InvalidArgumentException( + 'Expected $name to be a scalar,' . gettype($name) . ' given' + ); + } + + if (!is_scalar($value)) { + throw new \InvalidArgumentException( + 'Expected $value to be a scalar,' . gettype($value) . ' given' + ); + } + + $this->name = $name; + $this->value = $value; + + foreach ($filters as $filter) { + if (!$filter instanceof Filter) { + throw new \InvalidArgumentException( + 'Filter must be derived from IDS_Filter' + ); + } + + $this->filters[] = $filter; + } + } + + /** + * Returns event name + * + * The name of the event usually is the key of the variable that was + * considered to be malicious + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns event value + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns calculated impact + * + * @return integer + */ + public function getImpact() + { + if (!$this->impact) { + $this->impact = 0; + foreach ($this->filters as $filter) { + $this->impact += $filter->getImpact(); + } + } + + return $this->impact; + } + + /** + * Returns affected tags + * + * @return string[]|array + */ + public function getTags() + { + foreach ($this->getFilters() as $filter) { + $this->tags = array_merge($this->tags, $filter->getTags()); + } + + return $this->tags = array_values(array_unique($this->tags)); + } + + /** + * Returns list of filter objects + * + * @return Filter[]|array + */ + public function getFilters() + { + return $this->filters; + } + + /** + * Returns number of filters + * + * To implement interface Countable this returns the number of filters + * appended. + * + * @return integer + */ + public function count() + { + return count($this->getFilters()); + } + + /** + * IteratorAggregate iterator getter + * + * Returns an iterator to iterate over the appended filters. + * + * @return \Iterator the filter collection + */ + public function getIterator() + { + return new \ArrayIterator($this->getFilters()); + } +} diff --git a/interface/lib/classes/IDS/Filter.php b/interface/lib/classes/IDS/Filter.php new file mode 100644 index 0000000..573988f --- /dev/null +++ b/interface/lib/classes/IDS/Filter.php @@ -0,0 +1,175 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +/** + * PHPIDS Filter object + * + * Each object of this class serves as a container for a specific filter. The + * object provides methods to get information about this particular filter and + * also to match an arbitrary string against it. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class Filter +{ + /** + * Filter rule + * + * @var string + */ + protected $rule; + + /** + * List of tags of the filter + * + * @var string[]|array + */ + protected $tags = array(); + + /** + * Filter impact level + * + * @var integer + */ + protected $impact = 0; + + /** + * Filter description + * + * @var string + */ + protected $description = ''; + + /** + * Constructor + * + * @param integer $id filter id + * @param string $rule filter rule + * @param string $description filter description + * @param string[]|array $tags list of tags + * @param integer $impact filter impact level + * + * @return \IDS\Filter + */ + public function __construct($id, $rule, $description, array $tags, $impact) + { + $this->id = $id; + $this->rule = $rule; + $this->tags = $tags; + $this->impact = $impact; + $this->description = $description; + } + + /** + * Matches a string against current filter + * + * Matches given string against the filter rule the specific object of this + * class represents + * + * @param string $input the string input to match + * + * @throws \InvalidArgumentException if argument is no string + * @return boolean + */ + public function match($input) + { + if (!is_string($input)) { + throw new \InvalidArgumentException( + 'Invalid argument. Expected a string, received ' . gettype($input) + ); + } + + return (bool) preg_match('/' . $this->getRule() . '/ms', strtolower($input)); + } + + /** + * Returns filter description + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Return list of affected tags + * + * Each filter rule is concerned with a certain kind of attack vectors. + * This method returns those affected kinds. + * + * @return string[]|array + */ + public function getTags() + { + return $this->tags; + } + + /** + * Returns filter rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Get filter impact level + * + * @return integer + */ + public function getImpact() + { + return $this->impact; + } + + /** + * Get filter ID + * + * @return integer + */ + public function getId() + { + return $this->id; + } +} diff --git a/interface/lib/classes/IDS/Filter/Storage.php b/interface/lib/classes/IDS/Filter/Storage.php new file mode 100644 index 0000000..f17549b --- /dev/null +++ b/interface/lib/classes/IDS/Filter/Storage.php @@ -0,0 +1,401 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS\Filter; + +use IDS\Init; +use IDS\Caching\CacheFactory; + +/** + * Filter Storage + * + * This class provides various default functions for gathering filter patterns + * to be used later on by the detection mechanism. You might extend this class + * to your requirements. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +class Storage +{ + /** + * Filter source file + * + * @var string + */ + protected $source = null; + + /** + * Holds caching settings + * + * @var array + */ + protected $cacheSettings = null; + + /** + * Cache container + * + * @var object IDS_Caching wrapper + */ + protected $cache = null; + + /** + * Filter container + * + * @var array + */ + protected $filterSet = array(); + + /** + * Constructor + * + * Loads filters based on provided IDS_Init settings. + * + * @param object $init IDS_Init instance + * + * @throws \InvalidArgumentException if unsupported filter type is given + * @return void + */ + final public function __construct(Init $init) + { + if ($init->config) { + + $caching = isset($init->config['Caching']['caching']) ? $init->config['Caching']['caching'] : 'none'; + + $type = $init->config['General']['filter_type']; + $this->source = $init->getBasePath(). $init->config['General']['filter_path']; + + if ($caching && $caching !== 'none') { + $this->cacheSettings = $init->config['Caching']; + $this->cache = CacheFactory::factory($init, 'storage'); + } + + switch ($type) { + case 'xml': + return $this->getFilterFromXML(); + case 'json': + return $this->getFilterFromJson(); + default: + throw new \InvalidArgumentException('Unsupported filter type.'); + } + } + } + + /** + * Sets the filter array + * + * @param array $filterSet array containing multiple IDS_Filter instances + * + * @return object $this + */ + final public function setFilterSet($filterSet) + { + foreach ($filterSet as $filter) { + $this->addFilter($filter); + } + + return $this; + } + + /** + * Returns registered filters + * + * @return array + */ + final public function getFilterSet() + { + return $this->filterSet; + } + + /** + * Adds a filter + * + * @param object $filter IDS_Filter instance + * + * @return object $this + */ + final public function addFilter(\IDS\Filter $filter) + { + $this->filterSet[] = $filter; + + return $this; + } + + /** + * Checks if any filters are cached + * + * @return mixed $filters cached filters or false + */ + private function isCached() + { + $filters = false; + + if ($this->cacheSettings) { + if ($this->cache) { + $filters = $this->cache->getCache(); + } + } + + return $filters; + } + + /** + * Loads filters from XML using SimpleXML + * + * This function parses the provided source file and stores the result. + * If caching mode is enabled the result will be cached to increase + * the performance. + * + * @throws \InvalidArgumentException if source file doesn't exist + * @throws \RuntimeException if problems with fetching the XML data occur + * @return object $this + */ + public function getFilterFromXML() + { + if (extension_loaded('SimpleXML')) { + + /* + * See if filters are already available in the cache + */ + $filters = $this->isCached(); + + /* + * If they aren't, parse the source file + */ + if (!$filters) { + + if (!file_exists($this->source)) { + throw new \InvalidArgumentException( + sprintf('Invalid config: %s doesn\'t exist.', $this->source) + ); + } + + if (LIBXML_VERSION >= 20621) { + $filters = simplexml_load_file($this->source, null, LIBXML_COMPACT); + } else { + $filters = simplexml_load_file($this->source); + } + } + + /* + * In case we still don't have any filters loaded and exception + * will be thrown + */ + if (empty($filters)) { + throw new \RuntimeException( + 'XML data could not be loaded.' . + ' Make sure you specified the correct path.' + ); + } + + /* + * Now the storage will be filled with IDS_Filter objects + */ + $nocache = $filters instanceof \SimpleXMLElement; + + if ($nocache) + { + // build filters and cache them for re-use on next run + $data = array(); + $filters = $filters->filter; + + foreach ($filters as $filter) { + $id = (string) $filter->id; + $rule = (string) $filter->rule; + $impact = (string) $filter->impact; + $tags = array_values((array) $filter->tags); + $description = (string) $filter->description; + + $this->addFilter( + new \IDS\Filter( + $id, + $rule, + $description, + (array) $tags[0], + (int) $impact + ) + ); + + $data[] = array( + 'id' => $id, + 'rule' => $rule, + 'impact' => $impact, + 'tags' => $tags, + 'description' => $description + ); + } + + /* + * If caching is enabled, the fetched data will be cached + */ + if ($this->cacheSettings) { + $this->cache->setCache($data); + } + + } else { + + // build filters from cached content + $this->addFiltersFromArray($filters); + } + + return $this; + } + + throw new \RuntimeException('SimpleXML is not loaded.'); + } + + /** + * Loads filters from Json file using ext/Json + * + * This function parses the provided source file and stores the result. + * If caching mode is enabled the result will be cached to increase + * the performance. + * + * @throws \RuntimeException if problems with fetching the JSON data occur + * @return object $this + */ + public function getFilterFromJson() + { + + if (extension_loaded('Json')) { + + /* + * See if filters are already available in the cache + */ + $filters = $this->isCached(); + + /* + * If they aren't, parse the source file + */ + if (!$filters) { + if (!file_exists($this->source)) { + throw new \InvalidArgumentException( + sprintf('Invalid config: %s doesn\'t exist.', $this->source) + ); + } + $filters = json_decode(file_get_contents($this->source)); + } + + if (!$filters) { + throw new \RuntimeException('JSON data could not be loaded. Make sure you specified the correct path.'); + } + + /* + * Now the storage will be filled with IDS_Filter objects + */ + $nocache = !is_array($filters); + + if ($nocache) { + + // build filters and cache them for re-use on next run + $data = array(); + $filters = $filters->filters->filter; + + foreach ($filters as $filter) { + + $id = (string) $filter->id; + $rule = (string) $filter->rule; + $impact = (string) $filter->impact; + $tags = array_values((array) $filter->tags); + $description = (string) $filter->description; + + $this->addFilter( + new \IDS\Filter( + $id, + $rule, + $description, + (array) $tags[0], + (int) $impact + ) + ); + + $data[] = array( + 'id' => $id, + 'rule' => $rule, + 'impact' => $impact, + 'tags' => $tags, + 'description' => $description + ); + } + + /* + * If caching is enabled, the fetched data will be cached + */ + if ($this->cacheSettings) { + $this->cache->setCache($data); + } + + } else { + + // build filters from cached content + $this->addFiltersFromArray($filters); + } + + return $this; + } + + throw new \RuntimeException('json extension is not loaded.'); + } + + /** + * This functions adds an array of filters to the IDS_Storage object. + * Each entry within the array is expected to be an simple array containing all parts of the filter. + * + * @param array $filters + */ + private function addFiltersFromArray(array $filters) + { + foreach ($filters as $filter) { + + $id = $filter['id']; + $rule = $filter['rule']; + $impact = $filter['impact']; + $tags = $filter['tags']; + $description = $filter['description']; + + $this->addFilter( + new \IDS\Filter( + $id, + $rule, + $description, + (array) $tags[0], + (int) $impact + ) + ); + } + } +} diff --git a/interface/lib/classes/IDS/Init.php b/interface/lib/classes/IDS/Init.php new file mode 100644 index 0000000..1b6269a --- /dev/null +++ b/interface/lib/classes/IDS/Init.php @@ -0,0 +1,174 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +/** + * Framework initiation + * + * This class is used for the purpose to initiate the framework and inhabits + * functionality to parse the needed configuration file. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Groupup + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + * @since Version 0.4 + */ +class Init +{ + /** + * Holds config settings + * + * @var array + */ + public $config = array(); + + /** + * Instance of this class depending on the supplied config file + * + * @var Init[]|array + * @static + */ + private static $instances = array(); + + /** + * Constructor + * + * Includes needed classes and parses the configuration file + * + * @param array $config + * + * @return \IDS\Init $this + */ + public function __construct(array $config = array()) + { + $this->config = $config; + } + + /** + * Returns an instance of this class. Also a PHP version check + * is being performed to avoid compatibility problems with PHP < 5.1.6 + * + * @param string|null $configPath the path to the config file + * + * @throws \InvalidArgumentException + * @return self + */ + public static function init($configPath = null) + { + if (!$configPath) { + return new self(); + } + if (!isset(self::$instances[$configPath])) { + if (!file_exists($configPath) || !is_readable($configPath)) { + throw new \InvalidArgumentException("Invalid config path '$configPath'"); + } + self::$instances[$configPath] = new static(parse_ini_file($configPath, true)); + } + + return self::$instances[$configPath]; + } + + /** + * This method checks if a base path is given and usage is set to true. + * If all that tests succeed the base path will be returned as a string - + * else null will be returned. + * + * @return string|null the base path or null + */ + public function getBasePath() + { + return (!empty($this->config['General']['base_path']) + && !empty($this->config['General']['use_base_path'])) + ? $this->config['General']['base_path'] : null; + } + + /** + * Merges new settings into the exsiting ones or overwrites them + * + * @param array $config the config array + * @param boolean $overwrite config overwrite flag + * + * @return void + */ + public function setConfig(array $config, $overwrite = false) + { + if ($overwrite) { + $this->config = $this->mergeConfig($this->config, $config); + } else { + $this->config = $this->mergeConfig($config, $this->config); + } + } + + /** + * Merge config hashes recursivly + * + * The algorithm merges configuration arrays recursively. If an element is + * an array in both, the values will be appended. If it is a scalar in both, + * the value will be replaced. + * + * @param array $current The legacy hash + * @param array $successor The hash which values count more when in doubt + * @return array Merged hash + */ + protected function mergeConfig($current, $successor) + { + if (is_array($current) and is_array($successor)) { + foreach ($successor as $key => $value) { + if (isset($current[$key]) + and is_array($value) + and is_array($current[$key])) { + + $current[$key] = $this->mergeConfig($current[$key], $value); + } else { + $current[$key] = $successor[$key]; + } + } + } + + return $current; + } + + /** + * Returns the config array + * + * @return array the config array + */ + public function getConfig() + { + return $this->config; + } +} diff --git a/interface/lib/classes/IDS/Monitor.php b/interface/lib/classes/IDS/Monitor.php new file mode 100644 index 0000000..f93e748 --- /dev/null +++ b/interface/lib/classes/IDS/Monitor.php @@ -0,0 +1,566 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +use IDS\Filter\Storage; + +/** + * Monitoring engine + * + * This class represents the core of the frameworks attack detection mechanism + * and provides functions to scan incoming data for malicious appearing script + * fragments. + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +class Monitor +{ + /** + * Tags to define what to search for + * + * Accepted values are xss, csrf, sqli, dt, id, lfi, rfe, spam, dos + * + * @var array + */ + private $tags = null; + + /** + * Container for filter rules + * + * Holds an instance of Storage + * + * @var Storage + */ + private $storage = null; + + /** + * Scan keys switch + * + * Enabling this property will cause the monitor to scan both the key and + * the value of variables + * + * @var boolean + */ + public $scanKeys = false; + + /** + * Exception container + * + * Using this array it is possible to define variables that must not be + * scanned. Per default, utmz google analytics parameters are permitted. + * + * @var array + */ + private $exceptions = array(); + + /** + * Html container + * + * Using this array it is possible to define variables that legally + * contain html and have to be prepared before hitting the rules to + * avoid too many false alerts + * + * @var array + */ + private $html = array(); + + /** + * JSON container + * + * Using this array it is possible to define variables that contain + * JSON data - and should be treated as such + * + * @var array + */ + private $json = array(); + + /** + * Holds HTMLPurifier object + * + * @var \HTMLPurifier + */ + private $htmlPurifier = null; + + /** + * HTMLPurifier cache directory + * + * @var string + */ + private $HTMLPurifierCache = ''; + + /** + * This property holds the tmp JSON string from the _jsonDecodeValues() callback + * + * @var string + */ + private $tmpJsonString = ''; + + /** + * Constructor + * + * @throws \InvalidArgumentException When PHP version is less than what the library supports + * @throws \Exception + * @param Init $init instance of IDS_Init + * @param array|null $tags list of tags to which filters should be applied + * @return Monitor + */ + public function __construct(Init $init, array $tags = null) + { + $this->storage = new Storage($init); + $this->tags = $tags; + $this->scanKeys = $init->config['General']['scan_keys']; + $this->exceptions = isset($init->config['General']['exceptions']) ? $init->config['General']['exceptions'] : array(); + $this->html = isset($init->config['General']['html']) ? $init->config['General']['html'] : array(); + $this->json = isset($init->config['General']['json']) ? $init->config['General']['json'] : array(); + + if (isset($init->config['General']['HTML_Purifier_Cache'])) { + $this->HTMLPurifierCache = $init->getBasePath() . $init->config['General']['HTML_Purifier_Cache']; + } + + $tmpPath = $init->getBasePath() . $init->config['General']['tmp_path']; + + if (!is_writeable($tmpPath)) { + throw new \InvalidArgumentException("Please make sure the folder '$tmpPath' is writable"); + } + } + + /** + * Starts the scan mechanism + * + * @param array $request + * @return Report + */ + public function run(array $request) + { + $report = new Report; + foreach ($request as $key => $value) { + $report = $this->iterate($key, $value, $report); + } + return $report; + } + + /** + * Iterates through given data and delegates it to IDS_Monitor::_detect() in + * order to check for malicious appearing fragments + * + * @param string $key the former array key + * @param array|string $value the former array value + * @param Report $report + * @return Report + */ + private function iterate($key, $value, Report $report) + { + if (is_array($value)) { + foreach ($value as $subKey => $subValue) { + $this->iterate("$key.$subKey", $subValue, $report); + } + } elseif (is_string($value)) { + if ($filter = $this->detect($key, $value)) { + $report->addEvent(new Event($key, $value, $filter)); + } + } + return $report; + } + + /** + * Checks whether given value matches any of the supplied filter patterns + * + * @param mixed $key the key of the value to scan + * @param mixed $value the value to scan + * + * @return Filter[] array of filter(s) that matched the value + */ + private function detect($key, $value) + { + // define the pre-filter + $preFilter = '([^\w\s/@!?\.]+|(?:\./)|(?:@@\w+)|(?:\+ADw)|(?:union\s+select))i'; + + // to increase performance, only start detection if value isn't alphanumeric + if ((!$this->scanKeys || !$key || !preg_match($preFilter, $key)) && (!$value || !preg_match($preFilter, $value))) { + return array(); + } + + // check if this field is part of the exceptions + foreach ($this->exceptions as $exception) { + $matches = array(); + if (($exception === $key) || preg_match('((/.*/[^eE]*)$)', $exception, $matches) && isset($matches[1]) && preg_match($matches[1], $key)) { + return array(); + } + } + + // check for magic quotes and remove them if necessary + if (function_exists('get_magic_quotes_gpc') && !get_magic_quotes_gpc()) { + $value = preg_replace('(\\\(["\'/]))im', '$1', $value); + } + + // if html monitoring is enabled for this field - then do it! + if (is_array($this->html) && in_array($key, $this->html, true)) { + list($key, $value) = $this->purifyValues($key, $value); + } + + // check if json monitoring is enabled for this field + if (is_array($this->json) && in_array($key, $this->json, true)) { + list($key, $value) = $this->jsonDecodeValues($key, $value); + } + + // use the converter + $value = Converter::runAll($value); + $value = Converter::runCentrifuge($value, $this); + + // scan keys if activated via config + $key = $this->scanKeys ? Converter::runAll($key) : $key; + $key = $this->scanKeys ? Converter::runCentrifuge($key, $this) : $key; + + $filterSet = $this->storage->getFilterSet(); + + if ($tags = $this->tags) { + $filterSet = array_filter( + $filterSet, + function (Filter $filter) use ($tags) { + return (bool) array_intersect($tags, $filter->getTags()); + } + ); + } + + $scanKeys = $this->scanKeys; + $filterSet = array_filter( + $filterSet, + function (Filter $filter) use ($key, $value, $scanKeys) { + return $filter->match($value) || $scanKeys && $filter->match($key); + } + ); + + return $filterSet; + } + + + /** + * Purifies given key and value variables using HTMLPurifier + * + * This function is needed whenever there is variables for which HTML + * might be allowed like e.g. WYSIWYG post bodies. It will detect malicious + * code fragments and leaves harmless parts untouched. + * + * @param mixed $key + * @param mixed $value + * @since 0.5 + * @throws \Exception + * + * @return array tuple [key,value] + */ + private function purifyValues($key, $value) + { + /* + * Perform a pre-check if string is valid for purification + */ + if ($this->purifierPreCheck($key, $value)) { + if (!is_writeable($this->HTMLPurifierCache)) { + throw new \Exception($this->HTMLPurifierCache . ' must be writeable'); + } + + /** @var $config \HTMLPurifier_Config */ + $config = \HTMLPurifier_Config::createDefault(); + $config->set('Attr.EnableID', true); + $config->set('Cache.SerializerPath', $this->HTMLPurifierCache); + $config->set('Output.Newline', "\n"); + $this->htmlPurifier = new \HTMLPurifier($config); + + $value = preg_replace('([\x0b-\x0c])', ' ', $value); + $key = preg_replace('([\x0b-\x0c])', ' ', $key); + + $purifiedValue = $this->htmlPurifier->purify($value); + $purifiedKey = $this->htmlPurifier->purify($key); + + $plainValue = strip_tags($value); + $plainKey = strip_tags($key); + + $value = $value != $purifiedValue || $plainValue ? $this->diff($value, $purifiedValue, $plainValue) : null; + $key = $key != $purifiedKey ? $this->diff($key, $purifiedKey, $plainKey) : null; + } + return array($key, $value); + } + + /** + * This method makes sure no dangerous markup can be smuggled in + * attributes when HTML mode is switched on. + * + * If the precheck considers the string too dangerous for + * purification false is being returned. + * + * @param string $key + * @param string $value + * @since 0.6 + * + * @return boolean + */ + private function purifierPreCheck($key = '', $value = '') + { + /* + * Remove control chars before pre-check + */ + $tmpValue = preg_replace('/\p{C}/', null, $value); + $tmpKey = preg_replace('/\p{C}/', null, $key); + + $preCheck = '/<(script|iframe|applet|object)\W/i'; + return !(preg_match($preCheck, $tmpKey) || preg_match($preCheck, $tmpValue)); + } + + /** + * This method calculates the difference between the original + * and the purified markup strings. + * + * @param string $original the original markup + * @param string $purified the purified markup + * @param string $plain the string without html + * @since 0.5 + * + * @return string the difference between the strings + */ + private function diff($original, $purified, $plain) + { + /* + * deal with over-sensitive alt-attribute addition of the purifier + * and other common html formatting problems + */ + $purified = preg_replace('/\s+alt="[^"]*"/m', null, $purified); + $purified = preg_replace('/=?\s*"\s*"/m', null, $purified); + $original = preg_replace('/\s+alt="[^"]*"/m', null, $original); + $original = preg_replace('/=?\s*"\s*"/m', null, $original); + $original = preg_replace('/style\s*=\s*([^"])/m', 'style = "$1', $original); + + # deal with oversensitive CSS normalization + $original = preg_replace('/(?:([\w\-]+:)+\s*([^;]+;\s*))/m', '$1$2', $original); + + # strip whitespace between tags + $original = trim(preg_replace('/>\s*</m', '><', $original)); + $purified = trim(preg_replace('/>\s*</m', '><', $purified)); + + $original = preg_replace('/(=\s*(["\'`])[^>"\'`]*>[^>"\'`]*["\'`])/m', 'alt$1', $original); + + // no purified html is left + if (!$purified) { + return $original; + } + + // calculate the diff length + $length = mb_strlen($original) - mb_strlen($purified); + + /* + * Calculate the difference between the original html input + * and the purified string. + */ + $array1 = preg_split('/(?<!^)(?!$)/u', html_entity_decode(urldecode($original))); + $array2 = preg_split('/(?<!^)(?!$)/u', $purified); + + // create an array containing the single character differences + $differences = array_diff_assoc($array1, $array2); + + // return the diff - ready to hit the converter and the rules + $differences = trim(implode('', $differences)); + $diff = $length <= 10 ? $differences : mb_substr($differences, 0, strlen($original)); + + // clean up spaces between tag delimiters + $diff = preg_replace('/>\s*</m', '><', $diff); + + // correct over-sensitively stripped bad html elements + $diff = preg_replace('/[^<](iframe|script|embed|object|applet|base|img|style)/m', '<$1', $diff ); + + return mb_strlen($diff) >= 4 ? $diff . $plain : null; + } + + /** + * This method prepares incoming JSON data for the PHPIDS detection + * process. It utilizes _jsonConcatContents() as callback and returns a + * string version of the JSON data structures. + * + * @param string $key + * @param string $value + * @since 0.5.3 + * + * @return array tuple [key,value] + */ + private function jsonDecodeValues($key, $value) + { + $decodedKey = json_decode($key); + $decodedValue = json_decode($value); + + if ($decodedValue && is_array($decodedValue) || is_object($decodedValue)) { + array_walk_recursive($decodedValue, array($this, 'jsonConcatContents')); + $value = $this->tmpJsonString; + } else { + $this->tmpJsonString .= " " . $decodedValue . "\n"; + } + + if ($decodedKey && is_array($decodedKey) || is_object($decodedKey)) { + array_walk_recursive($decodedKey, array($this, 'jsonConcatContents')); + $key = $this->tmpJsonString; + } else { + $this->tmpJsonString .= " " . $decodedKey . "\n"; + } + + return array($key, $value); + } + + /** + * This is the callback used in _jsonDecodeValues(). The method + * concatenates key and value and stores them in $this->tmpJsonString. + * + * @param mixed $key + * @param mixed $value + * @since 0.5.3 + * + * @return void + */ + private function jsonConcatContents($key, $value) + { + if (is_string($key) && is_string($value)) { + $this->tmpJsonString .= $key . " " . $value . "\n"; + } else { + $this->jsonDecodeValues(json_encode($key), json_encode($value)); + } + } + + /** + * Sets exception array + * + * @param string[]|string $exceptions the thrown exceptions + * + * @return void + */ + public function setExceptions($exceptions) + { + $this->exceptions = (array) $exceptions; + } + + /** + * Returns exception array + * + * @return array + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Sets html array + * + * @param string[]|string $html the fields containing html + * @since 0.5 + * + * @return void + */ + public function setHtml($html) + { + $this->html = (array) $html; + } + + /** + * Adds a value to the html array + * + * @since 0.5 + * + * @param mixed $value + * @return void + */ + public function addHtml($value) + { + $this->html[] = $value; + } + + /** + * Returns html array + * + * @since 0.5 + * + * @return array the fields that contain allowed html + */ + public function getHtml() + { + return $this->html; + } + + /** + * Sets json array + * + * @param string[]|string $json the fields containing json + * @since 0.5.3 + * + * @return void + */ + public function setJson($json) + { + $this->json = (array) $json; + } + + /** + * Adds a value to the json array + * + * @param string $value the value containing JSON data + * @since 0.5.3 + * + * @return void + */ + public function addJson($value) + { + $this->json[] = $value; + } + + /** + * Returns json array + * + * @since 0.5.3 + * + * @return array the fields that contain json + */ + public function getJson() + { + return $this->json; + } + + /** + * Returns storage container + * + * @return array + */ + public function getStorage() + { + return $this->storage; + } +} diff --git a/interface/lib/classes/IDS/Report.php b/interface/lib/classes/IDS/Report.php new file mode 100644 index 0000000..daebfc0 --- /dev/null +++ b/interface/lib/classes/IDS/Report.php @@ -0,0 +1,339 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +/** + * PHPIDS report object + * + * The report objects collects a number of events and thereby presents the + * detected results. It provides a convenient API to work with the results. + * + * Note that this class implements Countable, IteratorAggregate and + * a __toString() method + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +class Report implements \Countable, \IteratorAggregate +{ + /** + * Event container + * + * @var Event[]|array + */ + protected $events = array(); + + /** + * List of affected tags + * + * This list of tags is collected from the collected event objects on + * demand when IDS_Report->getTags() is called + * + * @var string[]|array + */ + protected $tags = array(); + + /** + * Impact level + * + * The impact level is calculated on demand by adding the results of the + * event objects on IDS\Report->getImpact() + * + * @var integer + */ + protected $impact = 0; + + /** + * Centrifuge data + * + * This variable - initiated as an empty array - carries all information + * about the centrifuge data if available + * + * @var array + */ + protected $centrifuge = array(); + + /** + * Constructor + * + * @param array $events the events the report should include + * + * @return Report + */ + public function __construct(array $events = null) + { + foreach ((array) $events as $event) { + $this->addEvent($event); + } + } + + /** + * Adds an IDS_Event object to the report + * + * @param Event $event IDS_Event + * + * @return self $this + */ + public function addEvent(Event $event) + { + $this->clear(); + $this->events[$event->getName()] = $event; + + return $this; + } + + /** + * Get event by name + * + * In most cases an event is identified by the key of the variable that + * contained maliciously appearing content + * + * @param string|integer $name the event name + * + * @throws \InvalidArgumentException if argument is invalid + * @return Event|null IDS_Event object or false if the event does not exist + */ + public function getEvent($name) + { + if (!is_scalar($name)) { + throw new \InvalidArgumentException('Invalid argument type given'); + } + + return $this->hasEvent($name) ? $this->events[$name] : null; + } + + /** + * Returns list of events + * + * @return string[]|array + */ + + public function getEvents() + { + return $this->events; + } + + /** + * Returns list of affected tags + * + * @return string[]|array + */ + public function getTags() + { + if (!$this->tags) { + $this->tags = array(); + + foreach ($this->events as $event) { + $this->tags = array_merge($this->tags, $event->getTags()); + } + + $this->tags = array_values(array_unique($this->tags)); + } + + return $this->tags; + } + + /** + * Returns total impact + * + * Each stored IDS_Event object and its IDS_Filter sub-object are called + * to calculate the overall impact level of this request + * + * @return integer + */ + public function getImpact() + { + if (!$this->impact) { + $this->impact = 0; + foreach ($this->events as $event) { + $this->impact += $event->getImpact(); + } + } + + return $this->impact; + } + + /** + * Checks if a specific event with given name exists + * + * @param string|integer $name the event name + * + * @throws \InvalidArgumentException if argument is illegal + * @return boolean + */ + public function hasEvent($name) + { + if (!is_scalar($name)) { + throw new \InvalidArgumentException('Invalid argument given'); + } + + return isset($this->events[$name]); + } + + /** + * Returns total amount of events + * + * @return integer + */ + public function count() + { + return count($this->events); + } + + /** + * Return iterator object + * + * In order to provide the possibility to directly iterate over the + * IDS_Event object the IteratorAggregate is implemented. One can easily + * use foreach() to iterate through all stored IDS_Event objects. + * + * @return \Iterator the event collection + */ + public function getIterator() + { + return new \ArrayIterator($this->events); + } + + /** + * Checks if any events are registered + * + * @return boolean + */ + public function isEmpty() + { + return empty($this->events); + } + + /** + * Clears calculated/collected values + * + * @return void + */ + protected function clear() + { + $this->impact = 0; + $this->tags = array(); + } + + /** + * This method returns the centrifuge property or null if not + * filled with data + * + * @return array + */ + public function getCentrifuge() + { + return $this->centrifuge; + } + + /** + * This method sets the centrifuge property + * + * @param array $centrifuge the centrifuge data + * + * @throws \InvalidArgumentException if argument is illegal + * @return void + */ + public function setCentrifuge(array $centrifuge = array()) + { + if (!$centrifuge) { + throw new \InvalidArgumentException('Empty centrifuge given'); + } + $this->centrifuge = $centrifuge; + } + + /** + * Directly outputs all available information + * + * @return string + */ + public function __toString() + { + $output = ''; + if (!$this->isEmpty()) { + $output .= vsprintf( + "Total impact: %d<br/>\nAffected tags: %s<br/>\n", + array( + $this->getImpact(), + implode(', ', $this->getTags()) + ) + ); + + foreach ($this->events as $event) { + $output .= vsprintf( + "<br/>\nVariable: %s | Value: %s<br/>\nImpact: %d | Tags: %s<br/>\n", + array( + htmlspecialchars($event->getName()), + htmlspecialchars($event->getValue()), + $event->getImpact(), + implode(', ', $event->getTags()) + ) + ); + + foreach ($event as $filter) { + $output .= vsprintf( + "Description: %s | Tags: %s | ID %s<br/>\n", + array( + $filter->getDescription(), + implode(', ', $filter->getTags()), + $filter->getId() + ) + ); + } + } + + $output .= '<br/>'; + + if ($centrifuge = $this->getCentrifuge()) { + $output .= vsprintf( + "Centrifuge detection data<br/> Threshold: %s<br/> Ratio: %s", + array( + isset($centrifuge['threshold']) && $centrifuge['threshold'] ? $centrifuge['threshold'] : '---', + isset($centrifuge['ratio']) && $centrifuge['ratio'] ? $centrifuge['ratio'] : '---' + ) + ); + if (isset($centrifuge['converted'])) { + $output .= '<br/> Converted: ' . $centrifuge['converted']; + } + $output .= "<br/><br/>\n"; + } + } + + return $output; + } +} diff --git a/interface/lib/classes/IDS/Version.php b/interface/lib/classes/IDS/Version.php new file mode 100644 index 0000000..b32438f --- /dev/null +++ b/interface/lib/classes/IDS/Version.php @@ -0,0 +1,49 @@ +<?php +/** + * PHPIDS + * + * Requirements: PHP5, SimpleXML + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. + * + * PHP version 5.1.6+ + * + * @category Security + * @package PHPIDS + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Christian Matthies <ch0012@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +namespace IDS; + +/** + * PHPIDS version class + * + * @category Security + * @package PHPIDS + * @author Christian Matthies <ch0012@gmail.com> + * @author Mario Heiderich <mario.heiderich@gmail.com> + * @author Lars Strojny <lars@strojny.net> + * @copyright 2007-2009 The PHPIDS Group + * @license http://www.gnu.org/licenses/lgpl.html LGPL + * @link http://php-ids.org/ + */ +abstract class Version +{ + const VERSION = 'master'; +} diff --git a/interface/lib/classes/IDS/default_filter.json b/interface/lib/classes/IDS/default_filter.json new file mode 100644 index 0000000..c158ded --- /dev/null +++ b/interface/lib/classes/IDS/default_filter.json @@ -0,0 +1,933 @@ +{ + "filters":{ + "filter":[ + { + "id":"1", + "rule":"(?:\"[^\"]*[^-]?>)|(?:[^\\w\\s]\\s*\\\/>)|(?:>\")", + "description":"Finds html breaking injections including whitespace attacks", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"2", + "rule":"(?:\"+.*[<=]\\s*\"[^\"]+\")|(?:\"\\s*\\w+\\s*=)|(?:>\\w=\\\/)|(?:#.+\\)[\"\\s]*>)|(?:\"\\s*(?:src|style|on\\w+)\\s*=\\s*\")|(?:[^\"]?\"[,;\\s]+\\w*[\\[\\(])", + "description":"Finds attribute breaking injections including whitespace attacks", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"3", + "rule":"(?:^>[\\w\\s]*<\\\/?\\w{2,}>)", + "description":"Finds unquoted attribute breaking injections", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"2" + }, + { + "id":"4", + "rule":"(?:[+\\\/]\\s*name[\\W\\d]*[)+])|(?:;\\W*url\\s*=)|(?:[^\\w\\s\\\/?:>]\\s*(?:location|referrer|name)\\s*[^\\\/\\w\\s-])", + "description":"Detects url-, name-, JSON, and referrer-contained payload attacks", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"5", + "rule":"(?:\\W\\s*hash\\s*[^\\w\\s-])|(?:\\w+=\\W*[^,]*,[^\\s(]\\s*\\()|(?:\\?\"[^\\s\"]\":)|(?:(?<!\\\/)__[a-z]+__)|(?:(?:^|[\\s)\\]\\}])(?:s|g)etter\\s*=)", + "description":"Detects hash-contained xss payload attacks, setter usage and property overloading", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"6", + "rule":"(?:with\\s*\\(\\s*.+\\s*\\)\\s*\\w+\\s*\\()|(?:(?:do|while|for)\\s*\\([^)]*\\)\\s*\\{)|(?:\\\/[\\w\\s]*\\[\\W*\\w)", + "description":"Detects self contained xss via with(), common loops and regex to string conversion", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"7", + "rule":"(?:[=(].+\\?.+:)|(?:with\\([^)]*\\)\\))|(?:\\.\\s*source\\W)", + "description":"Detects JavaScript with(), ternary operators and XML predicate attacks", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"8", + "rule":"(?:\\\/\\w*\\s*\\)\\s*\\()|(?:\\([\\w\\s]+\\([\\w\\s]+\\)[\\w\\s]+\\))|(?:(?<!(?:mozilla\\\/\\d\\.\\d\\s))\\([^)[]+\\[[^\\]]+\\][^)]*\\))|(?:[^\\s!][{([][^({[]+[{([][^}\\])]+[}\\])][\\s+\",\\d]*[}\\])])|(?:\"\\)?\\]\\W*\\[)|(?:=\\s*[^\\s:;]+\\s*[{([][^}\\])]+[}\\])];)", + "description":"Detects self-executing JavaScript functions", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"9", + "rule":"(?:\\\\u00[a-f0-9]{2})|(?:\\\\x0*[a-f0-9]{2})|(?:\\\\\\d{2,3})", + "description":"Detects the IE octal, hex and unicode entities", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"2" + }, + { + "id":"10", + "rule":"(?:(?:\\\/|\\\\)?\\.+(\\\/|\\\\)(?:\\.+)?)|(?:\\w+\\.exe\\??\\s)|(?:;\\s*\\w+\\s*\\\/[\\w*-]+\\\/)|(?:\\d\\.\\dx\\|)|(?:%(?:c0\\.|af\\.|5c\\.))|(?:\\\/(?:%2e){2})", + "description":"Detects basic directory traversal", + "tags":{ + "tag":[ + "dt", + "id", + "lfi" + ] + }, + "impact":"5" + }, + { + "id":"11", + "rule":"(?:%c0%ae\\\/)|(?:(?:\\\/|\\\\)(home|conf|usr|etc|proc|opt|s?bin|local|dev|tmp|kern|[br]oot|sys|system|windows|winnt|program|%[a-z_-]{3,}%)(?:\\\/|\\\\))|(?:(?:\\\/|\\\\)inetpub|localstart\\.asp|boot\\.ini)", + "description":"Detects specific directory and path traversal", + "tags":{ + "tag":[ + "dt", + "id", + "lfi" + ] + }, + "impact":"5" + }, + { + "id":"12", + "rule":"(?:etc\\\/\\W*passwd)", + "description":"Detects etc\/passwd inclusion attempts", + "tags":{ + "tag":[ + "dt", + "id", + "lfi" + ] + }, + "impact":"5" + }, + { + "id":"13", + "rule":"(?:%u(?:ff|00|e\\d)\\w\\w)|(?:(?:%(?:e\\w|c[^3\\W]|))(?:%\\w\\w)(?:%\\w\\w)?)", + "description":"Detects halfwidth\/fullwidth encoded unicode HTML breaking attempts", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"3" + }, + { + "id":"14", + "rule":"(?:#@~\\^\\w+)|(?:\\w+script:|@import[^\\w]|;base64|base64,)|(?:\\w\\s*\\([\\w\\s]+,[\\w\\s]+,[\\w\\s]+,[\\w\\s]+,[\\w\\s]+,[\\w\\s]+\\))", + "description":"Detects possible includes, VBSCript\/JScript encodeed and packed functions", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"5" + }, + { + "id":"15", + "rule":"([^*:\\s\\w,.\\\/?+-]\\s*)?(?<![a-z]\\s)(?<![a-z\\\/_@\\-\\|])(\\s*return\\s*)?(?:create(?:element|attribute|textnode)|[a-z]+events?|setattribute|getelement\\w+|appendchild|createrange|createcontextualfragment|removenode|parentnode|decodeuricomponent|\\wettimeout|(?:ms)?setimmediate|option|useragent)(?(1)[^\\w%\"]|(?:\\s*[^@\\s\\w%\",.+\\-]))", + "description":"Detects JavaScript DOM\/miscellaneous properties and methods", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"6" + }, + { + "id":"16", + "rule":"([^*\\s\\w,.\\\/?+-]\\s*)?(?<![a-mo-z]\\s)(?<![a-z\\\/_@])(\\s*return\\s*)?(?:alert|inputbox|showmod(?:al|eless)dialog|showhelp|infinity|isnan|isnull|iterator|msgbox|executeglobal|expression|prompt|write(?:ln)?|confirm|dialog|urn|(?:un)?eval|exec|execscript|tostring|status|execute|window|unescape|navigate|jquery|getscript|extend|prototype)(?(1)[^\\w%\"]|(?:\\s*[^@\\s\\w%\",.:\\\/+\\-]))", + "description":"Detects possible includes and typical script methods", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"5" + }, + { + "id":"17", + "rule":"([^*:\\s\\w,.\\\/?+-]\\s*)?(?<![a-z]\\s)(?<![a-z\\\/_@])(\\s*return\\s*)?(?:hash|name|href|navigateandfind|source|pathname|close|constructor|port|protocol|assign|replace|back|forward|document|ownerdocument|window|top|this|self|parent|frames|_?content|date|cookie|innerhtml|innertext|csstext+?|outerhtml|print|moveby|resizeto|createstylesheet|stylesheets)(?(1)[^\\w%\"]|(?:\\s*[^@\\\/\\s\\w%.+\\-]))", + "description":"Detects JavaScript object properties and methods", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"18", + "rule":"([^*:\\s\\w,.\\\/?+-]\\s*)?(?<![a-z]\\s)(?<![a-z\\\/_@\\-\\|])(\\s*return\\s*)?(?:join|pop|push|reverse|reduce|concat|map|shift|sp?lice|sort|unshift)(?(1)[^\\w%\"]|(?:\\s*[^@\\s\\w%,.+\\-]))", + "description":"Detects JavaScript array properties and methods", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"19", + "rule":"([^*:\\s\\w,.\\\/?+-]\\s*)?(?<![a-z]\\s)(?<![a-z\\\/_@\\-\\|])(\\s*return\\s*)?(?:set|atob|btoa|charat|charcodeat|charset|concat|crypto|frames|fromcharcode|indexof|lastindexof|match|navigator|toolbar|menubar|replace|regexp|slice|split|substr|substring|escape|\\w+codeuri\\w*)(?(1)[^\\w%\"]|(?:\\s*[^@\\s\\w%,.+\\-]))", + "description":"Detects JavaScript string properties and methods", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"20", + "rule":"(?:\\)\\s*\\[)|([^*\":\\s\\w,.\\\/?+-]\\s*)?(?<![a-z]\\s)(?<![a-z_@\\|])(\\s*return\\s*)?(?:globalstorage|sessionstorage|postmessage|callee|constructor|content|domain|prototype|try|catch|top|call|apply|url|function|object|array|string|math|if|for\\s*(?:each)?|elseif|case|switch|regex|boolean|location|(?:ms)?setimmediate|settimeout|setinterval|void|setexpression|namespace|while)(?(1)[^\\w%\"]|(?:\\s*[^@\\s\\w%\".+\\-\\\/]))", + "description":"Detects JavaScript language constructs", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"21", + "rule":"(?:,\\s*(?:alert|showmodaldialog|eval)\\s*,)|(?::\\s*eval\\s*[^\\s])|([^:\\s\\w,.\\\/?+-]\\s*)?(?<![a-z\\\/_@])(\\s*return\\s*)?(?:(?:document\\s*\\.)?(?:.+\\\/)?(?:alert|eval|msgbox|showmod(?:al|eless)dialog|showhelp|prompt|write(?:ln)?|confirm|dialog|open))\\s*(?:[^.a-z\\s\\-]|(?:\\s*[^\\s\\w,.@\\\/+-]))|(?:java[\\s\\\/]*\\.[\\s\\\/]*lang)|(?:\\w\\s*=\\s*new\\s+\\w+)|(?:&\\s*\\w+\\s*\\)[^,])|(?:\\+[\\W\\d]*new\\s+\\w+[\\W\\d]*\\+)|(?:document\\.\\w)", + "description":"Detects very basic XSS probings", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"3" + }, + { + "id":"22", + "rule":"(?:=\\s*(?:top|this|window|content|self|frames|_content))|(?:\\\/\\s*[gimx]*\\s*[)}])|(?:[^\\s]\\s*=\\s*script)|(?:\\.\\s*constructor)|(?:default\\s+xml\\s+namespace\\s*=)|(?:\\\/\\s*\\+[^+]+\\s*\\+\\s*\\\/)", + "description":"Detects advanced XSS probings via Script(), RexExp, constructors and XML namespaces", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"5" + }, + { + "id":"23", + "rule":"(?:\\.\\s*\\w+\\W*=)|(?:\\W\\s*(?:location|document)\\s*\\W[^({[;]+[({[;])|(?:\\(\\w+\\?[:\\w]+\\))|(?:\\w{2,}\\s*=\\s*\\d+[^&\\w]\\w+)|(?:\\]\\s*\\(\\s*\\w+)", + "description":"Detects JavaScript location\/document property access and window access obfuscation", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"24", + "rule":"(?:[\".]script\\s*\\()|(?:\\$\\$?\\s*\\(\\s*[\\w\"])|(?:\\\/[\\w\\s]+\\\/\\.)|(?:=\\s*\\\/\\w+\\\/\\s*\\.)|(?:(?:this|window|top|parent|frames|self|content)\\[\\s*[(,\"]*\\s*[\\w\\$])|(?:,\\s*new\\s+\\w+\\s*[,;)])", + "description":"Detects basic obfuscated JavaScript script injections", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"25", + "rule":"(?:=\\s*[$\\w]\\s*[\\(\\[])|(?:\\(\\s*(?:this|top|window|self|parent|_?content)\\s*\\))|(?:src\\s*=s*(?:\\w+:|\\\/\\\/))|(?:\\w+\\[(\"\\w+\"|\\w+\\|\\|))|(?:[\\d\\W]\\|\\|[\\d\\W]|\\W=\\w+,)|(?:\\\/\\s*\\+\\s*[a-z\"])|(?:=\\s*\\$[^([]*\\()|(?:=\\s*\\(\\s*\")", + "description":"Detects obfuscated JavaScript script injections", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"26", + "rule":"(?:[^:\\s\\w]+\\s*[^\\w\\\/](href|protocol|host|hostname|pathname|hash|port|cookie)[^\\w])", + "description":"Detects JavaScript cookie stealing and redirection attempts", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"27", + "rule":"(?:(?:vbs|vbscript|data):.*[,+])|(?:\\w+\\s*=\\W*(?!https?)\\w+:)|(jar:\\w+:)|(=\\s*\"?\\s*vbs(?:ript)?:)|(language\\s*=\\s?\"?\\s*vbs(?:ript)?)|on\\w+\\s*=\\*\\w+\\-\"?", + "description":"Detects data: URL injections, VBS injections and common URI schemes", + "tags":{ + "tag":[ + "xss", + "rfe" + ] + }, + "impact":"5" + }, + { + "id":"28", + "rule":"(?:firefoxurl:\\w+\\|)|(?:(?:file|res|telnet|nntp|news|mailto|chrome)\\s*:\\s*[%&#xu\\\/]+)|(wyciwyg|firefoxurl\\s*:\\s*\\\/\\s*\\\/)", + "description":"Detects IE firefoxurl injections, cache poisoning attempts and local file inclusion\/execution", + "tags":{ + "tag":[ + "xss", + "rfe", + "lfi", + "csrf" + ] + }, + "impact":"5" + }, + { + "id":"29", + "rule":"(?:binding\\s?=|moz-binding|behavior\\s?=)|(?:[\\s\\\/]style\\s*=\\s*[-\\\\])", + "description":"Detects bindings and behavior injections", + "tags":{ + "tag":[ + "xss", + "csrf", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"30", + "rule":"(?:=\\s*\\w+\\s*\\+\\s*\")|(?:\\+=\\s*\\(\\s\")|(?:!+\\s*[\\d.,]+\\w?\\d*\\s*\\?)|(?:=\\s*\\[s*\\])|(?:\"\\s*\\+\\s*\")|(?:[^\\s]\\[\\s*\\d+\\s*\\]\\s*[;+])|(?:\"\\s*[&|]+\\s*\")|(?:\\\/\\s*\\?\\s*\")|(?:\\\/\\s*\\)\\s*\\[)|(?:\\d\\?.+:\\d)|(?:]\\s*\\[\\W*\\w)|(?:[^\\s]\\s*=\\s*\\\/)", + "description":"Detects common XSS concatenation patterns 1\/2", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"31", + "rule":"(?:=\\s*\\d*\\.\\d*\\?\\d*\\.\\d*)|(?:[|&]{2,}\\s*\")|(?:!\\d+\\.\\d*\\?\")|(?:\\\/:[\\w.]+,)|(?:=[\\d\\W\\s]*\\[[^]]+\\])|(?:\\?\\w+:\\w+)", + "description":"Detects common XSS concatenation patterns 2\/2", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe" + ] + }, + "impact":"4" + }, + { + "id":"32", + "rule":"(?:[^\\w\\s=]on(?!g\\>)\\w+[^=_+-]*=[^$]+(?:\\W|\\>)?)", + "description":"Detects possible event handlers", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"33", + "rule":"(?:\\<\\w*:?\\s(?:[^\\>]*)t(?!rong))|(?:\\<scri)|(<\\w+:\\w+)", + "description":"Detects obfuscated script tags and XML wrapped HTML", + "tags":{ + "tag":"xss" + }, + "impact":"4" + }, + { + "id":"34", + "rule":"(?:\\<\\\/\\w+\\s\\w+)|(?:@(?:cc_on|set)[\\s@,\"=])", + "description":"Detects attributes in closing tags and conditional compilation tokens", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"35", + "rule":"(?:--[^\\n]*$)|(?:\\<!-|-->)|(?:[^*]\\\/\\*|\\*\\\/[^*])|(?:(?:[\\W\\d]#|--|{)$)|(?:\\\/{3,}.*$)|(?:<!\\[\\W)|(?:\\]!>)", + "description":"Detects common comment types", + "tags":{ + "tag":[ + "xss", + "csrf", + "id" + ] + }, + "impact":"3" + }, + { + "id":"37", + "rule":"(?:\\<base\\s+)|(?:<!(?:element|entity|\\[CDATA))", + "description":"Detects base href injections and XML entity injections", + "tags":{ + "tag":[ + "xss", + "csrf", + "id" + ] + }, + "impact":"5" + }, + { + "id":"38", + "rule":"(?:\\<[\\\/]?(?:[i]?frame|applet|isindex|marquee|keygen|script|audio|video|input|button|textarea|style|base|body|meta|link|object|embed|param|plaintext|xm\\w+|image|im(?:g|port)))", + "description":"Detects possibly malicious html elements including some attributes", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe", + "lfi" + ] + }, + "impact":"4" + }, + { + "id":"39", + "rule":"(?:\\\\x[01fe][\\db-ce-f])|(?:%[01fe][\\db-ce-f])|(?:&#[01fe][\\db-ce-f])|(?:\\\\[01fe][\\db-ce-f])|(?:&#x[01fe][\\db-ce-f])", + "description":"Detects nullbytes and other dangerous characters", + "tags":{ + "tag":[ + "id", + "rfe", + "xss" + ] + }, + "impact":"5" + }, + { + "id":"40", + "rule":"(?:\\)\\s*when\\s*\\d+\\s*then)|(?:\"\\s*(?:#|--|{))|(?:\\\/\\*!\\s?\\d+)|(?:ch(?:a)?r\\s*\\(\\s*\\d)|(?:(?:(n?and|x?or|not)\\s+|\\|\\||\\&\\&)\\s*\\w+\\()", + "description":"Detects MySQL comments, conditions and ch(a)r injections", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"6" + }, + { + "id":"41", + "rule":"(?:[\\s()]case\\s*\\()|(?:\\)\\s*like\\s*\\()|(?:having\\s*[^\\s]+\\s*[^\\w\\s])|(?:if\\s?\\([\\d\\w]\\s*[=<>~])", + "description":"Detects conditional SQL injection attempts", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"6" + }, + { + "id":"42", + "rule":"(?:\"\\s*or\\s*\"?\\d)|(?:\\\\x(?:23|27|3d))|(?:^.?\"$)|(?:(?:^[\"\\\\]*(?:[\\d\"]+|[^\"]+\"))+\\s*(?:n?and|x?or|not|\\|\\||\\&\\&)\\s*[\\w\"[+&!@(),.-])|(?:[^\\w\\s]\\w+\\s*[|-]\\s*\"\\s*\\w)|(?:@\\w+\\s+(and|or)\\s*[\"\\d]+)|(?:@[\\w-]+\\s(and|or)\\s*[^\\w\\s])|(?:[^\\w\\s:]\\s*\\d\\W+[^\\w\\s]\\s*\".)|(?:\\Winformation_schema|table_name\\W)", + "description":"Detects classic SQL injection probings 1\/2", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"6" + }, + { + "id":"43", + "rule":"(?:\"\\s*\\*.+(?:or|id)\\W*\"\\d)|(?:\\^\")|(?:^[\\w\\s\"-]+(?<=and\\s)(?<=or\\s)(?<=xor\\s)(?<=nand\\s)(?<=not\\s)(?<=\\|\\|)(?<=\\&\\&)\\w+\\()|(?:\"[\\s\\d]*[^\\w\\s]+\\W*\\d\\W*.*[\"\\d])|(?:\"\\s*[^\\w\\s?]+\\s*[^\\w\\s]+\\s*\")|(?:\"\\s*[^\\w\\s]+\\s*[\\W\\d].*(?:#|--))|(?:\".*\\*\\s*\\d)|(?:\"\\s*or\\s[^\\d]+[\\w-]+.*\\d)|(?:[()*<>%+-][\\w-]+[^\\w\\s]+\"[^,])", + "description":"Detects classic SQL injection probings 2\/2", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"6" + }, + { + "id":"44", + "rule":"(?:\\d\"\\s+\"\\s+\\d)|(?:^admin\\s*\"|(\\\/\\*)+\"+\\s?(?:--|#|\\\/\\*|{)?)|(?:\"\\s*or[\\w\\s-]+\\s*[+<>=(),-]\\s*[\\d\"])|(?:\"\\s*[^\\w\\s]?=\\s*\")|(?:\"\\W*[+=]+\\W*\")|(?:\"\\s*[!=|][\\d\\s!=+-]+.*[\"(].*$)|(?:\"\\s*[!=|][\\d\\s!=]+.*\\d+$)|(?:\"\\s*like\\W+[\\w\"(])|(?:\\sis\\s*0\\W)|(?:where\\s[\\s\\w\\.,-]+\\s=)|(?:\"[<>~]+\")", + "description":"Detects basic SQL authentication bypass attempts 1\/3", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"45", + "rule":"(?:union\\s*(?:all|distinct|[(!@]*)\\s*[([]*\\s*select)|(?:\\w+\\s+like\\s+\\\")|(?:like\\s*\"\\%)|(?:\"\\s*like\\W*[\"\\d])|(?:\"\\s*(?:n?and|x?or|not |\\|\\||\\&\\&)\\s+[\\s\\w]+=\\s*\\w+\\s*having)|(?:\"\\s*\\*\\s*\\w+\\W+\")|(?:\"\\s*[^?\\w\\s=.,;)(]+\\s*[(@\"]*\\s*\\w+\\W+\\w)|(?:select\\s*[\\[\\]()\\s\\w\\.,\"-]+from)|(?:find_in_set\\s*\\()", + "description":"Detects basic SQL authentication bypass attempts 2\/3", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"46", + "rule":"(?:in\\s*\\(+\\s*select)|(?:(?:n?and|x?or|not |\\|\\||\\&\\&)\\s+[\\s\\w+]+(?:regexp\\s*\\(|sounds\\s+like\\s*\"|[=\\d]+x))|(\"\\s*\\d\\s*(?:--|#))|(?:\"[%&<>^=]+\\d\\s*(=|or))|(?:\"\\W+[\\w+-]+\\s*=\\s*\\d\\W+\")|(?:\"\\s*is\\s*\\d.+\"?\\w)|(?:\"\\|?[\\w-]{3,}[^\\w\\s.,]+\")|(?:\"\\s*is\\s*[\\d.]+\\s*\\W.*\")", + "description":"Detects basic SQL authentication bypass attempts 3\/3", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"47", + "rule":"(?:[\\d\\W]\\s+as\\s*[\"\\w]+\\s*from)|(?:^[\\W\\d]+\\s*(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc))|(?:(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\\s+(?:(?:group_)concat|char|load_file)\\s?\\(?)|(?:end\\s*\\);)|(\"\\s+regexp\\W)|(?:[\\s(]load_file\\s*\\()", + "description":"Detects concatenated basic SQL injection and SQLLFI attempts", + "tags":{ + "tag":[ + "sqli", + "id", + "lfi" + ] + }, + "impact":"5" + }, + { + "id":"48", + "rule":"(?:@.+=\\s*\\(\\s*select)|(?:\\d+\\s*or\\s*\\d+\\s*[\\-+])|(?:\\\/\\w+;?\\s+(?:having|and|or|select)\\W)|(?:\\d\\s+group\\s+by.+\\()|(?:(?:;|#|--)\\s*(?:drop|alter))|(?:(?:;|#|--)\\s*(?:update|insert)\\s*\\w{2,})|(?:[^\\w]SET\\s*@\\w+)|(?:(?:n?and|x?or|not |\\|\\||\\&\\&)[\\s(]+\\w+[\\s)]*[!=+]+[\\s\\d]*[\"=()])", + "description":"Detects chained SQL injection attempts 1\/2", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"6" + }, + { + "id":"49", + "rule":"(?:\"\\s+and\\s*=\\W)|(?:\\(\\s*select\\s*\\w+\\s*\\()|(?:\\*\\\/from)|(?:\\+\\s*\\d+\\s*\\+\\s*@)|(?:\\w\"\\s*(?:[-+=|@]+\\s*)+[\\d(])|(?:coalesce\\s*\\(|@@\\w+\\s*[^\\w\\s])|(?:\\W!+\"\\w)|(?:\";\\s*(?:if|while|begin))|(?:\"[\\s\\d]+=\\s*\\d)|(?:order\\s+by\\s+if\\w*\\s*\\()|(?:[\\s(]+case\\d*\\W.+[tw]hen[\\s(])", + "description":"Detects chained SQL injection attempts 2\/2", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"6" + }, + { + "id":"50", + "rule":"(?:(select|;)\\s+(?:benchmark|if|sleep)\\s*?\\(\\s*\\(?\\s*\\w+)", + "description":"Detects SQL benchmark and sleep injection attempts including conditional queries", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"4" + }, + { + "id":"51", + "rule":"(?:create\\s+function\\s+\\w+\\s+returns)|(?:;\\s*(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\\s*[\\[(]?\\w{2,})", + "description":"Detects MySQL UDF injection and other data\/structure manipulation attempts", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"6" + }, + { + "id":"52", + "rule":"(?:alter\\s*\\w+.*character\\s+set\\s+\\w+)|(\";\\s*waitfor\\s+time\\s+\")|(?:\";.*:\\s*goto)", + "description":"Detects MySQL charset switch and MSSQL DoS attempts", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"6" + }, + { + "id":"53", + "rule":"(?:procedure\\s+analyse\\s*\\()|(?:;\\s*(declare|open)\\s+[\\w-]+)|(?:create\\s+(procedure|function)\\s*\\w+\\s*\\(\\s*\\)\\s*-)|(?:declare[^\\w]+[@#]\\s*\\w+)|(exec\\s*\\(\\s*@)", + "description":"Detects MySQL and PostgreSQL stored procedure\/function injections", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"7" + }, + { + "id":"54", + "rule":"(?:select\\s*pg_sleep)|(?:waitfor\\s*delay\\s?\"+\\s?\\d)|(?:;\\s*shutdown\\s*(?:;|--|#|\\\/\\*|{))", + "description":"Detects Postgres pg_sleep injection, waitfor delay attacks and database shutdown attempts", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"5" + }, + { + "id":"55", + "rule":"(?:\\sexec\\s+xp_cmdshell)|(?:\"\\s*!\\s*[\"\\w])|(?:from\\W+information_schema\\W)|(?:(?:(?:current_)?user|database|schema|connection_id)\\s*\\([^\\)]*)|(?:\";?\\s*(?:select|union|having)\\s*[^\\s])|(?:\\wiif\\s*\\()|(?:exec\\s+master\\.)|(?:union select @)|(?:union[\\w(\\s]*select)|(?:select.*\\w?user\\()|(?:into[\\s+]+(?:dump|out)file\\s*\")", + "description":"Detects MSSQL code execution and information gathering attempts", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"5" + }, + { + "id":"56", + "rule":"(?:merge.*using\\s*\\()|(execute\\s*immediate\\s*\")|(?:\\W+\\d*\\s*having\\s*[^\\s\\-])|(?:match\\s*[\\w(),+-]+\\s*against\\s*\\()", + "description":"Detects MATCH AGAINST, MERGE, EXECUTE IMMEDIATE and HAVING injections", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"5" + }, + { + "id":"57", + "rule":"(?:,.*[)\\da-f\"]\"(?:\".*\"|\\Z|[^\"]+))|(?:\\Wselect.+\\W*from)|((?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\\s*\\(\\s*space\\s*\\()", + "description":"Detects MySQL comment-\/space-obfuscated injections and backtick termination", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"5" + }, + { + "id":"58", + "rule":"(?:@[\\w-]+\\s*\\()|(?:]\\s*\\(\\s*[\"!]\\s*\\w)|(?:<[?%](?:php)?.*(?:[?%]>)?)|(?:;[\\s\\w|]*\\$\\w+\\s*=)|(?:\\$\\w+\\s*=(?:(?:\\s*\\$?\\w+\\s*[(;])|\\s*\".*\"))|(?:;\\s*\\{\\W*\\w+\\s*\\()", + "description":"Detects code injection attempts 1\/3", + "tags":{ + "tag":[ + "id", + "rfe", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"59", + "rule":"(?:(?:[;]+|(<[?%](?:php)?)).*(?:define|eval|file_get_contents|include|require|require_once|set|shell_exec|phpinfo|system|passthru|preg_\\w+|execute)\\s*[\"(@])", + "description":"Detects code injection attempts 2\/3", + "tags":{ + "tag":[ + "id", + "rfe", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"60", + "rule":"(?:(?:[;]+|(<[?%](?:php)?)).*[^\\w](?:echo|print|print_r|var_dump|[fp]open))|(?:;\\s*rm\\s+-\\w+\\s+)|(?:;.*{.*\\$\\w+\\s*=)|(?:\\$\\w+\\s*\\[\\]\\s*=\\s*)", + "description":"Detects code injection attempts 3\/3", + "tags":{ + "tag":[ + "id", + "rfe", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"62", + "rule":"(?:function[^(]*\\([^)]*\\))|(?:(?:delete|void|throw|instanceof|new|typeof)[^\\w.]+\\w+\\s*[([])|([)\\]]\\s*\\.\\s*\\w+\\s*=)|(?:\\(\\s*new\\s+\\w+\\s*\\)\\.)", + "description":"Detects common function declarations and special JS operators", + "tags":{ + "tag":[ + "id", + "rfe", + "lfi" + ] + }, + "impact":"5" + }, + { + "id":"63", + "rule":"(?:[\\w.-]+@[\\w.-]+%(?:[01][\\db-ce-f])+\\w+:)", + "description":"Detects common mail header injections", + "tags":{ + "tag":[ + "id", + "spam" + ] + }, + "impact":"5" + }, + { + "id":"64", + "rule":"(?:\\.pl\\?\\w+=\\w?\\|\\w+;)|(?:\\|\\(\\w+=\\*)|(?:\\*\\s*\\)+\\s*;)", + "description":"Detects perl echo shellcode injection and LDAP vectors", + "tags":{ + "tag":[ + "lfi", + "rfe" + ] + }, + "impact":"5" + }, + { + "id":"65", + "rule":"(?:(^|\\W)const\\s+[\\w\\-]+\\s*=)|(?:(?:do|for|while)\\s*\\([^;]+;+\\))|(?:(?:^|\\W)on\\w+\\s*=[\\w\\W]*(?:on\\w+|alert|eval|print|confirm|prompt))|(?:groups=\\d+\\(\\w+\\))|(?:(.)\\1{128,})", + "description":"Detects basic XSS DoS attempts", + "tags":{ + "tag":[ + "rfe", + "dos" + ] + }, + "impact":"5" + }, + { + "id":"67", + "rule":"(?:\\({2,}\\+{2,}:{2,})|(?:\\({2,}\\+{2,}:+)|(?:\\({3,}\\++:{2,})|(?:\\$\\[!!!\\])", + "description":"Detects unknown attack vectors based on PHPIDS Centrifuge detection", + "tags":{ + "tag":[ + "xss", + "csrf", + "id", + "rfe", + "lfi" + ] + }, + "impact":"7" + }, + { + "id":"68", + "rule":"(?:[\\s\\\/\"]+[-\\w\\\/\\\\\\*]+\\s*=.+(?:\\\/\\s*>))", + "description":"Finds attribute breaking injections including obfuscated attributes", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"69", + "rule":"(?:(?:msgbox|eval)\\s*\\+|(?:language\\s*=\\*vbscript))", + "description":"Finds basic VBScript injection attempts", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"4" + }, + { + "id":"70", + "rule":"(?:\\[\\$(?:ne|eq|lte?|gte?|n?in|mod|all|size|exists|type|slice|or)\\])", + "description":"Finds basic MongoDB SQL injection attempts", + "tags":{ + "tag":"sqli" + }, + "impact":"4" + }, + { + "id":"71", + "rule":"(?:[\\s\\d\\\/\"]+(?:on\\w+|style|poster|background)=[$\"\\w])|(?:-type\\s*:\\s*multipart)", + "description":"Finds malicious attribute injection attempts and MHTML attacks", + "tags":{ + "tag":[ + "xss", + "csrf" + ] + }, + "impact":"6" + }, + { + "id":"72", + "rule":"(?:(sleep\\((\\s*)(\\d*)(\\s*)\\)|benchmark\\((.*)\\,(.*)\\)))", + "description":"Detects blind sqli tests using sleep() or benchmark().", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"4" + }, + { + "id":"73", + "rule":"(?:(\\%SYSTEMROOT\\%))", + "description":"An attacker is trying to locate a file to read or write.", + "tags":{ + "tag":[ + "files", + "id" + ] + }, + "impact":"4" + }, + { + "id":"75", + "rule":"(?:(((.*)\\%[c|d|i|e|f|g|o|s|u|x|p|n]){8}))", + "description":"Looking for a format string attack", + "tags":{ + "tag":"format string" + }, + "impact":"4" + }, + { + "id":"76", + "rule":"(?:(union(.*)select(.*)from))", + "description":"Looking for basic sql injection. Common attack string for mysql, oracle and others.", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"3" + }, + { + "id":"77", + "rule":"(?:^(-0000023456|4294967295|4294967296|2147483648|2147483647|0000012345|-2147483648|-2147483649|0000023456|2.2250738585072007e-308|1e309)$)", + "description":"Looking for integer overflow attacks, these are taken from skipfish, except 2.2250738585072007e-308 is the \"magic number\" crash", + "tags":{ + "tag":[ + "sqli", + "id" + ] + }, + "impact":"3" + }, + { + "id":"78", + "rule":"(?:%23.*?%0a)", + "description":"Detects SQL comment filter evasion", + "tags":{ + "tag":[ + "format string" + ] + }, + "impact":"4" + } + ] + } +} diff --git a/interface/lib/classes/IDS/default_filter.xml b/interface/lib/classes/IDS/default_filter.xml new file mode 100644 index 0000000..fdd9cef --- /dev/null +++ b/interface/lib/classes/IDS/default_filter.xml @@ -0,0 +1,787 @@ +<filters> + <filter> + <id>1</id> + <rule><![CDATA[(?:"[^"]*[^-]?>)|(?:[^\w\s]\s*\/>)|(?:>")]]></rule> + <description>Finds html breaking injections including whitespace attacks</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>2</id> + <rule><![CDATA[(?:"+.*[<=]\s*"[^"]+")|(?:"\s*\w+\s*=)|(?:>\w=\/)|(?:#.+\)["\s]*>)|(?:"\s*(?:src|style|on\w+)\s*=\s*")|(?:[^"]?"[,;\s]+\w*[\[\(])]]></rule> + <description>Finds attribute breaking injections including whitespace attacks</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>3</id> + <rule><![CDATA[(?:^>[\w\s]*<\/?\w{2,}>)]]></rule> + <description>Finds unquoted attribute breaking injections</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>2</impact> + </filter> + <filter> + <id>4</id> + <rule><![CDATA[(?:[+\/]\s*name[\W\d]*[)+])|(?:;\W*url\s*=)|(?:[^\w\s\/?:>]\s*(?:location|referrer|name)\s*[^\/\w\s-])]]></rule> + <description>Detects url-, name-, JSON, and referrer-contained payload attacks</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>5</id> + <rule><![CDATA[(?:\W\s*hash\s*[^\w\s-])|(?:\w+=\W*[^,]*,[^\s(]\s*\()|(?:\?"[^\s"]":)|(?:(?<!\/)__[a-z]+__)|(?:(?:^|[\s)\]\}])(?:s|g)etter\s*=)]]></rule> + <description>Detects hash-contained xss payload attacks, setter usage and property overloading</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>6</id> + <rule><![CDATA[(?:with\s*\(\s*.+\s*\)\s*\w+\s*\()|(?:(?:do|while|for)\s*\([^)]*\)\s*\{)|(?:\/[\w\s]*\[\W*\w)]]></rule> + <description>Detects self contained xss via with(), common loops and regex to string conversion</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>7</id> + <rule><![CDATA[(?:[=(].+\?.+:)|(?:with\([^)]*\)\))|(?:\.\s*source\W)]]></rule> + <description>Detects JavaScript with(), ternary operators and XML predicate attacks</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>8</id> + <rule><![CDATA[(?:\/\w*\s*\)\s*\()|(?:\([\w\s]+\([\w\s]+\)[\w\s]+\))|(?:(?<!(?:mozilla\/\d\.\d\s))\([^)[]+\[[^\]]+\][^)]*\))|(?:[^\s!][{([][^({[]+[{([][^}\])]+[}\])][\s+",\d]*[}\])])|(?:"\)?\]\W*\[)|(?:=\s*[^\s:;]+\s*[{([][^}\])]+[}\])];)]]></rule> + <description>Detects self-executing JavaScript functions</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>9</id> + <rule><![CDATA[(?:\\u00[a-f0-9]{2})|(?:\\x0*[a-f0-9]{2})|(?:\\\d{2,3})]]></rule> + <description>Detects the IE octal, hex and unicode entities</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>2</impact> + </filter> + <filter> + <id>10</id> + <rule><![CDATA[(?:(?:\/|\\)?\.+(\/|\\)(?:\.+)?)|(?:\w+\.exe\??\s)|(?:;\s*\w+\s*\/[\w*-]+\/)|(?:\d\.\dx\|)|(?:%(?:c0\.|af\.|5c\.))|(?:\/(?:%2e){2})]]></rule> + <description>Detects basic directory traversal</description> + <tags> + <tag>dt</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>11</id> + <rule><![CDATA[(?:%c0%ae\/)|(?:(?:\/|\\)(home|conf|usr|etc|proc|opt|s?bin|local|dev|tmp|kern|[br]oot|sys|system|windows|winnt|program|%[a-z_-]{3,}%)(?:\/|\\))|(?:(?:\/|\\)inetpub|localstart\.asp|boot\.ini)]]></rule> + <description>Detects specific directory and path traversal</description> + <tags> + <tag>dt</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>12</id> + <rule><![CDATA[(?:etc\/\W*passwd)]]></rule> + <description>Detects etc/passwd inclusion attempts</description> + <tags> + <tag>dt</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>13</id> + <rule><![CDATA[(?:%u(?:ff|00|e\d)\w\w)|(?:(?:%(?:e\w|c[^3\W]|))(?:%\w\w)(?:%\w\w)?)]]></rule> + <description>Detects halfwidth/fullwidth encoded unicode HTML breaking attempts</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>3</impact> + </filter> + <filter> + <id>14</id> + <rule><![CDATA[(?:#@~\^\w+)|(?:\w+script:|@import[^\w]|;base64|base64,)|(?:\w\s*\([\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+\))]]></rule> + <description>Detects possible includes, VBSCript/JScript encodeed and packed functions</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>15</id> + <rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:create(?:element|attribute|textnode)|[a-z]+events?|setattribute|getelement\w+|appendchild|createrange|createcontextualfragment|removenode|parentnode|decodeuricomponent|\wettimeout|(?:ms)?setimmediate|option|useragent)(?(1)[^\w%"]|(?:\s*[^@\s\w%",.+\-]))]]></rule> + <description>Detects JavaScript DOM/miscellaneous properties and methods</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>16</id> + <rule><![CDATA[([^*\s\w,.\/?+-]\s*)?(?<![a-mo-z]\s)(?<![a-z\/_@])(\s*return\s*)?(?:alert|inputbox|showmod(?:al|eless)dialog|showhelp|infinity|isnan|isnull|iterator|msgbox|executeglobal|expression|prompt|write(?:ln)?|confirm|dialog|urn|(?:un)?eval|exec|execscript|tostring|status|execute|window|unescape|navigate|jquery|getscript|extend|prototype)(?(1)[^\w%"]|(?:\s*[^@\s\w%",.:\/+\-]))]]></rule> + <description>Detects possible includes and typical script methods</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>17</id> + <rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@])(\s*return\s*)?(?:hash|name|href|navigateandfind|source|pathname|close|constructor|port|protocol|assign|replace|back|forward|document|ownerdocument|window|top|this|self|parent|frames|_?content|date|cookie|innerhtml|innertext|csstext+?|outerhtml|print|moveby|resizeto|createstylesheet|stylesheets)(?(1)[^\w%"]|(?:\s*[^@\/\s\w%.+\-]))]]></rule> + <description>Detects JavaScript object properties and methods</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>18</id> + <rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:join|pop|push|reverse|reduce|concat|map|shift|sp?lice|sort|unshift)(?(1)[^\w%"]|(?:\s*[^@\s\w%,.+\-]))]]></rule> + <description>Detects JavaScript array properties and methods</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>19</id> + <rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:set|atob|btoa|charat|charcodeat|charset|concat|crypto|frames|fromcharcode|indexof|lastindexof|match|navigator|toolbar|menubar|replace|regexp|slice|split|substr|substring|escape|\w+codeuri\w*)(?(1)[^\w%"]|(?:\s*[^@\s\w%,.+\-]))]]></rule> + <description>Detects JavaScript string properties and methods</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>20</id> + <rule><![CDATA[(?:\)\s*\[)|([^*":\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z_@\|])(\s*return\s*)?(?:globalstorage|sessionstorage|postmessage|callee|constructor|content|domain|prototype|try|catch|top|call|apply|url|function|object|array|string|math|if|for\s*(?:each)?|elseif|case|switch|regex|boolean|location|(?:ms)?setimmediate|settimeout|setinterval|void|setexpression|namespace|while)(?(1)[^\w%"]|(?:\s*[^@\s\w%".+\-\/]))]]></rule> + <description>Detects JavaScript language constructs</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>21</id> + <rule><![CDATA[(?:,\s*(?:alert|showmodaldialog|eval)\s*,)|(?::\s*eval\s*[^\s])|([^:\s\w,.\/?+-]\s*)?(?<![a-z\/_@])(\s*return\s*)?(?:(?:document\s*\.)?(?:.+\/)?(?:alert|eval|msgbox|showmod(?:al|eless)dialog|showhelp|prompt|write(?:ln)?|confirm|dialog|open))\s*(?:[^.a-z\s\-]|(?:\s*[^\s\w,.@\/+-]))|(?:java[\s\/]*\.[\s\/]*lang)|(?:\w\s*=\s*new\s+\w+)|(?:&\s*\w+\s*\)[^,])|(?:\+[\W\d]*new\s+\w+[\W\d]*\+)|(?:document\.\w)]]></rule> + <description>Detects very basic XSS probings</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>3</impact> + </filter> + <filter> + <id>22</id> + <rule><![CDATA[(?:=\s*(?:top|this|window|content|self|frames|_content))|(?:\/\s*[gimx]*\s*[)}])|(?:[^\s]\s*=\s*script)|(?:\.\s*constructor)|(?:default\s+xml\s+namespace\s*=)|(?:\/\s*\+[^+]+\s*\+\s*\/)]]></rule> + <description>Detects advanced XSS probings via Script(), RexExp, constructors and XML namespaces</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>23</id> + <rule><![CDATA[(?:\.\s*\w+\W*=)|(?:\W\s*(?:location|document)\s*\W[^({[;]+[({[;])|(?:\(\w+\?[:\w]+\))|(?:\w{2,}\s*=\s*\d+[^&\w]\w+)|(?:\]\s*\(\s*\w+)]]></rule> + <description>Detects JavaScript location/document property access and window access obfuscation</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>24</id> + <rule><![CDATA[(?:[".]script\s*\()|(?:\$\$?\s*\(\s*[\w"])|(?:\/[\w\s]+\/\.)|(?:=\s*\/\w+\/\s*\.)|(?:(?:this|window|top|parent|frames|self|content)\[\s*[(,"]*\s*[\w\$])|(?:,\s*new\s+\w+\s*[,;)])]]></rule> + <description>Detects basic obfuscated JavaScript script injections</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>25</id> + <rule><![CDATA[(?:=\s*[$\w]\s*[\(\[])|(?:\(\s*(?:this|top|window|self|parent|_?content)\s*\))|(?:src\s*=s*(?:\w+:|\/\/))|(?:\w+\[("\w+"|\w+\|\|))|(?:[\d\W]\|\|[\d\W]|\W=\w+,)|(?:\/\s*\+\s*[a-z"])|(?:=\s*\$[^([]*\()|(?:=\s*\(\s*")]]></rule> + <description>Detects obfuscated JavaScript script injections</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>26</id> + <rule><![CDATA[(?:[^:\s\w]+\s*[^\w\/](href|protocol|host|hostname|pathname|hash|port|cookie)[^\w])]]></rule> + <description>Detects JavaScript cookie stealing and redirection attempts</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>27</id> + <rule><![CDATA[(?:(?:vbs|vbscript|data):.*[,+])|(?:\w+\s*=\W*(?!https?)\w+:)|(jar:\w+:)|(=\s*"?\s*vbs(?:ript)?:)|(language\s*=\s?"?\s*vbs(?:ript)?)|on\w+\s*=\*\w+\-"?]]></rule> + <description>Detects data: URL injections, VBS injections and common URI schemes</description> + <tags> + <tag>xss</tag> + <tag>rfe</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>28</id> + <rule><![CDATA[(?:firefoxurl:\w+\|)|(?:(?:file|res|telnet|nntp|news|mailto|chrome)\s*:\s*[%&#xu\/]+)|(wyciwyg|firefoxurl\s*:\s*\/\s*\/)]]></rule> + <description>Detects IE firefoxurl injections, cache poisoning attempts and local file inclusion/execution</description> + <tags> + <tag>xss</tag> + <tag>rfe</tag> + <tag>lfi</tag> + <tag>csrf</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>29</id> + <rule><![CDATA[(?:binding\s?=|moz-binding|behavior\s?=)|(?:[\s\/]style\s*=\s*[-\\])]]></rule> + <description>Detects bindings and behavior injections</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>30</id> + <rule><![CDATA[(?:=\s*\w+\s*\+\s*")|(?:\+=\s*\(\s")|(?:!+\s*[\d.,]+\w?\d*\s*\?)|(?:=\s*\[s*\])|(?:"\s*\+\s*")|(?:[^\s]\[\s*\d+\s*\]\s*[;+])|(?:"\s*[&|]+\s*")|(?:\/\s*\?\s*")|(?:\/\s*\)\s*\[)|(?:\d\?.+:\d)|(?:]\s*\[\W*\w)|(?:[^\s]\s*=\s*\/)]]></rule> + <description>Detects common XSS concatenation patterns 1/2</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>31</id> + <rule><![CDATA[(?:=\s*\d*\.\d*\?\d*\.\d*)|(?:[|&]{2,}\s*")|(?:!\d+\.\d*\?")|(?:\/:[\w.]+,)|(?:=[\d\W\s]*\[[^]]+\])|(?:\?\w+:\w+)]]></rule> + <description>Detects common XSS concatenation patterns 2/2</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>32</id> + <rule><![CDATA[(?:[^\w\s=]on(?!g\>)\w+[^=_+-]*=[^$]+(?:\W|\>)?)]]></rule> + <description>Detects possible event handlers</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>33</id> + <rule><![CDATA[(?:\<\w*:?\s(?:[^\>]*)t(?!rong))|(?:\<scri)|(<\w+:\w+)]]></rule> + <description>Detects obfuscated script tags and XML wrapped HTML</description> + <tags> + <tag>xss</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>34</id> + <rule><![CDATA[(?:\<\/\w+\s\w+)|(?:@(?:cc_on|set)[\s@,"=])]]></rule> + <description>Detects attributes in closing tags and conditional compilation tokens</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>35</id> + <rule><![CDATA[(?:--[^\n]*$)|(?:\<!-|-->)|(?:[^*]\/\*|\*\/[^*])|(?:(?:[\W\d]#|--|{)$)|(?:\/{3,}.*$)|(?:<!\[\W)|(?:\]!>)]]></rule> + <description>Detects common comment types</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + </tags> + <impact>3</impact> + </filter> + <filter> + <id>37</id> + <rule><![CDATA[(?:\<base\s+)|(?:<!(?:element|entity|\[CDATA))]]></rule> + <description>Detects base href injections and XML entity injections</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>38</id> + <rule><![CDATA[(?:\<[\/]?(?:[i]?frame|applet|isindex|marquee|keygen|script|audio|video|input|button|textarea|style|base|body|meta|link|object|embed|param|plaintext|xm\w+|image|im(?:g|port)))]]></rule> + <description>Detects possibly malicious html elements including some attributes</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>39</id> + <rule><![CDATA[(?:\\x[01fe][\db-ce-f])|(?:%[01fe][\db-ce-f])|(?:&#[01fe][\db-ce-f])|(?:\\[01fe][\db-ce-f])|(?:&#x[01fe][\db-ce-f])]]></rule> + <description>Detects nullbytes and other dangerous characters</description> + <tags> + <tag>id</tag> + <tag>rfe</tag> + <tag>xss</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>40</id> + <rule><![CDATA[(?:\)\s*when\s*\d+\s*then)|(?:"\s*(?:#|--|{))|(?:\/\*!\s?\d+)|(?:ch(?:a)?r\s*\(\s*\d)|(?:(?:(n?and|x?or|not)\s+|\|\||\&\&)\s*\w+\()]]></rule> + <description>Detects MySQL comments, conditions and ch(a)r injections</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>41</id> + <rule><![CDATA[(?:[\s()]case\s*\()|(?:\)\s*like\s*\()|(?:having\s*[^\s]+\s*[^\w\s])|(?:if\s?\([\d\w]\s*[=<>~])]]></rule> + <description>Detects conditional SQL injection attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>42</id> + <rule><![CDATA[(?:"\s*or\s*"?\d)|(?:\\x(?:23|27|3d))|(?:^.?"$)|(?:(?:^["\\]*(?:[\d"]+|[^"]+"))+\s*(?:n?and|x?or|not|\|\||\&\&)\s*[\w"[+&!@(),.-])|(?:[^\w\s]\w+\s*[|-]\s*"\s*\w)|(?:@\w+\s+(and|or)\s*["\d]+)|(?:@[\w-]+\s(and|or)\s*[^\w\s])|(?:[^\w\s:]\s*\d\W+[^\w\s]\s*".)|(?:\Winformation_schema|table_name\W)]]></rule> + <description>Detects classic SQL injection probings 1/2</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>43</id> + <rule><![CDATA[(?:"\s*\*.+(?:or|id)\W*"\d)|(?:\^")|(?:^[\w\s"-]+(?<=and\s)(?<=or\s)(?<=xor\s)(?<=nand\s)(?<=not\s)(?<=\|\|)(?<=\&\&)\w+\()|(?:"[\s\d]*[^\w\s]+\W*\d\W*.*["\d])|(?:"\s*[^\w\s?]+\s*[^\w\s]+\s*")|(?:"\s*[^\w\s]+\s*[\W\d].*(?:#|--))|(?:".*\*\s*\d)|(?:"\s*or\s[^\d]+[\w-]+.*\d)|(?:[()*<>%+-][\w-]+[^\w\s]+"[^,])]]></rule> + <description>Detects classic SQL injection probings 2/2</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>44</id> + <rule><![CDATA[(?:\d"\s+"\s+\d)|(?:^admin\s*"|(\/\*)+"+\s?(?:--|#|\/\*|{)?)|(?:"\s*or[\w\s-]+\s*[+<>=(),-]\s*[\d"])|(?:"\s*[^\w\s]?=\s*")|(?:"\W*[+=]+\W*")|(?:"\s*[!=|][\d\s!=+-]+.*["(].*$)|(?:"\s*[!=|][\d\s!=]+.*\d+$)|(?:"\s*like\W+[\w"(])|(?:\sis\s*0\W)|(?:where\s[\s\w\.,-]+\s=)|(?:"[<>~]+")]]></rule> + <description>Detects basic SQL authentication bypass attempts 1/3</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>45</id> + <rule><![CDATA[(?:union\s*(?:all|distinct|[(!@]*)\s*[([]*\s*select)|(?:\w+\s+like\s+\")|(?:like\s*"\%)|(?:"\s*like\W*["\d])|(?:"\s*(?:n?and|x?or|not |\|\||\&\&)\s+[\s\w]+=\s*\w+\s*having)|(?:"\s*\*\s*\w+\W+")|(?:"\s*[^?\w\s=.,;)(]+\s*[(@"]*\s*\w+\W+\w)|(?:select\s*[\[\]()\s\w\.,"-]+from)|(?:find_in_set\s*\()]]></rule> + <description>Detects basic SQL authentication bypass attempts 2/3</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>46</id> + <rule><![CDATA[(?:in\s*\(+\s*select)|(?:(?:n?and|x?or|not |\|\||\&\&)\s+[\s\w+]+(?:regexp\s*\(|sounds\s+like\s*"|[=\d]+x))|("\s*\d\s*(?:--|#))|(?:"[%&<>^=]+\d\s*(=|or))|(?:"\W+[\w+-]+\s*=\s*\d\W+")|(?:"\s*is\s*\d.+"?\w)|(?:"\|?[\w-]{3,}[^\w\s.,]+")|(?:"\s*is\s*[\d.]+\s*\W.*")]]></rule> + <description>Detects basic SQL authentication bypass attempts 3/3</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>47</id> + <rule><![CDATA[(?:[\d\W]\s+as\s*["\w]+\s*from)|(?:^[\W\d]+\s*(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc))|(?:(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s+(?:(?:group_)concat|char|load_file)\s?\(?)|(?:end\s*\);)|("\s+regexp\W)|(?:[\s(]load_file\s*\()]]></rule> + <description>Detects concatenated basic SQL injection and SQLLFI attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + <tag>lfi</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>48</id> + <rule><![CDATA[(?:@.+=\s*\(\s*select)|(?:\d+\s*or\s*\d+\s*[\-+])|(?:\/\w+;?\s+(?:having|and|or|select)\W)|(?:\d\s+group\s+by.+\()|(?:(?:;|#|--)\s*(?:drop|alter))|(?:(?:;|#|--)\s*(?:update|insert)\s*\w{2,})|(?:[^\w]SET\s*@\w+)|(?:(?:n?and|x?or|not |\|\||\&\&)[\s(]+\w+[\s)]*[!=+]+[\s\d]*["=()])]]></rule> + <description>Detects chained SQL injection attempts 1/2</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>49</id> + <rule><![CDATA[(?:"\s+and\s*=\W)|(?:\(\s*select\s*\w+\s*\()|(?:\*\/from)|(?:\+\s*\d+\s*\+\s*@)|(?:\w"\s*(?:[-+=|@]+\s*)+[\d(])|(?:coalesce\s*\(|@@\w+\s*[^\w\s])|(?:\W!+"\w)|(?:";\s*(?:if|while|begin))|(?:"[\s\d]+=\s*\d)|(?:order\s+by\s+if\w*\s*\()|(?:[\s(]+case\d*\W.+[tw]hen[\s(])]]></rule> + <description>Detects chained SQL injection attempts 2/2</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>50</id> + <rule><![CDATA[(?:(select|;)\s+(?:benchmark|if|sleep)\s*?\(\s*\(?\s*\w+)]]></rule> + <description>Detects SQL benchmark and sleep injection attempts including conditional queries</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>51</id> + <rule><![CDATA[(?:create\s+function\s+\w+\s+returns)|(?:;\s*(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s*[\[(]?\w{2,})]]></rule> + <description>Detects MySQL UDF injection and other data/structure manipulation attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>52</id> + <rule><![CDATA[(?:alter\s*\w+.*character\s+set\s+\w+)|(";\s*waitfor\s+time\s+")|(?:";.*:\s*goto)]]></rule> + <description>Detects MySQL charset switch and MSSQL DoS attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>53</id> + <rule><![CDATA[(?:procedure\s+analyse\s*\()|(?:;\s*(declare|open)\s+[\w-]+)|(?:create\s+(procedure|function)\s*\w+\s*\(\s*\)\s*-)|(?:declare[^\w]+[@#]\s*\w+)|(exec\s*\(\s*@)]]></rule> + <description>Detects MySQL and PostgreSQL stored procedure/function injections</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>54</id> + <rule><![CDATA[(?:select\s*pg_sleep)|(?:waitfor\s*delay\s?"+\s?\d)|(?:;\s*shutdown\s*(?:;|--|#|\/\*|{))]]></rule> + <description>Detects Postgres pg_sleep injection, waitfor delay attacks and database shutdown attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>55</id> + <rule><![CDATA[(?:\sexec\s+xp_cmdshell)|(?:"\s*!\s*["\w])|(?:from\W+information_schema\W)|(?:(?:(?:current_)?user|database|schema|connection_id)\s*\([^\)]*)|(?:";?\s*(?:select|union|having)\s*[^\s])|(?:\wiif\s*\()|(?:exec\s+master\.)|(?:union select @)|(?:union[\w(\s]*select)|(?:select.*\w?user\()|(?:into[\s+]+(?:dump|out)file\s*")]]></rule> + <description>Detects MSSQL code execution and information gathering attempts</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>56</id> + <rule><![CDATA[(?:merge.*using\s*\()|(execute\s*immediate\s*")|(?:\W+\d*\s*having\s*[^\s\-])|(?:match\s*[\w(),+-]+\s*against\s*\()]]></rule> + <description>Detects MATCH AGAINST, MERGE, EXECUTE IMMEDIATE and HAVING injections</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>57</id> + <rule><![CDATA[(?:,.*[)\da-f"]"(?:".*"|\Z|[^"]+))|(?:\Wselect.+\W*from)|((?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s*\(\s*space\s*\()]]></rule> + <description>Detects MySQL comment-/space-obfuscated injections and backtick termination</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>58</id> + <rule><![CDATA[(?:@[\w-]+\s*\()|(?:]\s*\(\s*["!]\s*\w)|(?:<[?%](?:php)?.*(?:[?%]>)?)|(?:;[\s\w|]*\$\w+\s*=)|(?:\$\w+\s*=(?:(?:\s*\$?\w+\s*[(;])|\s*".*"))|(?:;\s*\{\W*\w+\s*\()]]></rule> + <description>Detects code injection attempts 1/3</description> + <tags> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>59</id> + <rule><![CDATA[(?:(?:[;]+|(<[?%](?:php)?)).*(?:define|eval|file_get_contents|include|require|require_once|set|shell_exec|phpinfo|system|passthru|preg_\w+|execute)\s*["(@])]]></rule> + <description>Detects code injection attempts 2/3</description> + <tags> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>60</id> + <rule><![CDATA[(?:(?:[;]+|(<[?%](?:php)?)).*[^\w](?:echo|print|print_r|var_dump|[fp]open))|(?:;\s*rm\s+-\w+\s+)|(?:;.*{.*\$\w+\s*=)|(?:\$\w+\s*\[\]\s*=\s*)]]></rule> + <description>Detects code injection attempts 3/3</description> + <tags> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>62</id> + <rule><![CDATA[(?:function[^(]*\([^)]*\))|(?:(?:delete|void|throw|instanceof|new|typeof)[^\w.]+\w+\s*[([])|([)\]]\s*\.\s*\w+\s*=)|(?:\(\s*new\s+\w+\s*\)\.)]]></rule> + <description>Detects common function declarations and special JS operators</description> + <tags> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>63</id> + <rule><![CDATA[(?:[\w.-]+@[\w.-]+%(?:[01][\db-ce-f])+\w+:)]]></rule> + <description>Detects common mail header injections</description> + <tags> + <tag>id</tag> + <tag>spam</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>64</id> + <rule><![CDATA[(?:\.pl\?\w+=\w?\|\w+;)|(?:\|\(\w+=\*)|(?:\*\s*\)+\s*;)]]></rule> + <description>Detects perl echo shellcode injection and LDAP vectors</description> + <tags> + <tag>lfi</tag> + <tag>rfe</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>65</id> + <rule><![CDATA[(?:(^|\W)const\s+[\w\-]+\s*=)|(?:(?:do|for|while)\s*\([^;]+;+\))|(?:(?:^|\W)on\w+\s*=[\w\W]*(?:on\w+|alert|eval|print|confirm|prompt))|(?:groups=\d+\(\w+\))|(?:(.)\1{128,})]]></rule> + <description>Detects basic XSS DoS attempts</description> + <tags> + <tag>rfe</tag> + <tag>dos</tag> + </tags> + <impact>5</impact> + </filter> + <filter> + <id>67</id> + <rule><![CDATA[(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|(?:\({3,}\++:{2,})|(?:\$\[!!!\])]]></rule> + <description>Detects unknown attack vectors based on PHPIDS Centrifuge detection</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + <tag>id</tag> + <tag>rfe</tag> + <tag>lfi</tag> + </tags> + <impact>7</impact> + </filter> + <filter> + <id>68</id> + <rule><![CDATA[(?:[\s\/"]+[-\w\/\\\*]+\s*=.+(?:\/\s*>))]]></rule> + <description>Finds attribute breaking injections including obfuscated attributes</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>69</id> + <rule><![CDATA[(?:(?:msgbox|eval)\s*\+|(?:language\s*=\*vbscript))]]></rule> + <description>Finds basic VBScript injection attempts</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>70</id> + <rule><![CDATA[(?:\[\$(?:ne|eq|lte?|gte?|n?in|mod|all|size|exists|type|slice|or)\])]]></rule> + <description>Finds basic MongoDB SQL injection attempts</description> + <tags> + <tag>sqli</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>71</id> + <rule><![CDATA[(?:[\s\d\/"]+(?:on\w+|style|poster|background)=[$"\w])|(?:-type\s*:\s*multipart)]]></rule> + <description>Finds malicious attribute injection attempts and MHTML attacks</description> + <tags> + <tag>xss</tag> + <tag>csrf</tag> + </tags> + <impact>6</impact> + </filter> + <filter> + <id>72</id> + <rule><![CDATA[(?:(sleep\((\s*)(\d*)(\s*)\)|benchmark\((.*)\,(.*)\)))]]></rule> + <description>Detects blind sqli tests using sleep() or benchmark().</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>73</id> + <rule><![CDATA[(?:(\%SYSTEMROOT\%))]]></rule> + <description>An attacker is trying to locate a file to read or write.</description> + <tags> + <tag>files</tag> + <tag>id</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>75</id> + <rule><![CDATA[(?:(((.*)\%[c|d|i|e|f|g|o|s|u|x|p|n]){8}))]]></rule> + <description>Looking for a format string attack</description> + <tags> + <tag>format string</tag> + </tags> + <impact>4</impact> + </filter> + <filter> + <id>76</id> + <rule><![CDATA[(?:(union(.*)select(.*)from))]]></rule> + <description>Looking for basic sql injection. Common attack string for mysql, oracle and others.</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>3</impact> + </filter> + <filter> + <id>77</id> + <rule><![CDATA[(?:^(-0000023456|4294967295|4294967296|2147483648|2147483647|0000012345|-2147483648|-2147483649|0000023456|2.2250738585072007e-308|1e309)$)]]></rule> + <description>Looking for integer overflow attacks, these are taken from skipfish, except 2.2250738585072007e-308 is the "magic number" crash</description> + <tags> + <tag>sqli</tag> + <tag>id</tag> + </tags> + <impact>3</impact> + </filter> + <filter> + <id>78</id> + <rule><![CDATA[(?:%23.*?%0a)]]></rule> + <description>Detects SQL comment filter evasion</description> + <tags> + <tag>format string</tag> + </tags> + <impact>4</impact> + </filter> +</filters> diff --git a/interface/lib/classes/IDS/license.txt b/interface/lib/classes/IDS/license.txt new file mode 100644 index 0000000..4a3cd1b --- /dev/null +++ b/interface/lib/classes/IDS/license.txt @@ -0,0 +1,18 @@ + * + * The files in the folder IDS and its subfolders belong to the + * PHP Intrusion Detection System software and are licensed under LGPL. + * + * Copyright (c) 2008 PHPIDS group (https://phpids.org) + * + * PHPIDS is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * PHPIDS 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>. \ No newline at end of file diff --git a/interface/lib/classes/db_mysql.inc.php b/interface/lib/classes/db_mysql.inc.php index 56d43f8..b96b8d0 100644 --- a/interface/lib/classes/db_mysql.inc.php +++ b/interface/lib/classes/db_mysql.inc.php @@ -121,6 +121,52 @@ parent::query( 'SET NAMES '.$this->dbCharset); parent::query( "SET character_set_results = '".$this->dbCharset."', character_set_client = '".$this->dbCharset."', character_set_connection = '".$this->dbCharset."', character_set_database = '".$this->dbCharset."', character_set_server = '".$this->dbCharset."'"); } + + private function securityScan($string) { + global $app, $conf; + + // get security config + if(isset($app)) { + $app->uses('getconf'); + $ids_config = $app->getconf->get_security_config('ids'); + + if($ids_config['sql_scan_enabled'] == 'yes') { + + $string_orig = $string; + + //echo $string; + $chars = array(';', '#', '/*', '*/', '--', ' UNION ', '\\\'', '\\"'); + + $string = str_replace('\\\\', '', $string); + $string = preg_replace('/(^|[^\\\])([\'"])(.*?[^\\\])\\2/is', '$1', $string); + $ok = true; + + if(substr_count($string, "`") % 2 != 0 || substr_count($string, "'") % 2 != 0 || substr_count($string, '"') % 2 != 0) { + $app->log("SQL injection warning (" . $string_orig . ")",2); + $ok = false; + } else { + foreach($chars as $char) { + if(strpos($string, $char) !== false) { + $ok = false; + $app->log("SQL injection warning (" . $string_orig . ")",2); + break; + } + } + } + if($ok == true) { + return true; + } else { + if($ids_config['sql_scan_action'] == 'warn') { + // we return false in warning level. + return false; + } else { + // if sql action = 'block' or anything else then stop here. + $app->error('Possible SQL injection. All actions have been logged.'); + } + } + } + } + } public function query($queryString) { global $conf; @@ -143,6 +189,7 @@ } } } while($ok == false); + $this->securityScan($queryString); $this->queryId = parent::query($queryString); $this->updateError('DB::query('.$queryString.') -> mysqli_query'); if($this->errorNumber && $conf['demo_mode'] === false) debug_print_backtrace(); diff --git a/interface/lib/classes/getconf.inc.php b/interface/lib/classes/getconf.inc.php index 5cc223a..0cc282e 100644 --- a/interface/lib/classes/getconf.inc.php +++ b/interface/lib/classes/getconf.inc.php @@ -31,6 +31,7 @@ class getconf { private $config; + private $security_config; public function get_server_config($server_id, $section = '') { global $app; @@ -55,15 +56,20 @@ return ($section == '') ? $this->config['global'] : $this->config['global'][$section]; } + // Function has been moved to $app->get_security_config($section) public function get_security_config($section = '') { global $app; + + if(is_array($this->security_config)) { + return ($section == '') ? $this->security_config : $this->security_config[$section]; + } else { + $this->uses('ini_parser'); + $security_config_path = '/usr/local/ispconfig/security/security_settings.ini'; + if(!is_file($security_config_path)) $security_config_path = realpath(ISPC_ROOT_PATH.'/../security/security_settings.ini'); + $this->security_config = $this->ini_parser->parse_ini_string(file_get_contents($security_config_path)); - $app->uses('ini_parser'); - $security_config_path = '/usr/local/ispconfig/security/security_settings.ini'; - if(!is_file($security_config_path)) $security_config_path = realpath(ISPC_ROOT_PATH.'/../security/security_settings.ini'); - $security_config = $app->ini_parser->parse_ini_string(file_get_contents($security_config_path)); - - return ($section == '') ? $security_config : $security_config[$section]; + return ($section == '') ? $this->security_config : $this->security_config[$section]; + } } } diff --git a/interface/lib/classes/ids.inc.php b/interface/lib/classes/ids.inc.php new file mode 100644 index 0000000..a98b0b2 --- /dev/null +++ b/interface/lib/classes/ids.inc.php @@ -0,0 +1,149 @@ +<?php + +/* +Copyright (c) 2014, Till Brehm, ISPConfig UG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +class ids { + + public function start() + { + global $app, $conf; + + $security_config = $app->getconf->get_security_config('ids'); + + set_include_path( + get_include_path() + . PATH_SEPARATOR + . ISPC_CLASS_PATH.'/' + ); + + require_once(ISPC_CLASS_PATH.'/IDS/Init.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Monitor.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Filter.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Filter/Storage.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Report.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Event.php'); + require_once(ISPC_CLASS_PATH.'/IDS/Converter.php'); + + $ids_request = array( + 'SESSION' => $_SESSION, + 'GET' => $_GET, + 'POST' => $_POST, + 'COOKIE' => $_COOKIE + ); + + $ids_init = IDS\Init::init(ISPC_CLASS_PATH.'/IDS/Config/Config.ini.php'); + + $ids_init->config['General']['base_path'] = ISPC_CLASS_PATH.'/IDS/'; + $ids_init->config['General']['tmp_path'] = '../../../temp'; + $ids_init->config['General']['use_base_path'] = true; + $ids_init->config['Caching']['caching'] = 'none'; + $ids_init->config['Logging']['path'] = '../../../temp/ids.log'; + + $current_script_name = trim($_SERVER['SCRIPT_NAME']); + + // Get whitelist + $whitelist_path = '/usr/local/ispconfig/security/ids.whitelist'; + if(is_file('/usr/local/ispconfig/security/ids.whitelist.custom')) $whitelist_path = '/usr/local/ispconfig/security/ids.whitelist.custom'; + if(!is_file($whitelist_path)) $whitelist_path = realpath(ISPC_ROOT_PATH.'/../security/ids.whitelist'); + + $whitelist_lines = file($whitelist_path); + if(is_array($whitelist_lines)) { + foreach($whitelist_lines as $line) { + $line = trim($line); + if(substr($line,0,1) != '#') { + list($user,$path,$varname) = explode(':',$line); + if($current_script_name == $path) { + if($user = 'any' + || ($user == 'user' && ($_SESSION['s']['user']['typ'] == 'user' || $_SESSION['s']['user']['typ'] == 'admin')) + || ($user == 'admin' && $_SESSION['s']['user']['typ'] == 'admin')) { + $ids_init->config['General']['exceptions'][] = $varname; + + } + } + } + } + } + + // Get HTML fields + $htmlfield_path = '/usr/local/ispconfig/security/ids.htmlfield'; + if(is_file('/usr/local/ispconfig/security/ids.htmlfield.custom')) $htmlfield_path = '/usr/local/ispconfig/security/ids.htmlfield.custom'; + if(!is_file($htmlfield_path)) $htmlfield_path = realpath(ISPC_ROOT_PATH.'/../security/ids.htmlfield'); + + $htmlfield_lines = file($htmlfield_path); + if(is_array($htmlfield_lines)) { + foreach($htmlfield_lines as $line) { + $line = trim($line); + if(substr($line,0,1) != '#') { + list($user,$path,$varname) = explode(':',$line); + if($current_script_name == $path) { + if($user = 'any' + || ($user == 'user' && ($_SESSION['s']['user']['typ'] == 'user' || $_SESSION['s']['user']['typ'] == 'admin')) + || ($user == 'admin' && $_SESSION['s']['user']['typ'] == 'admin')) { + $ids_init->config['General']['html'][] = $varname; + } + } + } + } + } + + $ids = new IDS\Monitor($ids_init); + $ids_result = $ids->run($ids_request); + + if (!$ids_result->isEmpty()) { + + $impact = $ids_result->getImpact(); + + if($impact >= $security_config['ids_log_level']) { + $ids_log = ISPC_ROOT_PATH.'/temp/ids.log'; + if(!is_file($ids_log)) touch($ids_log); + + $user = isset($_SESSION['s']['user']['typ'])?$_SESSION['s']['user']['typ']:'any'; + + $log_lines = ''; + foreach ($ids_result->getEvents() as $event) { + $log_lines .= $user.':'.$current_script_name.':'.$event->getName()."\n"; + } + file_put_contents($ids_log,$log_lines,FILE_APPEND); + + } + + if($impact >= $security_config['ids_warn_level']) { + $app->log("PHP IDS Alert.".$ids_result, 2); + } + + if($impact >= $security_config['ids_block_level']) { + $app->error("Possible attack detected. This action has been logged.",'', true, 2); + } + + } + } + +} + +?> diff --git a/security/apache_directives.blacklist b/security/apache_directives.blacklist new file mode 100644 index 0000000..edb4b50 --- /dev/null +++ b/security/apache_directives.blacklist @@ -0,0 +1,3 @@ +/^\s*(LoadModule|LoadFile|Include)(\s+|[\\\\])/mi +/^\s*(SuexecUserGroup|suPHP_UserGroup|suPHP_PHPPath|suPHP_ConfigPath)(\s+|[\\\\])/mi +/^\s*(FCGIWrapper|FastCgiExternalServer)(\s+|[\\\\])/mi \ No newline at end of file diff --git a/security/ids.htmlfield b/security/ids.htmlfield new file mode 100644 index 0000000..2c6e92a --- /dev/null +++ b/security/ids.htmlfield @@ -0,0 +1,5 @@ +# Format: usertype:url_path:field +# usertype can be: any/client/admin +# Example: +# admin:/admin/language_edit.php:POST.records.weak_password_txt +admin:/admin/language_edit.php:POST.records \ No newline at end of file diff --git a/security/ids.whitelist b/security/ids.whitelist new file mode 100644 index 0000000..58a96e6 --- /dev/null +++ b/security/ids.whitelist @@ -0,0 +1,45 @@ +# Format: usertype:url_path:field +# usertype can be: any/client/admin +# Example: +# admin:/admin/language_edit.php:POST.records.weak_password_txt +admin:/admin/server_config_edit.php:POST.maildir_path +admin:/admin/server_config_edit.php:POST.website_path +admin:/admin/server_config_edit.php:POST.website_symlinks +admin:/admin/server_config_edit.php:POST.vhost_conf_dir +admin:/admin/server_config_edit.php:POST.vhost_conf_enabled_dir +admin:/admin/server_config_edit.php:POST.nginx_vhost_conf_dir +admin:/admin/server_config_edit.php:POST.nginx_vhost_conf_enabled_dir +admin:/admin/server_config_edit.php:POST.php_open_basedir +admin:/admin/server_config_edit.php:POST.awstats_pl +admin:/admin/server_config_edit.php:POST.fastcgi_starter_path +admin:/admin/server_config_edit.php:POST.fastcgi_bin +admin:/admin/server_config_edit.php:POST.jailkit_chroot_home +admin:/admin/remote_user_edit.php:POST.remote_functions.1 +admin:/admin/firewall_edit.php:POST.tcp_port +admin:/admin/system_config_edit.php:POST.admin_dashlets_right +admin:/admin/system_config_edit.php:POST.reseller_dashlets_left +admin:/admin/system_config_edit.php:POST.reseller_dashlets_right +admin:/admin/language_edit.php:POST.records.weak_password_txt +user:/client/client_message.php:POST.message +user:/client/message_template_edit.php:POST.subject +admin:/dns/dns_template_edit.php:POST.template +admin:/nav.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_sys_state.php:SESSION.s.module.nav.1.items.0.title +admin:/capp.php:SESSION.s.module.nav.1.items.0.title +admin:/keepalive.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/log_list.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/datalog_list.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_data.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_sys_state.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_monit.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_munin.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_data.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/show_log.php:SESSION.s.module.nav.1.items.0.title +admin:/monitor/log_del.php:SESSION.s.module.nav.1.items.0.title +admin:/keepalive.php:SESSION.s.module.nav.1.items.0.title +admin:/capp.php:SESSION.s.module.nav.1.items.0.title +admin:/sites/web_vhost_subdomain_edit.php:POST.php_open_basedir +admin:/sites/web_domain_edit.php:POST.php_open_basedir +admin:/sites/web_domain_edit.php:POST.apache_directives +user:/sites/shell_user_edit.php:POST.ssh_rsa +user:/sites/cron_edit.php:POST.command \ No newline at end of file diff --git a/security/security_settings.ini b/security/security_settings.ini index 4dfe001..43bcebf 100644 --- a/security/security_settings.ini +++ b/security/security_settings.ini @@ -16,6 +16,15 @@ admin_allow_software_repo=superadmin remote_api_allowed=yes +[ids] +ids_enabled=yes +ids_log_level=1 +ids_warn_level=5 +ids_block_level=30 +sql_scan_enabled=yes +sql_scan_action=warn +apache_directives_scan_enabled=yes + [systemcheck] security_admin_email=root@localhost security_admin_email_subject=Security alert from server -- Gitblit v1.9.1