Mar 21st 2024 update

This commit is contained in:
Ned Wright
2024-03-21 15:31:38 +00:00
parent 0d22790ebe
commit c20fa9f966
100 changed files with 3851 additions and 453 deletions

View File

@@ -86,6 +86,7 @@ add_library(waap_clib
ParserPercentEncode.cc
ParserPairs.cc
Waf2Util2.cc
ParserPDF.cc
)
add_definitions("-Wno-unused-function")

View File

@@ -26,6 +26,7 @@
#include "ParserPercentEncode.h"
#include "ParserPairs.h"
#include "ParserDelimiter.h"
#include "ParserPDF.h"
#include "WaapAssetState.h"
#include "Waf2Regex.h"
#include "Waf2Util.h"
@@ -1146,9 +1147,15 @@ DeepParser::createInternalParser(
));
offset = 0;
} else if (isTopData && (isBinaryType || m_pWaapAssetState->isBinarySampleType(cur_val))) {
dbgTrace(D_WAAP_DEEP_PARSER) << "Starting to parse a binary file";
m_parsersDeque.push_back(std::make_shared<BufferedParser<ParserBinary>>(*this, parser_depth + 1));
offset = 0;
if (isPDFDetected(cur_val)) {
dbgTrace(D_WAAP_DEEP_PARSER) << "Starting to parse a PDF file";
m_parsersDeque.push_back(std::make_shared<BufferedParser<ParserPDF>>(*this, parser_depth + 1));
offset = 0;
} else {
dbgTrace(D_WAAP_DEEP_PARSER) << "Starting to parse a binary file";
m_parsersDeque.push_back(std::make_shared<BufferedParser<ParserBinary>>(*this, parser_depth + 1));
offset = 0;
}
}
}
if (offset < 0) {
@@ -1473,3 +1480,12 @@ DeepParser::shouldEnforceDepthLimit(const std::shared_ptr<ParserBase> &parser) c
}
return false;
}
bool
DeepParser::isPDFDetected(const std::string &cur_val) const
{
static const std::string PDF_header("%PDF-");
if (cur_val.size() < 10)
return false;
return cur_val.substr(0, cur_val.size() > 64 ? 64 : cur_val.size()).find(PDF_header) != std::string::npos;
}

View File

@@ -173,6 +173,7 @@ private:
bool shouldEnforceDepthLimit(const std::shared_ptr<ParserBase>& parser) const;
void setLocalMaxObjectDepth(size_t depth) { m_localMaxObjectDepth = depth; }
void setGlobalMaxObjectDepthReached() { m_globalMaxObjectDepthReached = true; }
bool isPDFDetected(const std::string &cur_val) const;
bool m_deepParserFlag;
std::stack<std::tuple<size_t, size_t, std::string>> m_splitTypesStack; // depth, splitIndex, splitType
std::deque<std::shared_ptr<ParserBase>> m_parsersDeque;

View File

@@ -0,0 +1,13 @@
// 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.

View File

@@ -0,0 +1,111 @@
// 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 "ParserPDF.h"
#include "Waf2Util.h"
#include "debug.h"
#include <string.h>
USE_DEBUG_FLAG(D_WAAP_PARSER_PDF);
USE_DEBUG_FLAG(D_WAAP);
const std::string ParserPDF::m_parserName = "ParserPDF";
const char* PDF_TAIL = "%%EOF";
ParserPDF::ParserPDF(
IParserStreamReceiver &receiver,
size_t parser_depth
) :
m_receiver(receiver),
m_state(s_start),
m_parser_depth(parser_depth)
{}
ParserPDF::~ParserPDF()
{}
size_t
ParserPDF::push(const char *buf, size_t len)
{
dbgTrace(D_WAAP_PARSER_PDF)
<< "buf="
<< buf
<< "len="
<< len;
const char *c;
if (m_state == s_error) {
return 0;
}
if (len == 0)
{
dbgTrace(D_WAAP_PARSER_PDF) << "ParserPDF::push(): end of stream. m_state=" << m_state;
if (m_state == s_end) {
m_receiver.onKvDone();
} else {
m_state = s_error;
}
return 0;
}
switch (m_state) {
case s_start:
m_state = s_body;
CP_FALL_THROUGH;
case s_body:
c = strstr(buf + len - MAX_TAIL_LOOKUP, PDF_TAIL);
dbgTrace(D_WAAP_PARSER_PDF) << "ParserPDF::push(): c=" << c;
if (c) {
m_state = s_end;
CP_FALL_THROUGH;
} else {
break;
}
case s_end:
if (m_receiver.onKey("PDF", 3) != 0) {
m_state = s_error;
return 0;
}
if (m_receiver.onValue("", 0) != 0) {
m_state = s_error;
return 0;
}
break;
case s_error:
break;
default:
dbgTrace(D_WAAP_PARSER_PDF) << "ParserPDF::push(): unknown state: " << m_state;
m_state = s_error;
return 0;
}
return len;
}
void ParserPDF::finish()
{
push(NULL, 0);
}
const std::string& ParserPDF::name() const
{
return m_parserName;
}
bool ParserPDF::error() const
{
return m_state == s_error;
}

