HydroFlow Console is a didactic simulator of a hydroelectric control panel. It mimics the physical behavior of a dam with turbines, gates, and environmental conditions. The application includes a deliberately insecure implementation of session handling using pickle and is meant for educational use only.
pip install -r requirements.txt
python app.pyAccess the login page at /login. Only user l.perez can log in without a password. After login, go to /dashboard to see the control panel. Use /logout to sign out. The homepage / displays a welcome page with team info.
The login form collects a username and password. A valid login generates a session cookie with a dictionary containing the username and role (viewer, engineer, or admin). The cookie is serialized with pickle and base64-encoded. On /dashboard, this cookie is deserialized to control user access.
Since the session cookie is not signed or encrypted, it can be manipulated. By decoding it from base64, editing the role, and re-encoding it, you can escalate privileges. For example, changing role: viewer to admin gives full access.
⚠️ You might try decoding thesessioncookie with a tool like Python or CyberChef, and you’ll likely see something readable like:{'user': 'john.doe', 'role': 'viewer'}But if you edit that, change
'viewer'to'admin', re-encode it in base64 and send it back... the server still won't accept it. Why?Because
pickleuses a binary serialization format, not plain text. Simply editing the readable string can break the structure internally.Any mismatch in length, type, or structure will make
pickle.loads()throw an exception.This demonstrates that although the cookie isn't signed or encrypted, it's not trivial to tamper with unless you recreate the binary payload properly using Python.
The deserialization uses:
pickle.loads(base64.b64decode(request.cookies.get("session")))This is insecure by design and allows injection of malicious objects if not properly validated.
At launch, all gates are closed, and turbines are off. A background thread updates the simulation every 3 seconds.
NUM_GATES= 5MAX_LEVEL= 250 mDAM_AREA= 1000 m²WATER_DENSITY= 1000 kg/m³GRAVITY= 9.81 m/s²RPM_MIN= 2000,RPM_WARN= 4500,RPM_MAX= 5000PRESSURE_MAX= 100 barPOWER_MAX= 200 MW
Random weather affects inflow:
- Sunny → 0.2 m³/s
- Rain → 1.0 m³/s
- Heavy rain → 2.7 m³/s + 60s timer
Outflow depends on open gates:
flow = open_gates * 1.0
water_level += inflow - flowPressure is calculated:
pressure = water_level * 1.12Turbines activate if more than two gates are open:
target_rpm = max(pressure * 8, RPM_MIN)Each 1000 rpm adds 1 °C to turbine temp. Power output:
P = ρ * g * Q * H / 1_000_000- Dam collapse: pressure > 100 bar or overflow for over 60s
- Overload: power > 200 MW
Triggers a 500 error with flag{electric_power}.
The dashboard uses Bootstrap and Chart.js to show:
- Water level
- Pressure
- Outflow
- RPM & temperature
- Generated power
Admins can open/close gates and update firmware. Weather includes humidity and wind. A mini-navbar displays the logged user and logout option.
The autopilot keeps pressure between 45–55 bar. It can be toggled by uploading a firmware7331.bin file to /firmware/update with these lines:
autopilot: on
warnings: on
Admins have a shortcut to the update page. The default firmware is included in firmware_uploads/.
In the Exploit/ folder:
exploit.py: uses Selenium to hijack a browser, modify the cookie, and upload firmware.exploitv2.py: usesurllibto forge a cookie, disable autopilot, and trigger a system fail.
Both demonstrate the security flaws via manipulated cookies and firmware.
This project is for educational use only. Never use pickle with user-controlled data in real applications.
Created by Jordi Serrano (@j0rd1s3rr4n0)
HydroFlow Console es un simulador didáctico del panel de control de una central hidroeléctrica. Recrea el comportamiento físico de una presa con turbinas, compuertas y condiciones meteorológicas. La aplicación incluye una vulnerabilidad intencionada basada en pickle, pensada para fines educativos.
pip install -r requirements.txt
python app.pyAccede a /login e introduce usuario y contraseña (solo l.perez puede entrar sin contraseña). Luego abre /dashboard. Usa /logout para cerrar sesión. La página principal (/) muestra un mensaje de bienvenida y el equipo.
El login genera una cookie session con un diccionario que contiene nombre de usuario y rol (viewer, engineer o admin). Esta cookie se serializa con pickle y se codifica con base64. Al entrar en /dashboard, se decodifica y se otorgan permisos según el rol.
Como la cookie no está firmada ni cifrada, se puede manipular. Basta con decodificarla desde base64, cambiar role a admin, volver a codificarla y recargar.
⚠️ Puedes intentar decodificar la cookiesessionusando Python o CyberChef, y verás algo como:{'user': 'john.doe', 'role': 'viewer'}Pero si modificas
'viewer'por'admin', vuelves a codificarla en base64 y la envías… el servidor no la acepta. ¿Por qué?Porque
pickleutiliza un formato binario, no texto plano. Al cambiar manualmente el contenido, puedes romper su estructura interna.Si la longitud o el tipo no coinciden,
pickle.loads()lanzará una excepción.Esto demuestra que, aunque la cookie no esté firmada ni cifrada, no es tan fácil manipularla a mano si no sabes recrear el binario con precisión.
El servidor la deserializa con:
pickle.loads(base64.b64decode(request.cookies.get("session")))Esto es inseguro por diseño y permite incluso inyección de objetos maliciosos.
Al arrancar, todas las compuertas están cerradas y las turbinas apagadas. Un hilo de fondo actualiza el estado cada 3 segundos.
NUM_GATES= 5MAX_LEVEL= 250 mDAM_AREA= 1000 m²WATER_DENSITY= 1000 kg/m³GRAVITY= 9.81 m/s²RPM_MIN= 2000,RPM_WARN= 4500,RPM_MAX= 5000PRESSURE_MAX= 100 barPOWER_MAX= 200 MW
Cada ciclo genera un clima aleatorio:
- Soleado → 0.2 m³/s
- Lluvia → 1.0 m³/s
- Lluvia fuerte → 2.7 m³/s + temporizador de 60s
El caudal depende del número de compuertas abiertas:
flow = open_gates * 1.0
water_level += inflow - flowLa presión se calcula así:
pressure = water_level * 1.12Si hay más de dos compuertas abiertas:
target_rpm = max(pressure * 8, RPM_MIN)Cada 1000 rpm añade 1 °C. La potencia generada:
P = ρ * g * Q * H / 1_000_000- Colapso de presa: presión > 100 bar o desbordamiento constante
- Sobrecarga eléctrica: potencia > 200 MW
Activa error 500 con flag{electric_power}.
El dashboard usa Bootstrap y Chart.js para mostrar:
- Nivel de agua
- Presión
- Caudal
- RPM y temperatura
- Potencia generada
Admins pueden abrir compuertas y subir firmware. El clima incluye humedad y viento. Hay una cabecera con usuario y logout.
El autopilot mantiene la presión entre 45 y 55 bar. Se puede desactivar subiendo un fichero firmware7331.bin a /firmware/update con:
autopilot: on
warnings: on
El firmware por defecto está en firmware_uploads/.
En la carpeta Exploit/:
exploit.py: usa Selenium para modificar la cookie y subir firmware.exploitv2.py: usaurllibpara escalar privilegios y desactivar el autopilot.
Demuestran cómo modificar la lógica de la app sin autenticación real.
Este proyecto es solo para fines educativos. No uses pickle con datos controlados por el usuario en apps reales.
Creado por Jordi Serrano (@j0rd1s3rr4n0)
