diff --git a/headers/modsecurity/rule.h b/headers/modsecurity/rule.h index b979e2fc..b8689df0 100644 --- a/headers/modsecurity/rule.h +++ b/headers/modsecurity/rule.h @@ -59,6 +59,7 @@ class Rule { std::vector getActionNames(); std::vector getActionsByName(const std::string& name); + bool containsTag(const std::string& name, Transaction *t); std::vector *variables; int phase; diff --git a/headers/modsecurity/transaction.h b/headers/modsecurity/transaction.h index d5f0d23b..194e6805 100644 --- a/headers/modsecurity/transaction.h +++ b/headers/modsecurity/transaction.h @@ -268,6 +268,11 @@ class Transaction { */ Rules *m_rules; + /** + * + */ + std::list< std::pair > m_ruleRemoteTargetByTag; + /** * The list m_auditLogModifier contains modifications to the `auditlogs' * for this specific request, those modifications can happens via the diff --git a/src/Makefile.am b/src/Makefile.am index 93f957c1..3b08cbc7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,6 +103,7 @@ ACTIONS = \ actions/ctl_audit_log_parts.cc \ actions/ctl_request_body_processor_json.cc \ actions/ctl_request_body_processor_xml.cc \ + actions/ctl_rule_remove_target_by_tag.cc \ actions/init_col.cc \ actions/deny.cc \ actions/log.cc \ diff --git a/src/actions/ctl_rule_remove_target_by_tag.cc b/src/actions/ctl_rule_remove_target_by_tag.cc new file mode 100644 index 00000000..8c5ade07 --- /dev/null +++ b/src/actions/ctl_rule_remove_target_by_tag.cc @@ -0,0 +1,48 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include "actions/ctl_rule_remove_target_by_tag.h" + +#include +#include + +#include "modsecurity/transaction.h" + +namespace modsecurity { +namespace actions { + +bool CtlRuleRemoveTargetByTag::init(std::string *error) { + std::string what(m_parser_payload, 22, m_parser_payload.size() - 22); + std::vector param = split(what, ';'); + + if (param.size() < 2) { + error->assign(what + " is not a valid `TAG;VARIABLE'"); + return false; + } + + m_tag = param[0]; + m_target = param[1]; + + return true; +} + +bool CtlRuleRemoveTargetByTag::evaluate(Rule *rule, Transaction *transaction) { + transaction->m_ruleRemoteTargetByTag.push_back( + std::make_pair(m_tag, m_target)); + return true; +} + +} // namespace actions +} // namespace modsecurity diff --git a/src/actions/ctl_rule_remove_target_by_tag.h b/src/actions/ctl_rule_remove_target_by_tag.h new file mode 100644 index 00000000..42155900 --- /dev/null +++ b/src/actions/ctl_rule_remove_target_by_tag.h @@ -0,0 +1,44 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include + +#include "actions/action.h" +#include "modsecurity/transaction.h" +#include "utils.h" + +#ifndef SRC_ACTIONS_CTL_RULE_REMOVE_TARGET_BY_TAG_H_ +#define SRC_ACTIONS_CTL_RULE_REMOVE_TARGET_BY_TAG_H_ + +namespace modsecurity { +namespace actions { + + +class CtlRuleRemoveTargetByTag : public Action { + public: + explicit CtlRuleRemoveTargetByTag(std::string action) + : Action(action, RunTimeOnlyIfMatchKind) { } + + bool init(std::string *error) override; + bool evaluate(Rule *rule, Transaction *transaction) override; + + std::string m_tag; + std::string m_target; +}; + +} // namespace actions +} // namespace modsecurity + +#endif // SRC_ACTIONS_CTL_RULE_REMOVE_TARGET_BY_TAG_H_ diff --git a/src/actions/tag.cc b/src/actions/tag.cc index 3bb99cce..b1d45a5a 100644 --- a/src/actions/tag.cc +++ b/src/actions/tag.cc @@ -50,8 +50,14 @@ namespace modsecurity { namespace actions { -bool Tag::evaluate(Rule *rule, Transaction *transaction, RuleMessage *rm) { +std::string Tag::getName(Transaction *transaction) { std::string tag = MacroExpansion::expand(m_parser_payload, transaction); + return tag; +} + + +bool Tag::evaluate(Rule *rule, Transaction *transaction, RuleMessage *rm) { + std::string tag = getName(transaction); #ifndef NO_LOGS transaction->debug(9, "Rule tag: " + tag); diff --git a/src/actions/tag.h b/src/actions/tag.h index 9086f89a..75194235 100644 --- a/src/actions/tag.h +++ b/src/actions/tag.h @@ -32,6 +32,8 @@ class Tag : public Action { explicit Tag(std::string action) : Action(action, RunTimeOnlyIfMatchKind) { } + std::string getName(Transaction *transaction); + bool evaluate(Rule *rule, Transaction *transaction, RuleMessage *rm) override; }; diff --git a/src/parser/seclang-parser.yy b/src/parser/seclang-parser.yy index 5cfc8b26..d241477f 100644 --- a/src/parser/seclang-parser.yy +++ b/src/parser/seclang-parser.yy @@ -26,6 +26,7 @@ class Driver; #include "actions/ctl_audit_log_parts.h" #include "actions/ctl_request_body_processor_json.h" #include "actions/ctl_request_body_processor_xml.h" +#include "actions/ctl_rule_remove_target_by_tag.h" #include "actions/init_col.h" #include "actions/set_sid.h" #include "actions/set_uid.h" @@ -298,6 +299,7 @@ using modsecurity::Variables::XML; %token ACTION_CTL_RULE_ENGINE %token ACTION_CTL_FORCE_REQ_BODY_VAR %token CONFIG_SEC_COLLECTION_TIMEOUT +%token ACTION_CTL_RULE_REMOVE_TARGET_BY_TAG %type *> actions %type *> variables @@ -316,7 +318,7 @@ using modsecurity::Variables::XML; input: END { - return NULL; + return 0; } | input line | line @@ -1210,6 +1212,15 @@ act: { $$ = new modsecurity::actions::CtlRequestBodyProcessorJSON($1); } + | ACTION_CTL_RULE_REMOVE_TARGET_BY_TAG + { + std::string error; + $$ = new modsecurity::actions::CtlRuleRemoveTargetByTag($1); + if ($$->init(&error) == false) { + driver.error(@0, error); + YYERROR; + } + } | ACTION_CTL_AUDIT_LOG_PARTS { std::string error; diff --git a/src/parser/seclang-scanner.ll b/src/parser/seclang-scanner.ll index f8b61050..2f2565c6 100755 --- a/src/parser/seclang-scanner.ll +++ b/src/parser/seclang-scanner.ll @@ -77,6 +77,8 @@ CONFIG_DIR_RES_BODY_LIMIT (?i:SecResponseBodyLimit) CONFIG_DIR_REQ_BODY_LIMIT_ACTION (?i:SecRequestBodyLimitAction) CONFIG_DIR_RES_BODY_LIMIT_ACTION (?i:SecResponseBodyLimitAction) +ACTION_CTL_RULE_REMOVE_TARGET_BY_TAG (?i:ctl:ruleRemoveTargetByTag) + CONFIG_DIR_GEO_DB (?i:SecGeoLookupDb) CONFIG_DIR_RULE_ENG (?i:SecRuleEngine) @@ -170,6 +172,7 @@ CONFIG_VALUE_ABORT (?i:Abort) CONFIG_VALUE_WARN (?i:Warn) CONFIG_VALUE_PATH [0-9A-Za-z_\/\.\-\*\:]+ +CONFIG_VALUE_PATH2 [0-9A-Za-z_\/\.\-\*\:\;]+ AUDIT_PARTS [ABCDEFHJKIZ]+ CONFIG_VALUE_NUMBER [0-9]+ @@ -233,6 +236,7 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile) %{ /* Remove Rules */ %} {CONFIG_SEC_REMOVE_RULES_BY_ID}[ ]{FREE_TEXT_NEW_LINE} { return yy::seclang_parser::make_CONFIG_SEC_RULE_REMOVE_BY_ID(strchr(yytext, ' ') + 1, *driver.loc.back()); } +{ACTION_CTL_RULE_REMOVE_TARGET_BY_TAG}[=]{CONFIG_VALUE_PATH2} { return yy::seclang_parser::make_ACTION_CTL_RULE_REMOVE_TARGET_BY_TAG(yytext, *driver.loc.back()); } %{ /* Upload */ %} {CONFIG_UPLOAD_FILE_LIMIT}[ ]{CONFIG_VALUE_NUMBER} { return yy::seclang_parser::make_CONFIG_UPLOAD_FILE_LIMIT(strchr(yytext, ' ') + 1, *driver.loc.back()); } diff --git a/src/rule.cc b/src/rule.cc index 955ae98d..195c214f 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -28,11 +28,13 @@ #include "actions/action.h" #include "modsecurity/modsecurity.h" #include "actions/transformations/none.h" +#include "actions/tag.h" #include "variables/variations/exclusion.h" #include "src/utils.h" #include "modsecurity/rules.h" #include "src/macro_expansion.h" + using modsecurity::Variables::Variations::Exclusion; namespace modsecurity { @@ -329,6 +331,7 @@ bool Rule::evaluate(Transaction *trasn) { variable->evaluateInternal(trasn, this, &e); for (auto &v : e) { + bool ignoreVariable = false; if (std::find(exclusions.begin(), exclusions.end(), v->m_key) != exclusions.end()) { #ifndef NO_LOGS @@ -337,6 +340,24 @@ bool Rule::evaluate(Transaction *trasn) { #endif continue; } + + for (auto &i : trasn->m_ruleRemoteTargetByTag) { + std::string tag = i.first; + std::string args = i.second; + if (containsTag(tag, trasn) == false) { + continue; + } + if (args == v->m_key) { + trasn->debug(9, "Variable: " + v->m_key + + " was excluded by ruleRemoteTargetByTag..."); + ignoreVariable = true; + break; + } + } + if (ignoreVariable) { + continue; + } + std::string value = v->m_value; int none = 0; for (Action *a : this->actions_runtime_pre) { @@ -578,4 +599,16 @@ std::vector Rule::getActionsByName(const std::string& name) { return ret; } + +bool Rule::containsTag(const std::string& name, Transaction *t) { + std::vector ret; + for (auto &z : this->actions_runtime_pos) { + actions::Tag *tag = dynamic_cast (z); + if (tag != NULL && tag->getName(t) == name) { + return true; + } + } + return false; +} + } // namespace modsecurity diff --git a/test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json b/test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json new file mode 100644 index 00000000..7457ffa1 --- /dev/null +++ b/test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json @@ -0,0 +1,66 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing CtlRuleRemoteTargetByTag (1)", + "expected":{ + "debug_log": "Variable: ARGS:pwd was excluded by ruleRemoteTargetByTag..." + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/wp-login.php?whee&pwd=lhebs", + "method":"GET", + "body": [ ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRule REQUEST_FILENAME \"@endsWith /wp-login.php\" \"id:9002100,phase:2,t:none,nolog,pass,ctl:ruleRemoveTargetByTag=CRS;ARGS:pwd\"", + "SecRule ARGS \"@contais whe\" \"id:1,phase:3,t:none,nolog,pass,tag:'CRS'\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing CtlRuleRemoteTargetByTag (2)", + "expected":{ + "debug_log": "Target value: .*Variable: ARGS:pwd" + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/wp-login.php?whee&pwd=lhebs", + "method":"GET", + "body": [ ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRule REQUEST_FILENAME \"@endsWith /wp-login.php\" \"id:9002100,phase:2,t:none,nolog,pass,ctl:ruleRemoveTargetByTag=CRS;ARGS:pwd\"", + "SecRule ARGS \"@contais whe\" \"id:1,phase:3,t:none,nolog,pass,tag:'CRS2'\"" + ] + } +]