diff --git a/README.md b/README.md index 8e644b0..31f6a7e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index 2240996..89b0417 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,42 +20,9 @@ using namespace std; #ifndef NETFILTER_CLASSES_HPP #define NETFILTER_CLASSES_HPP +typedef Tins::TCPIP::StreamIdentifier stream_id; +typedef map 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 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 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 + 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(); + + 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(); + 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) { diff --git a/backend/binsrc/nfqueue.cpp b/backend/binsrc/nfqueue.cpp index 632bc96..d961807 100644 --- a/backend/binsrc/nfqueue.cpp +++ b/backend/binsrc/nfqueue.cpp @@ -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; diff --git a/docs/FiregexInternals.png b/docs/FiregexInternals.png index 065c78d..6a19f3c 100644 Binary files a/docs/FiregexInternals.png and b/docs/FiregexInternals.png differ