Skip to content

Instantly share code, notes, and snippets.

@diggit
Created August 30, 2025 23:22
Show Gist options
  • Select an option

  • Save diggit/761722a3048e42e45dd78735965f2469 to your computer and use it in GitHub Desktop.

Select an option

Save diggit/761722a3048e42e45dd78735965f2469 to your computer and use it in GitHub Desktop.
Vodafone CZ Cable modem firewall (re)disable tool
#!/usr/bin/env python
# publisneed under CC0 license
# vodafone_disable_firewall - Script to disable the firewall on Vodafone cable network modem
# Written in 2025 by diggit @ github
# To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
# You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
# WHAT?
# Simple script to disable firewall on the Vodafone cable network modem.
# WHY?
# Because this piece of cr*p re-activates firewall within 24h and
# describes this behavior as "safety" feature.
# HOW?
# After spending ~4 hours with Firefox debugger, wireshark, python cryptography and requests docs,
# I figured out how to authenticate and send requests to the modem.
# In theory, other features could be added, but those persist,
# so you can configure them via web UI.
# How to use?
# Update `password` and `host` variables below and execute the script.
# Do this periodically to keep firewall disabled.
# Curse you Vodafone for this piece of cr*p!
# possible systemd unit file
# [Unit]
# Description=Vodafone modem firewall reset
# After=network.target
# [Service]
# Type=simple
# ExecStart=/usr/bin/python /path/to/modem_disable_firewall.py
# possible systemd timer file
# [Unit]
# Description=Reset Vodafone modem FW every 6h
# [Timer]
# # every 6h
# OnCalendar=*-*-* 00/06:00:00
# [Install]
# WantedBy=timers.target
# systemctl enable --now your.timer
# tested with:
# - Firmware version: AR01.04.137.08_072324_7249.PC20.10
# - DsLite (IPv6 + CGNAT IPv4) mode
password = "FILL_IN"
host = "http://192.168.0.1"
import requests
import re
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
from random import randint
ar_nonce = f"{randint(10000, 99999)}"
# too lazy to exract those, should be constant anyway
PBKDF2_KEYSIZE_BITS = 128
PBKDF2_ITERATIONS = 1000
session = requests.Session()
# ajaky is CORS sensitive and thus requires these headers
headers = {
"csrfNonce" : "undefined", # updated after login
"X-Requested-With" : "XMLHttpRequest",
"Content-Type" : "application/json",
"Referer" : "http://192.168.0.1/",
"Origin" : "http://192.168.0.1"
}
session.headers.update(headers)
session.params = {"_n": ar_nonce}
def getVariables(session : requests.Session) -> dict[str, str]:
print("get variables", end = " ", flush = True)
index_response = session.get(host)
print(index_response.reason)
# print(session.cookies)
# yes this is quite crude approach to extarct data from html webpage...
js_vars : list[tuple[str, str]] = re.findall(r'var\s+([a-zA-Z0-9_]+)\s*=\s*([\'\"].*?[\'\"]|[^;\n]+);', index_response.text)
variables : dict[str, str]= {}
for var, val in js_vars:
if var not in ["currentSessionId", "myIv", "mySalt", "encryptflag", "loginUserName", ]:
continue
variables[var] = val.strip('\'"')
# this is how it is defined in javascript sources...
variables["plain_text"] = f'{{"Password": "PLACEHOLDER", "Nonce": "{variables["currentSessionId"]}"}}'
variables["authData"] = "loginPassword"
# print("extracted variables")
# for k, v in variables.items():
# print(f"{k} = {v}")
variables["plain_text"] = variables["plain_text"].replace("PLACEHOLDER", password)
return variables
def login(session: requests.Session, variables: dict[str, str]):
print("login", end = " ", flush = True)
key = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=PBKDF2_KEYSIZE_BITS//8,
salt=bytes.fromhex(variables["mySalt"]),
iterations=PBKDF2_ITERATIONS
).derive(password.encode())
# print(f"derived key = {key.hex()}")
aesccm = AESCCM(key)
ct = aesccm.encrypt(
bytes.fromhex(variables["myIv"]),
variables["plain_text"].encode(),
variables["authData"].encode())
# print(f"ct = {ct.hex()}")
login_url = f"{host}/php/ajaxSet_Password.php"
# print(f"login_url = {login_url}")
login_response = session.post(login_url, json={
"AuthData" : variables["authData"],
"EncryptData" : ct.hex(),
"Name" : variables["loginUserName"]
})
# print(session.cookies)
print(login_response.reason)
# print(login_response.content)
# NOTE: another good indicaton of successful login i new cookies value
login_response_json = login_response.json()
# print(login_response_json)
match login_response_json:
case {"p_status" : "Lockout"} :
print("Wrong password!")
exit(1)
case {"p_status" : "AdminMatch"} :
pass
case {"p_status" : unknown} :
print(f"WARNING: Unknown status: {unknown}, trying to continue anyway!")
# exit(1)
response_ct : str = login_response.json().get("encryptData")
# print(f"response_ct = {response_ct}")
csrf_nonce_raw = aesccm.decrypt(
bytes.fromhex(variables["myIv"]),
bytes.fromhex(response_ct),
"nonce".encode()
)
csrf_nonce = csrf_nonce_raw.decode()
# print(f"csrf_nonce_raw = {csrf_nonce}")
headers["csrfNonce"] = csrf_nonce
session.headers.update(headers)
def isFirewallEnabled(session : requests.Session) -> bool:
print("fw get", end = " ", flush = True)
firewall_get_url = f"{host}/php/net_firewall_data.php"
# print(f"firewall_get_url = {firewall_get_url}")
firewall_get_response = session.get(firewall_get_url)
print(firewall_get_response.reason)
# print(firewall_get_response.content)
firewall_get_response_json = firewall_get_response.json()
# print(firewall_get_response_json)
return firewall_get_response_json["Enable"] == "true"
def firewallConfigure(session : requests.Session, enabled : bool):
print(f"fw set ({enabled})", end = " ", flush = True)
firewall_set_url = f"{host}/php/ajaxSet_net_firewall_data.php"
# print(f"firewall_set_url = {firewall_set_url}")
firewall_set_response = session.post(firewall_set_url, json= {
"Enable": enabled
})
print(firewall_set_response.reason)
# print(firewall_set_response.content)
def isLoggedIn(session : requests.Session) -> bool:
print("session check", end = " ", flush = True)
# not sure if this is needed
session_url = f"{host}/php/ajaxSet_Session.php"
# print(f"session_url = {session_url}")
session_response = session.post(session_url)
print(session_response.reason)
# print(session_response.content)
session_response_json = session_response.json()
# print(session_response_json)
return session_response_json.get("LoginStatus", 'no') == 'yes'
def logout(session : requests.Session):
print("logout", end = " ", flush = True)
# not sure if this is needed
logout_url = f"{host}/php/logout.php"
# print(f"logout_url = {logout_url}")
logout_response = session.post(logout_url)
print(logout_response.reason)
# print(logout_response.content)
# print(logout_response.json())
variables = getVariables(session)
login(session, variables)
logged_in = isLoggedIn(session)
if not logged_in:
print("Not logged in, exiting!")
exit(1)
fw_enabled_before = isFirewallEnabled(session)
print(f"Firewall enabled before: {fw_enabled_before}")
firewallConfigure(session, False)
fw_enabled_after = isFirewallEnabled(session)
print(f"Firewall enabled after: {fw_enabled_after}")
logout(session)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment