mirror of
https://github.com/openappsec/openappsec.git
synced 2025-06-28 16:41:02 +03:00
281 lines
10 KiB
C++
Executable File
281 lines
10 KiB
C++
Executable File
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "Signatures.h"
|
|
#include "waap.h"
|
|
#include <fstream>
|
|
|
|
USE_DEBUG_FLAG(D_WAAP);
|
|
|
|
typedef picojson::value::object JsObj;
|
|
typedef picojson::value JsVal;
|
|
typedef picojson::value::array JsArr;
|
|
typedef std::map<std::string, std::vector<std::string>> filtered_parameters_t;
|
|
|
|
|
|
static std::vector<std::string> to_strvec(const picojson::value::array& jsV)
|
|
{
|
|
std::vector<std::string> r;
|
|
|
|
for (auto it = jsV.begin(); it != jsV.end(); ++it) {
|
|
r.push_back(it->get<std::string>());
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static std::set<std::string> to_strset(const picojson::value::array& jsA)
|
|
{
|
|
std::set<std::string> r;
|
|
|
|
for (auto it = jsA.begin(); it != jsA.end(); ++it) {
|
|
r.insert(it->get<std::string>());
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static std::map<std::string, Regex*> to_regexmap(const picojson::value::object& jsO, bool& error)
|
|
{
|
|
std::map<std::string, Regex*> r;
|
|
|
|
for (auto it = jsO.begin(); it != jsO.end(); ++it) {
|
|
const std::string& n = it->first;
|
|
// convert name to lowercase now (so we don't need to do it at runtime every time).
|
|
std::string n_lower;
|
|
for (std::string::const_iterator pCh = n.begin(); pCh != n.end(); ++pCh) {
|
|
n_lower += std::tolower(*pCh);
|
|
}
|
|
const picojson::value& v = it->second;
|
|
|
|
if (error) {
|
|
// stop loading regexes if there's previous error...
|
|
break;
|
|
}
|
|
|
|
// Pointers to Regex instances are stored instead of instances themselves to avoid
|
|
// the need to make the Regex objects copyable.
|
|
// However, these pointers must be freed by the holder of the returned map!
|
|
// note: in our case this freeing is happening in the destructor of the WaapAssetState class.
|
|
r[n] = new Regex(v.get<std::string>(), error, n_lower);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static filtered_parameters_t to_filtermap(const picojson::value::object& JsObj)
|
|
{
|
|
filtered_parameters_t result;
|
|
for (auto it = JsObj.begin(); it != JsObj.end(); ++it)
|
|
{
|
|
const std::string parameter = it->first;
|
|
const picojson::value::array& arr = it->second.get<picojson::value::array>();
|
|
result[parameter] = to_strvec(arr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string genDelimitedKeyValPattern(const std::string& delim)
|
|
{
|
|
std::string pattern = "^([^" + delim + "]+?=[^" + delim + "]+?" + delim + ")+"
|
|
"([^" + delim + "]+?=[^" + delim + "]+?)" + delim + "?$";
|
|
return pattern;
|
|
}
|
|
|
|
Signatures::Signatures(const std::string& filepath) :
|
|
sigsSource(loadSource(filepath)),
|
|
error(false),
|
|
m_regexPreconditions(std::make_shared<Waap::RegexPreconditions>(sigsSource, error)),
|
|
words_regex(
|
|
to_strvec(sigsSource["words_regex_list"].get<picojson::value::array>()),
|
|
error,
|
|
"words_regex_list",
|
|
m_regexPreconditions
|
|
),
|
|
specific_acuracy_keywords_regex(
|
|
to_strvec(sigsSource["specific_acuracy_keywords_regex_list"].get<picojson::value::array>()),
|
|
error,
|
|
"specific_acuracy_keywords_regex_list",
|
|
m_regexPreconditions
|
|
),
|
|
pattern_regex(
|
|
to_strvec(sigsSource["pattern_regex_list"].get<picojson::value::array>()),
|
|
error,
|
|
"pattern_regex_list",
|
|
m_regexPreconditions
|
|
),
|
|
un_escape_pattern(sigsSource["un_escape_pattern"].get<std::string>(), error, "un_escape_pattern"),
|
|
quotes_ev_pattern(sigsSource["quotes_ev_pattern"].get<std::string>(), error, "quotes_ev_pattern"),
|
|
comment_ev_pattern(sigsSource["comment_ev_pattern"].get<std::string>(), error, "comment_ev_pattern"),
|
|
quotes_space_ev_pattern(
|
|
sigsSource["quotes_space_ev_fast_reg"].get<std::string>(), error,
|
|
"quotes_space_ev_fast_reg"
|
|
),
|
|
allowed_text_re(sigsSource["allowed_text_re"].get<std::string>(), error, "allowed_text_re"),
|
|
pipe_split_re(
|
|
"([\\w\\=\\-\\_\\.\\,\\(\\)\\[\\]\\/\\%\\s]+?)\\||([\\w\\=\\-\\_\\.\\,\\(\\)\\[\\]\\/\\%\\s]+)|\\|()",
|
|
error,
|
|
"pipe_decode"),
|
|
semicolon_split_re("([\\w\\=\\-\\_\\.\\,\\(\\)\\%]+?);|([\\w\\=\\-\\_\\.\\,\\(\\)\\%]+)|;()", error, "sem_decode"),
|
|
longtext_re(sigsSource["longtext_re"].get<std::string>(), error, "longtext_re"),
|
|
nospaces_long_value_re("^[^\\s]{16,}$", error, "nospaces_long_value_re"),
|
|
good_header_name_re(sigsSource["good_header_name_re"].get<std::string>(), error, "good_header_name"),
|
|
good_header_value_re(sigsSource["good_header_value_re"].get<std::string>(), error, "good_header_value"),
|
|
ignored_for_nospace_long_value(
|
|
to_strset(sigsSource["ignored_for_nospace_long_value"].get<picojson::value::array>())),
|
|
global_ignored_keywords(
|
|
to_strset(
|
|
sigsSource["global_ignored"].get<picojson::value::object>()["keys"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
global_ignored_patterns(
|
|
to_strset(
|
|
sigsSource["global_ignored"].get<picojson::value::object>()["patterns"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
url_ignored_keywords(
|
|
to_strset(
|
|
sigsSource["ignored_for_url"].get<picojson::value::object>()["keys"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
url_ignored_patterns(
|
|
to_strset(
|
|
sigsSource["ignored_for_url"].get<picojson::value::object>()["patterns"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
url_ignored_re(
|
|
sigsSource["ignored_for_url"].get<picojson::value::object>()["regex"].get<std::string>(),
|
|
error,
|
|
"url_ignored"
|
|
),
|
|
header_ignored_keywords(
|
|
to_strset(
|
|
sigsSource["ignored_for_headers"].get<picojson::value::object>()["keys"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
header_ignored_patterns(
|
|
to_strset(
|
|
sigsSource["ignored_for_headers"].get<picojson::value::object>()
|
|
["patterns"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
header_ignored_re(
|
|
sigsSource["ignored_for_headers"].get<picojson::value::object>()["regex"].get<std::string>(),
|
|
error,
|
|
"header_ignored"
|
|
),
|
|
filter_parameters(
|
|
to_filtermap(
|
|
sigsSource["filter_parameters"].get<picojson::object>()
|
|
)
|
|
),
|
|
m_attack_types(
|
|
to_filtermap(
|
|
sigsSource["attack_types_map"].get<picojson::object>()
|
|
)
|
|
),
|
|
// Removed by Pavel's request. Leaving here in case he'll want to add this back...
|
|
#if 0
|
|
cookie_ignored_keywords(
|
|
to_strset(
|
|
sigsSource["ignored_for_cookies"].get<picojson::value::object>()["keys"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
cookie_ignored_patterns(
|
|
to_strset(
|
|
sigsSource["ignored_for_cookies"].get<picojson::value::object>()
|
|
["patterns"].get<picojson::value::array>()
|
|
)
|
|
),
|
|
cookie_ignored_re(
|
|
sigsSource["ignored_for_cookies"].get<picojson::value::object>()["regex"].get<std::string>(),
|
|
error,
|
|
"cookie_ignored"
|
|
),
|
|
#endif
|
|
php_serialize_identifier("^(N;)|^([ibdsOoCcRra]:\\d+)", error, "php_serialize_identifier"),
|
|
html_regex("(<(?>body|head)\\b.*>(?>.|[\\r\\n]){0,400}){2}|<html", error, "htmlRegex"),
|
|
uri_parser_regex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)", error, "uriParserRegex"),
|
|
confluence_macro_re("{[^\"]+:(?>.+\\|)+.+}"),
|
|
pipes_delimited_key_val_re(genDelimitedKeyValPattern("\\|")),
|
|
semicolon_delimited_key_val_re(genDelimitedKeyValPattern(";")),
|
|
asterisk_delimited_key_val_re(genDelimitedKeyValPattern("\\*")),
|
|
comma_delimited_key_val_re(genDelimitedKeyValPattern(",")),
|
|
ampersand_delimited_key_val_re(genDelimitedKeyValPattern("&")),
|
|
headers_re(to_regexmap(sigsSource["headers_re"].get<JsObj>(), error)),
|
|
format_magic_binary_re(sigsSource["format_magic_binary_re"].get<std::string>(), error, "format_magic_binary_re"),
|
|
params_type_re(to_regexmap(sigsSource["format_types_regex_list"].get<JsObj>(), error)),
|
|
resp_hdr_pattern_regex_list(to_strvec(sigsSource["resp_hdr_pattern_regex_list"].get<JsArr>()),
|
|
error, "resp_hdr_pattern_regex_list", nullptr),
|
|
resp_hdr_words_regex_list(to_strvec(sigsSource["resp_hdr_words_regex_list"].get<JsArr>()),
|
|
error, "resp_hdr_words_regex_list", nullptr),
|
|
resp_body_pattern_regex_list(to_strvec(sigsSource["resp_body_pattern_regex_list"].get<JsArr>()),
|
|
error, "resp_body_pattern_regex_list", nullptr),
|
|
resp_body_words_regex_list(to_strvec(sigsSource["resp_body_words_regex_list"].get<JsArr>()),
|
|
error, "resp_body_words_regex_list", nullptr),
|
|
remove_keywords_always(
|
|
to_strset(sigsSource["remove_keywords_always"].get<JsArr>())),
|
|
user_agent_prefix_re(sigsSource["user_agent_prefix_re"].get<std::string>()),
|
|
binary_data_kw_filter(sigsSource["binary_data_kw_filter"].get<std::string>()),
|
|
wbxml_data_kw_filter(sigsSource["wbxml_data_kw_filter"].get<std::string>())
|
|
{
|
|
|
|
}
|
|
|
|
Signatures::~Signatures()
|
|
{
|
|
}
|
|
|
|
bool Signatures::fail()
|
|
{
|
|
return error;
|
|
}
|
|
|
|
picojson::value::object Signatures::loadSource(const std::string& waapDataFileName)
|
|
{
|
|
picojson::value doc;
|
|
std::ifstream f(waapDataFileName);
|
|
|
|
if (f.fail()) {
|
|
dbgError(D_WAAP) << "Failed to open json data file '" << waapDataFileName << "'!";
|
|
error = true; // flag an error
|
|
return picojson::value::object();
|
|
}
|
|
|
|
int length;
|
|
f.seekg(0, std::ios::end); // go to the end
|
|
length = f.tellg(); // report location (this is the length)
|
|
char* buffer = new char[length]; // allocate memory for a buffer of appropriate dimension
|
|
f.seekg(0, std::ios::beg); // go back to the beginning
|
|
f.read(buffer, length); // read the whole file into the buffer
|
|
f.close();
|
|
|
|
std::string dataObfuscated(buffer, length);
|
|
|
|
delete[] buffer;
|
|
|
|
|
|
std::stringstream ss(dataObfuscated);
|
|
|
|
ss >> doc;
|
|
|
|
if (!picojson::get_last_error().empty()) {
|
|
dbgError(D_WAAP) << "WaapAssetState::loadSource('" << waapDataFileName << "') failed (parse error: '" <<
|
|
picojson::get_last_error() << "').";
|
|
error = true; // flag an error
|
|
return picojson::value::object();
|
|
}
|
|
|
|
return doc.get<picojson::value::object>()["waap_signatures"].get<picojson::value::object>();
|
|
}
|