improves on the nfregex binary x2
This commit is contained in:
@@ -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/))
|
- 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/)
|
- 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
|
## Documentation
|
||||||
|
|
||||||
Find the documentation of the backend and of the frontend in the related README files
|
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"?
|
## 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.
|
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
|
# Credits
|
||||||
- Copyright (c) 2007 Arash Partow (http://www.partow.net) for the base of our proxy implementation
|
|
||||||
- Copyright (c) 2022 Pwnzer0tt1
|
- Copyright (c) 2022 Pwnzer0tt1
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <linux/netfilter/nfnetlink_conntrack.h>
|
#include <linux/netfilter/nfnetlink_conntrack.h>
|
||||||
#include <tins/tins.h>
|
#include <tins/tins.h>
|
||||||
#include <tins/tcp_ip/stream_follower.h>
|
#include <tins/tcp_ip/stream_follower.h>
|
||||||
|
#include <tins/tcp_ip/stream_identifier.h>
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
@@ -19,42 +20,9 @@ using namespace std;
|
|||||||
|
|
||||||
#ifndef NETFILTER_CLASSES_HPP
|
#ifndef NETFILTER_CLASSES_HPP
|
||||||
#define 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;
|
struct packet_info;
|
||||||
|
|
||||||
@@ -89,7 +57,7 @@ struct stream_ctx {
|
|||||||
struct packet_info {
|
struct packet_info {
|
||||||
string packet;
|
string packet;
|
||||||
string payload;
|
string payload;
|
||||||
string stream_id;
|
stream_id sid;
|
||||||
bool is_input;
|
bool is_input;
|
||||||
bool is_tcp;
|
bool is_tcp;
|
||||||
stream_ctx* sctx;
|
stream_ctx* sctx;
|
||||||
@@ -97,26 +65,6 @@ struct packet_info {
|
|||||||
|
|
||||||
typedef bool NetFilterQueueCallback(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>
|
template <NetFilterQueueCallback callback_func>
|
||||||
class NetfilterQueue {
|
class NetfilterQueue {
|
||||||
public:
|
public:
|
||||||
@@ -194,29 +142,25 @@ class NetfilterQueue {
|
|||||||
//Input data filtering
|
//Input data filtering
|
||||||
void on_client_data(Stream& stream) {
|
void on_client_data(Stream& stream) {
|
||||||
string data(stream.client_payload().begin(), stream.client_payload().end());
|
string data(stream.client_payload().begin(), stream.client_payload().end());
|
||||||
string stream_id = stream_identifier(stream);
|
sctx.tcp_match_util.pkt_info->is_input = true;
|
||||||
this->sctx.tcp_match_util.pkt_info->is_input = true;
|
sctx.tcp_match_util.matching_has_been_called = true;
|
||||||
this->sctx.tcp_match_util.pkt_info->stream_id = stream_id;
|
|
||||||
this->sctx.tcp_match_util.matching_has_been_called = true;
|
|
||||||
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
|
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
|
||||||
if (result){
|
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_client_data();
|
||||||
stream.ignore_server_data();
|
stream.ignore_server_data();
|
||||||
}
|
}
|
||||||
this->sctx.tcp_match_util.result = result;
|
sctx.tcp_match_util.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Server data filtering
|
//Server data filtering
|
||||||
void on_server_data(Stream& stream) {
|
void on_server_data(Stream& stream) {
|
||||||
string data(stream.server_payload().begin(), stream.server_payload().end());
|
string data(stream.server_payload().begin(), stream.server_payload().end());
|
||||||
string stream_id = stream_identifier(stream);
|
sctx.tcp_match_util.pkt_info->is_input = false;
|
||||||
this->sctx.tcp_match_util.pkt_info->is_input = false;
|
sctx.tcp_match_util.matching_has_been_called = true;
|
||||||
this->sctx.tcp_match_util.pkt_info->stream_id = stream_id;
|
|
||||||
this->sctx.tcp_match_util.matching_has_been_called = true;
|
|
||||||
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
|
bool result = callback_func(*sctx.tcp_match_util.pkt_info);
|
||||||
if (result){
|
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_client_data();
|
||||||
stream.ignore_server_data();
|
stream.ignore_server_data();
|
||||||
}
|
}
|
||||||
@@ -224,11 +168,10 @@ class NetfilterQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void on_new_stream(Stream& stream) {
|
void on_new_stream(Stream& stream) {
|
||||||
string stream_id = stream_identifier(stream);
|
|
||||||
if (stream.is_partial_stream()) {
|
if (stream.is_partial_stream()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cout << "[+] New connection " << stream_id << endl;
|
cerr << "[+] New connection!" << endl;
|
||||||
stream.auto_cleanup_payloads(true);
|
stream.auto_cleanup_payloads(true);
|
||||||
stream.client_data_callback(
|
stream.client_data_callback(
|
||||||
[&](auto a){this->on_client_data(a);}
|
[&](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);
|
auto stream_search = this->sctx.in_hs_streams.find(stream_id);
|
||||||
hs_stream_t* stream_match;
|
hs_stream_t* stream_match;
|
||||||
if (stream_search != this->sctx.in_hs_streams.end()){
|
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
|
// A stream was terminated. The second argument is the reason why it was terminated
|
||||||
void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) {
|
void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) {
|
||||||
string stream_id = stream_identifier(stream);
|
stream_id stream_id = stream_id::make_identifier(stream);
|
||||||
cout << "[+] Connection closed: " << stream_id << endl;
|
cerr << "[+] Connection closed: " << &stream_id << endl;
|
||||||
this->clean_stream_by_id(stream_id);
|
this->clean_stream_by_id(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +280,66 @@ class NetfilterQueue {
|
|||||||
}
|
}
|
||||||
sctx.out_hs_streams.clear();
|
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)
|
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));
|
nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id));
|
||||||
|
|
||||||
// Check IP protocol version
|
// Check IP protocol version
|
||||||
Tins::PDU *packet, *transport_layer;
|
if ( (payload[0] & 0xf0) == 0x40 ){
|
||||||
Tins::PDU::PDUType transport_layer_type;
|
build_verdict(Tins::IP(payload, plen), payload, plen, nlh_verdict, ph, sctx);
|
||||||
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);
|
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
Tins::IPv6 parsed = Tins::IPv6(payload, plen);
|
build_verdict(Tins::IPv6(payload, plen), payload, plen, nlh_verdict, ph, sctx);
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* example to set the connmark. First, start NFQA_CT section: */
|
|
||||||
nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT);
|
nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT);
|
||||||
|
|
||||||
/* then, add the connmark attribute: */
|
|
||||||
mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42));
|
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);
|
mnl_attr_nest_end(nlh_verdict, nest);
|
||||||
|
|
||||||
if (mnl_socket_sendto(sctx->nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) {
|
if (mnl_socket_sendto(sctx->nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ bool filter_callback(packet_info & info){
|
|||||||
hs_stream_t* stream_match;
|
hs_stream_t* stream_match;
|
||||||
if (conf->stream_mode()){
|
if (conf->stream_mode()){
|
||||||
matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams;
|
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 (stream_search == match_map.end()){
|
||||||
if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) {
|
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");
|
throw invalid_argument("Cannot open stream match on hyperscan");
|
||||||
}
|
}
|
||||||
if (info.is_tcp){
|
if (info.is_tcp){
|
||||||
match_map[info.stream_id] = stream_match;
|
match_map[info.sid] = stream_match;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
stream_match = stream_search->second;
|
stream_match = stream_search->second;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 116 KiB |
Reference in New Issue
Block a user