sysctl managmento for port hijacking

This commit is contained in:
Domingo Dirutigliano
2023-04-12 23:53:43 +02:00
parent 37c51d6c4a
commit 63a3014676
63 changed files with 105 additions and 50 deletions
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
+10 -1
View File
@@ -7,12 +7,18 @@ from jose import jwt
from passlib.context import CryptContext from passlib.context import CryptContext
from fastapi_socketio import SocketManager from fastapi_socketio import SocketManager
from utils.sqlite import SQLite from utils.sqlite import SQLite
from utils import API_VERSION, FIREGEX_PORT, JWT_ALGORITHM, get_interfaces, refresh_frontend, DEBUG from utils import API_VERSION, FIREGEX_PORT, JWT_ALGORITHM, get_interfaces, refresh_frontend, DEBUG, SysctlManager
from utils.loader import frontend_deploy, load_routers from utils.loader import frontend_deploy, load_routers
from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel
# DB init # DB init
db = SQLite('db/firegex.db') db = SQLite('db/firegex.db')
sysctl = SysctlManager({
"net.ipv4.conf.all.forwarding": True,
"net.ipv6.conf.all.forwarding": True,
"net.ipv4.conf.all.route_localnet": True,
"net.ipv4.ip_forward": True
})
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False) oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login", auto_error=False)
crypto = CryptContext(schemes=["bcrypt"], deprecated="auto") crypto = CryptContext(schemes=["bcrypt"], deprecated="auto")
@@ -114,6 +120,7 @@ async def startup_event():
db.init() db.init()
if os.getenv("HEX_SET_PSW"): if os.getenv("HEX_SET_PSW"):
set_psw(bytes.fromhex(os.getenv("HEX_SET_PSW")).decode()) set_psw(bytes.fromhex(os.getenv("HEX_SET_PSW")).decode())
sysctl.set()
await startup() await startup()
if not JWT_SECRET(): db.put("secret", secrets.token_hex(32)) if not JWT_SECRET(): db.put("secret", secrets.token_hex(32))
await refresh_frontend() await refresh_frontend()
@@ -121,6 +128,7 @@ async def startup_event():
@app.on_event("shutdown") @app.on_event("shutdown")
async def shutdown_event(): async def shutdown_event():
await shutdown() await shutdown()
sysctl.reset()
db.disconnect() db.disconnect()
@api.post('/reset', response_model=StatusMessageModel) @api.post('/reset', response_model=StatusMessageModel)
@@ -130,6 +138,7 @@ async def reset_firegex(form: ResetRequest):
db.delete() db.delete()
db.init() db.init()
db.put("secret", secrets.token_hex(32)) db.put("secret", secrets.token_hex(32))
sysctl.set()
await reset(form) await reset(form)
await refresh_frontend() await refresh_frontend()
return {'status': 'ok'} return {'status': 'ok'}
View File
Executable → Regular
View File
Executable → Regular
+28
View File
@@ -29,6 +29,34 @@ def refactor_name(name:str):
while " " in name: name = name.replace(" "," ") while " " in name: name = name.replace(" "," ")
return name return name
class SysctlManager:
def __init__(self, ctl_table):
self.old_table = {}
self.new_table = {}
if os.path.isdir("/sys_host/"):
self.old_table = dict()
self.new_table = dict(ctl_table)
for name in ctl_table.keys():
self.old_table[name] = read_sysctl(name)
def write_table(self, table):
for name, value in table.items():
write_sysctl(name, value)
def set(self):
self.write_table(self.new_table)
def reset(self):
self.write_table(self.old_table)
def read_sysctl(name:str):
with open(f"/sys_host/{name}", "rt") as f:
return "1" in f.read()
def write_sysctl(name:str, value:bool):
with open(f"/sys_host/{name}", "wt") as f:
f.write("1" if value else "0")
def list_files(mypath): def list_files(mypath):
from os import listdir from os import listdir
from os.path import isfile, join from os.path import isfile, join
Executable → Regular
View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Executable → Regular
View File
Vendored Executable → Regular
View File
Generated Executable → Regular
View File
Executable → Regular
View File
View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 710 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Executable → Regular
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Executable → Regular
View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
View File
View File
View File
View File
View File
Vendored Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
+66 -48
View File
@@ -4,6 +4,7 @@ import argparse, sys, platform, os, multiprocessing, subprocess, getpass
pref = "\033[" pref = "\033["
reset = f"{pref}0m" reset = f"{pref}0m"
composefile = "firegex-compose.yml"
class colors: class colors:
black = "30m" black = "30m"
@@ -72,6 +73,65 @@ args = parser.parse_args()
os.chdir(os.path.dirname(os.path.realpath(__file__))) os.chdir(os.path.dirname(os.path.realpath(__file__)))
run_checks() run_checks()
def write_compose(psw_set=None):
with open(composefile,"wt") as compose:
if "linux" in sys.platform and not 'microsoft-standard' in platform.uname().release: #Check if not is a wsl also
compose.write(f"""
version: '3.9'
services:
firewall:
restart: unless-stopped
container_name: firegex
{"build: ." if args.build else "image: ghcr.io/pwnzer0tt1/firegex"}
network_mode: "host"
environment:
- PORT={args.port}
- NTHREADS={args.threads}
{"- HEX_SET_PSW="+psw_set.encode().hex() if psw_set else ""}
volumes:
- /execute/db
- type: bind
source: /proc/sys/net/ipv4/conf/all/route_localnet
target: /sys_host/net.ipv4.conf.all.route_localnet
- type: bind
source: /proc/sys/net/ipv4/ip_forward
target: /sys_host/net.ipv4.ip_forward
- type: bind
source: /proc/sys/net/ipv4/conf/all/forwarding
target: /sys_host/net.ipv4.conf.all.forwarding
- type: bind
source: /proc/sys/net/ipv6/conf/all/forwarding
target: /sys_host/net.ipv6.conf.all.forwarding
cap_add:
- NET_ADMIN
""")
else:
sep()
puts("--- WARNING ---", color=colors.yellow)
puts("You are not in a linux machine, due to docker limitation on other platform, the firewall will not work in this machine. You will only see the interface of firegex.", color=colors.red)
compose.write(f"""
version: '3.9'
services:
firewall:
restart: unless-stopped
container_name: firegex
{"build: ." if args.build else "image: ghcr.io/pwnzer0tt1/firegex"}
ports:
- {args.port}:{args.port}
environment:
- PORT={args.port}
- NTHREADS={args.threads}
{"- HEX_SET_PSW="+psw_set.encode().hex() if psw_set else ""}
volumes:
- /execute/db
cap_add:
- NET_ADMIN
""")
start_operation = not (args.stop or args.restart) start_operation = not (args.stop or args.restart)
if args.build and not os.path.isfile("./Dockerfile"): if args.build and not os.path.isfile("./Dockerfile"):
@@ -81,9 +141,12 @@ if args.build and not os.path.isfile("./Dockerfile"):
if args.threads < 1: if args.threads < 1:
args.threads = multiprocessing.cpu_count() args.threads = multiprocessing.cpu_count()
if start_operation: if start_operation and (not args.build or args.keep):
if check_if_exists("docker ps --filter 'name=^firegex$' --no-trunc | grep firegex"): if check_if_exists("docker ps --filter 'name=^firegex$' --no-trunc | grep firegex"):
puts("Firegex is already running! use --help to see options useful to manage firegex execution", color=colors.yellow) if args.keep:
write_compose()
else:
puts("Firegex is already running! use --help to see options useful to manage firegex execution", color=colors.yellow)
exit() exit()
sep() sep()
puts(f"Firegex", color=colors.yellow, end="") puts(f"Firegex", color=colors.yellow, end="")
@@ -105,53 +168,8 @@ if start_operation:
else: else:
break break
composefile = "firegex-compose.yml" write_compose(psw_set)
with open(composefile,"wt") as compose:
if "linux" in sys.platform and not 'microsoft-standard' in platform.uname().release: #Check if not is a wsl also
compose.write(f"""
version: '3.9'
services:
firewall:
restart: unless-stopped
container_name: firegex
{"build: ." if args.build else "image: ghcr.io/pwnzer0tt1/firegex"}
network_mode: "host"
environment:
- PORT={args.port}
- NTHREADS={args.threads}
{"- HEX_SET_PSW="+psw_set.encode().hex() if psw_set else ""}
volumes:
- /execute/db
cap_add:
- NET_ADMIN
""")
else:
sep()
puts("--- WARNING ---", color=colors.yellow)
puts("You are not in a linux machine, due to docker limitation on other platform, the firewall will not work in this machine. You will only see the interface of firegex.", color=colors.red)
compose.write(f"""
version: '3.9'
services:
firewall:
restart: unless-stopped
container_name: firegex
{"build: ." if args.build else "image: ghcr.io/pwnzer0tt1/firegex"}
ports:
- {args.port}:{args.port}
environment:
- PORT={args.port}
- NTHREADS={args.threads}
{"- HEX_SET_PSW="+psw_set.encode().hex() if psw_set else ""}
volumes:
- /execute/db
cap_add:
- NET_ADMIN
""")
sep() sep()
if not args.no_autostart: if not args.no_autostart:
try: try:
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File