firegex 3.5.2__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- firegex-4.0.0/PKG-INFO +57 -0
- firegex-4.0.0/README.md +26 -0
- firegex-4.0.0/firegex/__init__.py +69 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/__init__.py +14 -5
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/models/__init__.py +7 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/models/http.py +134 -8
- firegex-4.0.0/firegex.egg-info/PKG-INFO +57 -0
- {firegex-3.5.2 → firegex-4.0.0}/setup.py +1 -1
- firegex-3.5.2/PKG-INFO +0 -182
- firegex-3.5.2/README.md +0 -151
- firegex-3.5.2/firegex/__init__.py +0 -5
- firegex-3.5.2/firegex.egg-info/PKG-INFO +0 -182
- {firegex-3.5.2 → firegex-4.0.0}/MANIFEST.in +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/fgex +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/__main__.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/cli.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/internals/__init__.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/internals/data.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/internals/exceptions.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/internals/models.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/models/tcp.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex/nfproxy/proxysim/__init__.py +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex.egg-info/SOURCES.txt +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex.egg-info/dependency_links.txt +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex.egg-info/requires.txt +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/firegex.egg-info/top_level.txt +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/requirements.txt +0 -0
- {firegex-3.5.2 → firegex-4.0.0}/setup.cfg +0 -0
firegex-4.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: firegex
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Firegex client
|
|
5
|
+
Home-page: https://github.com/pwnzer0tt1/firegex
|
|
6
|
+
Author: Pwnzer0tt1
|
|
7
|
+
Author-email: pwnzer0tt1@poliba.it
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: typer
|
|
14
|
+
Requires-Dist: pydantic>=2
|
|
15
|
+
Requires-Dist: typing-extensions>=4.7.1
|
|
16
|
+
Requires-Dist: pycryptodome
|
|
17
|
+
Requires-Dist: watchfiles
|
|
18
|
+
Requires-Dist: fgex
|
|
19
|
+
Requires-Dist: websockets
|
|
20
|
+
Requires-Dist: brotli
|
|
21
|
+
Requires-Dist: pyllhttp
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# Firegex Python Library and CLI
|
|
33
|
+
|
|
34
|
+
This is the Python library for [Firegex](https://github.com/Pwnzer0tt1/firegex), a firewall built for CTF Attack-Defense competitions. It provides the `firegex.nfproxy` package used to write [Netfilter Proxy](../docs/nfproxy.md) Python packet filters, the `fgex` CLI, and a local proxy simulator (`proxysim`) for testing filters without a running Firegex instance.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install -U firegex
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`fgex` is an alias package for `firegex`: installing either one gives you the same `firegex` module and the `fgex`/`firegex` CLI commands.
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
**➡️ [Netfilter Proxy: writing and testing `@pyfilter` scripts](../docs/nfproxy.md)** — the `pyfilter` decorator, every data structure (`RawPacket`, `TCPInputStream`/`TCPOutputStream`, `HttpRequest`/`HttpResponse` and their variants, `HttpHistory`), packet statements, stream limits, the `fgex nfproxy` CLI simulator, and a full worked example.
|
|
47
|
+
|
|
48
|
+
That page is also what's shown in the Firegex web UI itself (the docs button on the nfproxy pages) — both are generated from that single file, so they can never drift out of sync.
|
|
49
|
+
|
|
50
|
+
## Firegex's other filtering modules
|
|
51
|
+
|
|
52
|
+
This library only covers Netfilter Proxy. Firegex ships several other independent modules, each with its own guide:
|
|
53
|
+
|
|
54
|
+
- **[Netfilter Regex](../docs/nfregex.md)** — fast, PCRE2/hyperscan-based regex matching against raw traffic.
|
|
55
|
+
- **[Hijack Port to Proxy](../docs/porthijack.md)** — redirect traffic to your own external proxy without touching the target service.
|
|
56
|
+
- **[Firewall Rules](../docs/firewall.md)** — plain nftables allow/drop/reject rules.
|
|
57
|
+
- **[TLS Decryption](../docs/tls.md)** — decrypt-and-reinspect bridge for services that speak TLS natively.
|
firegex-4.0.0/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Firegex Python Library and CLI
|
|
2
|
+
|
|
3
|
+
This is the Python library for [Firegex](https://github.com/Pwnzer0tt1/firegex), a firewall built for CTF Attack-Defense competitions. It provides the `firegex.nfproxy` package used to write [Netfilter Proxy](../docs/nfproxy.md) Python packet filters, the `fgex` CLI, and a local proxy simulator (`proxysim`) for testing filters without a running Firegex instance.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -U firegex
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`fgex` is an alias package for `firegex`: installing either one gives you the same `firegex` module and the `fgex`/`firegex` CLI commands.
|
|
12
|
+
|
|
13
|
+
## Documentation
|
|
14
|
+
|
|
15
|
+
**➡️ [Netfilter Proxy: writing and testing `@pyfilter` scripts](../docs/nfproxy.md)** — the `pyfilter` decorator, every data structure (`RawPacket`, `TCPInputStream`/`TCPOutputStream`, `HttpRequest`/`HttpResponse` and their variants, `HttpHistory`), packet statements, stream limits, the `fgex nfproxy` CLI simulator, and a full worked example.
|
|
16
|
+
|
|
17
|
+
That page is also what's shown in the Firegex web UI itself (the docs button on the nfproxy pages) — both are generated from that single file, so they can never drift out of sync.
|
|
18
|
+
|
|
19
|
+
## Firegex's other filtering modules
|
|
20
|
+
|
|
21
|
+
This library only covers Netfilter Proxy. Firegex ships several other independent modules, each with its own guide:
|
|
22
|
+
|
|
23
|
+
- **[Netfilter Regex](../docs/nfregex.md)** — fast, PCRE2/hyperscan-based regex matching against raw traffic.
|
|
24
|
+
- **[Hijack Port to Proxy](../docs/porthijack.md)** — redirect traffic to your own external proxy without touching the target service.
|
|
25
|
+
- **[Firewall Rules](../docs/firewall.md)** — plain nftables allow/drop/reject rules.
|
|
26
|
+
- **[TLS Decryption](../docs/tls.md)** — decrypt-and-reinspect bridge for services that speak TLS natively.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
def _get_version():
|
|
3
|
+
v = "4.0.0" if "{" not in "4.0.0" else "0.0.0"
|
|
4
|
+
if v == "0.0.0":
|
|
5
|
+
import os
|
|
6
|
+
env_v = os.getenv("FIREGEX_VERSION")
|
|
7
|
+
if env_v:
|
|
8
|
+
return env_v
|
|
9
|
+
try:
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
def parse_version(ver):
|
|
13
|
+
ver = ver.lstrip("v")
|
|
14
|
+
parts = re.split(r'[^0-9]+', ver)
|
|
15
|
+
return tuple(int(p) if p.isdigit() else 0 for p in parts if p)
|
|
16
|
+
|
|
17
|
+
def find_git_dir(start_path):
|
|
18
|
+
current = start_path
|
|
19
|
+
while current and current != "/" and current != "":
|
|
20
|
+
gd = os.path.join(current, ".git")
|
|
21
|
+
if os.path.isdir(gd):
|
|
22
|
+
return gd
|
|
23
|
+
parent = os.path.dirname(current)
|
|
24
|
+
if parent == current:
|
|
25
|
+
break
|
|
26
|
+
current = parent
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
git_dir = find_git_dir(os.path.abspath(os.path.dirname(__file__)))
|
|
30
|
+
if not git_dir:
|
|
31
|
+
return v
|
|
32
|
+
|
|
33
|
+
branch = None
|
|
34
|
+
with open(os.path.join(git_dir, "HEAD"), "r") as f:
|
|
35
|
+
head = f.read().strip()
|
|
36
|
+
if head.startswith("ref: refs/heads/"):
|
|
37
|
+
branch = head.split("ref: refs/heads/")[1]
|
|
38
|
+
|
|
39
|
+
if branch != "main":
|
|
40
|
+
return v
|
|
41
|
+
|
|
42
|
+
tags = []
|
|
43
|
+
try:
|
|
44
|
+
with open(os.path.join(git_dir, "packed-refs"), "r") as f:
|
|
45
|
+
for line in f:
|
|
46
|
+
if " refs/tags/" in line:
|
|
47
|
+
tags.append(line.strip().split(" refs/tags/")[1])
|
|
48
|
+
except Exception:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
tags_dir = os.path.join(git_dir, "refs", "tags")
|
|
52
|
+
try:
|
|
53
|
+
for root, _, files in os.walk(tags_dir):
|
|
54
|
+
for file in files:
|
|
55
|
+
tags.append(os.path.relpath(os.path.join(root, file), tags_dir).replace(os.path.sep, "/"))
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
version_tags = [t for t in set(tags) if any(c.isdigit() for c in t)]
|
|
60
|
+
if version_tags:
|
|
61
|
+
return max(version_tags, key=parse_version)
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
return v
|
|
65
|
+
|
|
66
|
+
__version__ = _get_version()
|
|
67
|
+
|
|
68
|
+
#Exported functions
|
|
69
|
+
__all__ = []
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import functools
|
|
2
|
-
from firegex.nfproxy.models import
|
|
3
|
-
|
|
2
|
+
from firegex.nfproxy.models import (
|
|
3
|
+
RawPacket,
|
|
4
|
+
TCPInputStream,
|
|
5
|
+
TCPOutputStream,
|
|
6
|
+
TCPClientStream,
|
|
7
|
+
TCPServerStream,
|
|
8
|
+
HttpHistory,
|
|
9
|
+
HttpStreamHistory,
|
|
10
|
+
)
|
|
11
|
+
from firegex.nfproxy.internals.models import Action, FullStreamAction, ExceptionAction
|
|
4
12
|
|
|
5
13
|
ACCEPT = Action.ACCEPT
|
|
6
14
|
DROP = Action.DROP
|
|
@@ -35,7 +43,8 @@ def clear_pyfilter_registry():
|
|
|
35
43
|
pyfilter.registry.clear()
|
|
36
44
|
|
|
37
45
|
__all__ = [
|
|
38
|
-
"ACCEPT", "DROP", "REJECT", "UNSTABLE_MANGLE"
|
|
39
|
-
"Action", "FullStreamAction", "pyfilter",
|
|
40
|
-
"RawPacket", "TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream"
|
|
46
|
+
"ACCEPT", "DROP", "REJECT", "UNSTABLE_MANGLE",
|
|
47
|
+
"Action", "FullStreamAction", "ExceptionAction", "pyfilter",
|
|
48
|
+
"RawPacket", "TCPInputStream", "TCPOutputStream", "TCPClientStream", "TCPServerStream",
|
|
49
|
+
"HttpHistory", "HttpStreamHistory"
|
|
41
50
|
]
|
|
@@ -11,6 +11,8 @@ from firegex.nfproxy.models.http import (
|
|
|
11
11
|
HttpResponseHeader,
|
|
12
12
|
HttpFullRequest,
|
|
13
13
|
HttpFullResponse,
|
|
14
|
+
HttpHistory,
|
|
15
|
+
HttpStreamHistory,
|
|
14
16
|
)
|
|
15
17
|
from firegex.nfproxy.internals.data import RawPacket
|
|
16
18
|
from enum import Enum
|
|
@@ -31,6 +33,8 @@ type_annotations_associations = {
|
|
|
31
33
|
HttpResponseHeader: HttpResponseHeader._fetch_packet,
|
|
32
34
|
HttpFullRequest: HttpFullRequest._fetch_packet,
|
|
33
35
|
HttpFullResponse: HttpFullResponse._fetch_packet,
|
|
36
|
+
HttpHistory: HttpHistory._fetch_packet,
|
|
37
|
+
HttpStreamHistory: HttpStreamHistory._fetch_packet,
|
|
34
38
|
},
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -52,5 +56,8 @@ __all__ = [
|
|
|
52
56
|
"HttpResponseHeader",
|
|
53
57
|
"HttpFullRequest",
|
|
54
58
|
"HttpFullResponse",
|
|
59
|
+
"HttpHistory",
|
|
60
|
+
"HttpStreamHistory",
|
|
55
61
|
"Protocols",
|
|
56
62
|
]
|
|
63
|
+
|
|
@@ -48,6 +48,7 @@ class InternalHTTPMessage:
|
|
|
48
48
|
ws_stream: list[Frame] = field(default_factory=list) # Decoded websocket stream
|
|
49
49
|
upgrading_to_h2: bool = field(default=False)
|
|
50
50
|
upgrading_to_ws: bool = field(default=False)
|
|
51
|
+
added_to_history: bool = field(default=False)
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
@dataclass
|
|
@@ -393,6 +394,61 @@ class InternalHttpResponse(InternalCallbackHandler, pyllhttp.Response):
|
|
|
393
394
|
return False
|
|
394
395
|
|
|
395
396
|
|
|
397
|
+
class HttpHistory:
|
|
398
|
+
"""
|
|
399
|
+
HTTP History handler for pyfilters.
|
|
400
|
+
Provides access to completed previous requests and responses in the current TCP stream.
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
def __init__(
|
|
404
|
+
self,
|
|
405
|
+
requests: list["HttpFullRequest"] | None = None,
|
|
406
|
+
responses: list["HttpFullResponse"] | None = None,
|
|
407
|
+
):
|
|
408
|
+
self._requests = list(requests) if requests is not None else []
|
|
409
|
+
self._responses = list(responses) if responses is not None else []
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def requests(self) -> list["HttpFullRequest"]:
|
|
413
|
+
"""List of previous completed HTTP requests"""
|
|
414
|
+
return self._requests.copy()
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
def responses(self) -> list["HttpFullResponse"]:
|
|
418
|
+
"""List of previous completed HTTP responses"""
|
|
419
|
+
return self._responses.copy()
|
|
420
|
+
|
|
421
|
+
@classmethod
|
|
422
|
+
def _fetch_packet(cls, internal_data: DataStreamCtx):
|
|
423
|
+
if (
|
|
424
|
+
internal_data.current_pkt is None
|
|
425
|
+
or internal_data.current_pkt.is_tcp is False
|
|
426
|
+
):
|
|
427
|
+
raise NotReadyToRun()
|
|
428
|
+
|
|
429
|
+
for key in ("http_module", "http_full", "http_header"):
|
|
430
|
+
obj = internal_data.call_mem.get(f"_fetched_obj_{key}")
|
|
431
|
+
if obj is not None:
|
|
432
|
+
if isinstance(obj, list) and obj:
|
|
433
|
+
return [item.history for item in obj]
|
|
434
|
+
elif hasattr(obj, "history"):
|
|
435
|
+
return obj.history
|
|
436
|
+
|
|
437
|
+
req_history_deque = internal_data.data_handler_context.get(
|
|
438
|
+
"http_history_requests", deque()
|
|
439
|
+
)
|
|
440
|
+
resp_history_deque = internal_data.data_handler_context.get(
|
|
441
|
+
"http_history_responses", deque()
|
|
442
|
+
)
|
|
443
|
+
return HttpHistory(list(req_history_deque), list(resp_history_deque))
|
|
444
|
+
|
|
445
|
+
def __repr__(self):
|
|
446
|
+
return f"<HttpHistory requests={len(self._requests)} responses={len(self._responses)}>"
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
HttpStreamHistory = HttpHistory
|
|
450
|
+
|
|
451
|
+
|
|
396
452
|
class InternalBasicHttpMetaClass:
|
|
397
453
|
"""Internal class to handle HTTP requests and responses"""
|
|
398
454
|
|
|
@@ -404,15 +460,23 @@ class InternalBasicHttpMetaClass:
|
|
|
404
460
|
self._parser = parser
|
|
405
461
|
self.raised_error = False
|
|
406
462
|
self._message: InternalHTTPMessage | None = msg
|
|
463
|
+
self._history: HttpHistory | None = None
|
|
407
464
|
self._contructor_hook()
|
|
408
465
|
|
|
409
466
|
def _contructor_hook(self):
|
|
410
467
|
pass
|
|
411
468
|
|
|
469
|
+
@property
|
|
470
|
+
def history(self) -> HttpHistory:
|
|
471
|
+
"""HTTP History for the current stream connection"""
|
|
472
|
+
if self._history is None:
|
|
473
|
+
return HttpHistory([], [])
|
|
474
|
+
return self._history
|
|
475
|
+
|
|
412
476
|
@property
|
|
413
477
|
def total_size(self) -> int:
|
|
414
|
-
"""Total size of the
|
|
415
|
-
return self.
|
|
478
|
+
"""Total size of the message"""
|
|
479
|
+
return self._message.total_size
|
|
416
480
|
|
|
417
481
|
@property
|
|
418
482
|
def url(self) -> str | None:
|
|
@@ -462,7 +526,7 @@ class InternalBasicHttpMetaClass:
|
|
|
462
526
|
@property
|
|
463
527
|
def should_upgrade(self) -> bool:
|
|
464
528
|
"""If the message should upgrade"""
|
|
465
|
-
return self.
|
|
529
|
+
return self._message.should_upgrade
|
|
466
530
|
|
|
467
531
|
@property
|
|
468
532
|
def content_length(self) -> int | None:
|
|
@@ -493,6 +557,10 @@ class InternalBasicHttpMetaClass:
|
|
|
493
557
|
"""Get a header from the message without caring about the case"""
|
|
494
558
|
return self._message.lheaders.get(header.lower(), default)
|
|
495
559
|
|
|
560
|
+
@classmethod
|
|
561
|
+
def _should_release_message_headers(cls) -> bool:
|
|
562
|
+
return True
|
|
563
|
+
|
|
496
564
|
@staticmethod
|
|
497
565
|
def _before_fetch_callable_checks(internal_data: DataStreamCtx) -> bool:
|
|
498
566
|
raise NotImplementedError()
|
|
@@ -521,6 +589,9 @@ class InternalBasicHttpMetaClass:
|
|
|
521
589
|
parser: InternalHttpRequest | InternalHttpResponse = ParserType()
|
|
522
590
|
internal_data.data_handler_context[parser_key] = parser
|
|
523
591
|
|
|
592
|
+
parser.release_message_headers = cls._should_release_message_headers()
|
|
593
|
+
|
|
594
|
+
|
|
524
595
|
if not internal_data.call_mem.get(
|
|
525
596
|
cls._parser_class(), False
|
|
526
597
|
): # Need to parse HTTP
|
|
@@ -620,10 +691,55 @@ class InternalBasicHttpMetaClass:
|
|
|
620
691
|
|
|
621
692
|
if messages_to_call == 0:
|
|
622
693
|
raise NotReadyToRun()
|
|
623
|
-
elif messages_to_call == 1:
|
|
624
|
-
return cls(parser, messages_tosend[0])
|
|
625
694
|
|
|
626
|
-
|
|
695
|
+
raw_max = internal_data.filter_glob.get("FGEX_MAX_HISTORY_SIZE", 100)
|
|
696
|
+
try:
|
|
697
|
+
max_history = max(0, int(raw_max))
|
|
698
|
+
except (ValueError, TypeError):
|
|
699
|
+
max_history = 100
|
|
700
|
+
|
|
701
|
+
req_history_deque: deque = (
|
|
702
|
+
internal_data.data_handler_context.setdefault(
|
|
703
|
+
"http_history_requests", deque(maxlen=max_history)
|
|
704
|
+
)
|
|
705
|
+
)
|
|
706
|
+
if req_history_deque.maxlen != max_history:
|
|
707
|
+
req_history_deque = deque(req_history_deque, maxlen=max_history)
|
|
708
|
+
internal_data.data_handler_context["http_history_requests"] = req_history_deque
|
|
709
|
+
|
|
710
|
+
resp_history_deque: deque = (
|
|
711
|
+
internal_data.data_handler_context.setdefault(
|
|
712
|
+
"http_history_responses", deque(maxlen=max_history)
|
|
713
|
+
)
|
|
714
|
+
)
|
|
715
|
+
if resp_history_deque.maxlen != max_history:
|
|
716
|
+
resp_history_deque = deque(resp_history_deque, maxlen=max_history)
|
|
717
|
+
internal_data.data_handler_context["http_history_responses"] = resp_history_deque
|
|
718
|
+
|
|
719
|
+
built_instances = []
|
|
720
|
+
for msg in messages_tosend:
|
|
721
|
+
history_snapshot = HttpHistory(
|
|
722
|
+
list(req_history_deque), list(resp_history_deque)
|
|
723
|
+
)
|
|
724
|
+
instance = cls(parser, msg)
|
|
725
|
+
instance._history = history_snapshot
|
|
726
|
+
built_instances.append(instance)
|
|
727
|
+
|
|
728
|
+
if msg.message_complete and not msg.added_to_history:
|
|
729
|
+
msg.added_to_history = True
|
|
730
|
+
if internal_data.current_pkt.is_input:
|
|
731
|
+
req_history_deque.append(HttpFullRequest(parser, msg))
|
|
732
|
+
else:
|
|
733
|
+
resp_history_deque.append(HttpFullResponse(parser, msg))
|
|
734
|
+
|
|
735
|
+
if len(built_instances) == 1:
|
|
736
|
+
res = built_instances[0]
|
|
737
|
+
internal_data.call_mem[f"_fetched_obj_{cls._parser_class()}"] = res
|
|
738
|
+
return res
|
|
739
|
+
|
|
740
|
+
internal_data.call_mem[f"_fetched_obj_{cls._parser_class()}"] = built_instances
|
|
741
|
+
return built_instances
|
|
742
|
+
|
|
627
743
|
|
|
628
744
|
|
|
629
745
|
class HttpRequest(InternalBasicHttpMetaClass):
|
|
@@ -639,7 +755,7 @@ class HttpRequest(InternalBasicHttpMetaClass):
|
|
|
639
755
|
@property
|
|
640
756
|
def method(self) -> bytes:
|
|
641
757
|
"""Method of the request"""
|
|
642
|
-
return self.
|
|
758
|
+
return self._message.method
|
|
643
759
|
|
|
644
760
|
@staticmethod
|
|
645
761
|
def _parser_class() -> str:
|
|
@@ -662,7 +778,8 @@ class HttpResponse(InternalBasicHttpMetaClass):
|
|
|
662
778
|
@property
|
|
663
779
|
def status_code(self) -> int:
|
|
664
780
|
"""Status code of the response"""
|
|
665
|
-
return self.
|
|
781
|
+
return self._message.status
|
|
782
|
+
|
|
666
783
|
|
|
667
784
|
@staticmethod
|
|
668
785
|
def _parser_class() -> str:
|
|
@@ -678,6 +795,10 @@ class HttpFullRequest(HttpRequest):
|
|
|
678
795
|
This data handler will be called when the request data is complete
|
|
679
796
|
"""
|
|
680
797
|
|
|
798
|
+
@classmethod
|
|
799
|
+
def _should_release_message_headers(cls) -> bool:
|
|
800
|
+
return False
|
|
801
|
+
|
|
681
802
|
def _contructor_hook(self):
|
|
682
803
|
self._parser.release_message_headers = False
|
|
683
804
|
|
|
@@ -695,6 +816,10 @@ class HttpFullResponse(HttpResponse):
|
|
|
695
816
|
This data handler will be called when the response data is complete
|
|
696
817
|
"""
|
|
697
818
|
|
|
819
|
+
@classmethod
|
|
820
|
+
def _should_release_message_headers(cls) -> bool:
|
|
821
|
+
return False
|
|
822
|
+
|
|
698
823
|
def _contructor_hook(self):
|
|
699
824
|
self._parser.release_message_headers = False
|
|
700
825
|
|
|
@@ -706,6 +831,7 @@ class HttpFullResponse(HttpResponse):
|
|
|
706
831
|
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}>"
|
|
707
832
|
|
|
708
833
|
|
|
834
|
+
|
|
709
835
|
class HttpRequestHeader(HttpRequest):
|
|
710
836
|
"""
|
|
711
837
|
HTTP Request Header handler
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: firegex
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Firegex client
|
|
5
|
+
Home-page: https://github.com/pwnzer0tt1/firegex
|
|
6
|
+
Author: Pwnzer0tt1
|
|
7
|
+
Author-email: pwnzer0tt1@poliba.it
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: typer
|
|
14
|
+
Requires-Dist: pydantic>=2
|
|
15
|
+
Requires-Dist: typing-extensions>=4.7.1
|
|
16
|
+
Requires-Dist: pycryptodome
|
|
17
|
+
Requires-Dist: watchfiles
|
|
18
|
+
Requires-Dist: fgex
|
|
19
|
+
Requires-Dist: websockets
|
|
20
|
+
Requires-Dist: brotli
|
|
21
|
+
Requires-Dist: pyllhttp
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: requires-python
|
|
30
|
+
Dynamic: summary
|
|
31
|
+
|
|
32
|
+
# Firegex Python Library and CLI
|
|
33
|
+
|
|
34
|
+
This is the Python library for [Firegex](https://github.com/Pwnzer0tt1/firegex), a firewall built for CTF Attack-Defense competitions. It provides the `firegex.nfproxy` package used to write [Netfilter Proxy](../docs/nfproxy.md) Python packet filters, the `fgex` CLI, and a local proxy simulator (`proxysim`) for testing filters without a running Firegex instance.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install -U firegex
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`fgex` is an alias package for `firegex`: installing either one gives you the same `firegex` module and the `fgex`/`firegex` CLI commands.
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
**➡️ [Netfilter Proxy: writing and testing `@pyfilter` scripts](../docs/nfproxy.md)** — the `pyfilter` decorator, every data structure (`RawPacket`, `TCPInputStream`/`TCPOutputStream`, `HttpRequest`/`HttpResponse` and their variants, `HttpHistory`), packet statements, stream limits, the `fgex nfproxy` CLI simulator, and a full worked example.
|
|
47
|
+
|
|
48
|
+
That page is also what's shown in the Firegex web UI itself (the docs button on the nfproxy pages) — both are generated from that single file, so they can never drift out of sync.
|
|
49
|
+
|
|
50
|
+
## Firegex's other filtering modules
|
|
51
|
+
|
|
52
|
+
This library only covers Netfilter Proxy. Firegex ships several other independent modules, each with its own guide:
|
|
53
|
+
|
|
54
|
+
- **[Netfilter Regex](../docs/nfregex.md)** — fast, PCRE2/hyperscan-based regex matching against raw traffic.
|
|
55
|
+
- **[Hijack Port to Proxy](../docs/porthijack.md)** — redirect traffic to your own external proxy without touching the target service.
|
|
56
|
+
- **[Firewall Rules](../docs/firewall.md)** — plain nftables allow/drop/reject rules.
|
|
57
|
+
- **[TLS Decryption](../docs/tls.md)** — decrypt-and-reinspect bridge for services that speak TLS natively.
|
|
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
6
6
|
with open('requirements.txt', 'r', encoding='utf-8') as f:
|
|
7
7
|
required = [ele.strip() for ele in f.read().splitlines() if not ele.strip().startswith("#") and ele.strip() != ""]
|
|
8
8
|
|
|
9
|
-
VERSION = "
|
|
9
|
+
VERSION = "4.0.0"
|
|
10
10
|
|
|
11
11
|
setuptools.setup(
|
|
12
12
|
name="firegex",
|
firegex-3.5.2/PKG-INFO
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: firegex
|
|
3
|
-
Version: 3.5.2
|
|
4
|
-
Summary: Firegex client
|
|
5
|
-
Home-page: https://github.com/pwnzer0tt1/firegex
|
|
6
|
-
Author: Pwnzer0tt1
|
|
7
|
-
Author-email: pwnzer0tt1@poliba.it
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.10
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
Requires-Dist: typer
|
|
14
|
-
Requires-Dist: pydantic>=2
|
|
15
|
-
Requires-Dist: typing-extensions>=4.7.1
|
|
16
|
-
Requires-Dist: pycryptodome
|
|
17
|
-
Requires-Dist: watchfiles
|
|
18
|
-
Requires-Dist: fgex
|
|
19
|
-
Requires-Dist: websockets
|
|
20
|
-
Requires-Dist: brotli
|
|
21
|
-
Requires-Dist: pyllhttp
|
|
22
|
-
Dynamic: author
|
|
23
|
-
Dynamic: author-email
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: requires-dist
|
|
29
|
-
Dynamic: requires-python
|
|
30
|
-
Dynamic: summary
|
|
31
|
-
|
|
32
|
-
# Firegex Python Library and CLI
|
|
33
|
-
|
|
34
|
-
This is the Python library for Firegex. It is used to get additional features of Firegex and use the feature of the command `fgex`.
|
|
35
|
-
|
|
36
|
-
## Installation
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
pip install -U firegex
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
fgex is an alias of firegex. You can use `fgex` instead of `firegex`.
|
|
43
|
-
|
|
44
|
-
## Command line usage:
|
|
45
|
-
|
|
46
|
-
```text
|
|
47
|
-
➤ fgex nfproxy -h
|
|
48
|
-
|
|
49
|
-
Usage: fgex nfproxy [OPTIONS] FILTER_FILE ADDRESS PORT
|
|
50
|
-
|
|
51
|
-
Run an nfproxy simulation
|
|
52
|
-
|
|
53
|
-
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
54
|
-
│ * filter_file TEXT The path to the filter file [default: None] [required] │
|
|
55
|
-
│ * address TEXT The address of the target to proxy [default: None] [required] │
|
|
56
|
-
│ * port INTEGER The port of the target to proxy [default: None] [required] │
|
|
57
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
58
|
-
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
59
|
-
│ --proto [tcp|http] The protocol to proxy [default: tcp] │
|
|
60
|
-
│ --from-address TEXT The address of the local server [default: None] │
|
|
61
|
-
│ --from-port INTEGER The port of the local server [default: 7474] │
|
|
62
|
-
│ -6 Use IPv6 for the connection │
|
|
63
|
-
│ --help -h Show this message and exit. │
|
|
64
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Library usage:
|
|
68
|
-
|
|
69
|
-
## NfProxy decorator
|
|
70
|
-
|
|
71
|
-
```python
|
|
72
|
-
from firegex.nfproxy import pyfilter
|
|
73
|
-
```
|
|
74
|
-
This decorator is used to create a filter for the nfproxy.
|
|
75
|
-
Example:
|
|
76
|
-
```python
|
|
77
|
-
@pyfilter
|
|
78
|
-
def my_filter(raw_packet: RawPacket): #Logging filter
|
|
79
|
-
print(raw_packet.data)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Data handlers
|
|
83
|
-
|
|
84
|
-
### RawPacket
|
|
85
|
-
```python
|
|
86
|
-
from firegex.nfproxy import RawPacket
|
|
87
|
-
```
|
|
88
|
-
This handler will be called every time arrives a packet from the network. It will receive a RawPacket object with the following properties:
|
|
89
|
-
- is_input: bool - It's true if the packet is an input packet, false if it's an output packet
|
|
90
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
91
|
-
- is_tcp: bool - It's true if the packet is a tcp packet, false if it's an udp packet
|
|
92
|
-
- data: bytes - The data of the packet assembled and sorted from TCP
|
|
93
|
-
- l4_size: int - The size of the layer 4 data
|
|
94
|
-
- raw_packet_header_len: int - The size of the original packet header
|
|
95
|
-
- l4_data: bytes - The layer 4 payload of the packet
|
|
96
|
-
- raw_packet: bytes - The raw packet with IP and TCP headers
|
|
97
|
-
|
|
98
|
-
### TCPInputStream
|
|
99
|
-
Alias: TCPClientStream
|
|
100
|
-
```python
|
|
101
|
-
from firegex.nfproxy import TCPInputStream
|
|
102
|
-
```
|
|
103
|
-
This handler will be called every time a TCP stream is assembled in input. It will receive a TCPInputStream object with the following properties:
|
|
104
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
105
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
106
|
-
- total_stream_size: int - The size of the stream
|
|
107
|
-
|
|
108
|
-
### TCPOutputStream
|
|
109
|
-
Alias: TCPServerStream
|
|
110
|
-
```python
|
|
111
|
-
from firegex.nfproxy import TCPOutputStream
|
|
112
|
-
```
|
|
113
|
-
This handler will be called every time a TCP stream is assembled in output. It will receive a TCPOutputStream object with the following properties:
|
|
114
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
115
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
116
|
-
- total_stream_size: int - The size of the stream
|
|
117
|
-
|
|
118
|
-
### HttpRequest
|
|
119
|
-
```python
|
|
120
|
-
from firegex.nfproxy import HttpRequest
|
|
121
|
-
```
|
|
122
|
-
This handler will be called twice: one for the request headers and one for the request body. It will receive a HttpRequest object with the following properties:
|
|
123
|
-
- method: bytes - The method of the request
|
|
124
|
-
- url: str - The url of the request
|
|
125
|
-
- headers: dict - The headers of the request
|
|
126
|
-
- user_agent: str - The user agent of the request
|
|
127
|
-
- content_encoding: str - The content encoding of the request
|
|
128
|
-
- body: bytes - The body of the request
|
|
129
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
130
|
-
- message_complete: bool - It's true if the message is complete
|
|
131
|
-
- http_version: str - The http version of the request
|
|
132
|
-
- keep_alive: bool - It's true if the request should keep alive
|
|
133
|
-
- should_upgrade: bool - It's true if the request should upgrade
|
|
134
|
-
- content_length: int - The content length of the request
|
|
135
|
-
- get_header(header: str, default=None): str - Get a header from the request without caring about the case
|
|
136
|
-
- total_size: int - The total size of the stream
|
|
137
|
-
- stream: bytes - The stream of the request
|
|
138
|
-
|
|
139
|
-
### HttpRequestHeader
|
|
140
|
-
```python
|
|
141
|
-
from firegex.nfproxy import HttpRequestHeader
|
|
142
|
-
```
|
|
143
|
-
This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
|
|
144
|
-
|
|
145
|
-
### HttpFullRequest
|
|
146
|
-
```python
|
|
147
|
-
from firegex.nfproxy import HttpFullRequest
|
|
148
|
-
```
|
|
149
|
-
This handler will be called only when the request is complete. It will receive a HttpFullRequest object with the same properties as HttpRequest.
|
|
150
|
-
|
|
151
|
-
### HttpResponse
|
|
152
|
-
```python
|
|
153
|
-
from firegex.nfproxy import HttpResponse
|
|
154
|
-
```
|
|
155
|
-
This handler will be called twice: one for the response headers and one for the response body. It will receive a HttpResponse object with the following properties:
|
|
156
|
-
- status_code: int - The status code of the response
|
|
157
|
-
- url: str - The url of the response
|
|
158
|
-
- headers: dict - The headers of the response
|
|
159
|
-
- user_agent: str - The user agent of the response
|
|
160
|
-
- content_encoding: str - The content encoding of the response
|
|
161
|
-
- body: bytes - The body of the response
|
|
162
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
163
|
-
- message_complete: bool - It's true if the message is complete
|
|
164
|
-
- http_version: str - The http version of the response
|
|
165
|
-
- keep_alive: bool - It's true if the response should keep alive
|
|
166
|
-
- should_upgrade: bool - It's true if the response should upgrade
|
|
167
|
-
- content_length: int - The content length of the response
|
|
168
|
-
- get_header(header: str, default=None): str - Get a header from the response without caring about the case
|
|
169
|
-
- total_size: int - The total size of the stream
|
|
170
|
-
- stream: bytes - The stream of the response
|
|
171
|
-
|
|
172
|
-
### HttpResponseHeader
|
|
173
|
-
```python
|
|
174
|
-
from firegex.nfproxy import HttpResponseHeader
|
|
175
|
-
```
|
|
176
|
-
This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
|
|
177
|
-
|
|
178
|
-
### HttpFullResponse
|
|
179
|
-
```python
|
|
180
|
-
from firegex.nfproxy import HttpFullResponse
|
|
181
|
-
```
|
|
182
|
-
This handler will be called only when the response is complete. It will receive a HttpFullResponse object with the same properties as HttpResponse.
|
firegex-3.5.2/README.md
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# Firegex Python Library and CLI
|
|
2
|
-
|
|
3
|
-
This is the Python library for Firegex. It is used to get additional features of Firegex and use the feature of the command `fgex`.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install -U firegex
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
fgex is an alias of firegex. You can use `fgex` instead of `firegex`.
|
|
12
|
-
|
|
13
|
-
## Command line usage:
|
|
14
|
-
|
|
15
|
-
```text
|
|
16
|
-
➤ fgex nfproxy -h
|
|
17
|
-
|
|
18
|
-
Usage: fgex nfproxy [OPTIONS] FILTER_FILE ADDRESS PORT
|
|
19
|
-
|
|
20
|
-
Run an nfproxy simulation
|
|
21
|
-
|
|
22
|
-
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
23
|
-
│ * filter_file TEXT The path to the filter file [default: None] [required] │
|
|
24
|
-
│ * address TEXT The address of the target to proxy [default: None] [required] │
|
|
25
|
-
│ * port INTEGER The port of the target to proxy [default: None] [required] │
|
|
26
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
27
|
-
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
28
|
-
│ --proto [tcp|http] The protocol to proxy [default: tcp] │
|
|
29
|
-
│ --from-address TEXT The address of the local server [default: None] │
|
|
30
|
-
│ --from-port INTEGER The port of the local server [default: 7474] │
|
|
31
|
-
│ -6 Use IPv6 for the connection │
|
|
32
|
-
│ --help -h Show this message and exit. │
|
|
33
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Library usage:
|
|
37
|
-
|
|
38
|
-
## NfProxy decorator
|
|
39
|
-
|
|
40
|
-
```python
|
|
41
|
-
from firegex.nfproxy import pyfilter
|
|
42
|
-
```
|
|
43
|
-
This decorator is used to create a filter for the nfproxy.
|
|
44
|
-
Example:
|
|
45
|
-
```python
|
|
46
|
-
@pyfilter
|
|
47
|
-
def my_filter(raw_packet: RawPacket): #Logging filter
|
|
48
|
-
print(raw_packet.data)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Data handlers
|
|
52
|
-
|
|
53
|
-
### RawPacket
|
|
54
|
-
```python
|
|
55
|
-
from firegex.nfproxy import RawPacket
|
|
56
|
-
```
|
|
57
|
-
This handler will be called every time arrives a packet from the network. It will receive a RawPacket object with the following properties:
|
|
58
|
-
- is_input: bool - It's true if the packet is an input packet, false if it's an output packet
|
|
59
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
60
|
-
- is_tcp: bool - It's true if the packet is a tcp packet, false if it's an udp packet
|
|
61
|
-
- data: bytes - The data of the packet assembled and sorted from TCP
|
|
62
|
-
- l4_size: int - The size of the layer 4 data
|
|
63
|
-
- raw_packet_header_len: int - The size of the original packet header
|
|
64
|
-
- l4_data: bytes - The layer 4 payload of the packet
|
|
65
|
-
- raw_packet: bytes - The raw packet with IP and TCP headers
|
|
66
|
-
|
|
67
|
-
### TCPInputStream
|
|
68
|
-
Alias: TCPClientStream
|
|
69
|
-
```python
|
|
70
|
-
from firegex.nfproxy import TCPInputStream
|
|
71
|
-
```
|
|
72
|
-
This handler will be called every time a TCP stream is assembled in input. It will receive a TCPInputStream object with the following properties:
|
|
73
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
74
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
75
|
-
- total_stream_size: int - The size of the stream
|
|
76
|
-
|
|
77
|
-
### TCPOutputStream
|
|
78
|
-
Alias: TCPServerStream
|
|
79
|
-
```python
|
|
80
|
-
from firegex.nfproxy import TCPOutputStream
|
|
81
|
-
```
|
|
82
|
-
This handler will be called every time a TCP stream is assembled in output. It will receive a TCPOutputStream object with the following properties:
|
|
83
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
84
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
85
|
-
- total_stream_size: int - The size of the stream
|
|
86
|
-
|
|
87
|
-
### HttpRequest
|
|
88
|
-
```python
|
|
89
|
-
from firegex.nfproxy import HttpRequest
|
|
90
|
-
```
|
|
91
|
-
This handler will be called twice: one for the request headers and one for the request body. It will receive a HttpRequest object with the following properties:
|
|
92
|
-
- method: bytes - The method of the request
|
|
93
|
-
- url: str - The url of the request
|
|
94
|
-
- headers: dict - The headers of the request
|
|
95
|
-
- user_agent: str - The user agent of the request
|
|
96
|
-
- content_encoding: str - The content encoding of the request
|
|
97
|
-
- body: bytes - The body of the request
|
|
98
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
99
|
-
- message_complete: bool - It's true if the message is complete
|
|
100
|
-
- http_version: str - The http version of the request
|
|
101
|
-
- keep_alive: bool - It's true if the request should keep alive
|
|
102
|
-
- should_upgrade: bool - It's true if the request should upgrade
|
|
103
|
-
- content_length: int - The content length of the request
|
|
104
|
-
- get_header(header: str, default=None): str - Get a header from the request without caring about the case
|
|
105
|
-
- total_size: int - The total size of the stream
|
|
106
|
-
- stream: bytes - The stream of the request
|
|
107
|
-
|
|
108
|
-
### HttpRequestHeader
|
|
109
|
-
```python
|
|
110
|
-
from firegex.nfproxy import HttpRequestHeader
|
|
111
|
-
```
|
|
112
|
-
This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
|
|
113
|
-
|
|
114
|
-
### HttpFullRequest
|
|
115
|
-
```python
|
|
116
|
-
from firegex.nfproxy import HttpFullRequest
|
|
117
|
-
```
|
|
118
|
-
This handler will be called only when the request is complete. It will receive a HttpFullRequest object with the same properties as HttpRequest.
|
|
119
|
-
|
|
120
|
-
### HttpResponse
|
|
121
|
-
```python
|
|
122
|
-
from firegex.nfproxy import HttpResponse
|
|
123
|
-
```
|
|
124
|
-
This handler will be called twice: one for the response headers and one for the response body. It will receive a HttpResponse object with the following properties:
|
|
125
|
-
- status_code: int - The status code of the response
|
|
126
|
-
- url: str - The url of the response
|
|
127
|
-
- headers: dict - The headers of the response
|
|
128
|
-
- user_agent: str - The user agent of the response
|
|
129
|
-
- content_encoding: str - The content encoding of the response
|
|
130
|
-
- body: bytes - The body of the response
|
|
131
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
132
|
-
- message_complete: bool - It's true if the message is complete
|
|
133
|
-
- http_version: str - The http version of the response
|
|
134
|
-
- keep_alive: bool - It's true if the response should keep alive
|
|
135
|
-
- should_upgrade: bool - It's true if the response should upgrade
|
|
136
|
-
- content_length: int - The content length of the response
|
|
137
|
-
- get_header(header: str, default=None): str - Get a header from the response without caring about the case
|
|
138
|
-
- total_size: int - The total size of the stream
|
|
139
|
-
- stream: bytes - The stream of the response
|
|
140
|
-
|
|
141
|
-
### HttpResponseHeader
|
|
142
|
-
```python
|
|
143
|
-
from firegex.nfproxy import HttpResponseHeader
|
|
144
|
-
```
|
|
145
|
-
This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
|
|
146
|
-
|
|
147
|
-
### HttpFullResponse
|
|
148
|
-
```python
|
|
149
|
-
from firegex.nfproxy import HttpFullResponse
|
|
150
|
-
```
|
|
151
|
-
This handler will be called only when the response is complete. It will receive a HttpFullResponse object with the same properties as HttpResponse.
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: firegex
|
|
3
|
-
Version: 3.5.2
|
|
4
|
-
Summary: Firegex client
|
|
5
|
-
Home-page: https://github.com/pwnzer0tt1/firegex
|
|
6
|
-
Author: Pwnzer0tt1
|
|
7
|
-
Author-email: pwnzer0tt1@poliba.it
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.10
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
Requires-Dist: typer
|
|
14
|
-
Requires-Dist: pydantic>=2
|
|
15
|
-
Requires-Dist: typing-extensions>=4.7.1
|
|
16
|
-
Requires-Dist: pycryptodome
|
|
17
|
-
Requires-Dist: watchfiles
|
|
18
|
-
Requires-Dist: fgex
|
|
19
|
-
Requires-Dist: websockets
|
|
20
|
-
Requires-Dist: brotli
|
|
21
|
-
Requires-Dist: pyllhttp
|
|
22
|
-
Dynamic: author
|
|
23
|
-
Dynamic: author-email
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: requires-dist
|
|
29
|
-
Dynamic: requires-python
|
|
30
|
-
Dynamic: summary
|
|
31
|
-
|
|
32
|
-
# Firegex Python Library and CLI
|
|
33
|
-
|
|
34
|
-
This is the Python library for Firegex. It is used to get additional features of Firegex and use the feature of the command `fgex`.
|
|
35
|
-
|
|
36
|
-
## Installation
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
pip install -U firegex
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
fgex is an alias of firegex. You can use `fgex` instead of `firegex`.
|
|
43
|
-
|
|
44
|
-
## Command line usage:
|
|
45
|
-
|
|
46
|
-
```text
|
|
47
|
-
➤ fgex nfproxy -h
|
|
48
|
-
|
|
49
|
-
Usage: fgex nfproxy [OPTIONS] FILTER_FILE ADDRESS PORT
|
|
50
|
-
|
|
51
|
-
Run an nfproxy simulation
|
|
52
|
-
|
|
53
|
-
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
54
|
-
│ * filter_file TEXT The path to the filter file [default: None] [required] │
|
|
55
|
-
│ * address TEXT The address of the target to proxy [default: None] [required] │
|
|
56
|
-
│ * port INTEGER The port of the target to proxy [default: None] [required] │
|
|
57
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
58
|
-
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
59
|
-
│ --proto [tcp|http] The protocol to proxy [default: tcp] │
|
|
60
|
-
│ --from-address TEXT The address of the local server [default: None] │
|
|
61
|
-
│ --from-port INTEGER The port of the local server [default: 7474] │
|
|
62
|
-
│ -6 Use IPv6 for the connection │
|
|
63
|
-
│ --help -h Show this message and exit. │
|
|
64
|
-
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Library usage:
|
|
68
|
-
|
|
69
|
-
## NfProxy decorator
|
|
70
|
-
|
|
71
|
-
```python
|
|
72
|
-
from firegex.nfproxy import pyfilter
|
|
73
|
-
```
|
|
74
|
-
This decorator is used to create a filter for the nfproxy.
|
|
75
|
-
Example:
|
|
76
|
-
```python
|
|
77
|
-
@pyfilter
|
|
78
|
-
def my_filter(raw_packet: RawPacket): #Logging filter
|
|
79
|
-
print(raw_packet.data)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Data handlers
|
|
83
|
-
|
|
84
|
-
### RawPacket
|
|
85
|
-
```python
|
|
86
|
-
from firegex.nfproxy import RawPacket
|
|
87
|
-
```
|
|
88
|
-
This handler will be called every time arrives a packet from the network. It will receive a RawPacket object with the following properties:
|
|
89
|
-
- is_input: bool - It's true if the packet is an input packet, false if it's an output packet
|
|
90
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
91
|
-
- is_tcp: bool - It's true if the packet is a tcp packet, false if it's an udp packet
|
|
92
|
-
- data: bytes - The data of the packet assembled and sorted from TCP
|
|
93
|
-
- l4_size: int - The size of the layer 4 data
|
|
94
|
-
- raw_packet_header_len: int - The size of the original packet header
|
|
95
|
-
- l4_data: bytes - The layer 4 payload of the packet
|
|
96
|
-
- raw_packet: bytes - The raw packet with IP and TCP headers
|
|
97
|
-
|
|
98
|
-
### TCPInputStream
|
|
99
|
-
Alias: TCPClientStream
|
|
100
|
-
```python
|
|
101
|
-
from firegex.nfproxy import TCPInputStream
|
|
102
|
-
```
|
|
103
|
-
This handler will be called every time a TCP stream is assembled in input. It will receive a TCPInputStream object with the following properties:
|
|
104
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
105
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
106
|
-
- total_stream_size: int - The size of the stream
|
|
107
|
-
|
|
108
|
-
### TCPOutputStream
|
|
109
|
-
Alias: TCPServerStream
|
|
110
|
-
```python
|
|
111
|
-
from firegex.nfproxy import TCPOutputStream
|
|
112
|
-
```
|
|
113
|
-
This handler will be called every time a TCP stream is assembled in output. It will receive a TCPOutputStream object with the following properties:
|
|
114
|
-
- data: bytes - The data of the packets assembled and sorted from TCP
|
|
115
|
-
- is_ipv6: bool - It's true if the packet is an ipv6 packet, false if it's an ipv4 packet
|
|
116
|
-
- total_stream_size: int - The size of the stream
|
|
117
|
-
|
|
118
|
-
### HttpRequest
|
|
119
|
-
```python
|
|
120
|
-
from firegex.nfproxy import HttpRequest
|
|
121
|
-
```
|
|
122
|
-
This handler will be called twice: one for the request headers and one for the request body. It will receive a HttpRequest object with the following properties:
|
|
123
|
-
- method: bytes - The method of the request
|
|
124
|
-
- url: str - The url of the request
|
|
125
|
-
- headers: dict - The headers of the request
|
|
126
|
-
- user_agent: str - The user agent of the request
|
|
127
|
-
- content_encoding: str - The content encoding of the request
|
|
128
|
-
- body: bytes - The body of the request
|
|
129
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
130
|
-
- message_complete: bool - It's true if the message is complete
|
|
131
|
-
- http_version: str - The http version of the request
|
|
132
|
-
- keep_alive: bool - It's true if the request should keep alive
|
|
133
|
-
- should_upgrade: bool - It's true if the request should upgrade
|
|
134
|
-
- content_length: int - The content length of the request
|
|
135
|
-
- get_header(header: str, default=None): str - Get a header from the request without caring about the case
|
|
136
|
-
- total_size: int - The total size of the stream
|
|
137
|
-
- stream: bytes - The stream of the request
|
|
138
|
-
|
|
139
|
-
### HttpRequestHeader
|
|
140
|
-
```python
|
|
141
|
-
from firegex.nfproxy import HttpRequestHeader
|
|
142
|
-
```
|
|
143
|
-
This handler will be called only when the request headers are complete. It will receive a HttpRequestHeader object with the same properties as HttpRequest.
|
|
144
|
-
|
|
145
|
-
### HttpFullRequest
|
|
146
|
-
```python
|
|
147
|
-
from firegex.nfproxy import HttpFullRequest
|
|
148
|
-
```
|
|
149
|
-
This handler will be called only when the request is complete. It will receive a HttpFullRequest object with the same properties as HttpRequest.
|
|
150
|
-
|
|
151
|
-
### HttpResponse
|
|
152
|
-
```python
|
|
153
|
-
from firegex.nfproxy import HttpResponse
|
|
154
|
-
```
|
|
155
|
-
This handler will be called twice: one for the response headers and one for the response body. It will receive a HttpResponse object with the following properties:
|
|
156
|
-
- status_code: int - The status code of the response
|
|
157
|
-
- url: str - The url of the response
|
|
158
|
-
- headers: dict - The headers of the response
|
|
159
|
-
- user_agent: str - The user agent of the response
|
|
160
|
-
- content_encoding: str - The content encoding of the response
|
|
161
|
-
- body: bytes - The body of the response
|
|
162
|
-
- headers_complete: bool - It's true if the headers are complete
|
|
163
|
-
- message_complete: bool - It's true if the message is complete
|
|
164
|
-
- http_version: str - The http version of the response
|
|
165
|
-
- keep_alive: bool - It's true if the response should keep alive
|
|
166
|
-
- should_upgrade: bool - It's true if the response should upgrade
|
|
167
|
-
- content_length: int - The content length of the response
|
|
168
|
-
- get_header(header: str, default=None): str - Get a header from the response without caring about the case
|
|
169
|
-
- total_size: int - The total size of the stream
|
|
170
|
-
- stream: bytes - The stream of the response
|
|
171
|
-
|
|
172
|
-
### HttpResponseHeader
|
|
173
|
-
```python
|
|
174
|
-
from firegex.nfproxy import HttpResponseHeader
|
|
175
|
-
```
|
|
176
|
-
This handler will be called only when the response headers are complete. It will receive a HttpResponseHeader object with the same properties as HttpResponse.
|
|
177
|
-
|
|
178
|
-
### HttpFullResponse
|
|
179
|
-
```python
|
|
180
|
-
from firegex.nfproxy import HttpFullResponse
|
|
181
|
-
```
|
|
182
|
-
This handler will be called only when the response is complete. It will receive a HttpFullResponse object with the same properties as HttpResponse.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|