Adds support to macro expansion in setvar action

This commit is contained in:
Felipe Zimmerle 2015-08-07 00:43:16 -03:00
parent e12d95b10d
commit 5fdb5b7d2e
7 changed files with 306 additions and 9 deletions

View File

@ -20,6 +20,7 @@
#include "modsecurity/assay.h" #include "modsecurity/assay.h"
#include "src/rule.h" #include "src/rule.h"
#include "src/macro_expansion.h"
namespace ModSecurity { namespace ModSecurity {
namespace actions { namespace actions {
@ -94,6 +95,7 @@ void SetVar::dump() {
bool SetVar::evaluate(Rule *rule, Assay *assay) { bool SetVar::evaluate(Rule *rule, Assay *assay) {
std::string targetValue; std::string targetValue;
variableName = MacroExpansion::expand(variableName, assay);
int value = 0; int value = 0;
try { try {
std::string *resolvedValue = std::string *resolvedValue =
@ -111,14 +113,16 @@ bool SetVar::evaluate(Rule *rule, Assay *assay) {
try { try {
pre = stoi(predicate); pre = stoi(predicate);
} catch (...) { } catch (...) {
/* perform a macro expansion. */ try {
pre = 0; pre = stoi(MacroExpansion::expand(predicate, assay));
} catch (...) {
pre = 0;
}
} }
switch (operation) { switch (operation) {
case setOperation: case setOperation:
/* perform a macro expansion. */ targetValue = MacroExpansion::expand(predicate, assay);
targetValue = predicate;
break; break;
case sumAndSetOperation: case sumAndSetOperation:
targetValue = std::to_string(value + pre); targetValue = std::to_string(value + pre);

View File

@ -1374,8 +1374,9 @@ std::string* Assay::resolve_variable_first(std::string var) {
std::string* Assay::resolve_variable_first(const std::string collectionName, std::string* Assay::resolve_variable_first(const std::string collectionName,
std::string var) { std::string var) {
for (auto &a : collections) { for (auto &a : collections) {
if (a.first == collectionName) { if (tolower(a.first) == tolower(collectionName)) {
auto range = a.second->equal_range(collectionName + ":" + var); auto range = a.second->equal_range(toupper(collectionName)
+ ":" + var);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
return &it->second; return &it->second;
} }
@ -1391,8 +1392,8 @@ void Assay::setCollection(const std::string& collectionName,
ModSecurityStringVariables *collection; ModSecurityStringVariables *collection;
try { try {
collection = collections.at(collectionName); collection = collections.at(toupper(collectionName));
collection->storeOrUpdateVariable(collectionName + ":" collection->storeOrUpdateVariable(toupper(collectionName) + ":"
+ variableName, targetValue); + variableName, targetValue);
} catch (...) { } catch (...) {
debug(9, "don't know any collection named: " debug(9, "don't know any collection named: "

View File

@ -14,10 +14,43 @@
*/ */
#include "src/macro_expansion.h" #include "src/macro_expansion.h"
#include "modsecurity/assay.h"
namespace ModSecurity { namespace ModSecurity {
MacroExpansion::MacroExpansion() { MacroExpansion::MacroExpansion() { }
std::string MacroExpansion::expand(const std::string& input, Assay *assay) {
std::string res(input);
size_t pos = res.find("%{");
while (pos != std::string::npos) {
size_t start = pos;
size_t end = res.find("}%");
if (end == std::string::npos) {
return res;
}
std::string variable(res, start + 2, end - (start + 2));
std::string *variableValue;
size_t collection = variable.find(".");
if (collection == std::string::npos) {
variableValue = assay->resolve_variable_first(variable);
} else {
std::string col = std::string(variable, 0, collection);
std::string var = std::string(variable, collection + 1,
variable.length() - (collection + 1));
variableValue = assay->resolve_variable_first(col, var);
}
res.erase(start, end + 2);
if (variableValue != NULL) {
res.insert(start, *variableValue);
}
pos = res.find("%{");
}
return res;
} }
} // namespace ModSecurity } // namespace ModSecurity

View File

@ -19,6 +19,7 @@
#include <vector> #include <vector>
#include "modsecurity/modsecurity.h" #include "modsecurity/modsecurity.h"
#include "modsecurity/assay.h"
#ifndef SRC_MACRO_EXPANSION_H_ #ifndef SRC_MACRO_EXPANSION_H_
#define SRC_MACRO_EXPANSION_H_ #define SRC_MACRO_EXPANSION_H_
@ -29,6 +30,8 @@ namespace ModSecurity {
class MacroExpansion { class MacroExpansion {
public: public:
MacroExpansion(); MacroExpansion();
static std::string expand(const std::string& input, Assay *assay);
}; };

View File

@ -112,6 +112,16 @@ std::string tolower(std::string str) {
return value; return value;
} }
std::string toupper(std::string str) {
std::locale loc;
std::string value;
for (std::string::size_type i=0; i < str.length(); ++i) {
value.assign(value + std::toupper(str[i], loc));
}
return value;
}
const char SAFE[256] = { const char SAFE[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */

View File

@ -32,6 +32,7 @@ namespace ModSecurity {
void chomp(std::string *str); void chomp(std::string *str);
std::string uri_decode(const std::string & sSrc); std::string uri_decode(const std::string & sSrc);
std::string tolower(std::string str); std::string tolower(std::string str);
std::string toupper(std::string str);
double cpu_seconds(void); double cpu_seconds(void);
int js_decode_nonstrict_inplace(unsigned char *input, int64_t input_len); int js_decode_nonstrict_inplace(unsigned char *input, int64_t input_len);
static unsigned char x2c(unsigned char *what); static unsigned char x2c(unsigned char *what);

View File

@ -0,0 +1,245 @@
[
{
"enabled":1,
"version_min":300000,
"version_max":0,
"title":"Testing collection :: TX (with macro) (1/4)",
"client":{
"ip":"200.249.12.31",
"port":2313
},
"server":{
"ip":"200.249.12.31",
"port":80
},
"request":{
"headers":{
"User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)",
"Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
"Accept-Language":"en-us,en;q=0.5",
"Accept-Encoding":"gzip,deflate",
"Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Keep-Alive":"300",
"Connection":"keep-alive",
"Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120",
"Pragma":"no-cache",
"Cache-Control":"no-cache"
},
"uri":"\/test.pl?param1= test &param2=test2",
"protocol":"GET",
"http_version":1.1,
"body":""
},
"response":{
"headers":{
"Content-Type":"text\/xml; charset=utf-8\n\r",
"Content-Length":"length\n\r"
},
"body":[
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\r",
"<soap:Envelope xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:soap=\"http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\">\n\r",
" <soap:Body>\n\r",
" <EnlightenResponse xmlns=\"http:\/\/clearforest.com\/\">\n\r",
" <EnlightenResult>string<\/EnlightenResult>\n\r",
" <\/EnlightenResponse>\n\r",
" <\/soap:Body>\n\r",
"<\/soap:Envelope>\n\r"
]
},
"expected":{
"audit_log":"",
"debug_log":"Target value: \"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120\" \\(Variable: TX:something\\)",
"error_log":""
},
"rules":[
"SecRuleEngine On",
"SecDebugLog \/tmp\/modsec_debug.log",
"SecDebugLogLevel 9",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something=%{REQUEST_HEADERS:Cookie}%\"",
"SecRule TX \"@contains to_test\" \"t:lowercase,t:none\""
]
},
{
"enabled":1,
"version_min":300000,
"version_max":0,
"title":"Testing collection :: TX (with macro) (2/4)",
"client":{
"ip":"200.249.12.31",
"port":2313
},
"server":{
"ip":"200.249.12.31",
"port":80
},
"request":{
"headers":{
"User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)",
"Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
"Accept-Language":"en-us,en;q=0.5",
"Accept-Encoding":"gzip,deflate",
"Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Keep-Alive":"300",
"Connection":"keep-alive",
"Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120",
"Pragma":"no-cache",
"Cache-Control":"no-cache"
},
"uri":"\/test.pl?param1= test &param2=test2",
"protocol":"GET",
"http_version":1.1,
"body":""
},
"response":{
"headers":{
"Content-Type":"text\/xml; charset=utf-8\n\r",
"Content-Length":"length\n\r"
},
"body":[
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\r",
"<soap:Envelope xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:soap=\"http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\">\n\r",
" <soap:Body>\n\r",
" <EnlightenResponse xmlns=\"http:\/\/clearforest.com\/\">\n\r",
" <EnlightenResult>string<\/EnlightenResult>\n\r",
" <\/EnlightenResponse>\n\r",
" <\/soap:Body>\n\r",
"<\/soap:Envelope>\n\r"
]
},
"expected":{
"audit_log":"",
"debug_log":"Target value: \"1\" \\(Variable: TX:somethingPHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120\\)",
"error_log":""
},
"rules":[
"SecRuleEngine On",
"SecDebugLog \/tmp\/modsec_debug.log",
"SecDebugLogLevel 9",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something%{REQUEST_HEADERS:Cookie}%\"",
"SecRule TX \"@contains to_test\" \"t:lowercase,t:none\""
]
},
{
"enabled":1,
"version_min":300000,
"version_max":0,
"title":"Testing collection :: TX (with macro) (3/4)",
"client":{
"ip":"200.249.12.31",
"port":2313
},
"server":{
"ip":"200.249.12.31",
"port":80
},
"request":{
"headers":{
"User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)",
"Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
"Accept-Language":"en-us,en;q=0.5",
"Accept-Encoding":"gzip,deflate",
"Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Keep-Alive":"300",
"Connection":"keep-alive",
"Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120",
"Pragma":"no-cache",
"Cache-Control":"no-cache"
},
"uri":"\/test.pl?param1= test &param2=test2",
"protocol":"GET",
"http_version":1.1,
"body":""
},
"response":{
"headers":{
"Content-Type":"text\/xml; charset=utf-8\n\r",
"Content-Length":"length\n\r"
},
"body":[
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\r",
"<soap:Envelope xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:soap=\"http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\">\n\r",
" <soap:Body>\n\r",
" <EnlightenResponse xmlns=\"http:\/\/clearforest.com\/\">\n\r",
" <EnlightenResult>string<\/EnlightenResult>\n\r",
" <\/EnlightenResponse>\n\r",
" <\/soap:Body>\n\r",
"<\/soap:Envelope>\n\r"
]
},
"expected":{
"audit_log":"",
"debug_log":"Target value: \"310\" \\(Variable: TX:something\\)",
"error_log":""
},
"rules":[
"SecRuleEngine On",
"SecDebugLog \/tmp\/modsec_debug.log",
"SecDebugLogLevel 9",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something=%{REQUEST_HEADERS:Keep-Alive}%\"",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something=+10\"",
"SecRule TX \"@contains to_test\" \"t:lowercase,t:none\""
]
},
{
"enabled":1,
"version_min":300000,
"version_max":0,
"title":"Testing collection :: TX (with macro) (4/4)",
"client":{
"ip":"200.249.12.31",
"port":2313
},
"server":{
"ip":"200.249.12.31",
"port":80
},
"request":{
"headers":{
"User-Agent":"Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.30729)",
"Accept":"text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8",
"Accept-Language":"en-us,en;q=0.5",
"Accept-Encoding":"gzip,deflate",
"Accept-Charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Keep-Alive":"300",
"Connection":"keep-alive",
"Cookie":"PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120",
"Pragma":"no-cache",
"Cache-Control":"no-cache"
},
"uri":"\/test.pl?param1= test &param2=test2",
"protocol":"GET",
"http_version":1.1,
"body":""
},
"response":{
"headers":{
"Content-Type":"text\/xml; charset=utf-8\n\r",
"Content-Length":"length\n\r"
},
"body":[
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\r",
"<soap:Envelope xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\" xmlns:soap=\"http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\">\n\r",
" <soap:Body>\n\r",
" <EnlightenResponse xmlns=\"http:\/\/clearforest.com\/\">\n\r",
" <EnlightenResult>string<\/EnlightenResult>\n\r",
" <\/EnlightenResponse>\n\r",
" <\/soap:Body>\n\r",
"<\/soap:Envelope>\n\r"
]
},
"expected":{
"audit_log":"",
"debug_log":"Target value: \"5\" \\(Variable: TX:something_else\\)",
"error_log":""
},
"rules":[
"SecRuleEngine On",
"SecDebugLog \/tmp\/modsec_debug.log",
"SecDebugLogLevel 9",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something=+10\"",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something_else=%{tx.something}%\"",
"SecRule REQUEST_HEADERS \"@contains PHPSESSID\" \"t:lowercase,t:none,setvar:TX.something_else=-5\"",
"SecRule TX:something_else \"@contains to_test\" \"t:lowercase,t:none\""
]
}
]