View File

@@ -0,0 +1,47 @@
// 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.
#ifndef __PARSER_PDF_H__
#define __PARSER_PDF_H__
#include "ParserBase.h"
#include <string.h>
#define MAX_HEADER_LOOKUP 64
#define MAX_TAIL_LOOKUP 5
class ParserPDF : public ParserBase {
public:
ParserPDF(IParserStreamReceiver &receiver, size_t parser_depth);
virtual ~ParserPDF();
virtual size_t push(const char *buf, size_t len);
virtual void finish();
virtual const std::string &name() const;
virtual bool error() const;
virtual size_t depth() { return 1; }
private:
enum state {
s_start,
s_body,
s_end,
s_error
};
IParserStreamReceiver &m_receiver;
enum state m_state;
static const std::string m_parserName;
size_t m_parser_depth;
};
#endif // __PARSER_PDF_H__

View File

@@ -706,6 +706,7 @@ void SerializeToLocalAndRemoteSyncBase::syncWorker()
MessageMetadata req_md(getLearningHost(), 80);
req_md.insertHeader("X-Tenant-Id", agentDetails->getTenantId());
req_md.setConnectioFlag(MessageConnectionConfig::UNSECURE_CONN);
bool ok = messaging->sendSyncMessageWithoutResponse(
HTTPMethod::POST,
"/api/sync",

View File

@@ -156,6 +156,7 @@ void
WaapTrafficTelemetrics::updateMetrics(const string &asset_id, const DecisionTelemetryData &data)
{
initMetrics();
average_latency.report(data.elapsedTime);
switch (data.method)
{
case POST:

View File

@@ -12,6 +12,7 @@
// limitations under the License.
#include <string>
#include <cctype>
#include <boost/regex.hpp>
#include "WaapOverrideFunctor.h"
#include "Waf2Engine.h"
@@ -47,14 +48,16 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const Waap::Util::C
bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex& rx)
{
boost::cmatch what;
std::string tagLower = tag;
std::transform(tagLower.begin(), tagLower.end(), tagLower.begin(), ::tolower);
try {
if (tag == "url") {
return NGEN::Regex::regexMatch(__FILE__, __LINE__, waf2Transaction.getUri().c_str(), what, rx);
if (tagLower == "url") {
return NGEN::Regex::regexMatch(__FILE__, __LINE__, waf2Transaction.getUriStr().c_str(), what, rx);
}
else if (tag == "hostname") {
else if (tagLower == "hostname") {
return NGEN::Regex::regexMatch(__FILE__, __LINE__, waf2Transaction.getHost().c_str(), what, rx);
}
else if (tag == "sourceidentifier") {
else if (tagLower == "sourceidentifier") {
return NGEN::Regex::regexMatch(
__FILE__,
__LINE__,
@@ -63,7 +66,7 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
rx
);
}
else if (tag == "keyword") {
else if (tagLower == "keyword") {
for (const std::string& keywordStr : waf2Transaction.getKeywordMatches()) {
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, keywordStr.c_str(), what, rx)) {
return true;
@@ -71,7 +74,7 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
}
return false;
}
else if (tag == "paramname" || tag == "paramName") {
else if (tagLower == "paramname") {
for (const DeepParser::KeywordInfo& keywordInfo : waf2Transaction.getKeywordInfo()) {
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, keywordInfo.getName().c_str(), what, rx)) {
return true;
@@ -85,7 +88,7 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
}
return false;
}
else if (tag == "paramvalue" || tag == "paramValue") {
else if (tagLower == "paramvalue") {
for (const DeepParser::KeywordInfo& keywordInfo : waf2Transaction.getKeywordInfo()) {
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, keywordInfo.getValue().c_str(), what, rx)) {
return true;
@@ -96,10 +99,10 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
}
return false;
}
else if (tag == "paramlocation" || tag == "paramLocation") {
else if (tagLower == "paramlocation") {
return NGEN::Regex::regexMatch(__FILE__, __LINE__, waf2Transaction.getLocation().c_str(), what, rx);
}
else if (tag == "responsebody" || tag == "responseBody") {
else if (tagLower == "responsebody") {
waf2Transaction.getResponseInspectReasons().setApplyOverride(true);
if (!waf2Transaction.getResponseBody().empty()) {
boost::smatch matcher;
@@ -108,6 +111,32 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
} else {
return false;
}
} else if (tagLower == "headername") {
if (!waf2Transaction.checkIsHeaderOverrideScanRequired()) {
dbgDebug(D_WAAP_OVERRIDE) << "Header name override scan is not required";
return false;
}
for (auto& hdr_pair : waf2Transaction.getHdrPairs()) {
std::string value = hdr_pair.first;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if(NGEN::Regex::regexMatch(__FILE__, __LINE__, value.c_str(), what, rx)) {
return true;
}
}
return false;
} else if (tagLower == "headervalue") {
if (!waf2Transaction.checkIsHeaderOverrideScanRequired()) {
dbgDebug(D_WAAP_OVERRIDE) << "Header value override scan is not required";
return false;
}
for (auto& hdr_pair : waf2Transaction.getHdrPairs()) {
std::string value = hdr_pair.second;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, value.c_str(), what, rx)) {
return true;
}
}
return false;
}
}
catch (std::runtime_error & e) {
@@ -116,6 +145,6 @@ bool WaapOverrideFunctor::operator()(const std::string& tag, const boost::regex&
}
// Unknown tag: should not occur
dbgWarning(D_WAAP) << "Invalid override tag:" << tag;
dbgWarning(D_WAAP) << "Invalid override tag: " << tag;
return false;
}

View File

@@ -26,7 +26,8 @@ namespace Waap {
errorLimiter(false),
rateLimiting(false),
collectResponseForLog(false),
applyOverride(false)
applyOverride(false),
triggerReport(false)
{
}
@@ -39,11 +40,12 @@ namespace Waap {
" RateLimiting=" << rateLimiting <<
" ErrorLimiter=" << errorLimiter <<
" collectResponseForLog=" << collectResponseForLog <<
" applyOverride=" << applyOverride;
" applyOverride=" << applyOverride <<
" triggerReport=" << triggerReport;
return
openRedirect || errorDisclosure || rateLimiting || errorLimiter ||
collectResponseForLog || applyOverride;
collectResponseForLog || applyOverride || triggerReport;
}
void
@@ -91,6 +93,14 @@ namespace Waap {
applyOverride = flag;
}
void
ResponseInspectReasons::setTriggerReport(bool flag)
{
dbgTrace(D_WAAP) << "Change ResponseInspectReasons(setTriggerReport) " << triggerReport << " to " <<
flag;
triggerReport = flag;
}
bool
ResponseInspectReasons::getApplyOverride(void)
{

View File

@@ -25,6 +25,7 @@ public:
void setErrorLimiter(bool flag);
void setCollectResponseForLog(bool flag);
void setApplyOverride(bool flag);
void setTriggerReport(bool flag);
bool getApplyOverride(void);
private:
@@ -34,6 +35,7 @@ private:
bool rateLimiting;
bool collectResponseForLog;
bool applyOverride;
bool triggerReport;
};
}

View File

@@ -56,8 +56,10 @@ USE_DEBUG_FLAG(D_WAAP);
USE_DEBUG_FLAG(D_WAAP_ULIMITS);
USE_DEBUG_FLAG(D_WAAP_BOT_PROTECTION);
USE_DEBUG_FLAG(D_OA_SCHEMA_UPDATER);
USE_DEBUG_FLAG(D_WAAP_HEADERS);
using namespace ReportIS;
using namespace std;
#define MAX_REQUEST_BODY_SIZE (2*1024)
#define MAX_RESPONSE_BODY_SIZE (2*1024)
@@ -89,6 +91,9 @@ void Waf2Transaction::start_response(int response_status, int http_version)
dbgTrace(D_WAAP) << "[transaction:" << this << "] start_response(response_status=" << response_status
<< "," << " http_version=" << http_version << ")";
m_responseStatus = response_status;
if (m_triggerReport) {
m_responseInspectReasons.setTriggerReport(false);
}
if(m_responseStatus == 404)
{
@@ -233,6 +238,12 @@ void Waf2Transaction::end_response_body()
dbgTrace(D_WAAP) << "[transaction:" << this << "] end_response_body";
}
const std::string &
Waf2Transaction::getResponseBody()
{
return m_response_body;
}
void Waf2Transaction::scanErrDisclosureBuffer()
{
if (m_responseStatus >= 400 && m_responseStatus <= 599) {
@@ -302,19 +313,25 @@ Waf2Transaction::Waf2Transaction() :
m_deepParser(m_pWaapAssetState, m_scanner, this),
m_deepParserReceiver(m_deepParser),
m_scanResult(NULL),
m_response_body(),
m_request_body_bytes_received(0),
m_response_body_bytes_received(0),
m_processedUri(false),
m_processedHeaders(false),
m_isHeaderOverrideScanRequired(false),
m_isScanningRequired(false),
m_responseStatus(0),
m_responseInspectReasons(),
m_responseInjectReasons(),
m_index(-1),
m_triggerLog(),
m_triggerReport(false),
is_schema_validation(false),
m_waf2TransactionFlags()
{}
{
I_TimeGet *timeGet = Singleton::Consume<I_TimeGet>::by<Waf2Transaction>();
m_entry_time = chrono::duration_cast<chrono::milliseconds>(timeGet->getMonotonicTime());
}
Waf2Transaction::Waf2Transaction(std::shared_ptr<WaapAssetState> pWaapAssetState) :
TableOpaqueSerialize<Waf2Transaction>(this),
@@ -334,19 +351,25 @@ Waf2Transaction::Waf2Transaction(std::shared_ptr<WaapAssetState> pWaapAssetState
m_deepParser(m_pWaapAssetState, m_scanner, this),
m_deepParserReceiver(m_deepParser),
m_scanResult(NULL),
m_response_body(),
m_request_body_bytes_received(0),
m_response_body_bytes_received(0),
m_processedUri(false),
m_processedHeaders(false),
m_isHeaderOverrideScanRequired(false),
m_isScanningRequired(false),
m_responseStatus(0),
m_responseInspectReasons(),
m_responseInjectReasons(),
m_index(-1),
m_triggerLog(),
m_triggerReport(false),
is_schema_validation(false),
m_waf2TransactionFlags()
{}
{
I_TimeGet *timeGet = Singleton::Consume<I_TimeGet>::by<Waf2Transaction>();
m_entry_time = chrono::duration_cast<chrono::milliseconds>(timeGet->getMonotonicTime());
}
Waf2Transaction::~Waf2Transaction() {
dbgTrace(D_WAAP) << "Waf2Transaction::~Waf2Transaction: deleting m_requestBodyParser";
@@ -362,6 +385,8 @@ HeaderType Waf2Transaction::detectHeaderType(const char* name, int name_len) {
static const char content_type[] = "content-Type";
static const char cookie[] = "cookie";
static const char referer[] = "referer";
static const char authorization[] = "authorization";
dbgTrace(D_WAAP_HEADERS) << "Detecting header type for header >>>" << std::string(name, name_len) << "<<<";
if (memcaseinsensitivecmp(name, name_len, host, sizeof(host) - 1)) {
return HeaderType::HOST_HEADER;
@@ -378,6 +403,9 @@ HeaderType Waf2Transaction::detectHeaderType(const char* name, int name_len) {
if (memcaseinsensitivecmp(name, name_len, referer, sizeof(referer) - 1)) {
return HeaderType::REFERER_HEADER;
}
if (memcaseinsensitivecmp(name, name_len, authorization, sizeof(authorization) - 1)) {
return HeaderType::AUTHORIZATION_HEADER;
}
return UNKNOWN_HEADER;
}
@@ -422,37 +450,100 @@ HeaderType Waf2Transaction::checkCleanHeader(const char* name, int name_len, con
return CLEAN_HEADER;
}
}
static const std::string authorization("authorization");
if (memcaseinsensitivecmp(name, name_len, authorization.data(), authorization.size())) {
dbgTrace(D_WAAP) << "[transaction:" << this << "] special header '" << std::string(name, name_len) <<
"' - detect base64 to determine cleanliness ...";
std::string result;
int decodedCount = 0;
int deletedCount = 0;
std::string v(value, value_len);
boost::algorithm::to_lower(v);
const std::string negotiate("negotiate ");
if (boost::algorithm::starts_with(v, negotiate)) {
v = v.substr(negotiate.size(), v.size() - negotiate.size());
// Detect potential base64 match after the "Negotiate " prefix
Waap::Util::b64Decode(v, b64DecodeChunk, decodedCount, deletedCount, result);
if (result.empty() && (deletedCount + decodedCount == 1)) {
// Decoded 1 base64 chunk and nothing left behind it
dbgTrace(D_WAAP) << "[transaction:" << this << "] special header '" <<
std::string(name, name_len) << " is clean";
return CLEAN_HEADER;
}
}
}
}
return UNKNOWN_HEADER;
}
void Waf2Transaction::parseAuthorization(const char* value, int value_len, const std::string &header_name)
{
#ifdef NO_HEADERS_SCAN
return;
#endif
static const std::string authorization("authorization");
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this
<< "] special header '" << header_name;
std::string result;
std::string v(value, value_len);
int decodedCount;
int deletedCount;
static const char *bearer = "bearer ";
static const std::string bearer_str(bearer);
if (memcaseinsensitivecmp(value, sizeof(bearer) - 1, bearer, sizeof(bearer) - 1)) {
dbgTrace(D_WAAP_HEADERS) << "Checking JWT chunk for alg = none" << v;
// parse buffer v till first dot,
// convert this chunk from base 64 to json string and get value for "alg" key
// if alg == "none" then this is JWT with no signature and we should block it
// if alg != "none" then this is JWT with signature and we should not block it
char* p = (char*)v.c_str() + bearer_str.size();
char* p1 = strchr(p, '.');
if (p1 != NULL) {
*p1 = '\0';
std::string header(p);
alignBase64Chunk(header);
Waap::Util::b64Decode(header, b64DecodeChunk, decodedCount, deletedCount, result);
if (!result.empty()) {
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] special header '" << header_name
<< " base64 encoded for JWT alg testing";
// parse JSON string and get value for "alg" key and check its value to be equal to "none"
// if alg == "none" then this is JWT with no signature and we should block it
boost::algorithm::to_lower(result);
static bool err = false;
static const SingleRegex invalid_jwt_alg_re(
"\"alg\"\\s*:\\s*\"none\"",
err,
"invalid_jwt_alg_"
);
if (invalid_jwt_alg_re.hasMatch(result)) {
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] special header '" << header_name
<< " bad JWT algotitm detected, blocking";
Waf2ScanResult new_res = Waf2ScanResult();
new_res.location = "header";
new_res.param_name = header_name;
new_res.unescaped_line = header_name + ": " + result;
new_res.score = 9.0;
new_res.keyword_matches.push_back("alg:none");
new_res.scoreArray.push_back(9.0);
new_res.attack_types.insert("JWT");
if (m_scanResult != NULL) {
dbgTrace(D_WAAP_HEADERS) << "LastScanResult = " << m_scanResult;
m_scanResult->mergeFrom(new_res);
} else {
dbgTrace(D_WAAP_HEADERS) << "LastScanResult is NULL";
m_scanResult = new Waf2ScanResult(new_res);
}
return;
}
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] special header '" << header_name
<< " clean JWT algotitm detected";
return;
} else {
parseGenericHeaderValue(header_name, value, value_len);
}
}
return;
}
static const char *negotiate = "negotiate ";
static const std::string negotiate_str(negotiate);
if (memcaseinsensitivecmp(value, sizeof(negotiate) - 1, negotiate, sizeof(negotiate) - 1)) {
v = v.substr(negotiate_str.size(), v.size() - negotiate_str.size());
// Detect potential base64 match after the "Negotiate " prefix
alignBase64Chunk(v);
Waap::Util::b64Decode(v, b64DecodeChunk, decodedCount, deletedCount, result);
if (result.empty() && (deletedCount + decodedCount == 1)) {
// Decoded 1 base64 chunk and nothing left behind it
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] special header '" << header_name
<< " is clean - clean base64 for negotiate";
return;
} else {
parseGenericHeaderValue(header_name, value, value_len);
}
}
}
// Methods below are callbacks that are called during HTTP transaction processing by the front-end server/proxy
void Waf2Transaction::start() {
dbgTrace(D_WAAP) << "[Waf2Transaction::start():" << this << "] start";
@@ -744,14 +835,14 @@ void Waf2Transaction::parseContentType(const char* value, int value_len)
ctp.push(value, value_len);
ctp.finish();
dbgTrace(D_WAAP) << "[transaction:" << this << "] ctp detected content type: '" <<
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] ctp detected content type: '" <<
ctp.contentTypeDetected.c_str() << "'";
// The above fills m_contentTypeDetected
m_contentType = Waap::Util::detectContentType(ctp.contentTypeDetected.c_str());
// extract boundary string required for parsing multipart-form-data stream
if (m_contentType == Waap::Util::CONTENT_TYPE_MULTIPART_FORM) {
dbgTrace(D_WAAP) << "content_type detected: " << Waap::Util::getContentTypeStr(m_contentType) <<
dbgTrace(D_WAAP_HEADERS) << "content_type detected: " << Waap::Util::getContentTypeStr(m_contentType) <<
"; boundary='" << ctp.boundaryFound.c_str() << "'";
m_deepParser.setMultipartBoundary(ctp.boundaryFound);
}
@@ -773,7 +864,7 @@ void Waf2Transaction::parseCookie(const char* value, int value_len)
#endif
if (value_len > 0) {
dbgTrace(D_WAAP) << "[transaction:" << this << "] scanning the cookie value";
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] scanning the cookie value";
m_deepParser.m_key.push("cookie", 6);
ParserUrlEncode cookieValueParser(m_deepParserReceiver, 0, ';');
cookieValueParser.push(value, value_len);
@@ -788,7 +879,7 @@ void Waf2Transaction::parseReferer(const char* value, int value_len)
#ifdef NO_HEADERS_SCAN
return;
#endif
dbgTrace(D_WAAP) << "Parsed Referer. Referer URI: " << m_uriReferer;
dbgTrace(D_WAAP_HEADERS) << "Parsed Referer. Referer URI: " << m_uriReferer;
std::string referer(value, value_len);
std::vector<RegexMatch> regex_matches;
@@ -815,7 +906,7 @@ void Waf2Transaction::parseUnknownHeaderName(const char* name, int name_len)
// Apply signatures on all other, header names, unless they are considered "good" ones to skip scanning them.
if (name_len &&
!m_pWaapAssetState->getSignatures()->good_header_name_re.hasMatch(std::string(name, name_len))) {
dbgTrace(D_WAAP) << "[transaction:" << this << "] scanning the header name";
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] scanning the header name";
m_deepParser.m_key.push("header", 6);
ParserRaw headerNameParser(m_deepParserReceiver, 0, std::string(name, name_len));
headerNameParser.push(name, name_len);
@@ -834,7 +925,7 @@ void Waf2Transaction::parseGenericHeaderValue(const std::string &headerName, con
return;
}
dbgTrace(D_WAAP) << "[transaction:" << this << "] scanning the header value";
dbgTrace(D_WAAP_HEADERS) << "[transaction:" << this << "] scanning the header value";
m_deepParser.m_key.push("header", 6);
ParserRaw headerValueParser(m_deepParserReceiver, 0, headerName);
headerValueParser.push(value, value_len);
@@ -844,11 +935,11 @@ void Waf2Transaction::parseGenericHeaderValue(const std::string &headerName, con
};
// Scan relevant headers to detect attacks inside them
void Waf2Transaction::scanSpecificHeder(const char* name, int name_len, const char* value, int value_len)
void Waf2Transaction::scanSpecificHeader(const char* name, int name_len, const char* value, int value_len)
{
HeaderType header_t = detectHeaderType(name, name_len);
std::string headerName = std::string(name, name_len);
dbgTrace(D_WAAP_HEADERS) << "Processing the header " << headerName;
switch (header_t)
{
case HeaderType::COOKIE_HEADER:
@@ -857,6 +948,9 @@ void Waf2Transaction::scanSpecificHeder(const char* name, int name_len, const ch
case HeaderType::REFERER_HEADER:
parseReferer(value, value_len);
break;
case HeaderType::AUTHORIZATION_HEADER:
parseAuthorization(value, value_len, headerName);
break;
case HeaderType::UNKNOWN_HEADER: {
HeaderType headerType = checkCleanHeader(name, name_len, value, value_len);
if(headerType == HeaderType::CLEAN_HEADER) {
@@ -954,7 +1048,7 @@ void Waf2Transaction::scanHeaders()
// Scan relevant headers for attacks
for (auto it = hdrs_map.begin(); it != hdrs_map.end(); ++it)
{
scanSpecificHeder(it->first.c_str(), it->first.size(),
scanSpecificHeader(it->first.c_str(), it->first.size(),
it->second.c_str(), it->second.size());
}
}
@@ -1171,6 +1265,12 @@ void Waf2Transaction::end_request() {
// Enable response headers processing if response scanning is enabled in policy
auto errorDisclosurePolicy = m_siteConfig ? m_siteConfig->get_ErrorDisclosurePolicy() : NULL;
m_responseInspectReasons.setErrorDisclosure(errorDisclosurePolicy && errorDisclosurePolicy->enable);
auto triggerPolicy = m_siteConfig ? m_siteConfig->get_TriggerPolicy() : NULL;
if (isTriggerReportExists(triggerPolicy)) {
m_responseInspectReasons.setTriggerReport(true);
dbgTrace(D_WAAP) << "setTriggerReport(true)";
}
}
void Waf2Transaction::extractEnvSourceIdentifier()
@@ -1393,6 +1493,7 @@ Waf2Transaction::decideAfterHeaders()
return false;
}
m_isHeaderOverrideScanRequired = true;
m_overrideState = getOverrideState(sitePolicy);
// Select scores pool by location (but use forced pool when forced)
@@ -1648,6 +1749,12 @@ Waf2Transaction::sendLog()
const auto& autonomousSecurityDecision = std::dynamic_pointer_cast<AutonomousSecurityDecision>(
m_waapDecision.getDecision(AUTONOMOUS_SECURITY_DECISION));
I_TimeGet *timeGet = Singleton::Consume<I_TimeGet>::by<Waf2Transaction>();
auto finish = timeGet->getMonotonicTime();
auto diff = chrono::duration_cast<chrono::milliseconds>(finish) - m_entry_time;
telemetryData.elapsedTime = diff.count();
dbgTrace(D_WAAP) << "latency updated, time: " << diff.count() << "ms";
if (m_methodStr == "POST") {
telemetryData.method = POST;
} else if (m_methodStr == "GET") {

View File

@@ -38,6 +38,7 @@
#include "i_transaction.h"
#include "i_waap_telemetry.h"
#include "i_deepAnalyzer.h"
#include "i_time_get.h"
#include "table_opaque.h"
#include "WaapResponseInspectReasons.h"
#include "WaapResponseInjectReasons.h"
@@ -45,6 +46,7 @@
#include "WaapOpenRedirectPolicy.h"
#include "WaapScanner.h"
#include "singleton.h"
#include "OASchemaUpdaterConfConstant.h"
struct DecisionTelemetryData;
class Waf2Transaction;
@@ -60,6 +62,7 @@ class Waf2Transaction :
public Singleton::Consume<I_Table>,
private boost::noncopyable,
Singleton::Consume<I_AgentDetails>,
Singleton::Consume<I_TimeGet>,
Singleton::Consume<I_Environment>
{
public:
@@ -76,6 +79,7 @@ public:
void set_host(const char *host);
// getters
uint64_t getElapsedTime() const;
const std::string& getRemoteAddr() const;
virtual const std::string getUri() const;
const std::string getUriStr() const;
@@ -143,6 +147,7 @@ public:
void start_response_body();
void add_response_body_chunk(const char* data, int data_len);
void end_response_body();
const std::string & getResponseBody();
void end_response();
void extractEnvSourceIdentifier();
void finish();
@@ -212,19 +217,21 @@ public:
}
// LCOV_EXCL_STOP
bool reportScanResult(const Waf2ScanResult &res);
bool shouldIgnoreOverride(const Waf2ScanResult &res);
Waap::OpenRedirect::State &getOpenRedirectState() { return m_openRedirectState; }
IWaapConfig* getSiteConfig() { return m_siteConfig; }
void addNote(const std::string &note) { m_notes.push_back(note); }
const std::string &getResponseBody(void) const { return m_response_body; }
Waap::ResponseInspectReasons &getResponseInspectReasons(void) { return m_responseInspectReasons; }
// LCOV_EXCL_START Reason: This function is tested in system tests
bool checkIsHeaderOverrideScanRequired();
// LCOV_EXCL_STOP
private:
int finalizeDecision(IWaapConfig *sitePolicy, bool shouldBlock);
const std::shared_ptr<Waap::Trigger::Log> getTriggerLog(const std::shared_ptr<Waap::Trigger::Policy>&
triggerPolicy) const;
bool isTriggerReportExists(const std::shared_ptr<Waap::Trigger::Policy> &triggerPolicy);
void sendAutonomousSecurityLog(
const std::shared_ptr<Waap::Trigger::Log>& triggerLog,
bool shouldBlock,
@@ -261,15 +268,17 @@ private:
void parseContentType(const char* value, int value_len);
void parseCookie(const char* value, int value_len);
void parseReferer(const char* value, int value_len);
void parseAuthorization(const char* value, int value_len, const std::string &header_name);
void parseUnknownHeaderName(const char* name, int name_len);
void parseGenericHeaderValue(const std::string &headerName, const char* value, int value_len);
void scanSpecificHeder(const char* name, int name_len, const char* value, int value_len);
void scanSpecificHeader(const char* name, int name_len, const char* value, int value_len);
void detectSpecificHeader(const char* name, int name_len, const char* value, int value_len);
void detectHeaders();
void scanHeaders();
void clearRequestParserState();
void scanErrDisclosureBuffer();
std::chrono::milliseconds m_entry_time;
std::shared_ptr<WaapAssetState> m_pWaapAssetState;
bool m_ignoreScore; // override the scoring filter and (effectively) take the last suspicious parameter,
// instead of the one with highest score that is > SCORE_THRESHOLD
@@ -334,6 +343,7 @@ private:
bool m_processedUri;
bool m_processedHeaders;
bool m_isHeaderOverrideScanRequired;
bool m_isScanningRequired;
int m_responseStatus;
Waap::ResponseInspectReasons m_responseInspectReasons;
@@ -345,6 +355,7 @@ private:
// Cached pointer to const triggerLog (hence mutable)
mutable std::shared_ptr<Waap::Trigger::Log> m_triggerLog;
bool m_triggerReport;
bool is_schema_validation = false;
Waf2TransactionFlags m_waf2TransactionFlags;
};

View File

@@ -551,6 +551,11 @@ const std::set<std::string> Waf2Transaction::getFoundPatterns() const
return m_found_patterns;
}
bool Waf2Transaction::checkIsHeaderOverrideScanRequired()
{
return m_isHeaderOverrideScanRequired;
}
Waap::Override::State Waf2Transaction::getOverrideState(IWaapConfig* sitePolicy)
{
Waap::Override::State overrideState;
@@ -566,6 +571,7 @@ Waap::Override::State Waf2Transaction::getOverrideState(IWaapConfig* sitePolicy)
if (overridePolicy) { // later we will run response overrides
overrideStateResponse.applyOverride(*overridePolicy, WaapOverrideFunctor(*this), m_matchedOverrideIds, false);
}
m_isHeaderOverrideScanRequired = false;
return overrideStateResponse;
}
@@ -592,6 +598,23 @@ const std::shared_ptr<Waap::Trigger::Log> Waf2Transaction::getTriggerLog(const s
return m_triggerLog;
}
bool Waf2Transaction::isTriggerReportExists(const std::shared_ptr<
Waap::Trigger::Policy> &triggerPolicy)
{
if (!triggerPolicy) {
return false;
}
if (m_triggerReport) {
return m_triggerReport;
}
for (const Waap::Trigger::Trigger &trigger : triggerPolicy->triggers) {
if (trigger.triggerType == "report") {
return m_triggerReport = true;
}
}
return m_triggerReport;
}
ReportIS::Severity Waf2Transaction::computeEventSeverityFromDecision() const
{
DecisionType type = m_waapDecision.getHighestPriorityDecisionToLog();

View File

@@ -732,6 +732,13 @@ inline void replaceAll(std::string& str, const std::string& from, const std::str
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
inline void alignBase64Chunk (std::string &chunk)
{
size_t len = chunk.length() % 4;
if (len >= 2) {
chunk.append(4-len, '=');
}
}
// Count items in v that are not in ignored_set
inline size_t countNotInSet(const std::vector<std::string> &v, const std::set<std::string> &ignored_set) {