compatible. To learn more about Hyperscan see
https://intel.github.io/hyperscan/dev-reference/
-==== Syntax
+==== Syntax
Snort provides `sd_pattern` as IPS rule option with no additional inspector
overhead. The Rule option takes the following syntax.
was matched by the patterns. This configuration is enabled by default.
ips =
- {
+ {
obfuscate_pii = true
}
in the config. So, Snort must be built and run with Hyperscan to have sd_pattern
IPS option available.
-2. Log obfuscation is only applicable to CMG and Unified2 logging formats.
+2. Log obfuscation is only applicable to the buffer that the pattern was found in.
-3. Log obfuscation doesn't support user defined PII patterns. It is
-currently only supported for the built-in patterns for Credit Cards and U.S.
-Social Security numbers.
+3. Log obfuscation is only applicable to CMG and Unified2 logging formats.
+Unified2 logger only supports obfuscation of http extra data.
-4. Log obfuscation doesn't work with stream rebuilt packet payloads. (This
-is a known bug).
+4. Log obfuscation doesn't support user defined PII patterns. It is
+currently only supported for the built-in patterns for Credit Cards and U.S.
+Social Security numbers, emails and U.S. phone numbers.
struct hsContext
{
hsContext(const SdPatternConfig& c_, Packet* p_, const uint8_t* const start_,
- const uint8_t* _buf, unsigned int _buflen )
- : config(c_), packet(p_), start(start_), buf(_buf), buflen(_buflen) { }
+ const uint8_t* _buf, unsigned int _buf_len, const char* _buf_name)
+ : config(c_), packet(p_), start(start_), buf(_buf), buf_name(_buf_name), buf_len(_buf_len)
+ { }
bool has_valid_bounds(unsigned long long from, unsigned long long len)
{
// validate the right side
- if ( from+len == buflen )
+ if ( from+len == buf_len )
right = true;
- else if ( from + len < buflen && !::isdigit((int)buf[from+len]) )
+ else if ( from + len < buf_len && !::isdigit((int)buf[from+len]) )
right = true;
return left and right;
Packet* packet = nullptr;
const uint8_t* const start = nullptr;
const uint8_t* buf = nullptr;
- unsigned int buflen = 0;
+ const char* buf_name;
+ unsigned int buf_len = 0;
+ bool buf_set = false;
};
static int hs_match(unsigned int /*id*/, unsigned long long from,
if ( !ctx->packet->obfuscator )
ctx->packet->obfuscator = new Obfuscator();
+ if ( !ctx->buf_set )
+ {
+ ctx->packet->obfuscator->set_buffer(ctx->buf_name);
+ ctx->buf_set = true;
+ }
+
// FIXIT-L Make configurable or don't show any PII partials (0 for user defined??)
uint32_t off = ctx->buf + from - ctx->start;
ctx->packet->obfuscator->push(off, len - 4);
{
const uint8_t* const start = c.buffer();
const uint8_t* buf = c.start();
- unsigned int buflen = c.length();
+ unsigned int buf_len = c.length();
- hsContext ctx(config, p, start, buf, buflen);
+ hsContext ctx(config, p, start, buf, buf_len, c.get_name());
- hs_error_t stat = hs_scan(config.db, (const char*)buf, buflen, 0,
+ hs_error_t stat = hs_scan(config.db, (const char*)buf, buf_len, 0,
scratcher->get(), hs_match, (void*)&ctx);
if ( stat == HS_SCAN_TERMINATED )
bool Obfuscator::first(ObfuscatorBlock &b)
{
- if ( blocks.empty() )
+ if (buffer_blocks.empty())
return false;
- it = blocks.begin();
+ if (cur_buf->second.empty())
+ return false;
+ it = cur_buf->second.begin();
b = *it;
return true;
}
bool Obfuscator::next(ObfuscatorBlock &b)
{
- if ( blocks.empty() )
+ if (buffer_blocks.empty())
+ return false;
+ if (cur_buf->second.empty())
return false;
- if ( it == blocks.end() )
+ if (it == cur_buf->second.end())
return false;
- if ( ++it == blocks.end() )
+ if (++it == cur_buf->second.end())
return false;
b = *it;
return true;
#include <cstddef>
#include <cstdint>
#include <set>
+#include <string>
+#include <unordered_map>
#include "main/snort_types.h"
};
using ObSet = std::set<ObfuscatorBlock, BlockCompare>;
+ using BufBlocks = std::unordered_map<std::string/*buf_name*/, ObSet>;
using const_iterator = ObSet::const_iterator;
using iterator = ObSet::iterator;
+ Obfuscator()
+ {
+ cur_buf = buffer_blocks.begin();
+ }
+
void push(uint32_t offset, uint32_t length)
{
- const auto push_res = blocks.emplace(offset, length);
-
+ if (cur_buf == buffer_blocks.end())
+ set_buffer("");
+ const auto push_res = cur_buf->second.emplace(offset, length);
+
if (!push_res.second and length > push_res.first->length)
{
- blocks.erase(push_res.first);
- blocks.emplace(offset, length);
+ cur_buf->second.erase(push_res.first);
+ cur_buf->second.emplace(offset, length);
}
}
+ bool select_buffer(const char* buf_key)
+ {
+ if (!buf_key)
+ return false;
+
+ auto buf = buffer_blocks.find(buf_key);
+ if (buf == buffer_blocks.end())
+ return false;
+ cur_buf = buf;
+ return true;
+ }
+
+ void set_buffer(const char* buf_key)
+ {
+ if (!buf_key)
+ return;
+
+ cur_buf = buffer_blocks.emplace(buf_key, ObSet()).first;
+ }
+
const_iterator begin() const
- { return blocks.cbegin(); }
+ { return cur_buf->second.cbegin(); }
const_iterator end() const
- { return blocks.cend(); }
+ { return cur_buf->second.cend(); }
bool first(ObfuscatorBlock &b);
bool next(ObfuscatorBlock &b);
{ return mask_char; }
private:
- ObSet blocks;
+ BufBlocks buffer_blocks;
+ BufBlocks::iterator cur_buf;
iterator it;
- static const char mask_char = 'X';
+ static constexpr char mask_char = 'X';
};
}
}
}
+static void ObfuscateLogNetData(TextLog* log, const uint8_t* data, const int len,
+ Packet* p, const char* buf_name, const char* buf_key, const char* ins_name)
+{
+ // FIXIT-P avoid string copy
+ std::string buf((const char*)data, len);
+ auto obf = p->obfuscator;
+
+ if ( obf and obf->select_buffer(buf_key) )
+ for ( const auto& b : *obf )
+ buf.replace(b.offset, b.length, b.length, obf->get_mask_char());
+
+ LogNetData(log, (const uint8_t*)buf.c_str(), len, p, buf_name, ins_name);
+}
+
using BufferIds = std::vector<unsigned>;
//-------------------------------------------------------------------------
InspectionBuffer buf;
if ( gadget->get_buf(id, p, buf) )
- LogNetData(fast_log, buf.data, buf.len, p, buffers[id-1], ins_name);
+ ObfuscateLogNetData(fast_log, buf.data, buf.len, p, buffers[id-1], buffers[id-1], ins_name);
log_pkt = (idv == rsp_ids);
}
}
if (p->has_ip())
LogIPPkt(fast_log, p);
-
- else if ( log_pkt and p->obfuscator )
- {
- // FIXIT-P avoid string copy
- std::string buf((const char*)p->data, p->dsize);
-
- for ( const auto& b : *p->obfuscator )
- buf.replace(b.offset, b.length, b.length, p->obfuscator->get_mask_char());
-
- LogNetData(fast_log, (const uint8_t*)buf.c_str(), p->dsize, p, nullptr, ins_name);
- }
else if ( log_pkt )
- LogNetData(fast_log, p->data, p->dsize, p, nullptr, ins_name);
+ ObfuscateLogNetData(fast_log, p->data, p->dsize, p, nullptr, "pkt_data", ins_name);
DataBuffer& buf = DetectionEngine::get_alt_buffer(p);
#include "detection/signature.h"
#include "detection/detection_util.h"
+#include "detection/detection_engine.h"
#include "events/event.h"
#include "framework/logger.h"
#include "framework/module.h"
Unified2Write(write_pkt_buffer, write_len, config);
}
+static void apply_mask(Obfuscator* obf, uint8_t* buf, const char* buf_key)
+{
+ if ( obf->select_buffer(buf_key) )
+ for ( const auto& b : *obf )
+ memset(buf + b.offset, obf->get_mask_char(), b.length);
+}
+
+static void obfuscate(uint8_t* buf, Obfuscator* obf, uint32_t type)
+{
+ switch (type)
+ {
+ case EVENT_INFO_HTTP_URI:
+ apply_mask(obf, buf, "http_uri");
+ break;
+ case EVENT_INFO_JSNORM_DATA:
+ apply_mask(obf, buf, "file_data");
+ break;
+ default:
+ break;
+ }
+}
+
static void _WriteExtraData(Unified2Config* config,
+ Obfuscator* obf,
uint32_t event_id,
uint32_t event_second,
const uint8_t* buffer,
memcpy_s(ptr + offset, sizeof(write_buffer) - offset, buffer, len);
+ if (obf)
+ obfuscate(ptr + offset, obf, type);
+
Unified2Write(write_buffer, write_len, config);
}
xid = ffs(xtradata_mask);
+ const IpsContext* c = DetectionEngine::get_context();
+ Obfuscator* obf = (c and c->packet) ? c->packet->obfuscator : nullptr;
+
while ( xid && (xid <= max_count) )
{
uint32_t len = 0;
if ( log_func(flow, &write_buffer, &len, &type) && (len > 0) )
{
- _WriteExtraData(config, event_id, event_second, write_buffer, len, type);
+ _WriteExtraData(config, obf, event_id, event_second, write_buffer, len, type);
}
xtradata_mask ^= BIT(xid);
xid = ffs(xtradata_mask);
if ( p->is_data() )
off = 0;
- for ( const auto& b : *p->obfuscator )
- memset(&start[ off + b.offset ], p->obfuscator->get_mask_char(), b.length);
+ if ( p->obfuscator->select_buffer("pkt_data") )
+ for ( const auto& b : *p->obfuscator )
+ memset(&start[ off + b.offset ], p->obfuscator->get_mask_char(), b.length);
}
}
if (p->ptrs.ip_api.is_ip6())
{
const SfIp* ip = p->ptrs.ip_api.get_src();
- _WriteExtraData(&config, event.get_event_id(), event.ref_time.tv_sec,
+ _WriteExtraData(&config, p->obfuscator, event.get_event_id(), event.ref_time.tv_sec,
(const uint8_t*) ip->get_ip6_ptr(), sizeof(struct in6_addr), EVENT_INFO_IPV6_SRC);
ip = p->ptrs.ip_api.get_dst();
- _WriteExtraData(&config, event.get_event_id(), event.ref_time.tv_sec,
+ _WriteExtraData(&config, p->obfuscator, event.get_event_id(), event.ref_time.tv_sec,
(const uint8_t*) ip->get_ip6_ptr(), sizeof(struct in6_addr), EVENT_INFO_IPV6_DST);
}
}