improves on the nfregex binary x2

This commit is contained in:
Domingo Dirutigliano
2025-02-03 15:18:22 +01:00
parent 3ffccdddfb
commit e8db930b40
4 changed files with 81 additions and 134 deletions

View File

@@ -35,9 +35,6 @@ All the configuration at the startup is customizable in [firegex.py](./start.py)
- Create basic firewall rules to allow and deny specific traffic, like ufw or iptables but using firegex graphic interface (by using [nftable](https://netfilter.org/projects/nftables/))
- Port Hijacking allows you to redirect the traffic on a specific port to another port. Thanks to this you can start your own proxy, connecting to the real service using the loopback interface. Firegex will be resposable about the routing of the packets using internally [nftables](https://netfilter.org/projects/nftables/)
DEPRECATED:
- TCP Proxy regex filter, create a proxy tunnel from the service internal port to a public port published by the proxy. Internally the c++ proxy filter the request with PCRE2 regexes. For mantaining the same public port you will need to open only in localhost the real services. (Available only on TCP/IPv4)
## Documentation
Find the documentation of the backend and of the frontend in the related README files
@@ -57,6 +54,5 @@ This means that firegex is projected to avoid any possibility to have the servic
## Why "Firegex"?
Initiially the project was based only on regex filters, and also now the main function uses regexes, but firegex have and will have also other filtering tools.
# Credits
- Copyright (c) 2007 Arash Partow (http://www.partow.net) for the base of our proxy implementation
# Credits
- Copyright (c) 2022 Pwnzer0tt1

View File

@@ -3,6 +3,7 @@
#include <linux/netfilter/nfnetlink_conntrack.h>
#include <tins/tins.h>
#include <tins/tcp_ip/stream_follower.h>
#include <tins/tcp_ip/stream_identifier.h>
#include <libmnl/libmnl.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
@@ -19,42 +20,9 @@ using namespace std;
#ifndef NETFILTER_CLASSES_HPP
#define NETFILTER_CLASSES_HPP
typedef Tins::TCPIP::StreamIdentifier stream_id;
typedef map<stream_id, hs_stream_t*> matching_map;
string inline client_endpoint(const Stream& stream) {
ostringstream output;
// Use the IPv4 or IPv6 address depending on which protocol the
// connection uses
if (stream.is_v6()) {
output << stream.client_addr_v6();
}
else {
output << stream.client_addr_v4();
}
output << ":" << stream.client_port();
return output.str();
}
// Convert the server endpoint to a readable string
string inline server_endpoint(const Stream& stream) {
ostringstream output;
if (stream.is_v6()) {
output << stream.server_addr_v6();
}
else {
output << stream.server_addr_v4();
}
output << ":" << stream.server_port();
return output.str();
}
// Concat both endpoints to get a readable stream identifier
string inline stream_identifier(const Stream& stream) {
ostringstream output;
output << client_endpoint(stream) << " - " << server_endpoint(stream);
return output.str();
}
typedef unordered_map<string, hs_stream_t*> matching_map;
struct packet_info;
@@ -89,7 +57,7 @@ struct stream_ctx {
struct packet_info {
string packet;
string payload;
string stream_id;
stream_id sid;
bool is_input;
bool is_tcp;
stream_ctx* sctx;
@@ -97,26 +65,6 @@ struct packet_info {
typedef bool NetFilterQueueCallback(packet_info &);
Tins::PDU * find_transport_layer(Tins::PDU* pkt, Tins::PDU::PDUType* type = nullptr, size_t* payload_size = nullptr){
Tins::PDU::PDUType fetched_type;
while(pkt != nullptr){
fetched_type = pkt->pdu_type();
if (fetched_type == Tins::PDU::TCP || fetched_type == Tins::PDU::UDP) {
if (type != nullptr){
*type = fetched_type;
}
if (payload_size != nullptr){
*payload_size = pkt->inner_pdu()->size();
}
return pkt;
}
pkt = pkt->inner_pdu();
}
return nullptr;
}
template <NetFilterQueueCallback callback_func>
class NetfilterQueue {
public:
@@ -194,29 +142,25 @@ class NetfilterQueue {
//Input data filtering
void on_client_data(Stream& stream) {
string data(stream.client_payload().begin(), stream.client_payload().end());
string stream_id = stream_identifier(stream);
this->sctx.tcp_match_util.pkt_info->is_input = true;
this->sctx.tcp_match_util.pkt_info->stream_id = stream_id;
this->sctx.tcp_match_util.matching_has_been_called = true;
sctx.tcp_match_util.pkt_info->is_input = true;
sctx.tcp_match_util.matching_has_been_called = true;
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
if (result){
this->clean_stream_by_id(stream_id);
clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid);
stream.ignore_client_data();
stream.ignore_server_data();
}
this->sctx.tcp_match_util.result = result;
sctx.tcp_match_util.result = result;
}
//Server data filtering
void on_server_data(Stream& stream) {
string data(stream.server_payload().begin(), stream.server_payload().end());
string stream_id = stream_identifier(stream);
this->sctx.tcp_match_util.pkt_info->is_input = false;
this->sctx.tcp_match_util.pkt_info->stream_id = stream_id;
this->sctx.tcp_match_util.matching_has_been_called = true;
sctx.tcp_match_util.pkt_info->is_input = false;
sctx.tcp_match_util.matching_has_been_called = true;
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
if (result){
this->clean_stream_by_id(stream_id);
clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid);
stream.ignore_client_data();
stream.ignore_server_data();
}
@@ -224,11 +168,10 @@ class NetfilterQueue {
}
void on_new_stream(Stream& stream) {
string stream_id = stream_identifier(stream);
if (stream.is_partial_stream()) {
return;
}
cout << "[+] New connection " << stream_id << endl;
cerr << "[+] New connection!" << endl;
stream.auto_cleanup_payloads(true);
stream.client_data_callback(
[&](auto a){this->on_client_data(a);}
@@ -238,7 +181,7 @@ class NetfilterQueue {
);
}
void clean_stream_by_id(string stream_id){
void clean_stream_by_id(stream_id stream_id){
auto stream_search = this->sctx.in_hs_streams.find(stream_id);
hs_stream_t* stream_match;
if (stream_search != this->sctx.in_hs_streams.end()){
@@ -263,8 +206,8 @@ class NetfilterQueue {
// A stream was terminated. The second argument is the reason why it was terminated
void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) {
string stream_id = stream_identifier(stream);
cout << "[+] Connection closed: " << stream_id << endl;
stream_id stream_id = stream_id::make_identifier(stream);
cerr << "[+] Connection closed: " << &stream_id << endl;
this->clean_stream_by_id(stream_id);
}
@@ -337,6 +280,66 @@ class NetfilterQueue {
}
sctx.out_hs_streams.clear();
}
template<typename T>
static void build_verdict(T packet, uint8_t *payload, uint16_t plen, nlmsghdr *nlh_verdict, nfqnl_msg_packet_hdr *ph, stream_ctx* sctx){
Tins::TCP* tcp = packet.template find_pdu<Tins::TCP>();
if (tcp){
Tins::PDU* application_layer = tcp->inner_pdu();
u_int16_t payload_size = 0;
if (application_layer != nullptr){
payload_size = application_layer->size();
}
packet_info pktinfo{
packet: string(payload, payload+plen),
payload: string(payload+plen - payload_size, payload+plen),
sid: stream_id::make_identifier(packet),
is_input: false, // Will be setted later in tcp stream follower
is_tcp: true,
sctx: sctx,
};
sctx->tcp_match_util.matching_has_been_called = false;
sctx->tcp_match_util.pkt_info = &pktinfo;
sctx->follower.process_packet(packet);
if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){
Tins::PDU* data_layer = tcp->release_inner_pdu();
if (data_layer != nullptr){
delete data_layer;
}
tcp->set_flag(Tins::TCP::FIN,1);
tcp->set_flag(Tins::TCP::ACK,1);
tcp->set_flag(Tins::TCP::SYN,0);
nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.serialize().data(), packet.size());
}
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
}else{
Tins::UDP* udp = packet.template find_pdu<Tins::UDP>();
if (!udp){
throw invalid_argument("Only TCP and UDP are supported");
}
Tins::PDU* application_layer = tcp->inner_pdu();
u_int16_t payload_size = 0;
if (application_layer != nullptr){
payload_size = application_layer->size();
}
if((udp->inner_pdu() == nullptr)){
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
}
packet_info pktinfo{
packet: string(payload, payload+plen),
payload: string(payload+plen - payload_size, payload+plen),
sid: stream_id::make_identifier(packet),
is_input: false, // TODO We need to detect this
is_tcp: false,
sctx: sctx,
};
if (callback_func(pktinfo)){
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
}else{
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP );
}
}
}
static int queue_cb(const nlmsghdr *nlh, void *data_ptr)
{
@@ -367,66 +370,14 @@ class NetfilterQueue {
nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id));
// Check IP protocol version
Tins::PDU *packet, *transport_layer;
Tins::PDU::PDUType transport_layer_type;
size_t payload_size;
if ( ((payload)[0] & 0xf0) == 0x40 ){
Tins::IP parsed = Tins::IP(payload, plen);
packet = &parsed;
transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size);
if ( (payload[0] & 0xf0) == 0x40 ){
build_verdict(Tins::IP(payload, plen), payload, plen, nlh_verdict, ph, sctx);
}else{
Tins::IPv6 parsed = Tins::IPv6(payload, plen);
packet = &parsed;
transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size);
}
if(transport_layer == nullptr || transport_layer->inner_pdu() == nullptr){
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
}else{
bool is_tcp = transport_layer_type == Tins::PDU::TCP;
packet_info pktinfo{
packet: string(payload, payload+plen),
payload: string(payload+plen - payload_size, payload+plen),
stream_id: "udp", // For udp we don't need to track the stream
is_input: false, // TODO We need to detect this
is_tcp: is_tcp,
sctx: sctx,
};
cerr << "PKT " << endl;
if (is_tcp){
cerr << "PORCO8888" << endl;
sctx->tcp_match_util.matching_has_been_called = false;
sctx->tcp_match_util.pkt_info = &pktinfo;
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
//sctx->follower.process_packet(*packet);
cerr << "NOT BLOCKED BUT YES 0_0 " << (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result) << endl;
/*
if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){
auto tcp_layer = (Tins::TCP *)transport_layer;
tcp_layer->release_inner_pdu();
tcp_layer->set_flag(Tins::TCP::FIN,1);
tcp_layer->set_flag(Tins::TCP::ACK,1);
tcp_layer->set_flag(Tins::TCP::SYN,0);
nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet->serialize().data(), packet->size());
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
delete tcp_layer;
}
*/
}else if(callback_func(pktinfo)){
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );
} else{
nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP );
}
build_verdict(Tins::IPv6(payload, plen), payload, plen, nlh_verdict, ph, sctx);
}
/* example to set the connmark. First, start NFQA_CT section: */
nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT);
/* then, add the connmark attribute: */
mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42));
/* more conntrack attributes, e.g. CTA_LABELS could be set here */
/* end conntrack section */
mnl_attr_nest_end(nlh_verdict, nest);
if (mnl_socket_sendto(sctx->nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) {

View File

@@ -78,7 +78,7 @@ bool filter_callback(packet_info & info){
hs_stream_t* stream_match;
if (conf->stream_mode()){
matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams;
auto stream_search = match_map.find(info.stream_id);
auto stream_search = match_map.find(info.sid);
if (stream_search == match_map.end()){
if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) {
@@ -86,7 +86,7 @@ bool filter_callback(packet_info & info){
throw invalid_argument("Cannot open stream match on hyperscan");
}
if (info.is_tcp){
match_map[info.stream_id] = stream_match;
match_map[info.sid] = stream_match;
}
}else{
stream_match = stream_search->second;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 116 KiB