feature: HttpFullRequest and HttpFullResponse implementation
This commit is contained in:
@@ -111,6 +111,12 @@ from firegex.nfproxy import HttpRequestHeader
|
|||||||
```
|
```
|
||||||
This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
|
This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
|
||||||
|
|
||||||
|
### HttpFullRequest
|
||||||
|
```python
|
||||||
|
from firegex.nfproxy import HttpFullRequest
|
||||||
|
```
|
||||||
|
This handler will be called only when the request is complete. It will receive a HttpFullRequest object with the same properties as HttpRequest.
|
||||||
|
|
||||||
### HttpResponse
|
### HttpResponse
|
||||||
```python
|
```python
|
||||||
from firegex.nfproxy import HttpResponse
|
from firegex.nfproxy import HttpResponse
|
||||||
@@ -138,3 +144,8 @@ from firegex.nfproxy import HttpResponseHeader
|
|||||||
```
|
```
|
||||||
This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
|
This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
|
||||||
|
|
||||||
|
### HttpFullResponse
|
||||||
|
```python
|
||||||
|
from firegex.nfproxy import HttpFullResponse
|
||||||
|
```
|
||||||
|
This handler will be called only when the response is complete. It will receive a HttpFullResponse object with the same properties as HttpResponse.
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
from firegex.nfproxy.models.tcp import TCPInputStream, TCPOutputStream, TCPClientStream, TCPServerStream
|
from firegex.nfproxy.models.tcp import (
|
||||||
from firegex.nfproxy.models.http import HttpRequest, HttpResponse, HttpRequestHeader, HttpResponseHeader
|
TCPInputStream,
|
||||||
|
TCPOutputStream,
|
||||||
|
TCPClientStream,
|
||||||
|
TCPServerStream,
|
||||||
|
)
|
||||||
|
from firegex.nfproxy.models.http import (
|
||||||
|
HttpRequest,
|
||||||
|
HttpResponse,
|
||||||
|
HttpRequestHeader,
|
||||||
|
HttpResponseHeader,
|
||||||
|
HttpFullRequest,
|
||||||
|
HttpFullResponse,
|
||||||
|
)
|
||||||
from firegex.nfproxy.internals.data import RawPacket
|
from firegex.nfproxy.internals.data import RawPacket
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -17,15 +29,28 @@ type_annotations_associations = {
|
|||||||
HttpResponse: HttpResponse._fetch_packet,
|
HttpResponse: HttpResponse._fetch_packet,
|
||||||
HttpRequestHeader: HttpRequestHeader._fetch_packet,
|
HttpRequestHeader: HttpRequestHeader._fetch_packet,
|
||||||
HttpResponseHeader: HttpResponseHeader._fetch_packet,
|
HttpResponseHeader: HttpResponseHeader._fetch_packet,
|
||||||
|
HttpFullRequest: HttpFullRequest._fetch_packet,
|
||||||
|
HttpFullResponse: HttpFullResponse._fetch_packet,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class Protocols(Enum):
|
class Protocols(Enum):
|
||||||
TCP = "tcp"
|
TCP = "tcp"
|
||||||
HTTP = "http"
|
HTTP = "http"
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"RawPacket",
|
"RawPacket",
|
||||||
"TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream",
|
"TCPInputStream",
|
||||||
"HttpRequest", "HttpResponse", "HttpRequestHeader", "HttpResponseHeader", "Protocols"
|
"TCPOutputStream",
|
||||||
|
"TCPClientStream",
|
||||||
|
"TCPServerStream",
|
||||||
|
"HttpRequest",
|
||||||
|
"HttpResponse",
|
||||||
|
"HttpRequestHeader",
|
||||||
|
"HttpResponseHeader",
|
||||||
|
"HttpFullRequest",
|
||||||
|
"HttpFullResponse",
|
||||||
|
"Protocols",
|
||||||
]
|
]
|
||||||
@@ -73,6 +73,7 @@ class InternalCallbackHandler:
|
|||||||
messages: deque[InternalHTTPMessage] = deque()
|
messages: deque[InternalHTTPMessage] = deque()
|
||||||
_ws_extentions = None
|
_ws_extentions = None
|
||||||
_ws_raised_error = False
|
_ws_raised_error = False
|
||||||
|
release_message_headers = True
|
||||||
|
|
||||||
def reset_data(self):
|
def reset_data(self):
|
||||||
self.msg = InternalHTTPMessage()
|
self.msg = InternalHTTPMessage()
|
||||||
@@ -604,6 +605,7 @@ class InternalBasicHttpMetaClass:
|
|||||||
if (
|
if (
|
||||||
not internal_data.call_mem["headers_were_set"]
|
not internal_data.call_mem["headers_were_set"]
|
||||||
and parser.msg.headers_complete
|
and parser.msg.headers_complete
|
||||||
|
and parser.release_message_headers
|
||||||
):
|
):
|
||||||
messages_tosend.append(
|
messages_tosend.append(
|
||||||
parser.msg
|
parser.msg
|
||||||
@@ -641,7 +643,7 @@ class HttpRequest(InternalBasicHttpMetaClass):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parser_class() -> str:
|
def _parser_class() -> str:
|
||||||
return "full_http"
|
return "http_module"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<HttpRequest method={self.method} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
return f"<HttpRequest method={self.method} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
||||||
@@ -664,12 +666,46 @@ class HttpResponse(InternalBasicHttpMetaClass):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parser_class() -> str:
|
def _parser_class() -> str:
|
||||||
return "full_http"
|
return "http_module"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<HttpResponse status_code={self.status_code} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
return f"<HttpResponse status_code={self.status_code} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
||||||
|
|
||||||
|
|
||||||
|
class HttpFullRequest(HttpRequest):
|
||||||
|
"""
|
||||||
|
HTTP Request handler
|
||||||
|
This data handler will be called when the request data is complete
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _contructor_hook(self):
|
||||||
|
self._parser.release_message_headers = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parser_class() -> str:
|
||||||
|
return "http_full"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<HttpFullRequest method={self.method} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
||||||
|
|
||||||
|
|
||||||
|
class HttpFullResponse(HttpResponse):
|
||||||
|
"""
|
||||||
|
HTTP Response handler
|
||||||
|
This data handler will be called when the response data is complete
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _contructor_hook(self):
|
||||||
|
self._parser.release_message_headers = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parser_class() -> str:
|
||||||
|
return "http_full"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<HttpFullResponse status_code={self.status_code} url={self.url} headers={self.headers} body=[{0 if not self.body else len(self.body)} bytes] http_version={self.http_version} keep_alive={self.keep_alive} should_upgrade={self.should_upgrade} headers_complete={self.headers_complete} message_complete={self.message_complete} content_length={self.content_length} stream={self.stream} ws_stream={self.ws_stream}>"
|
||||||
|
|
||||||
|
|
||||||
class HttpRequestHeader(HttpRequest):
|
class HttpRequestHeader(HttpRequest):
|
||||||
"""
|
"""
|
||||||
HTTP Request Header handler
|
HTTP Request Header handler
|
||||||
@@ -681,7 +717,7 @@ class HttpRequestHeader(HttpRequest):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parser_class() -> str:
|
def _parser_class() -> str:
|
||||||
return "header_http"
|
return "http_header"
|
||||||
|
|
||||||
|
|
||||||
class HttpResponseHeader(HttpResponse):
|
class HttpResponseHeader(HttpResponse):
|
||||||
@@ -695,4 +731,4 @@ class HttpResponseHeader(HttpResponse):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parser_class() -> str:
|
def _parser_class() -> str:
|
||||||
return "header_http"
|
return "http_header"
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { CodeHighlight } from "@mantine/code-highlight";
|
import { CodeHighlight } from "@mantine/code-highlight";
|
||||||
import { Container, Title, Text, List, Code, Space, Badge, Box } from "@mantine/core";
|
import {
|
||||||
|
Container,
|
||||||
|
Title,
|
||||||
|
Text,
|
||||||
|
List,
|
||||||
|
Code,
|
||||||
|
Space,
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
} from "@mantine/core";
|
||||||
import { CgEditBlackPoint } from "react-icons/cg";
|
import { CgEditBlackPoint } from "react-icons/cg";
|
||||||
import { EXAMPLE_PYFILTER } from "./utils";
|
import { EXAMPLE_PYFILTER } from "./utils";
|
||||||
|
|
||||||
const IMPORT_CODE_EXAMPLE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT`
|
const IMPORT_CODE_EXAMPLE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT`;
|
||||||
|
|
||||||
const FOO_FILTER_CODE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT
|
const FOO_FILTER_CODE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT
|
||||||
|
|
||||||
@@ -17,8 +26,7 @@ def none_filter(): # This is a filter that does nothing
|
|||||||
useless_function()
|
useless_function()
|
||||||
return ACCEPT
|
return ACCEPT
|
||||||
|
|
||||||
`
|
`;
|
||||||
|
|
||||||
|
|
||||||
const TYPING_ARGS_EXAMPLE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT
|
const TYPING_ARGS_EXAMPLE = `from firegex.nfproxy import pyfilter, ACCEPT, REJECT
|
||||||
from firegex.nfproxy.models import HttpRequest
|
from firegex.nfproxy.models import HttpRequest
|
||||||
@@ -28,7 +36,7 @@ def filter_with_args(http_request: HttpRequest) -> int:
|
|||||||
if http_request.body:
|
if http_request.body:
|
||||||
if b"ILLEGAL" in http_request.body:
|
if b"ILLEGAL" in http_request.body:
|
||||||
return REJECT
|
return REJECT
|
||||||
`
|
`;
|
||||||
|
|
||||||
const IMPORT_FULL_ACTION_STREAM = `from firegex.nfproxy import FullStreamAction
|
const IMPORT_FULL_ACTION_STREAM = `from firegex.nfproxy import FullStreamAction
|
||||||
|
|
||||||
@@ -39,7 +47,7 @@ class FullStreamAction(Enum):
|
|||||||
ACCEPT = 1
|
ACCEPT = 1
|
||||||
REJECT = 2
|
REJECT = 2
|
||||||
DROP = 3
|
DROP = 3
|
||||||
`
|
`;
|
||||||
|
|
||||||
const ENUM_IMPORT_AND_DEFINITION = `from firegex.nfproxy import ExceptionAction
|
const ENUM_IMPORT_AND_DEFINITION = `from firegex.nfproxy import ExceptionAction
|
||||||
|
|
||||||
@@ -50,7 +58,7 @@ class ExceptionAction(Enum):
|
|||||||
DROP = 1 # Drop the connection that caused the exception
|
DROP = 1 # Drop the connection that caused the exception
|
||||||
REJECT = 2 # Reject the connection that caused the exception
|
REJECT = 2 # Reject the connection that caused the exception
|
||||||
NOACTION = 3 # Do nothing, the excpetion will be signaled and the stream will be accepted without calling anymore the pyfilters (for the current stream)
|
NOACTION = 3 # Do nothing, the excpetion will be signaled and the stream will be accepted without calling anymore the pyfilters (for the current stream)
|
||||||
`
|
`;
|
||||||
|
|
||||||
export const HELP_NFPROXY_SIM = `➤ fgex nfproxy -h
|
export const HELP_NFPROXY_SIM = `➤ fgex nfproxy -h
|
||||||
|
|
||||||
@@ -69,390 +77,756 @@ export const HELP_NFPROXY_SIM = `➤ fgex nfproxy -h
|
|||||||
│ --from-port INTEGER The port of the local server [default: 7474] │
|
│ --from-port INTEGER The port of the local server [default: 7474] │
|
||||||
│ -6 Use IPv6 for the connection │
|
│ -6 Use IPv6 for the connection │
|
||||||
│ --help -h Show this message and exit. │
|
│ --help -h Show this message and exit. │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯`
|
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯`;
|
||||||
|
|
||||||
const HttpBadge = () => {
|
const HttpBadge = () => {
|
||||||
return <Badge size="md" ml="xs" radius="md" variant="gradient" gradient={{ from: 'red', to: 'grape', deg: 107 }} style={{ fontSize: "13px", fontWeight: "bolder"}}>HTTP</Badge>
|
return (
|
||||||
}
|
<Badge
|
||||||
|
size="md"
|
||||||
|
ml="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="gradient"
|
||||||
|
gradient={{ from: "red", to: "grape", deg: 107 }}
|
||||||
|
style={{ fontSize: "13px", fontWeight: "bolder" }}
|
||||||
|
>
|
||||||
|
HTTP
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const TCPBadge = () => {
|
const TCPBadge = () => {
|
||||||
return <Badge size="md" ml="xs" radius="md" variant="gradient" gradient={{ from: 'indigo', to: 'teal', deg: 164 }} style={{ fontSize: "13px", fontWeight: "bolder"}}>TCP</Badge>
|
return (
|
||||||
}
|
<Badge
|
||||||
|
size="md"
|
||||||
|
ml="xs"
|
||||||
|
radius="md"
|
||||||
|
variant="gradient"
|
||||||
|
gradient={{ from: "indigo", to: "teal", deg: 164 }}
|
||||||
|
style={{ fontSize: "13px", fontWeight: "bolder" }}
|
||||||
|
>
|
||||||
|
TCP
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const NFProxyDocs = () => {
|
export const NFProxyDocs = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title order={1}>🌐 Netfilter Proxy Documentation</Title>
|
<Title order={1}>🌐 Netfilter Proxy Documentation</Title>
|
||||||
|
|
||||||
<Title order={2} mt="xl" mb="sm">📖 Overview</Title>
|
<Title order={2} mt="xl" mb="sm">
|
||||||
|
📖 Overview
|
||||||
|
</Title>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
Netfilter Proxy is a simulated proxy that leverages <a href="https://netfilter.org/projects/libnetfilter_queue/">nfqueue</a> to intercept network packets.
|
Netfilter Proxy is a simulated proxy that leverages{" "}
|
||||||
It follows a similar workflow to NFRegex but introduces Python-based filtering capabilities,
|
<a href="https://netfilter.org/projects/libnetfilter_queue/">
|
||||||
|
nfqueue
|
||||||
|
</a>{" "}
|
||||||
|
to intercept network packets. It follows a similar workflow to
|
||||||
|
NFRegex but introduces Python-based filtering capabilities,
|
||||||
providing users with the flexibility to upload custom filters.
|
providing users with the flexibility to upload custom filters.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Title order={2} mt="lg" mb="sm">⚙️ How to use Netfilter Proxy</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
⚙️ How to use Netfilter Proxy
|
||||||
|
</Title>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
To use Netfilter Proxy, simply create and upload a Python filter. The filter is passed to the C++ binary,
|
To use Netfilter Proxy, simply create and upload a Python
|
||||||
which then processes packets using the provided logic. This allows you to tailor the filtering behavior
|
filter. The filter is passed to the C++ binary, which then
|
||||||
to your needs.
|
processes packets using the provided logic. This allows you to
|
||||||
|
tailor the filtering behavior to your needs.
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">💡 How to write pyfilters?</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
💡 How to write pyfilters?
|
||||||
|
</Title>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
First of all install the firegex lib and update it running <Code>pip install -U fgex</Code>.
|
First of all install the firegex lib and update it running{" "}
|
||||||
After that you can use <Code>firegex</Code> module.
|
<Code>pip install -U fgex</Code>. After that you can use{" "}
|
||||||
<CodeHighlight code={IMPORT_CODE_EXAMPLE} language="python" my="sm"/>
|
<Code>firegex</Code> module.
|
||||||
With this code we imported the <Code>pyfilter</Code> decorator and the <Code>ACCEPT</Code> and <Code>REJECT</Code> statements.<br />
|
<CodeHighlight
|
||||||
|
code={IMPORT_CODE_EXAMPLE}
|
||||||
|
language="python"
|
||||||
|
my="sm"
|
||||||
|
/>
|
||||||
|
With this code we imported the <Code>pyfilter</Code> decorator
|
||||||
|
and the <Code>ACCEPT</Code> and <Code>REJECT</Code> statements.
|
||||||
|
<br />
|
||||||
Let's create a first (useless) filter to see the syntax:
|
Let's create a first (useless) filter to see the syntax:
|
||||||
<CodeHighlight code={FOO_FILTER_CODE} language="python" my="sm"/>
|
<CodeHighlight
|
||||||
You see that the filter must be decorated with the <Code>pyfilter</Code> decorator and must return a statement about how to manage that packet.
|
code={FOO_FILTER_CODE}
|
||||||
<br/><Space h="sm" />
|
language="python"
|
||||||
You can save every data about the current flow in the global variables, the code you write will be executed only once for flow. The globals variables are isolated between flows.
|
my="sm"
|
||||||
For each packet the filter functions will be called with the required paramethers and using the same globals as before.
|
/>
|
||||||
<br/><Space h="sm" />
|
You see that the filter must be decorated with the{" "}
|
||||||
<strong>Saving data in globals of other modules is not recommended, because that memory is shared by the flows managed by the same thread and lead to unexpected behaviors.</strong>
|
<Code>pyfilter</Code> decorator and must return a statement
|
||||||
<br/><Space h="sm" />
|
about how to manage that packet.
|
||||||
<strong>Global variables that starts with '__firegex' are reserved for internal use, don't use them.</strong>
|
<br />
|
||||||
<br/><Space h="sm" />
|
<Space h="sm" />
|
||||||
You can manage when the function is called and also getting some data specifying some paramethers, using type decorators.
|
You can save every data about the current flow in the global
|
||||||
Default values of the paramethers will be ignored, also kvargs values will be ignored.
|
variables, the code you write will be executed only once for
|
||||||
<br/><Space h="sm" />
|
flow. The globals variables are isolated between flows. For each
|
||||||
<strong>Functions with no type decorator are considered invalid pyfilters!</strong>
|
packet the filter functions will be called with the required
|
||||||
<br/><Space h="sm" />
|
paramethers and using the same globals as before.
|
||||||
<CodeHighlight code={TYPING_ARGS_EXAMPLE} language="python" my="sm"/>
|
<br />
|
||||||
In this code we are filtering all the http requests that contains the word "ILLEGAL" in the body. All the other packets will be accepted (default behavior).
|
<Space h="sm" />
|
||||||
The function will be called only if at least internally teh HTTP request header has been parsed, and also when the body will be parsed.
|
<strong>
|
||||||
<br/><Space h="sm" />
|
Saving data in globals of other modules is not recommended,
|
||||||
If we have multiple paramether, the function will be called only if with the packet arrived is possible to build all the paramethers.
|
because that memory is shared by the flows managed by the
|
||||||
|
same thread and lead to unexpected behaviors.
|
||||||
|
</strong>
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
<strong>
|
||||||
|
Global variables that starts with '__firegex' are reserved
|
||||||
|
for internal use, don't use them.
|
||||||
|
</strong>
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
You can manage when the function is called and also getting some
|
||||||
|
data specifying some paramethers, using type decorators. Default
|
||||||
|
values of the paramethers will be ignored, also kvargs values
|
||||||
|
will be ignored.
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
<strong>
|
||||||
|
Functions with no type decorator are considered invalid
|
||||||
|
pyfilters!
|
||||||
|
</strong>
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
<CodeHighlight
|
||||||
|
code={TYPING_ARGS_EXAMPLE}
|
||||||
|
language="python"
|
||||||
|
my="sm"
|
||||||
|
/>
|
||||||
|
In this code we are filtering all the http requests that
|
||||||
|
contains the word "ILLEGAL" in the body. All the other packets
|
||||||
|
will be accepted (default behavior). The function will be called
|
||||||
|
only if at least internally teh HTTP request header has been
|
||||||
|
parsed, and also when the body will be parsed.
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
If we have multiple paramether, the function will be called only
|
||||||
|
if with the packet arrived is possible to build all the
|
||||||
|
paramethers.
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">🔧 How can I test the filter?</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
🔧 How can I test the filter?
|
||||||
|
</Title>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
You can test your filter by using <Code>fgex</Code> command installed by firegex lib: This will run a local proxy to a remote destination with the filter you specified.
|
You can test your filter by using <Code>fgex</Code> command
|
||||||
<br/><Space h="sm" />
|
installed by firegex lib: This will run a local proxy to a
|
||||||
This can be done by running for instance: <Code>fgex nfproxy test_http.py 127.0.0.1 8080 --proto http</Code>
|
remote destination with the filter you specified.
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
|
This can be done by running for instance:{" "}
|
||||||
|
<Code>
|
||||||
|
fgex nfproxy test_http.py 127.0.0.1 8080 --proto http
|
||||||
|
</Code>
|
||||||
<CodeHighlight code={HELP_NFPROXY_SIM} language="" my="sm" />
|
<CodeHighlight code={HELP_NFPROXY_SIM} language="" my="sm" />
|
||||||
You don't need to restart the proxy every time you change the filter, the filter will be reloaded automatically.
|
You don't need to restart the proxy every time you change the
|
||||||
|
filter, the filter will be reloaded automatically.
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">📦 Packet Statements</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
📦 Packet Statements
|
||||||
|
</Title>
|
||||||
<Text size="lg" my="xs">
|
<Text size="lg" my="xs">
|
||||||
Here there are all the statments you can return from a filter:
|
Here there are all the statments you can return from a filter:
|
||||||
<List>
|
<List>
|
||||||
<List.Item><strong>ACCEPT: </strong> The packet will be accepted and forwarded to the destination. (default if None is returned)</List.Item>
|
<List.Item>
|
||||||
<List.Item><strong>REJECT: </strong> The connection will be closed and all the packets will be dropped.</List.Item>
|
<strong>ACCEPT: </strong> The packet will be accepted
|
||||||
<List.Item><strong>DROP: </strong> This packet and all the following will be dropped. (This not simulate a connection closure)</List.Item>
|
and forwarded to the destination. (default if None is
|
||||||
<List.Item><strong>UNSTABLE_MANGLE: </strong> The packet will be modified and forwarded. You can edit the packet only with RawPacket data handler. (This is an unstable statement, use it carefully)</List.Item>
|
returned)
|
||||||
|
</List.Item>
|
||||||
|
<List.Item>
|
||||||
|
<strong>REJECT: </strong> The connection will be closed
|
||||||
|
and all the packets will be dropped.
|
||||||
|
</List.Item>
|
||||||
|
<List.Item>
|
||||||
|
<strong>DROP: </strong> This packet and all the
|
||||||
|
following will be dropped. (This not simulate a
|
||||||
|
connection closure)
|
||||||
|
</List.Item>
|
||||||
|
<List.Item>
|
||||||
|
<strong>UNSTABLE_MANGLE: </strong> The packet will be
|
||||||
|
modified and forwarded. You can edit the packet only
|
||||||
|
with RawPacket data handler. (This is an unstable
|
||||||
|
statement, use it carefully)
|
||||||
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">⚙️ Data Structures</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
⚙️ Data Structures
|
||||||
|
</Title>
|
||||||
<Text size="lg" my="xs">
|
<Text size="lg" my="xs">
|
||||||
Here there are all the data structure you can use for your filters:
|
Here there are all the data structure you can use for your
|
||||||
|
filters:
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> RawPacket</Title><Space w="sm" /><TCPBadge /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} /> RawPacket
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<TCPBadge />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">This data is the raw packet processed by nfqueue. It contains:</Text>
|
<Text size="lg">
|
||||||
|
This data is the raw packet processed by nfqueue. It contains:
|
||||||
|
</Text>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Text size="lg" ml="xs">
|
<Text size="lg" ml="xs">
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>data: </strong> The raw packet data assembled by libtins (read only).
|
<strong>data: </strong> The raw packet data assembled by
|
||||||
|
libtins (read only).
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>is_input: </strong> It's true if the packet is incoming, false if it's outgoing. (read only)
|
<strong>is_input: </strong> It's true if the packet is
|
||||||
|
incoming, false if it's outgoing. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>is_ipv6: </strong> It's true if the packet is IPv6, false if it's IPv4. (read only)
|
<strong>is_ipv6: </strong> It's true if the packet is
|
||||||
|
IPv6, false if it's IPv4. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>is_tcp: </strong> It's true if the packet is TCP, false if it's UDP. (read only)
|
<strong>is_tcp: </strong> It's true if the packet is
|
||||||
|
TCP, false if it's UDP. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>l4_size: </strong> The size of l4 payload (read only)
|
<strong>l4_size: </strong> The size of l4 payload (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>raw_packet_header_len: </strong> The size of the raw packet header (read only)
|
<strong>raw_packet_header_len: </strong> The size of the
|
||||||
|
raw packet header (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>raw_packet: </strong> The raw packet data with ip and TCP header. You can edit all the packet content and it will be modified if you send
|
<strong>raw_packet: </strong> The raw packet data with
|
||||||
the UNSTABLE_MANGLE statement. <strong>Be careful, beacause the associated layer 4 data can be different from 'data' filed that instead arrives from libtins.</strong>
|
ip and TCP header. You can edit all the packet content
|
||||||
When you edit this field, l4_size and l4_data will be updated automatically.
|
and it will be modified if you send the UNSTABLE_MANGLE
|
||||||
|
statement.{" "}
|
||||||
|
<strong>
|
||||||
|
Be careful, beacause the associated layer 4 data can
|
||||||
|
be different from 'data' filed that instead arrives
|
||||||
|
from libtins.
|
||||||
|
</strong>
|
||||||
|
When you edit this field, l4_size and l4_data will be
|
||||||
|
updated automatically.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>l4_data: </strong> The l4 payload data, directly taken by the raw packet. You can edit all the packet content and it will be modified if you send
|
<strong>l4_data: </strong> The l4 payload data, directly
|
||||||
the UNSTABLE_MANGLE statement. <strong>Be careful, beacause the associated layer 4 data can be different from 'data' filed that instead arrives from libtins.</strong> When you edit this field, l4_size and raw_packet will be updated automatically.
|
taken by the raw packet. You can edit all the packet
|
||||||
|
content and it will be modified if you send the
|
||||||
|
UNSTABLE_MANGLE statement.{" "}
|
||||||
|
<strong>
|
||||||
|
Be careful, beacause the associated layer 4 data can
|
||||||
|
be different from 'data' filed that instead arrives
|
||||||
|
from libtins.
|
||||||
|
</strong>{" "}
|
||||||
|
When you edit this field, l4_size and raw_packet will be
|
||||||
|
updated automatically.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> TCPInputStream (alias: TCPClientStream)</Title><Space w="sm" /><TCPBadge /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
TCPInputStream (alias: TCPClientStream)
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<TCPBadge />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">This data is the TCP input stream: this handler is called only on is_input=True packets. The filters that handles this data will be called only in this case.</Text>
|
<Text size="lg">
|
||||||
|
This data is the TCP input stream: this handler is called only
|
||||||
|
on is_input=True packets. The filters that handles this data
|
||||||
|
will be called only in this case.
|
||||||
|
</Text>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Text size="lg" ml="xs">
|
<Text size="lg" ml="xs">
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>data: </strong> The entire stream in input direction. (read only)
|
<strong>data: </strong> The entire stream in input
|
||||||
|
direction. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>total_stream_size: </strong> The size of the entire stream in input direction. (read only)
|
<strong>total_stream_size: </strong> The size of the
|
||||||
|
entire stream in input direction. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>is_ipv6: </strong> It's true if the stream is IPv6, false if it's IPv4. (read only)
|
<strong>is_ipv6: </strong> It's true if the stream is
|
||||||
|
IPv6, false if it's IPv4. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> TCPOutputStream (alias TCPServerStream)</Title><Space w="sm" /><TCPBadge /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
TCPOutputStream (alias TCPServerStream)
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<TCPBadge />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">This data is the TCP output stream: this handler is called only on is_input=False packets. The filters that handles this data will be called only in this case.</Text>
|
<Text size="lg">
|
||||||
|
This data is the TCP output stream: this handler is called only
|
||||||
|
on is_input=False packets. The filters that handles this data
|
||||||
|
will be called only in this case.
|
||||||
|
</Text>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Text size="lg" ml="xs">
|
<Text size="lg" ml="xs">
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>data: </strong> The entire stream in output direction. (read only)
|
<strong>data: </strong> The entire stream in output
|
||||||
|
direction. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>total_stream_size: </strong> The size of the entire stream in output direction. (read only)
|
<strong>total_stream_size: </strong> The size of the
|
||||||
|
entire stream in output direction. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>is_ipv6: </strong> It's true if the stream is IPv6, false if it's IPv4. (read only)
|
<strong>is_ipv6: </strong> It's true if the stream is
|
||||||
|
IPv6, false if it's IPv4. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> HttpRequest</Title><Space w="sm" /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpRequest
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">This data is the Http request processed by nfqueue. This handler can be called twice per request: once when the http headers are complete, and once when the body is complete.</Text>
|
<Text size="lg">
|
||||||
<Text size="lg">If the http data arrives in 1 single TCP packet, this handler will be called once</Text>
|
This data is the Http request processed by nfqueue. This handler
|
||||||
|
can be called twice per request: once when the http headers are
|
||||||
|
complete, and once when the body is complete.
|
||||||
|
</Text>
|
||||||
|
<Text size="lg">
|
||||||
|
If the http data arrives in 1 single TCP packet, this handler
|
||||||
|
will be called once
|
||||||
|
</Text>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Text size="lg" ml="xs">
|
<Text size="lg" ml="xs">
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>url: </strong> The url of the request (read only)
|
<strong>url: </strong> The url of the request (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>headers: </strong> The headers of the request (read only). The keys and values are exactly the same as the original request (case sensitive). (values can be list in case the same header field is repeated)
|
<strong>headers: </strong> The headers of the request
|
||||||
|
(read only). The keys and values are exactly the same as
|
||||||
|
the original request (case sensitive). (values can be
|
||||||
|
list in case the same header field is repeated)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>get_header(key:str, default = None): </strong> A function that returns the value of a header: it matches the key without case sensitivity. If the header is not found, it returns the default value. (if the same header field is repeated, its value is concatenated with a comma, this function will never return a list)
|
<strong>get_header(key:str, default = None): </strong> A
|
||||||
|
function that returns the value of a header: it matches
|
||||||
|
the key without case sensitivity. If the header is not
|
||||||
|
found, it returns the default value. (if the same header
|
||||||
|
field is repeated, its value is concatenated with a
|
||||||
|
comma, this function will never return a list)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>user_agent: </strong> The user agent of the request (read only)
|
<strong>user_agent: </strong> The user agent of the
|
||||||
|
request (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>content_encoding: </strong> The content encoding of the request (read only)
|
<strong>content_encoding: </strong> The content encoding
|
||||||
|
of the request (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>content_length: </strong> The content length of the request (read only)
|
<strong>content_length: </strong> The content length of
|
||||||
|
the request (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>body: </strong> The body of the request (read only). It's None if the body has not arrived yet.
|
<strong>body: </strong> The body of the request (read
|
||||||
|
only). It's None if the body has not arrived yet.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>body_decoded: </strong> By default the body will be decoded following the content encoding. gzip, br, deflate and zstd are supported. If the decoding fails and body is not None this paramether will be False.
|
<strong>body_decoded: </strong> By default the body will
|
||||||
|
be decoded following the content encoding. gzip, br,
|
||||||
|
deflate and zstd are supported. If the decoding fails
|
||||||
|
and body is not None this paramether will be False.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>http_version: </strong> The http version of the request (read only)
|
<strong>http_version: </strong> The http version of the
|
||||||
|
request (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>keep_alive: </strong> It's true if the connection was marked for keep alive, false if it's not. (read only)
|
<strong>keep_alive: </strong> It's true if the
|
||||||
|
connection was marked for keep alive, false if it's not.
|
||||||
|
(read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>should_upgrade: </strong> It's true if the connection should be upgraded, false if it's not. (read only)
|
<strong>should_upgrade: </strong> It's true if the
|
||||||
|
connection should be upgraded, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>upgrading_to_h2: </strong> It's true if the connection is upgrading to h2, false if it's not. (read only)
|
<strong>upgrading_to_h2: </strong> It's true if the
|
||||||
|
connection is upgrading to h2, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>ws_stream: </strong> It's a list of websockets.frames.Frame decoded (permessage-deflate is supported). (read only) [<a href="https://websockets.readthedocs.io/en/stable/">docs</a>]
|
<strong>ws_stream: </strong> It's a list of
|
||||||
|
websockets.frames.Frame decoded (permessage-deflate is
|
||||||
|
supported). (read only) [
|
||||||
|
<a href="https://websockets.readthedocs.io/en/stable/">
|
||||||
|
docs
|
||||||
|
</a>
|
||||||
|
]
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>upgrading_to_ws: </strong> It's true if the connection is upgrading to ws, false if it's not. (read only)
|
<strong>upgrading_to_ws: </strong> It's true if the
|
||||||
|
connection is upgrading to ws, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>method: </strong> The method of the request (read only)
|
<strong>method: </strong> The method of the request
|
||||||
|
(read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>headers_complete: </strong> It's true if the headers are complete, false if they are not. (read only)
|
<strong>headers_complete: </strong> It's true if the
|
||||||
|
headers are complete, false if they are not. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>message_complete: </strong> It's true if the message is complete, false if it's not. (read only)
|
<strong>message_complete: </strong> It's true if the
|
||||||
|
message is complete, false if it's not. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>total_size: </strong> The size of the entire http request (read only)
|
<strong>total_size: </strong> The size of the entire
|
||||||
|
http request (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>stream: </strong> It's the buffer that contains the stream of the websocket traffic in input. This is used only if should_upgrade is True. (read only)
|
<strong>stream: </strong> It's the buffer that contains
|
||||||
|
the stream of the websocket traffic in input. This is
|
||||||
|
used only if should_upgrade is True. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> HttpRequestHeader</Title><Space w="sm" /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpRequestHeader
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">Same as HttpRequest, but this handler is called only when the headers are complete and body is not buffered. Body will always be None</Text>
|
<Text size="lg">
|
||||||
|
Same as HttpRequest, but this handler is called only when the
|
||||||
|
headers are complete and body is not buffered. Body will always
|
||||||
|
be None
|
||||||
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> HttpResponse</Title><Space w="sm" /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpFullRequest
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">This data is the Http response processed by nfqueue. This handler can be called twice per response: once when the http headers are complete, and once when the body is complete.</Text>
|
<Text size="lg">
|
||||||
<Text size="lg">If the http data arrives in 1 single TCP packet, this handler will be called once</Text>
|
Same as HttpRequest, but this handler is called only when the
|
||||||
|
request data is complete
|
||||||
|
</Text>
|
||||||
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpResponse
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
|
</Box>
|
||||||
|
<Text size="lg">
|
||||||
|
This data is the Http response processed by nfqueue. This
|
||||||
|
handler can be called twice per response: once when the http
|
||||||
|
headers are complete, and once when the body is complete.
|
||||||
|
</Text>
|
||||||
|
<Text size="lg">
|
||||||
|
If the http data arrives in 1 single TCP packet, this handler
|
||||||
|
will be called once
|
||||||
|
</Text>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<Text size="lg" ml="xs">
|
<Text size="lg" ml="xs">
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>url: </strong> The url of the response (read only)
|
<strong>url: </strong> The url of the response (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>headers: </strong> The headers of the response (read only). The keys and values are exactly the same as the original response (case sensitive). (values can be list in case the same header field is repeated)
|
<strong>headers: </strong> The headers of the response
|
||||||
|
(read only). The keys and values are exactly the same as
|
||||||
|
the original response (case sensitive). (values can be
|
||||||
|
list in case the same header field is repeated)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>get_header(key:str, default = None): </strong> A function that returns the value of a header: it matches the key without case sensitivity. If the header is not found, it returns the default value. (if the same header field is repeated, its value is concatenated with a comma, this function will never return a list)
|
<strong>get_header(key:str, default = None): </strong> A
|
||||||
|
function that returns the value of a header: it matches
|
||||||
|
the key without case sensitivity. If the header is not
|
||||||
|
found, it returns the default value. (if the same header
|
||||||
|
field is repeated, its value is concatenated with a
|
||||||
|
comma, this function will never return a list)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>user_agent: </strong> The user agent of the response (read only)
|
<strong>user_agent: </strong> The user agent of the
|
||||||
|
response (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>content_encoding: </strong> The content encoding of the response (read only)
|
<strong>content_encoding: </strong> The content encoding
|
||||||
|
of the response (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>content_length: </strong> The content length of the response (read only)
|
<strong>content_length: </strong> The content length of
|
||||||
|
the response (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>body: </strong> The body of the response (read only). It's None if the body has not arrived yet.
|
<strong>body: </strong> The body of the response (read
|
||||||
|
only). It's None if the body has not arrived yet.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>body_decoded: </strong> By default the body will be decoded following the content encoding. gzip, br, deflate and zstd are supported. If the decoding fails and body is not None this paramether will be False.
|
<strong>body_decoded: </strong> By default the body will
|
||||||
|
be decoded following the content encoding. gzip, br,
|
||||||
|
deflate and zstd are supported. If the decoding fails
|
||||||
|
and body is not None this paramether will be False.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>http_version: </strong> The http version of the response (read only)
|
<strong>http_version: </strong> The http version of the
|
||||||
|
response (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>keep_alive: </strong> It's true if the connection was marked for keep alive, false if it's not. (read only)
|
<strong>keep_alive: </strong> It's true if the
|
||||||
|
connection was marked for keep alive, false if it's not.
|
||||||
|
(read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>should_upgrade: </strong> It's true if the connection should be upgraded, false if it's not. (read only)
|
<strong>should_upgrade: </strong> It's true if the
|
||||||
|
connection should be upgraded, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>upgrading_to_h2: </strong> It's true if the connection is upgrading to h2, false if it's not. (read only)
|
<strong>upgrading_to_h2: </strong> It's true if the
|
||||||
|
connection is upgrading to h2, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>ws_stream: </strong> It's a list of websockets.frames.Frame decoded (permessage-deflate is supported). (read only) [<a href="https://websockets.readthedocs.io/en/stable/">docs</a>]
|
<strong>ws_stream: </strong> It's a list of
|
||||||
|
websockets.frames.Frame decoded (permessage-deflate is
|
||||||
|
supported). (read only) [
|
||||||
|
<a href="https://websockets.readthedocs.io/en/stable/">
|
||||||
|
docs
|
||||||
|
</a>
|
||||||
|
]
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>upgrading_to_ws: </strong> It's true if the connection is upgrading to ws, false if it's not. (read only)
|
<strong>upgrading_to_ws: </strong> It's true if the
|
||||||
|
connection is upgrading to ws, false if it's not. (read
|
||||||
|
only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>status_code: </strong> The status code of the response (read only) (int)
|
<strong>status_code: </strong> The status code of the
|
||||||
|
response (read only) (int)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>headers_complete: </strong> It's true if the headers are complete, false if they are not. (read only)
|
<strong>headers_complete: </strong> It's true if the
|
||||||
|
headers are complete, false if they are not. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>message_complete: </strong> It's true if the message is complete, false if it's not. (read only)
|
<strong>message_complete: </strong> It's true if the
|
||||||
|
message is complete, false if it's not. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>total_size: </strong> The size of the entire http response (read only)
|
<strong>total_size: </strong> The size of the entire
|
||||||
|
http response (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>stream: </strong> It's the buffer that contains the stream of the websocket traffic in output. This is used only if should_upgrade is True. (read only)
|
<strong>stream: </strong> It's the buffer that contains
|
||||||
|
the stream of the websocket traffic in output. This is
|
||||||
|
used only if should_upgrade is True. (read only)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" style={{ alignItems: "center" }}>
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
<Title order={3} my="xs"><CgEditBlackPoint style={{marginBottom: -3}}/> HttpResponseHeader</Title><Space w="sm" /><HttpBadge />
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpResponseHeader
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="lg">Same as HttpResponse, but this handler is called only when the headers are complete and body is not buffered. Body will always be None</Text>
|
<Text size="lg">
|
||||||
<Title order={2} mt="lg" mb="sm">⚠️ Stream Limiter</Title>
|
Same as HttpResponse, but this handler is called only when the
|
||||||
|
headers are complete and body is not buffered. Body will always
|
||||||
|
be None
|
||||||
|
</Text>
|
||||||
|
<Box display="flex" style={{ alignItems: "center" }}>
|
||||||
|
<Title order={3} my="xs">
|
||||||
|
<CgEditBlackPoint style={{ marginBottom: -3 }} />{" "}
|
||||||
|
HttpFullResponse
|
||||||
|
</Title>
|
||||||
|
<Space w="sm" />
|
||||||
|
<HttpBadge />
|
||||||
|
</Box>
|
||||||
|
<Text size="lg">
|
||||||
|
Same as HttpResponse, but this handler is called only when the
|
||||||
|
response data is complete
|
||||||
|
</Text>
|
||||||
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
⚠️ Stream Limiter
|
||||||
|
</Title>
|
||||||
<Text size="lg" my="xs">
|
<Text size="lg" my="xs">
|
||||||
What happen if in a specific TCP stream you have a lot of data? The stream limiter will be activated and some action will be taken.
|
What happen if in a specific TCP stream you have a lot of data?
|
||||||
You can configure the action performed by setting some option in the globals:
|
The stream limiter will be activated and some action will be
|
||||||
<br /><Space h="sm" />
|
taken. You can configure the action performed by setting some
|
||||||
|
option in the globals:
|
||||||
|
<br />
|
||||||
|
<Space h="sm" />
|
||||||
First import the FullStreamAction enum:
|
First import the FullStreamAction enum:
|
||||||
<CodeHighlight code={IMPORT_FULL_ACTION_STREAM} language="python" my="sm" />
|
<CodeHighlight
|
||||||
|
code={IMPORT_FULL_ACTION_STREAM}
|
||||||
|
language="python"
|
||||||
|
my="sm"
|
||||||
|
/>
|
||||||
Then you can set in the globals these options:
|
Then you can set in the globals these options:
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>FGEX_STREAM_MAX_SIZE: </strong> Sets the maximum size of the stream. If the stream exceeds this size, the FGEX_FULL_STREAM_ACTION will be performed. (this limit is applyed at the single stream related to the single data handler).
|
<strong>FGEX_STREAM_MAX_SIZE: </strong> Sets the maximum
|
||||||
For example if TCPInputStream has reached the limit but HttpResponse has not, the action will be performed only on the TCPInputStream. The default is 1MB.
|
size of the stream. If the stream exceeds this size, the
|
||||||
|
FGEX_FULL_STREAM_ACTION will be performed. (this limit
|
||||||
|
is applyed at the single stream related to the single
|
||||||
|
data handler). For example if TCPInputStream has reached
|
||||||
|
the limit but HttpResponse has not, the action will be
|
||||||
|
performed only on the TCPInputStream. The default is
|
||||||
|
1MB.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>FGEX_FULL_STREAM_ACTION: </strong> Sets the action performed when the stream exceeds the FGEX_STREAM_MAX_SIZE. The default is FullStreamAction.FLUSH.
|
<strong>FGEX_FULL_STREAM_ACTION: </strong> Sets the
|
||||||
|
action performed when the stream exceeds the
|
||||||
|
FGEX_STREAM_MAX_SIZE. The default is
|
||||||
|
FullStreamAction.FLUSH.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
Heres will be explained every type of action you can set:
|
Heres will be explained every type of action you can set:
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>FLUSH: </strong> Flush the stream and continue to acquire new packets (default)
|
<strong>FLUSH: </strong> Flush the stream and continue
|
||||||
|
to acquire new packets (default)
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>DROP: </strong> Drop the next stream packets - like a DROP action by filter
|
<strong>DROP: </strong> Drop the next stream packets -
|
||||||
|
like a DROP action by filter
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>REJECT: </strong> Reject the stream and close the connection - like a REJECT action by filter
|
<strong>REJECT: </strong> Reject the stream and close
|
||||||
|
the connection - like a REJECT action by filter
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>ACCEPT: </strong> Stops to call pyfilters and accept the traffic
|
<strong>ACCEPT: </strong> Stops to call pyfilters and
|
||||||
|
accept the traffic
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">⚠️ Other Options</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
⚠️ Other Options
|
||||||
|
</Title>
|
||||||
<Text size="lg" my="xs">
|
<Text size="lg" my="xs">
|
||||||
Here's other enums that you could need to use:
|
Here's other enums that you could need to use:
|
||||||
<CodeHighlight code={ENUM_IMPORT_AND_DEFINITION} language="python" my="sm" />
|
<CodeHighlight
|
||||||
|
code={ENUM_IMPORT_AND_DEFINITION}
|
||||||
|
language="python"
|
||||||
|
my="sm"
|
||||||
|
/>
|
||||||
Then you can set in the globals these options:
|
Then you can set in the globals these options:
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<strong>FGEX_INVALID_ENCODING_ACTION: </strong> Sets the action performed when the stream has an invalid encoding (due to a parser crash). The default is ExceptionAction.REJECT.
|
<strong>FGEX_INVALID_ENCODING_ACTION: </strong> Sets the
|
||||||
|
action performed when the stream has an invalid encoding
|
||||||
|
(due to a parser crash). The default is
|
||||||
|
ExceptionAction.REJECT.
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Text>
|
</Text>
|
||||||
<Title order={2} mt="lg" mb="sm">🚀 How It Works</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
🚀 How It Works
|
||||||
|
</Title>
|
||||||
<Text mb="sm" size="lg">
|
<Text mb="sm" size="lg">
|
||||||
The proxy is built on a multi-threaded architecture and integrates Python for dynamic filtering:
|
The proxy is built on a multi-threaded architecture and
|
||||||
|
integrates Python for dynamic filtering:
|
||||||
</Text>
|
</Text>
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
<strong>Packet Interception: </strong>
|
<strong>Packet Interception: </strong>
|
||||||
The <a href="https://netfilter.org/projects/libnetfilter_queue/">nfqueue</a> kernel module intercepts network packets(a <a href="https://netfilter.org/">netfilter</a> module) 🔍<br />
|
The{" "}
|
||||||
The rules for attach the nfqueue on the network traffic is done by the nftables lib with json APIs by the python manager.
|
<a href="https://netfilter.org/projects/libnetfilter_queue/">
|
||||||
|
nfqueue
|
||||||
|
</a>{" "}
|
||||||
|
kernel module intercepts network packets(a{" "}
|
||||||
|
<a href="https://netfilter.org/">netfilter</a> module)
|
||||||
|
🔍
|
||||||
|
<br />
|
||||||
|
The rules for attach the nfqueue on the network traffic
|
||||||
|
is done by the nftables lib with json APIs by the python
|
||||||
|
manager.
|
||||||
</Text>
|
</Text>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
<strong>Packet Reading: </strong>
|
<strong>Packet Reading: </strong>A dedicated thread
|
||||||
A dedicated thread reads packets from <a href="https://netfilter.org/projects/libnetfilter_queue/">nfqueue</a>. 🧵
|
reads packets from{" "}
|
||||||
|
<a href="https://netfilter.org/projects/libnetfilter_queue/">
|
||||||
|
nfqueue
|
||||||
|
</a>
|
||||||
|
. 🧵
|
||||||
</Text>
|
</Text>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
<strong>Multi-threaded Analysis: </strong>
|
<strong>Multi-threaded Analysis: </strong>
|
||||||
The C++ binary launches multiple threads, each starting its own Python interpreter.
|
The C++ binary launches multiple threads, each starting
|
||||||
Thanks to Python 3.12’s support for <a href="https://peps.python.org/pep-0684/">a per-interpeter GIL</a>, real multithreading is achieved.
|
its own Python interpreter. Thanks to Python 3.12’s
|
||||||
Traffic is distributed among threads based on IP addresses and port hashing, ensuring that
|
support for{" "}
|
||||||
packets belonging to the same flow are processed by the same thread. ⚡️
|
<a href="https://peps.python.org/pep-0684/">
|
||||||
|
a per-interpeter GIL
|
||||||
|
</a>
|
||||||
|
, real multithreading is achieved. Traffic is
|
||||||
|
distributed among threads based on IP addresses and port
|
||||||
|
hashing, ensuring that packets belonging to the same
|
||||||
|
flow are processed by the same thread. ⚡️
|
||||||
</Text>
|
</Text>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
<strong>Python Filter Integration: </strong>
|
<strong>Python Filter Integration: </strong>
|
||||||
Users can upload custom Python filters which are then executed by the interpreter,
|
Users can upload custom Python filters which are then
|
||||||
allowing for dynamic and flexible packet handling. 🐍
|
executed by the interpreter, allowing for dynamic and
|
||||||
|
flexible packet handling. 🐍
|
||||||
</Text>
|
</Text>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
<strong>HTTP Parsing: </strong>
|
<strong>HTTP Parsing: </strong>
|
||||||
<a href="https://github.com/domysh/pyllhttp">A Python wrapper for llhttp</a> (forked and adapted for working with multi-interpeters) is used to parse HTTP connections, making it easier to handle
|
<a href="https://github.com/domysh/pyllhttp">
|
||||||
and analyze HTTP traffic. 📡
|
A Python wrapper for llhttp
|
||||||
|
</a>{" "}
|
||||||
|
(forked and adapted for working with multi-interpeters)
|
||||||
|
is used to parse HTTP connections, making it easier to
|
||||||
|
handle and analyze HTTP traffic. 📡
|
||||||
</Text>
|
</Text>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
<Space h="xl" />
|
<Space h="xl" />
|
||||||
<Title order={2} mt="lg" mb="sm">📚 Additional Resources</Title>
|
<Title order={2} mt="lg" mb="sm">
|
||||||
|
📚 Additional Resources
|
||||||
|
</Title>
|
||||||
<Text size="lg">
|
<Text size="lg">
|
||||||
Here's a pyfilter code commented example:
|
Here's a pyfilter code commented example:
|
||||||
<CodeHighlight code={EXAMPLE_PYFILTER} language="python" my="sm"/>
|
<CodeHighlight
|
||||||
|
code={EXAMPLE_PYFILTER}
|
||||||
|
language="python"
|
||||||
|
my="sm"
|
||||||
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,12 +7,35 @@ import secrets
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/")
|
parser.add_argument(
|
||||||
parser.add_argument("--password", "-p", type=str, required=True, help='Firegex password')
|
"--address",
|
||||||
parser.add_argument("--service_name", "-n", type=str , required=False, help='Name of the test service', default="Test Service")
|
"-a",
|
||||||
parser.add_argument("--port", "-P", type=int , required=False, help='Port of the test service', default=1337)
|
type=str,
|
||||||
parser.add_argument("--ipv6", "-6" , action="store_true", help='Test Ipv6', default=False)
|
required=False,
|
||||||
parser.add_argument("--verbose", "-V" , action="store_true", help='Verbose output', default=False)
|
help="Address of firegex backend",
|
||||||
|
default="http://127.0.0.1:4444/",
|
||||||
|
)
|
||||||
|
parser.add_argument("--password", "-p", type=str, required=True, help="Firegex password")
|
||||||
|
parser.add_argument(
|
||||||
|
"--service_name",
|
||||||
|
"-n",
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
help="Name of the test service",
|
||||||
|
default="Test Service",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port",
|
||||||
|
"-P",
|
||||||
|
type=int,
|
||||||
|
required=False,
|
||||||
|
help="Port of the test service",
|
||||||
|
default=1337,
|
||||||
|
)
|
||||||
|
parser.add_argument("--ipv6", "-6", action="store_true", help="Test Ipv6", default=False)
|
||||||
|
parser.add_argument(
|
||||||
|
"--verbose", "-V", action="store_true", help="Verbose output", default=False
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
sep()
|
sep()
|
||||||
@@ -22,7 +45,7 @@ puts(f"{args.address}", color=colors.yellow)
|
|||||||
firegex = FiregexAPI(args.address)
|
firegex = FiregexAPI(args.address)
|
||||||
|
|
||||||
# Login
|
# Login
|
||||||
if (firegex.login(args.password)):
|
if firegex.login(args.password):
|
||||||
puts("Sucessfully logged in ✔", color=colors.green)
|
puts("Sucessfully logged in ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Unknown response or wrong passowrd ✗", color=colors.red)
|
puts("Test Failed: Unknown response or wrong passowrd ✗", color=colors.red)
|
||||||
@@ -33,16 +56,19 @@ server = TcpServer(args.port,ipv6=args.ipv6, verbose=args.verbose)
|
|||||||
|
|
||||||
srvs = firegex.nfproxy_get_services()
|
srvs = firegex.nfproxy_get_services()
|
||||||
for ele in srvs:
|
for ele in srvs:
|
||||||
if ele['name'] == args.service_name:
|
if ele["name"] == args.service_name:
|
||||||
firegex.nfproxy_delete_service(ele['service_id'])
|
firegex.nfproxy_delete_service(ele["service_id"])
|
||||||
|
|
||||||
service_id = firegex.nfproxy_add_service(args.service_name, args.port, "http" , "::1" if args.ipv6 else "127.0.0.1" )
|
service_id = firegex.nfproxy_add_service(
|
||||||
|
args.service_name, args.port, "http", "::1" if args.ipv6 else "127.0.0.1"
|
||||||
|
)
|
||||||
if service_id:
|
if service_id:
|
||||||
puts(f"Sucessfully created service {service_id} ✔", color=colors.green)
|
puts(f"Sucessfully created service {service_id} ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Failed to create service ✗", color=colors.red)
|
puts("Test Failed: Failed to create service ✗", color=colors.red)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
def exit_test(code):
|
def exit_test(code):
|
||||||
if service_id:
|
if service_id:
|
||||||
server.stop()
|
server.stop()
|
||||||
@@ -54,7 +80,8 @@ def exit_test(code):
|
|||||||
"""
|
"""
|
||||||
exit(code)
|
exit(code)
|
||||||
|
|
||||||
if(firegex.nfproxy_start_service(service_id)):
|
|
||||||
|
if firegex.nfproxy_start_service(service_id):
|
||||||
puts("Sucessfully started service ✔", color=colors.green)
|
puts("Sucessfully started service ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Failed to start service ✗", color=colors.red)
|
puts("Test Failed: Failed to start service ✗", color=colors.red)
|
||||||
@@ -85,14 +112,20 @@ def verdict_test(packet:RawPacket):
|
|||||||
|
|
||||||
BASE_FILTER_VERDICT_NAME = "verdict_test"
|
BASE_FILTER_VERDICT_NAME = "verdict_test"
|
||||||
|
|
||||||
|
|
||||||
def get_vedict_test(to_match: str, action: str, mangle_to: str = "REDACTED"):
|
def get_vedict_test(to_match: str, action: str, mangle_to: str = "REDACTED"):
|
||||||
return BASE_FILTER_VERDICT_TEST.replace("%%TEST%%", to_match).replace("%%ACTION%%", action).replace("%%MANGLE%%", mangle_to)
|
return (
|
||||||
|
BASE_FILTER_VERDICT_TEST.replace("%%TEST%%", to_match)
|
||||||
|
.replace("%%ACTION%%", action)
|
||||||
|
.replace("%%MANGLE%%", mangle_to)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Check if filter is present in the service
|
# Check if filter is present in the service
|
||||||
n_blocked = 0
|
n_blocked = 0
|
||||||
n_mangled = 0
|
n_mangled = 0
|
||||||
|
|
||||||
|
|
||||||
def checkFilter(match_bytes, filter_name, should_work=True, mangle_with=None):
|
def checkFilter(match_bytes, filter_name, should_work=True, mangle_with=None):
|
||||||
if mangle_with:
|
if mangle_with:
|
||||||
if should_work:
|
if should_work:
|
||||||
@@ -107,31 +140,58 @@ def checkFilter(match_bytes, filter_name, should_work=True, mangle_with=None):
|
|||||||
real_response = server.recv_packet()
|
real_response = server.recv_packet()
|
||||||
expected_response = pre_packet + mangle_with + post_packet
|
expected_response = pre_packet + mangle_with + post_packet
|
||||||
if real_response == expected_response:
|
if real_response == expected_response:
|
||||||
puts("The malicious request was successfully mangled ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious request was successfully mangled ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
n_mangled += 1
|
n_mangled += 1
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if firegex.nfproxy_get_pyfilter(service_id, filter_name)["edited_packets"] == n_mangled:
|
if (
|
||||||
puts("The packet was reported as mangled in the API ✔", color=colors.green)
|
firegex.nfproxy_get_pyfilter(service_id, filter_name)[
|
||||||
|
"edited_packets"
|
||||||
|
]
|
||||||
|
== n_mangled
|
||||||
|
):
|
||||||
|
puts(
|
||||||
|
"The packet was reported as mangled in the API ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The packet wasn't reported as mangled in the API ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The packet wasn't reported as mangled in the API ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.send_packet(pre_packet)
|
server.send_packet(pre_packet)
|
||||||
if server.recv_packet() == pre_packet:
|
if server.recv_packet() == pre_packet:
|
||||||
puts("Is able to communicate after mangle ✔", color=colors.green)
|
puts(
|
||||||
|
"Is able to communicate after mangle ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Couldn't communicate after mangle ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: Couldn't communicate after mangle ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The request wasn't mangled ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The request wasn't mangled ✗", color=colors.red
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
return
|
return
|
||||||
puts("Test Failed: The filter wasn't found ✗", color=colors.red)
|
puts("Test Failed: The filter wasn't found ✗", color=colors.red)
|
||||||
else:
|
else:
|
||||||
if server.sendCheckData(secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)):
|
if server.sendCheckData(
|
||||||
|
secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)
|
||||||
|
):
|
||||||
puts("The request wasn't mangled ✔", color=colors.green)
|
puts("The request wasn't mangled ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The request was mangled when it shouldn't have", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The request was mangled when it shouldn't have",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
else:
|
else:
|
||||||
if should_work:
|
if should_work:
|
||||||
@@ -139,33 +199,60 @@ def checkFilter(match_bytes, filter_name, should_work=True, mangle_with=None):
|
|||||||
for r in firegex.nfproxy_get_service_pyfilters(service_id):
|
for r in firegex.nfproxy_get_service_pyfilters(service_id):
|
||||||
if r["name"] == filter_name:
|
if r["name"] == filter_name:
|
||||||
# Test the filter
|
# Test the filter
|
||||||
if not server.sendCheckData(secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)):
|
if not server.sendCheckData(
|
||||||
puts("The malicious request was successfully blocked ✔", color=colors.green)
|
secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)
|
||||||
|
):
|
||||||
|
puts(
|
||||||
|
"The malicious request was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
n_blocked += 1
|
n_blocked += 1
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if firegex.nfproxy_get_pyfilter(service_id, filter_name)["blocked_packets"] == n_blocked:
|
if (
|
||||||
puts("The packet was reported as blocked in the API ✔", color=colors.green)
|
firegex.nfproxy_get_pyfilter(service_id, filter_name)[
|
||||||
|
"blocked_packets"
|
||||||
|
]
|
||||||
|
== n_blocked
|
||||||
|
):
|
||||||
|
puts(
|
||||||
|
"The packet was reported as blocked in the API ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The packet wasn't reported as blocked in the API ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The packet wasn't reported as blocked in the API ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The request wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The request wasn't blocked ✗", color=colors.red
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
return
|
return
|
||||||
puts("Test Failed: The filter wasn't found ✗", color=colors.red)
|
puts("Test Failed: The filter wasn't found ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
else:
|
else:
|
||||||
if server.sendCheckData(secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)):
|
if server.sendCheckData(
|
||||||
|
secrets.token_bytes(40) + match_bytes + secrets.token_bytes(40)
|
||||||
|
):
|
||||||
puts("The request wasn't blocked ✔", color=colors.green)
|
puts("The request wasn't blocked ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The request was blocked when it shouldn't have", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The request was blocked when it shouldn't have",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
|
|
||||||
|
|
||||||
# Add new filter
|
# Add new filter
|
||||||
secret = bytes(secrets.token_hex(16).encode())
|
secret = bytes(secrets.token_hex(16).encode())
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "REJECT")):
|
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "REJECT")):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} in REJECT mode ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} in REJECT mode ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -210,6 +297,7 @@ else:
|
|||||||
|
|
||||||
checkFilter(secret, BASE_FILTER_VERDICT_NAME)
|
checkFilter(secret, BASE_FILTER_VERDICT_NAME)
|
||||||
|
|
||||||
|
|
||||||
def remove_filters():
|
def remove_filters():
|
||||||
global n_blocked, n_mangled
|
global n_blocked, n_mangled
|
||||||
server.stop()
|
server.stop()
|
||||||
@@ -220,6 +308,7 @@ def remove_filters():
|
|||||||
n_blocked = 0
|
n_blocked = 0
|
||||||
n_mangled = 0
|
n_mangled = 0
|
||||||
|
|
||||||
|
|
||||||
remove_filters()
|
remove_filters()
|
||||||
|
|
||||||
# Check if it's actually deleted
|
# Check if it's actually deleted
|
||||||
@@ -227,7 +316,9 @@ checkFilter(secret, BASE_FILTER_VERDICT_NAME, should_work=False)
|
|||||||
|
|
||||||
# Check if DROP works
|
# Check if DROP works
|
||||||
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "DROP")):
|
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "DROP")):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} in DROP mode ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} in DROP mode ✔", color=colors.green
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -238,8 +329,14 @@ remove_filters()
|
|||||||
|
|
||||||
# Check if UNSTABLE_MANGLE works
|
# Check if UNSTABLE_MANGLE works
|
||||||
mangle_result = secrets.token_hex(4).encode() # Mangle to a smaller packet
|
mangle_result = secrets.token_hex(4).encode() # Mangle to a smaller packet
|
||||||
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "UNSTABLE_MANGLE", mangle_result.decode())):
|
if firegex.nfproxy_set_code(
|
||||||
puts(f"Sucessfully added filter for {str(secret)} in UNSTABLE_MANGLE mode to a smaller packet size ✔", color=colors.green)
|
service_id,
|
||||||
|
get_vedict_test(secret.decode(), "UNSTABLE_MANGLE", mangle_result.decode()),
|
||||||
|
):
|
||||||
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} in UNSTABLE_MANGLE mode to a smaller packet size ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -250,8 +347,14 @@ remove_filters()
|
|||||||
|
|
||||||
# Check if UNSTABLE_MANGLE works
|
# Check if UNSTABLE_MANGLE works
|
||||||
mangle_result = secrets.token_hex(60).encode() # Mangle to a bigger packet
|
mangle_result = secrets.token_hex(60).encode() # Mangle to a bigger packet
|
||||||
if firegex.nfproxy_set_code(service_id, get_vedict_test(secret.decode(), "UNSTABLE_MANGLE", mangle_result.decode())):
|
if firegex.nfproxy_set_code(
|
||||||
puts(f"Sucessfully added filter for {str(secret)} in UNSTABLE_MANGLE mode to a bigger packet size ✔", color=colors.green)
|
service_id,
|
||||||
|
get_vedict_test(secret.decode(), "UNSTABLE_MANGLE", mangle_result.decode()),
|
||||||
|
):
|
||||||
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} in UNSTABLE_MANGLE mode to a bigger packet size ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -273,7 +376,10 @@ def data_type_test(packet:TCPInputStream):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, TCP_INPUT_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, TCP_INPUT_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for TCPInputStream ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for TCPInputStream ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -309,7 +415,10 @@ def data_type_test(packet:TCPOutputStream):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, TCP_OUTPUT_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, TCP_OUTPUT_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for TCPOutputStream ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for TCPOutputStream ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -363,7 +472,10 @@ def data_type_test(req:HttpRequest):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for HttpRequest ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpRequest ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -371,23 +483,94 @@ else:
|
|||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(REQUEST_HEADER_TEST.encode())
|
server.send_packet(REQUEST_HEADER_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious header was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious header wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(REQUEST_BODY_TEST.encode())
|
server.send_packet(REQUEST_BODY_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious body was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious body was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious body wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious body wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
remove_filters()
|
remove_filters()
|
||||||
|
|
||||||
|
HTTP_FULL_REQUEST_STREAM_TEST = f"""
|
||||||
|
from firegex.nfproxy.models import HttpFullRequest
|
||||||
|
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
||||||
|
|
||||||
|
@pyfilter
|
||||||
|
def data_type_test(req:HttpFullRequest):
|
||||||
|
if not req.body:
|
||||||
|
return ACCEPT
|
||||||
|
if not req.get_header("x-test"):
|
||||||
|
return ACCEPT
|
||||||
|
if {repr(secret.decode())} in req.get_header("x-test"):
|
||||||
|
return REJECT
|
||||||
|
if {repr(secret)} in req.body:
|
||||||
|
return REJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if firegex.nfproxy_set_code(service_id, HTTP_FULL_REQUEST_STREAM_TEST):
|
||||||
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpFullRequest ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
|
exit_test(1)
|
||||||
|
|
||||||
|
server.connect_client()
|
||||||
|
server.send_packet(REQUEST_HEADER_TEST.encode())
|
||||||
|
if not server.recv_packet():
|
||||||
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
|
exit_test(1)
|
||||||
|
server.close_client()
|
||||||
|
|
||||||
|
server.connect_client()
|
||||||
|
server.send_packet(REQUEST_BODY_TEST.encode())
|
||||||
|
if not server.recv_packet():
|
||||||
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious body was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious body wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
|
exit_test(1)
|
||||||
|
server.close_client()
|
||||||
|
|
||||||
|
remove_filters()
|
||||||
|
|
||||||
|
|
||||||
HTTP_REQUEST_HEADER_STREAM_TEST = f"""
|
HTTP_REQUEST_HEADER_STREAM_TEST = f"""
|
||||||
from firegex.nfproxy.models import HttpRequestHeader
|
from firegex.nfproxy.models import HttpRequestHeader
|
||||||
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
||||||
@@ -400,7 +583,10 @@ def data_type_test(req:HttpRequestHeader):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_HEADER_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_HEADER_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for HttpRequestHeader ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpRequestHeader ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -408,9 +594,15 @@ else:
|
|||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(REQUEST_HEADER_TEST.encode())
|
server.send_packet(REQUEST_HEADER_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious header was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious header wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
@@ -447,7 +639,10 @@ def data_type_test(req:HttpResponse):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, HTTP_RESPONSE_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, HTTP_RESPONSE_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for HttpResponse ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpResponse ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -455,23 +650,95 @@ else:
|
|||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(RESPONSE_HEADER_TEST.encode())
|
server.send_packet(RESPONSE_HEADER_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious header was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious header wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(RESPONSE_BODY_TEST.encode())
|
server.send_packet(RESPONSE_BODY_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious body was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious body was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious body wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious body wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
remove_filters()
|
remove_filters()
|
||||||
|
|
||||||
|
|
||||||
|
HTTP_FULL_RESPONSE_STREAM_TEST = f"""
|
||||||
|
from firegex.nfproxy.models import HttpFullResponse
|
||||||
|
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
||||||
|
|
||||||
|
@pyfilter
|
||||||
|
def data_type_test(req:HttpFullResponse):
|
||||||
|
if not req.body:
|
||||||
|
return ACCEPT
|
||||||
|
if not req.get_header("x-test"):
|
||||||
|
return ACCEPT
|
||||||
|
if {repr(secret.decode())} in req.get_header("x-test"):
|
||||||
|
return REJECT
|
||||||
|
if {repr(secret)} in req.body:
|
||||||
|
return REJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if firegex.nfproxy_set_code(service_id, HTTP_FULL_RESPONSE_STREAM_TEST):
|
||||||
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpFullResponse ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
|
exit_test(1)
|
||||||
|
|
||||||
|
server.connect_client()
|
||||||
|
server.send_packet(RESPONSE_HEADER_TEST.encode())
|
||||||
|
if not server.recv_packet():
|
||||||
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
|
exit_test(1)
|
||||||
|
server.close_client()
|
||||||
|
|
||||||
|
server.connect_client()
|
||||||
|
server.send_packet(RESPONSE_BODY_TEST.encode())
|
||||||
|
if not server.recv_packet():
|
||||||
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious body was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious body wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
|
exit_test(1)
|
||||||
|
server.close_client()
|
||||||
|
|
||||||
|
remove_filters()
|
||||||
|
|
||||||
|
|
||||||
HTTP_RESPONSE_HEADER_STREAM_TEST = f"""
|
HTTP_RESPONSE_HEADER_STREAM_TEST = f"""
|
||||||
from firegex.nfproxy.models import HttpResponseHeader
|
from firegex.nfproxy.models import HttpResponseHeader
|
||||||
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
from firegex.nfproxy import pyfilter, ACCEPT, UNSTABLE_MANGLE, DROP, REJECT
|
||||||
@@ -484,7 +751,10 @@ def data_type_test(req:HttpResponseHeader):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, HTTP_RESPONSE_HEADER_STREAM_TEST):
|
if firegex.nfproxy_set_code(service_id, HTTP_RESPONSE_HEADER_STREAM_TEST):
|
||||||
puts(f"Sucessfully added filter for {str(secret)} for HttpResponseHeader ✔", color=colors.green)
|
puts(
|
||||||
|
f"Sucessfully added filter for {str(secret)} for HttpResponseHeader ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
puts(f"Test Failed: Couldn't add the filter {str(secret)} ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -492,9 +762,15 @@ else:
|
|||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(RESPONSE_HEADER_TEST.encode())
|
server.send_packet(RESPONSE_HEADER_TEST.encode())
|
||||||
if not server.recv_packet():
|
if not server.recv_packet():
|
||||||
puts("The malicious HTTP request with the malicious header was successfully blocked ✔", color=colors.green)
|
puts(
|
||||||
|
"The malicious HTTP request with the malicious header was successfully blocked ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP request with the malicious header wasn't blocked ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP request with the malicious header wasn't blocked ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
@@ -502,8 +778,8 @@ remove_filters()
|
|||||||
|
|
||||||
# Simulating requests is more complex due to websocket extensions handshake
|
# Simulating requests is more complex due to websocket extensions handshake
|
||||||
|
|
||||||
WS_REQUEST_PARSING_TEST = b'GET /sock/?EIO=4&transport=websocket HTTP/1.1\r\nHost: localhost:8080\r\nConnection: Upgrade\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)\xac AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36\r\nUpgrade: websocket\r\nOrigin: http://localhost:8080\r\nSec-WebSocket-Version: 13\r\nAccept-Encoding: gzip, deflate, br, zstd\r\nAccept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5\r\nCookie: cookie-consent=true; _iub_cs-86405163=%7B%22timestamp%22%3A%222024-09-12T18%3A20%3A18.627Z%22%2C%22version%22%3A%221.65.1%22%2C%22purposes%22%3A%7B%221%22%3Atrue%2C%224%22%3Atrue%7D%2C%22id%22%3A86405163%2C%22cons%22%3A%7B%22rand%22%3A%222b09e6%22%7D%7D\r\nSec-WebSocket-Key: eE01O3/ZShPKsrykACLAaA==\r\nSec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n\xc1\x84#\x8a\xb2\xbb\x11\xbb\xb2\xbb'
|
WS_REQUEST_PARSING_TEST = b"GET /sock/?EIO=4&transport=websocket HTTP/1.1\r\nHost: localhost:8080\r\nConnection: Upgrade\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)\xac AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36\r\nUpgrade: websocket\r\nOrigin: http://localhost:8080\r\nSec-WebSocket-Version: 13\r\nAccept-Encoding: gzip, deflate, br, zstd\r\nAccept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5\r\nCookie: cookie-consent=true; _iub_cs-86405163=%7B%22timestamp%22%3A%222024-09-12T18%3A20%3A18.627Z%22%2C%22version%22%3A%221.65.1%22%2C%22purposes%22%3A%7B%221%22%3Atrue%2C%224%22%3Atrue%7D%2C%22id%22%3A86405163%2C%22cons%22%3A%7B%22rand%22%3A%222b09e6%22%7D%7D\r\nSec-WebSocket-Key: eE01O3/ZShPKsrykACLAaA==\r\nSec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n\xc1\x84#\x8a\xb2\xbb\x11\xbb\xb2\xbb"
|
||||||
WS_RESPONSE_PARSING_TEST = b'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: eGnJqUSoSKE3wOfKD2M3G82RsS8=\r\nSec-WebSocket-Extensions: permessage-deflate\r\ndate: Sat, 15 Mar 2025 12:04:19 GMT\r\nserver: uvicorn\r\n\r\n\xc1_2\xa8V*\xceLQ\xb2Rr1\xb4\xc8\xf6r\x0c\xf3\xaf\xd25\xf7\x8e\xf4\xb3LsttrW\xd2Q*-H/JLI-V\xb2\x8a\x8e\xd5Q*\xc8\xccK\x0f\xc9\xccM\xcd/-Q\xb222\x00\x02\x88\x98g^IjQYb\x0eP\xd0\x14,\x98\x9bX\x11\x90X\x99\x93\x9f\x084\xda\xd0\x00\x0cj\x01\x00\xc1\x1b21\x80\xd9e\xe1n\x19\x9e\xe3RP\x9a[Z\x99\x93j\xea\x15\x00\xb4\xcbC\xa9\x16\x00'
|
WS_RESPONSE_PARSING_TEST = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: eGnJqUSoSKE3wOfKD2M3G82RsS8=\r\nSec-WebSocket-Extensions: permessage-deflate\r\ndate: Sat, 15 Mar 2025 12:04:19 GMT\r\nserver: uvicorn\r\n\r\n\xc1_2\xa8V*\xceLQ\xb2Rr1\xb4\xc8\xf6r\x0c\xf3\xaf\xd25\xf7\x8e\xf4\xb3LsttrW\xd2Q*-H/JLI-V\xb2\x8a\x8e\xd5Q*\xc8\xccK\x0f\xc9\xccM\xcd/-Q\xb222\x00\x02\x88\x98g^IjQYb\x0eP\xd0\x14,\x98\x9bX\x11\x90X\x99\x93\x9f\x084\xda\xd0\x00\x0cj\x01\x00\xc1\x1b21\x80\xd9e\xe1n\x19\x9e\xe3RP\x9a[Z\x99\x93j\xea\x15\x00\xb4\xcbC\xa9\x16\x00"
|
||||||
|
|
||||||
HTTP_REQUEST_WS_PARSING_TEST = """
|
HTTP_REQUEST_WS_PARSING_TEST = """
|
||||||
from firegex.nfproxy.models import HttpRequest, HttpResponse
|
from firegex.nfproxy.models import HttpRequest, HttpResponse
|
||||||
@@ -520,7 +796,10 @@ def data_type_test(req:HttpResponse):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_WS_PARSING_TEST):
|
if firegex.nfproxy_set_code(service_id, HTTP_REQUEST_WS_PARSING_TEST):
|
||||||
puts("Sucessfully added filter websocket parsing with HttpRequest and HttpResponse ✔", color=colors.green)
|
puts(
|
||||||
|
"Sucessfully added filter websocket parsing with HttpRequest and HttpResponse ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Couldn't add the websocket parsing filter ✗", color=colors.red)
|
puts("Test Failed: Couldn't add the websocket parsing filter ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
@@ -528,9 +807,15 @@ else:
|
|||||||
server.connect_client()
|
server.connect_client()
|
||||||
server.send_packet(WS_REQUEST_PARSING_TEST, server_reply=WS_RESPONSE_PARSING_TEST)
|
server.send_packet(WS_REQUEST_PARSING_TEST, server_reply=WS_RESPONSE_PARSING_TEST)
|
||||||
if server.recv_packet():
|
if server.recv_packet():
|
||||||
puts("The HTTP websocket upgrade request was successfully parsed ✔", color=colors.green)
|
puts(
|
||||||
|
"The HTTP websocket upgrade request was successfully parsed ✔",
|
||||||
|
color=colors.green,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: The HTTP websocket upgrade request wasn't parsed (an error occurred) ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: The HTTP websocket upgrade request wasn't parsed (an error occurred) ✗",
|
||||||
|
color=colors.red,
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
server.close_client()
|
server.close_client()
|
||||||
|
|
||||||
@@ -552,19 +837,27 @@ else:
|
|||||||
exit_test(1)
|
exit_test(1)
|
||||||
|
|
||||||
# Rename back service
|
# Rename back service
|
||||||
if(firegex.nfproxy_rename_service(service_id,f"{args.service_name}")):
|
if firegex.nfproxy_rename_service(service_id, f"{args.service_name}"):
|
||||||
puts(f"Sucessfully renamed service to {args.service_name} ✔", color=colors.green)
|
puts(f"Sucessfully renamed service to {args.service_name} ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Coulnd't rename service ✗", color=colors.red)
|
puts("Test Failed: Coulnd't rename service ✗", color=colors.red)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
|
|
||||||
# Change settings
|
# Change settings
|
||||||
if(firegex.nfproxy_settings_service(service_id, 1338, "::dead:beef" if args.ipv6 else "123.123.123.123", True)):
|
if firegex.nfproxy_settings_service(
|
||||||
|
service_id, 1338, "::dead:beef" if args.ipv6 else "123.123.123.123", True
|
||||||
|
):
|
||||||
srv_updated = firegex.nfproxy_get_service(service_id)
|
srv_updated = firegex.nfproxy_get_service(service_id)
|
||||||
if srv_updated["port"] == 1338 and ("::dead:beef" if args.ipv6 else "123.123.123.123") in srv_updated["ip_int"] and srv_updated["fail_open"]:
|
if (
|
||||||
|
srv_updated["port"] == 1338
|
||||||
|
and ("::dead:beef" if args.ipv6 else "123.123.123.123") in srv_updated["ip_int"]
|
||||||
|
and srv_updated["fail_open"]
|
||||||
|
):
|
||||||
puts("Sucessfully changed service settings ✔", color=colors.green)
|
puts("Sucessfully changed service settings ✔", color=colors.green)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Service settings weren't updated correctly ✗", color=colors.red)
|
puts(
|
||||||
|
"Test Failed: Service settings weren't updated correctly ✗", color=colors.red
|
||||||
|
)
|
||||||
exit_test(1)
|
exit_test(1)
|
||||||
else:
|
else:
|
||||||
puts("Test Failed: Coulnd't change service settings ✗", color=colors.red)
|
puts("Test Failed: Coulnd't change service settings ✗", color=colors.red)
|
||||||
|
|||||||
Reference in New Issue
Block a user