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

0
.dockerignore Executable file → Normal file
View File

0
.gitignore vendored Executable file → Normal file
View File

0
Dockerfile Executable file → Normal file
View File

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'}

0
backend/modules/regexproxy/proxy.py Executable file → Normal file
View File

0
backend/requirements.txt Executable file → Normal file
View File

30
backend/utils/__init__.py Executable file → Normal file
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
@@ -105,4 +133,4 @@ class NFTableManager(Singleton):
def raw_list(self): def raw_list(self):
return self.cmd({"list": {"ruleset": None}})["nftables"] return self.cmd({"list": {"ruleset": None}})["nftables"]

0
docs/FiregexLogo.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

0
frontend/README.md Executable file → Normal file
View File

0
frontend/globals.d.ts vendored Executable file → Normal file
View File

0
frontend/package-lock.json generated Executable file → Normal file
View File

0
frontend/package.json Executable file → Normal file
View File

0
frontend/public/android-chrome-192x192.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

0
frontend/public/android-chrome-512x512.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

0
frontend/public/apple-touch-icon.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

0
frontend/public/favicon-16x16.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 710 B

0
frontend/public/favicon-32x32.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

0
frontend/public/favicon.ico Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
frontend/public/header-logo.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

0
frontend/public/index.html Executable file → Normal file
View File

0
frontend/public/robots.txt Executable file → Normal file
View File

0
frontend/public/site.webmanifest Executable file → Normal file
View File

0
frontend/src/App.tsx Executable file → Normal file
View File

0
frontend/src/_vars.scss Executable file → Normal file
View File

0
frontend/src/components/AddNewRegex.tsx Executable file → Normal file
View File

0
frontend/src/components/FilterTypeSelector.tsx Executable file → Normal file
View File

0
frontend/src/components/Footer/index.module.scss Executable file → Normal file
View File

0
frontend/src/components/Footer/index.tsx Executable file → Normal file
View File

0
frontend/src/components/Header/index.module.scss Executable file → Normal file
View File

0
frontend/src/components/Header/index.tsx Executable file → Normal file
View File

0
frontend/src/components/MainLayout.tsx Executable file → Normal file
View File

0
frontend/src/components/NFRegex/AddNewService.tsx Executable file → Normal file
View File

View File

0
frontend/src/components/NFRegex/ServiceRow/index.tsx Executable file → Normal file
View File

0
frontend/src/components/NFRegex/utils.ts Executable file → Normal file
View File

0
frontend/src/components/PortHijack/AddNewService.tsx Executable file → Normal file
View File

View File

View File

0
frontend/src/components/PortHijack/utils.ts Executable file → Normal file
View File

0
frontend/src/components/RegexProxy/AddNewService.tsx Executable file → Normal file
View File

View File

View File

View File

0
frontend/src/components/RegexView/index.module.scss Executable file → Normal file
View File

0
frontend/src/components/RegexView/index.tsx Executable file → Normal file
View File

0
frontend/src/components/YesNoModal.tsx Executable file → Normal file
View File

0
frontend/src/index.scss Executable file → Normal file
View File

0
frontend/src/index.tsx Executable file → Normal file
View File

0
frontend/src/js/utils.tsx Executable file → Normal file
View File

0
frontend/src/pages/NFRegex/ServiceDetails.tsx Executable file → Normal file
View File

0
frontend/src/pages/NFRegex/index.tsx Executable file → Normal file
View File

0
frontend/src/pages/PortHijack/index.tsx Executable file → Normal file
View File

0
frontend/src/pages/RegexProxy/ServiceDetails.tsx Executable file → Normal file
View File

0
frontend/src/pages/RegexProxy/index.tsx Executable file → Normal file
View File

0
frontend/src/react-app-env.d.ts vendored Executable file → Normal file
View File

0
frontend/tsconfig.json Executable file → Normal file
View File

114
start.py Executable file → Normal file
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:

0
tests/api_test.py Executable file → Normal file
View File

0
tests/benchmark.py Executable file → Normal file
View File

0
tests/nf_test.py Executable file → Normal file
View File

0
tests/ph_test.py Executable file → Normal file
View File

0
tests/px_test.py Executable file → Normal file
View File

0
tests/run_tests.sh Executable file → Normal file
View File