From f74d0d8e320091a2d5394e9938cf867f0436331c Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 2 Jun 2026 21:50:23 +0200 Subject: [PATCH] feat(cybersecurity): auto-commit con 10 cambios Co-Authored-By: Claude Opus 4.7 (1M context) --- .../browser/launch_chromium_proxy.md | 74 ++++++++++ .../browser/launch_chromium_proxy.sh | 104 ++++++++++++++ .../cybersecurity/query_mitm_flows.md | 88 ++++++++++++ .../cybersecurity/query_mitm_flows.sh | 101 ++++++++++++++ .../cybersecurity/start_mitm_capture.md | 80 +++++++++++ .../cybersecurity/start_mitm_capture.sh | 114 ++++++++++++++++ docs/capabilities/INDEX.md | 1 + docs/capabilities/web-proxy.md | 71 ++++++++++ .../cybersecurity/rotate_capture_flows.md | 65 +++++++++ .../cybersecurity/rotate_capture_flows.py | 127 ++++++++++++++++++ 10 files changed, 825 insertions(+) create mode 100644 bash/functions/browser/launch_chromium_proxy.md create mode 100644 bash/functions/browser/launch_chromium_proxy.sh create mode 100644 bash/functions/cybersecurity/query_mitm_flows.md create mode 100644 bash/functions/cybersecurity/query_mitm_flows.sh create mode 100644 bash/functions/cybersecurity/start_mitm_capture.md create mode 100644 bash/functions/cybersecurity/start_mitm_capture.sh create mode 100644 docs/capabilities/web-proxy.md create mode 100644 python/functions/cybersecurity/rotate_capture_flows.md create mode 100644 python/functions/cybersecurity/rotate_capture_flows.py diff --git a/bash/functions/browser/launch_chromium_proxy.md b/bash/functions/browser/launch_chromium_proxy.md new file mode 100644 index 00000000..2e5421cc --- /dev/null +++ b/bash/functions/browser/launch_chromium_proxy.md @@ -0,0 +1,74 @@ +--- +name: launch_chromium_proxy +kind: function +lang: bash +domain: browser +version: "1.0.0" +purity: impure +signature: "launch_chromium_proxy [--proxy URL] [--profile DIR] [--url URL] [--ca-cert PATH] [--extra \"ARGS\"]" +description: "Lanza Chromium (o Chrome) apuntando a un proxy HTTP/HTTPS local con un perfil completamente aislado del perfil real del usuario. Pensado para capturar trafico con un proxy de interceptacion (mitmproxy, Burp Suite) sin contaminar la sesion normal de navegacion. Emite un JSON con el PID del proceso lanzado." +tags: [chromium, chrome, proxy, mitmproxy, burp, browser, web-proxy, intercept, tls] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +params: + - name: "--proxy URL" + desc: "URL del proxy HTTP/HTTPS local, ej. http://127.0.0.1:8080. Se pasa a Chromium como --proxy-server=URL. Default: http://127.0.0.1:8080." + - name: "--profile DIR" + desc: "Directorio de perfil aislado para Chromium (--user-data-dir). Se crea automaticamente si no existe. Default: /tmp/chromium-proxy. Usar un path distinto por sesion si se quiere aislamiento total entre corridas." + - name: "--url URL" + desc: "URL inicial a abrir al arrancar el navegador. Opcional. Si se omite, Chromium abre su pagina de nueva pestana." + - name: "--ca-cert PATH" + desc: "Ruta a un CA cert PEM del proxy (ej. ~/.mitmproxy/mitmproxy-ca-cert.pem). Si se pasa, la funcion NO agrega --ignore-certificate-errors y asume que el usuario ya importo el CA en el perfil o en el sistema. Si se omite, se agrega --ignore-certificate-errors automaticamente para que el proxy MITM no rompa HTTPS (menos seguro)." + - name: "--extra \"ARGS\"" + desc: "Flags extra que se pasan directamente a chromium, ej. --extra \"--disable-gpu --window-size=1280,800\". El valor completo debe ir entre comillas." +output: "JSON en stdout: {\"pid\": , \"browser\": \"\", \"proxy\": \"\", \"profile\": \"\", \"log\": \"\"}. Mensajes de estado e informacion de CA en stderr. El navegador corre en background desacoplado de la sesion. Exit codes: 0=lanzado correctamente, 1=binario no encontrado o argumento invalido o error al crear el directorio de perfil." +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/browser/launch_chromium_proxy.sh" +--- + +## Ejemplo + +```bash +source bash/functions/browser/launch_chromium_proxy.sh + +# Caso mas comun: interceptar trafico con mitmproxy corriendo en 8080 +# (sin CA instalado: --ignore-certificate-errors se aplica automaticamente) +result=$(launch_chromium_proxy --proxy http://127.0.0.1:8080 --url https://httpbin.org/get) +echo "$result" +# {"pid":12345,"browser":"chromium","proxy":"http://127.0.0.1:8080","profile":"/tmp/chromium-proxy","log":"/tmp/chromium-proxy-12345.log"} + +# Con CA cert instalado (mitmproxy): sin --ignore-certificate-errors +launch_chromium_proxy \ + --proxy http://127.0.0.1:8080 \ + --ca-cert ~/.mitmproxy/mitmproxy-ca-cert.pem \ + --profile /tmp/mitm-session \ + --url https://api.ejemplo.com/v1/test + +# Con Burp Suite en puerto 8081, perfil aislado y ventana de tamano fijo +launch_chromium_proxy \ + --proxy http://127.0.0.1:8081 \ + --profile /tmp/burp-session \ + --extra "--window-size=1440,900" \ + --url https://app.objetivo.com +``` + +## Cuando usarla + +Cuando necesitas capturar y analizar trafico HTTPS de un navegador con mitmproxy, Burp Suite u otro proxy de interceptacion, sin tocar el perfil real del usuario ni sus cookies/credenciales guardadas. Ideal antes de hacer analisis de trafico de una app web o API, o al reproducir un flujo autenticado desde una sesion limpia. + +## Gotchas + +- **Deteccion de binario en orden**: la funcion prueba `chromium`, `chromium-browser`, `google-chrome-stable`, `google-chrome`. En sistemas donde solo existe `google-chrome`, ese sera el binario usado. Si ninguno esta en el PATH, retorna exit 1. Instalar con `sudo apt install chromium` o `chromium-browser`. +- **`--ignore-certificate-errors` sin `--ca-cert`**: este flag desactiva toda la validacion TLS del navegador. Es conveniente para empezar rapido, pero reduce la seguridad de la sesion. Para produccion o analisis de seguridad serio, instalar el CA del proxy en el sistema (`sudo cp mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt && sudo update-ca-certificates`) o en el perfil de Chromium (chrome://settings/certificates), y pasar `--ca-cert` para que la funcion omita el flag inseguro. +- **`--proxy-bypass-list="<-loopback>"`**: fuerza que el trafico loopback (127.0.0.1, localhost) TAMBIEN pase por el proxy. Sin esto, Chromium excluye loopback del proxy por defecto y no veras esas peticiones en mitmproxy. Si quieres el comportamiento estandar (excluir loopback), elimina este flag via `--extra`. +- **Perfil persistente entre sesiones**: el perfil en `/tmp/chromium-proxy` (o el directorio que elijas) persiste entre ejecuciones. Si quieres una sesion 100% limpia cada vez, pasa `--profile /tmp/chromium-proxy-$$` (usa el PID del shell como sufijo) o borra el directorio antes de llamar a la funcion. +- **`setsid` + `disown`**: el navegador se lanza desacoplado de la sesion del agente. Si la shell/sesion que llamo a la funcion termina, el proceso Chromium sigue vivo. Para matarlo, usar `kill ` con el PID del JSON de salida. +- **Log del navegador**: stdout y stderr de Chromium se redirigen a `/tmp/chromium-proxy-.log`. Si el navegador no arranca, revisar ese archivo para ver el error. +- **Chrome STABLE 138+**: al igual que `chrome_load_extensions`, algunos flags de automatizacion estan bloqueados en Chrome STABLE. Para interceptacion de trafico `--proxy-server` y `--user-data-dir` siguen funcionando en todas las versiones. Esta funcion es compatible con Chrome/Chromium en cualquier canal. +- **Multiples instancias**: si ya hay una instancia de Chromium corriendo con el mismo `--user-data-dir`, Chromium puede reusar esa instancia en lugar de abrir una nueva. Usar un directorio de perfil distinto por sesion concurrente. diff --git a/bash/functions/browser/launch_chromium_proxy.sh b/bash/functions/browser/launch_chromium_proxy.sh new file mode 100644 index 00000000..1f12e36e --- /dev/null +++ b/bash/functions/browser/launch_chromium_proxy.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# launch_chromium_proxy — Lanza Chromium apuntando a un proxy HTTP/HTTPS local con perfil aislado. + +launch_chromium_proxy() { + local proxy_url="http://127.0.0.1:8080" + local profile_dir="/tmp/chromium-proxy" + local start_url="" + local ca_cert="" + local extra_args="" + + # Parsear argumentos + while [[ $# -gt 0 ]]; do + case "$1" in + --proxy) + proxy_url="$2"; shift 2 ;; + --profile) + profile_dir="$2"; shift 2 ;; + --url) + start_url="$2"; shift 2 ;; + --ca-cert) + ca_cert="$2"; shift 2 ;; + --extra) + extra_args="$2"; shift 2 ;; + *) + echo "ERROR: argumento desconocido: $1" >&2 + return 1 ;; + esac + done + + # Detectar binario del navegador + local browser_bin="" + for candidate in chromium chromium-browser google-chrome-stable google-chrome; do + if command -v "$candidate" &>/dev/null; then + browser_bin="$candidate" + break + fi + done + + if [[ -z "$browser_bin" ]]; then + echo "ERROR: no se encontro ningun binario Chromium/Chrome en el PATH." >&2 + echo " Probados: chromium, chromium-browser, google-chrome-stable, google-chrome." >&2 + return 1 + fi + + # Crear directorio de perfil si no existe + if [[ ! -d "$profile_dir" ]]; then + mkdir -p "$profile_dir" || { + echo "ERROR: no se pudo crear el directorio de perfil: $profile_dir" >&2 + return 1 + } + fi + + # Construir argumentos del navegador + local args=( + "--proxy-server=${proxy_url}" + "--user-data-dir=${profile_dir}" + "--proxy-bypass-list=<-loopback>" + "--no-first-run" + "--no-default-browser-check" + ) + + # Manejo de certificados TLS + if [[ -n "$ca_cert" ]]; then + # El usuario instalo el CA en el perfil; no ignorar errores de certificado. + # (El CA se instala en el sistema o en el perfil antes de lanzar.) + echo "INFO: CA cert declarado: $ca_cert" >&2 + echo "INFO: Asegurate de haber importado el CA en el perfil o en el sistema antes de navegar HTTPS." >&2 + else + # Sin CA cert: ignorar errores de certificado para que mitmproxy/Burp funcionen sin configuracion extra. + # ADVERTENCIA: esto desactiva la validacion TLS completa del navegador. + args+=("--ignore-certificate-errors") + echo "WARN: --ignore-certificate-errors activo. Usa --ca-cert si instalaste el CA del proxy." >&2 + fi + + # URL inicial opcional + if [[ -n "$start_url" ]]; then + args+=("$start_url") + fi + + # Argumentos extra pasados por el usuario + # shellcheck disable=SC2206 + local extra_arr=() + if [[ -n "$extra_args" ]]; then + read -r -a extra_arr <<< "$extra_args" + args+=("${extra_arr[@]}") + fi + + # Log temporal para stderr/stdout del navegador + local log_file="/tmp/chromium-proxy-$$.log" + + # Lanzar en background desacoplado de la sesion del agente + setsid "$browser_bin" "${args[@]}" "$log_file" 2>&1 & + local browser_pid=$! + disown "$browser_pid" + + # Emitir JSON con informacion del proceso lanzado + printf '{"pid":%d,"browser":"%s","proxy":"%s","profile":"%s","log":"%s"}\n' \ + "$browser_pid" "$browser_bin" "$proxy_url" "$profile_dir" "$log_file" +} + +# Ejecutar si se llama directamente (fn run / bash ) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + launch_chromium_proxy "$@" +fi diff --git a/bash/functions/cybersecurity/query_mitm_flows.md b/bash/functions/cybersecurity/query_mitm_flows.md new file mode 100644 index 00000000..eec5a1a0 --- /dev/null +++ b/bash/functions/cybersecurity/query_mitm_flows.md @@ -0,0 +1,88 @@ +--- +name: query_mitm_flows +kind: function +lang: bash +domain: cybersecurity +version: "1.0.0" +purity: impure +signature: "query_mitm_flows(file_or_glob: string, filter?: string, har?: string, mitmdump?: string) -> void" +description: "Consulta capturas .mitm guardadas con mitmproxy: vuelca los flujos que coinciden con un filtro de mitmproxy a stdout, o exporta la captura a HAR. Acepta uno o varios archivos .mitm (incluyendo globs expandidos por el shell). Autodetecta mitmdump en PATH y $HOME/.local/bin." +tags: [bash, cybersecurity, mitmproxy, web-proxy, query, har, capture, traffic, proxy, network] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +params: + - name: file_or_glob + desc: "Ruta a un archivo .mitm o glob expandido por el shell (ej. ~/captures/traffic-*.mitm). Acepta múltiples archivos como argumentos posicionales." + - name: filter + desc: "Expresión de filtro mitmproxy (ej. '~m POST & ~u /api', '~c 500', '~d example.com'). Si se omite, vuelca todos los flujos." + - name: har + desc: "Ruta de salida para exportar en formato HAR (--set hardump=OUT). Si se omite, el volcado va a stdout." + - name: mitmdump + desc: "Ruta al binario mitmdump. Si se omite, autodetecta en PATH y luego en $HOME/.local/bin/mitmdump." +output: "Vuelca los flujos capturados a stdout (modo default) o exporta a un archivo HAR (con --har); informa la ruta de exportación en stderr al terminar. Exit code de mitmdump propagado." +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/cybersecurity/query_mitm_flows.sh" +--- + +## Ejemplo + +```bash +source bash/functions/cybersecurity/query_mitm_flows.sh + +# Volcar todos los flujos de una captura +query_mitm_flows ~/captures/traffic-20260602.mitm + +# Filtrar solo peticiones POST a /api (glob expandido por el shell) +query_mitm_flows ~/captures/traffic-20260602-*.mitm --filter "~m POST & ~u /api" + +# Ver solo respuestas con código 500 +query_mitm_flows session.mitm --filter "~c 500" + +# Exportar a HAR para abrir en el Network tab del browser DevTools +query_mitm_flows ~/captures/traffic-20260602.mitm --har salida.har + +# Exportar a HAR con filtro aplicado +query_mitm_flows session.mitm --filter "~d example.com" --har example_flows.har + +# Especificar mitmdump manualmente (ej. en un venv de Python) +query_mitm_flows session.mitm --mitmdump ~/.venv/bin/mitmdump --filter "~m POST" +``` + +## Cuando usarla + +Cuando hayas capturado tráfico HTTP/HTTPS con mitmproxy y necesites consultarlo después sin abrir una interfaz interactiva: filtrar flujos específicos por método, dominio, URL o código de respuesta, o exportar la captura a HAR para análisis posterior en herramientas externas (browser DevTools, Insomnia, Postman). Úsala desde scripts de análisis automatizados o pipelines de revisión de seguridad. + +## Gotchas + +**Sintaxis de filtros mitmproxy** — los filtros se pasan como expresión entre comillas: + +| Operador | Significado | Ejemplo | +|---|---|---| +| `~u ` | URL (path + query) | `~u /api/login` | +| `~d ` | Dominio del host | `~d example.com` | +| `~m ` | Método HTTP | `~m POST` | +| `~c ` | Código de respuesta | `~c 500` | +| `~bq ` | Body del request | `~bq password` | +| `~bs ` | Body del response | `~bs token` | +| `~t ` | Content-Type | `~t application/json` | +| `~s` | Solo respuestas | `~s` | +| `~q` | Solo requests | `~q` | + +Combinar con `&` (AND), `|` (OR), `!` (NOT). Ejemplos: +- `"~m POST & ~u /api"` — POST a rutas /api +- `"~c 500 | ~c 503"` — errores de servidor +- `"~d example.com & !~u /static"` — todo de example.com excepto estáticos + +**Globs:** el shell expande el glob *antes* de pasar los argumentos a la función. Pasar `~/captures/traffic-*.mitm` sin comillas para que el shell expanda; con comillas el glob llega literalmente y fallará si el archivo no existe. + +**Inspección interactiva:** para navegar los flujos con UI, usar `mitmweb -r ` (web) o `mitmproxy -r ` (TUI), no esta función. Esta función es para consulta programática y scripting. + +**Múltiples archivos con `-r`:** mitmdump acepta varios flags `-r` y concatena los flujos en orden. El filtro se aplica sobre el conjunto completo. + +**HAR y filtros juntos:** al usar `--har` con `--filter`, solo los flujos que pasen el filtro se exportan al HAR. diff --git a/bash/functions/cybersecurity/query_mitm_flows.sh b/bash/functions/cybersecurity/query_mitm_flows.sh new file mode 100644 index 00000000..a76183a4 --- /dev/null +++ b/bash/functions/cybersecurity/query_mitm_flows.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# query_mitm_flows — Consulta capturas .mitm con mitmdump: vuelca flujos a stdout +# o exporta a HAR. Acepta uno o varios archivos (también globs expandidos por el shell). + +query_mitm_flows() { + local -a files=() + local filter="" + local har_out="" + local mitmdump_bin="" + + # ── Parseo de argumentos ──────────────────────────────────────────────────── + while [[ $# -gt 0 ]]; do + case "$1" in + --filter) + [[ -z "${2:-}" ]] && { echo "ERROR: --filter requiere un valor" >&2; return 1; } + filter="$2"; shift 2 ;; + --har) + [[ -z "${2:-}" ]] && { echo "ERROR: --har requiere una ruta de salida" >&2; return 1; } + har_out="$2"; shift 2 ;; + --mitmdump) + [[ -z "${2:-}" ]] && { echo "ERROR: --mitmdump requiere la ruta al binario" >&2; return 1; } + mitmdump_bin="$2"; shift 2 ;; + --*) + echo "ERROR: opcion desconocida: $1" >&2; return 1 ;; + *) + files+=("$1"); shift ;; + esac + done + + # ── Validar que se paso al menos un archivo ───────────────────────────────── + if [[ ${#files[@]} -eq 0 ]]; then + echo "ERROR: se requiere al menos un archivo .mitm como argumento" >&2 + echo "Uso: query_mitm_flows [--filter EXPR] [--har OUT] [--mitmdump BIN]" >&2 + return 1 + fi + + # ── Verificar que cada archivo existe ────────────────────────────────────── + local -a valid_files=() + for f in "${files[@]}"; do + if [[ -f "$f" ]]; then + valid_files+=("$f") + else + echo "ERROR: archivo no encontrado: $f" >&2 + fi + done + + if [[ ${#valid_files[@]} -eq 0 ]]; then + echo "ERROR: ningun archivo valido encontrado (el patron no matcheo nada)" >&2 + return 1 + fi + + # ── Autodetectar mitmdump ─────────────────────────────────────────────────── + if [[ -z "$mitmdump_bin" ]]; then + if command -v mitmdump &>/dev/null; then + mitmdump_bin="mitmdump" + elif [[ -x "$HOME/.local/bin/mitmdump" ]]; then + mitmdump_bin="$HOME/.local/bin/mitmdump" + else + echo "ERROR: mitmdump no encontrado. Instala mitmproxy:" >&2 + echo " pip install mitmproxy o pip install --user mitmproxy" >&2 + return 1 + fi + fi + + if [[ ! -x "$(command -v "$mitmdump_bin" 2>/dev/null || echo "$mitmdump_bin")" ]]; then + echo "ERROR: binario mitmdump no ejecutable: $mitmdump_bin" >&2 + return 1 + fi + + # ── Construir y ejecutar el comando ──────────────────────────────────────── + local -a cmd=("$mitmdump_bin" -n) + + # Añadir todos los archivos de entrada con -r + for f in "${valid_files[@]}"; do + cmd+=(-r "$f") + done + + # Modo HAR + if [[ -n "$har_out" ]]; then + cmd+=(--set "hardump=${har_out}") + fi + + # Filtro de flujos (argumento posicional al final) + if [[ -n "$filter" ]]; then + cmd+=("$filter") + fi + + "${cmd[@]}" + local exit_code=$? + + if [[ -n "$har_out" && $exit_code -eq 0 ]]; then + echo "HAR exportado a ${har_out}" >&2 + fi + + return $exit_code +} + +# Ejecutar si se llama directamente +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + query_mitm_flows "$@" +fi diff --git a/bash/functions/cybersecurity/start_mitm_capture.md b/bash/functions/cybersecurity/start_mitm_capture.md new file mode 100644 index 00000000..5a95391a --- /dev/null +++ b/bash/functions/cybersecurity/start_mitm_capture.md @@ -0,0 +1,80 @@ +--- +name: start_mitm_capture +kind: function +lang: bash +domain: cybersecurity +version: "1.0.0" +purity: impure +signature: "start_mitm_capture([--port N] [--out DIR] [--rotate-min N] [--addon PATH] [--mitmdump BIN] [--log PATH]) -> string" +description: "Arranca mitmdump en modo headless en segundo plano como proxy de interceptación liviano, con rotación de capturas cada N minutos vía el addon rotate_capture_flows.py del registry. El proceso sobrevive al cierre de la shell (setsid). Emite JSON con PID, puerto, directorio de salida y ruta del log." +tags: [bash, cybersecurity, mitmproxy, proxy, capture, web-proxy, network, interception, mitmdump] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: [] +params: + - name: "--port N" + desc: "Puerto TCP donde mitmdump escucha conexiones del cliente proxy. Default: 8080." + - name: "--out DIR" + desc: "Directorio donde se guardan los archivos .mitm rotados. Se crea automáticamente si no existe. Default: $HOME/captures." + - name: "--rotate-min N" + desc: "Minutos de duración de cada archivo de captura antes de rotar. Default: 20." + - name: "--addon PATH" + desc: "Ruta al addon Python de rotación (rotate_capture_flows.py). Default: derivado desde FN_REGISTRY_ROOT o desde la ubicación del propio script (3 niveles arriba)." + - name: "--mitmdump BIN" + desc: "Ruta al binario mitmdump. Default: autodetectado con command -v mitmdump, luego $HOME/.local/bin/mitmdump. Si no se encuentra, falla con instrucción de instalación." + - name: "--log PATH" + desc: "Archivo de log del proceso mitmdump. Default: /mitmdump.log." +output: "JSON en stdout: {\"pid\": , \"port\": , \"out_dir\": \"\", \"rotate_min\": , \"log\": \"\"}. Exit 1 en error (binario no encontrado, addon ausente, proceso muerto al arrancar)." +tested: false +tests: [] +test_file_path: "" +file_path: "bash/functions/cybersecurity/start_mitm_capture.sh" +--- + +## Ejemplo + +```bash +source bash/functions/cybersecurity/start_mitm_capture.sh + +# Arranque con defaults (puerto 8080, capturas en ~/captures, rotación cada 20 min) +start_mitm_capture --port 8080 --out /home/enmanuel/captures --rotate-min 20 + +# Salida esperada: +# {"pid": 123456, "port": 8080, "out_dir": "/home/enmanuel/captures", "rotate_min": 20, "log": "/home/enmanuel/captures/mitmdump.log"} + +# Puerto alternativo y rotación más frecuente +start_mitm_capture --port 9090 --out /tmp/mitm_session --rotate-min 5 + +# Pasando addon y binario explícitos +start_mitm_capture \ + --port 8080 \ + --out /home/enmanuel/captures \ + --addon /home/enmanuel/fn_registry/python/functions/cybersecurity/rotate_capture_flows.py \ + --mitmdump /home/enmanuel/.local/bin/mitmdump + +# Leer el PID para poder parar el proceso más adelante +info=$(start_mitm_capture --port 8080 --out /home/enmanuel/captures) +pid=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin)['pid'])") +echo "Proxy corriendo con PID $pid" +# Para parar: kill "$pid" +``` + +## Cuando usarla + +Cuando necesites un proxy de interceptación pasivo siempre activo en segundo plano que capture y rote el tráfico HTTP/HTTPS de forma continua sin supervisión manual. Úsala antes de lanzar pruebas de integración, sesiones de auditoría web o scraping supervisado donde quieras replay o análisis posterior de las capturas `.mitm`. + +## Gotchas + +- **HTTPS requiere instalar el CA de mitmproxy en el cliente.** El certificado raíz está en `~/.mitmproxy/mitmproxy-ca-cert.pem` tras el primer arranque. En navegadores: importar como CA de confianza. En curl/httpx: `--cacert ~/.mitmproxy/mitmproxy-ca-cert.pem`. En Chrome/Chromium headless: `--ignore-certificate-errors` (solo para pruebas). +- **El proceso queda en background.** Para pararlo: `kill ` (el PID se devuelve en el JSON) o `port_kill 8080` si tienes la función del registry disponible. +- **El addon rotate_capture_flows.py debe existir.** La función lo busca en la raíz del registry derivada automáticamente; si el registry está en una ubicación no estándar, pasa `--addon` explícitamente o setea `FN_REGISTRY_ROOT`. +- **setsid desacopla el proceso de la shell.** Si cierras la terminal, mitmdump sigue corriendo. Necesitas matar el PID manualmente o via systemd/supervisor si quieres ciclo de vida gestionado. +- **El log crece sin límite.** El log de mitmdump no rota; solo rotan los archivos `.mitm` de capturas. Monitoriza el tamaño de `/mitmdump.log` en sesiones largas. +- **Solo funciona con mitmdump >= 9.x** (API de addons `--set` con parámetros por nombre). Versiones anteriores usan sintaxis distinta. + +## Notas + +La derivación de la raíz del registry cuando `FN_REGISTRY_ROOT` no está seteado asciende 3 niveles desde `bash/functions/cybersecurity/` hasta la raíz usando `dirname "${BASH_SOURCE[0]}"`. Esto funciona siempre que el script se sourcee desde su ubicación original en el registry. diff --git a/bash/functions/cybersecurity/start_mitm_capture.sh b/bash/functions/cybersecurity/start_mitm_capture.sh new file mode 100644 index 00000000..0e1d15da --- /dev/null +++ b/bash/functions/cybersecurity/start_mitm_capture.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# start_mitm_capture — Arranca mitmdump en segundo plano como proxy de interceptación +# con rotación de capturas cada N minutos vía addon Python del registry. + +start_mitm_capture() { + local port=8080 + local out_dir="$HOME/captures" + local rotate_min=20 + local addon_path="" + local mitmdump_bin="" + local log_path="" + + # Parseo de argumentos + while [[ $# -gt 0 ]]; do + case "$1" in + --port) port="$2"; shift 2 ;; + --out) out_dir="$2"; shift 2 ;; + --rotate-min) rotate_min="$2"; shift 2 ;; + --addon) addon_path="$2"; shift 2 ;; + --mitmdump) mitmdump_bin="$2"; shift 2 ;; + --log) log_path="$2"; shift 2 ;; + *) + echo "ERROR: argumento desconocido: $1" >&2 + echo "Uso: start_mitm_capture [--port N] [--out DIR] [--rotate-min N] [--addon PATH] [--mitmdump BIN] [--log PATH]" >&2 + return 1 + ;; + esac + done + + # Derivar raíz del registry cuando FN_REGISTRY_ROOT no está seteado + local registry_root + if [[ -n "${FN_REGISTRY_ROOT:-}" ]]; then + registry_root="$FN_REGISTRY_ROOT" + else + # bash/functions/cybersecurity/ -> 3 niveles arriba = raíz del registry + registry_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" + fi + + # Default addon usando la raíz derivada + if [[ -z "$addon_path" ]]; then + addon_path="${registry_root}/python/functions/cybersecurity/rotate_capture_flows.py" + fi + + # Default log path (depende de out_dir, se resuelve después de crear el dir) + # Se asigna más abajo si sigue vacío + + # Crear directorio de capturas si no existe + if [[ ! -d "$out_dir" ]]; then + mkdir -p "$out_dir" || { + echo "ERROR: no se pudo crear el directorio de capturas: $out_dir" >&2 + return 1 + } + fi + + # Asignar log por defecto ahora que out_dir existe + if [[ -z "$log_path" ]]; then + log_path="${out_dir}/mitmdump.log" + fi + + # Resolver binario mitmdump + if [[ -z "$mitmdump_bin" ]]; then + if command -v mitmdump &>/dev/null; then + mitmdump_bin="$(command -v mitmdump)" + elif [[ -x "$HOME/.local/bin/mitmdump" ]]; then + mitmdump_bin="$HOME/.local/bin/mitmdump" + else + echo "ERROR: mitmdump no encontrado; instala con: uv tool install mitmproxy" >&2 + return 1 + fi + else + if [[ ! -x "$mitmdump_bin" ]]; then + echo "ERROR: el binario indicado no existe o no es ejecutable: $mitmdump_bin" >&2 + return 1 + fi + fi + + # Verificar que el addon existe + if [[ ! -f "$addon_path" ]]; then + echo "ERROR: addon no encontrado: $addon_path" >&2 + echo " Asegúrate de que FN_REGISTRY_ROOT apunta a la raíz del registry" >&2 + echo " o pasa --addon con la ruta correcta a rotate_capture_flows.py" >&2 + return 1 + fi + + # Arrancar mitmdump en background con setsid (sobrevive al cierre de la shell). + # El redirect > "$log_path" 2>&1 & + local pid=$! + disown "$pid" 2>/dev/null || true + + # Esperar ~1s y verificar que el proceso sigue vivo + sleep 1 + if ! kill -0 "$pid" 2>/dev/null; then + echo "ERROR: mitmdump murió al arrancar. Últimas líneas del log:" >&2 + tail -20 "$log_path" >&2 + return 1 + fi + + # Emitir JSON con información del proceso arrancado + printf '{"pid": %d, "port": %d, "out_dir": "%s", "rotate_min": %d, "log": "%s"}\n' \ + "$pid" "$port" "$out_dir" "$rotate_min" "$log_path" +} + +# Ejecutar si se llama directamente (fn run / bash ) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + start_mitm_capture "$@" +fi diff --git a/docs/capabilities/INDEX.md b/docs/capabilities/INDEX.md index aa6c022c..0c3177e2 100644 --- a/docs/capabilities/INDEX.md +++ b/docs/capabilities/INDEX.md @@ -23,6 +23,7 @@ Indice de grupos de capacidades del registry. Cada grupo agrupa >=3 funciones qu | [nlp](nlp.md) | 33 | Extraccion NLP: PDFs, OCR, chunking, GLiNER/GLiREL, dedup, agregacion de entities/relations | | [docker](docker.md) | 38 | Operar Docker desde Go/Bash: build/run/stop, compose, networks, volumes, logs, deploys | | [android](android.md) | 37 | Toolbelt Android desde WSL2: adb, emuladores AVD, APK build/install, Capacitor, logcat | +| [web-proxy](web-proxy.md) | 4 | Captura de trafico HTTP/HTTPS liviana (mitmproxy): proxy con rotacion, navegador proxeado, consulta de capturas. Alternativa ligera a ZAP/Burp | | [metabase](metabase.md) | 106 | Operar Metabase via API REST: auth, cards, dashboards, collections, snippets, permissions | | [doctor](doctor.md) | 11 | Diagnostico read-only del registry: artefactos, servicios, drift, funciones huerfanas | | [notebook](notebook.md) | 5 | Operar Jupyter Lab colaborativo (discover/read/exec/write/kernel) | diff --git a/docs/capabilities/web-proxy.md b/docs/capabilities/web-proxy.md new file mode 100644 index 00000000..1210c37a --- /dev/null +++ b/docs/capabilities/web-proxy.md @@ -0,0 +1,71 @@ +# Web Proxy — Captura de trafico HTTP/HTTPS liviana (mitmproxy) + +Tag: `web-proxy`. Grupo de funciones para levantar un proxy de interceptacion ligero sobre `mitmproxy`, capturar todo el trafico de un navegador con rotacion temporal, y consultar las capturas guardadas. Alternativa liviana a OWASP ZAP / Burp Suite. Lo consume `apps/web_proxy` (CLI + servicio systemd --user). Crece a medida que se añaden formas de captura (WebSocket dumps, replay) o de consulta (filtros prearmados, export a otros formatos). + +Filtro MCP: `mcp__registry__fn_search query="" tag="web-proxy"`. + +## Funciones del grupo + +| ID | Firma corta | Que hace | +|---|---|---| +| [start_mitm_capture_bash_cybersecurity](../../bash/functions/cybersecurity/start_mitm_capture.md) | `start_mitm_capture [--port N] [--out DIR] [--rotate-min N]` | Arranca `mitmdump` headless en background como proxy de interceptacion, con rotacion de capturas via el addon del registry. Sobrevive al cierre de la shell (setsid). Emite JSON con PID y puerto. | +| [rotate_capture_flows_py_cybersecurity](../../python/functions/cybersecurity/rotate_capture_flows.md) | `mitmdump -s rotate_capture_flows.py --set rotate_min=N --set capture_dir=DIR` | Addon de mitmproxy que trocea las capturas en archivos `traffic-YYYYmmdd-HHMMSS.mitm` por ventanas de tiempo. Hace `flush()` por flujo, asi que la captura sobrevive a un `kill -9`. | +| [query_mitm_flows_bash_cybersecurity](../../bash/functions/cybersecurity/query_mitm_flows.md) | `query_mitm_flows [--filter EXPR] [--har OUT]` | Consulta capturas `.mitm` guardadas: vuelca los flujos que matchean un filtro de mitmproxy, o exporta a HAR. Acepta globs de varios archivos. | +| [launch_chromium_proxy_bash_browser](../../bash/functions/browser/launch_chromium_proxy.md) | `launch_chromium_proxy [--proxy URL] [--profile DIR] [--url URL]` | Lanza Chromium apuntando al proxy con un perfil aislado, sin contaminar la sesion normal. Maneja el CA del proxy o cae a `--ignore-certificate-errors`. | + +Complementa: `port_kill_bash_infra` (limpieza de puertos ocupados). + +## Ejemplo canonico end-to-end + +Levantar el proxy, traficar a traves de el, y consultar lo capturado. Todo en bash, usando las funciones del registry directamente: + +```bash +export FN_REGISTRY_ROOT="$HOME/fn_registry" +cd "$FN_REGISTRY_ROOT" + +# 1. Arrancar el proxy en el puerto 8080, rotando cada 20 min +JSON=$(bash bash/functions/cybersecurity/start_mitm_capture.sh \ + --port 8080 --out ~/captures --rotate-min 20) +PID=$(echo "$JSON" | python3 -c 'import sys,json;print(json.load(sys.stdin)["pid"])') + +# 2. Lanzar un navegador proxeado en perfil aislado +bash bash/functions/browser/launch_chromium_proxy.sh \ + --proxy http://127.0.0.1:8080 --url https://example.com + +# ... navegar; mitmproxy registra todo en ~/captures/traffic-*.mitm ... + +# 3. Consultar la ultima captura: solo peticiones POST +LAST=$(ls -t ~/captures/traffic-*.mitm | head -1) +bash bash/functions/cybersecurity/query_mitm_flows.sh "$LAST" --filter "~m POST" + +# 4. Exportar todo a HAR +bash bash/functions/cybersecurity/query_mitm_flows.sh ~/captures/traffic-*.mitm --har ~/sesion.har + +# 5. Parar el proxy +kill -TERM "$PID" +``` + +Para uso real, la app `apps/web_proxy` envuelve esto en un CLI con subcomandos +(`start`, `stop`, `status`, `browser`, `query`, `inspect`, `install-service`) y +gestiona el ciclo de vida como servicio `systemd --user` siempre activo. + +## Fronteras + +- **No es un escaner de vulnerabilidades**. Captura y consulta trafico; no hace + fuzzing, spidering ni deteccion de vulns como ZAP/Burp. Para eso, exporta a + HAR y procesa con otra herramienta. +- **No modifica trafico** (todavia). Los addons del grupo solo observan/graban. + Un addon de reescritura (estilo Burp Repeater/Intruder) seria una funcion + nueva del grupo, no una ampliacion de las actuales. +- **HTTPS requiere confiar en el CA** de mitmproxy en el cliente. La captura de + trafico cifrado sin el CA instalado solo funciona con + `--ignore-certificate-errors` en el navegador proxeado. +- **Persistencia del servicio**: `systemd --user` para al cerrar sesion salvo + linger. La gestion del servicio vive en la app, no en estas funciones. + +## Prerequisitos + +- `mitmproxy >= 10` instalado: `uv tool install mitmproxy` (da `mitmdump`, + `mitmproxy`, `mitmweb` en `~/.local/bin`). +- `chromium` (o `google-chrome`) en PATH para `launch_chromium_proxy`. +- `python3` para parsear el JSON de arranque y exportar/inspeccionar HAR. diff --git a/python/functions/cybersecurity/rotate_capture_flows.md b/python/functions/cybersecurity/rotate_capture_flows.md new file mode 100644 index 00000000..21ca57b4 --- /dev/null +++ b/python/functions/cybersecurity/rotate_capture_flows.md @@ -0,0 +1,65 @@ +--- +name: rotate_capture_flows +kind: function +lang: py +domain: cybersecurity +version: "1.0.0" +purity: impure +signature: "class Rotator — mitmproxy addon loaded via mitmdump -s" +description: "Addon de mitmproxy que rota el archivo de captura de flows cada N minutos. Crea archivos .mitm con timestamp en el nombre (traffic-YYYYmmdd-HHMMSS.mitm) y abre uno nuevo cuando vence el intervalo de rotacion. La rotacion ocurre en el evento response, por lo que solo sucede cuando hay trafico activo." +tags: [mitmproxy, capture, rotate, proxy, web-proxy, cybersecurity, flows, traffic] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: ["mitmproxy.io", "mitmproxy.ctx", "os", "time", "datetime"] +tested: false +tests: [] +test_file_path: "" +file_path: "python/functions/cybersecurity/rotate_capture_flows.py" +params: + - name: rotate_min + desc: "Minutos por archivo antes de rotar. Se pasa via --set rotate_min=N al invocar mitmdump. Default: 20." + - name: capture_dir + desc: "Directorio donde se escriben los archivos .mitm rotados. Se pasa via --set capture_dir=/ruta. Default: directorio de trabajo actual." +output: "Archivos .mitm con nombre traffic-.mitm en capture_dir, cada uno con los flows HTTP capturados durante una ventana de rotate_min minutos." +--- + +## Ejemplo + +```bash +# Capturar trafico HTTP/S en ventanas de 20 minutos en ~/captures/ +mitmdump \ + -s python/functions/cybersecurity/rotate_capture_flows.py \ + --set rotate_min=20 \ + --set capture_dir=/home/enmanuel/captures \ + --listen-port 8080 + +# Ventanas de 5 minutos para analisis rapido +mitmdump \ + -s python/functions/cybersecurity/rotate_capture_flows.py \ + --set rotate_min=5 \ + --set capture_dir=/tmp/proxy_session \ + --listen-port 8080 + +# Leer un archivo capturado con mitmproxy +mitmproxy -r /home/enmanuel/captures/traffic-20260602-143000.mitm +``` + +## Cuando usarla + +Cuando necesites capturar trafico HTTP/S durante periodos largos y quieras trocear las capturas en ventanas de tiempo manejables. Util antes de analizar el comportamiento de red de una aplicacion movil o web durante horas, cuando un unico archivo .mitm de varios GB seria dificil de navegar. + +## Gotchas + +- La rotacion ocurre en el evento `response`: si no llega ningun flujo completo durante el intervalo, el archivo no rota hasta que llegue el siguiente. El reloj es wall-clock del proceso, no del servidor. +- El archivo anterior se cierra (flush + close) justo antes de abrir el nuevo, por lo que no se pierden flows ya registrados al rotar. +- El addon hace `flush()` despues de cada flujo registrado. Esto garantiza que la captura sobrevive a una muerte abrupta del proceso (SIGKILL, crash, corte de energia): sin el flush, el FlowWriter bufferiza y el `.mitm` queda en 0 bytes si el proceso no llega a ejecutar `done()`. Verificado en smoke test matando con `kill -9` a media captura. +- Los flows que no tienen respuesta (timeouts de servidor, errores de conexion antes de recibir headers) no llegan al evento `response` y por tanto no se escriben en el archivo. Para capturarlos tambien, habria que sobrescribir adicionalmente el hook `error(self, flow)`. +- Requiere mitmproxy >= 10 en el entorno. Instalable con `uv tool install mitmproxy`. +- El directorio `capture_dir` se crea automaticamente con `os.makedirs(..., exist_ok=True)` si no existe. + +## Notas + +El addon usa la API de opciones de mitmproxy 10+ (`loader.add_option`). En versiones anteriores la API de opciones era distinta. Verificar version con `mitmdump --version` antes de cargar. diff --git a/python/functions/cybersecurity/rotate_capture_flows.py b/python/functions/cybersecurity/rotate_capture_flows.py new file mode 100644 index 00000000..5aae58fc --- /dev/null +++ b/python/functions/cybersecurity/rotate_capture_flows.py @@ -0,0 +1,127 @@ +"""mitmproxy addon that rotates the capture file every N minutes. + +Load with: mitmdump -s rotate_capture_flows.py --set rotate_min=20 --set capture_dir=/path/to/dir +""" + +import os +import time +from datetime import datetime + +from mitmproxy import ctx +import mitmproxy.io + + +class Rotator: + """Addon that writes flows to a rotating series of .mitm files. + + A new file is opened whenever the current one has been open for + at least ``rotate_min`` minutes. The rollover check happens on + every ``response`` event, so the file only rotates when traffic + is actually flowing through the proxy. + """ + + def __init__(self) -> None: + self._writer: mitmproxy.io.FlowWriter | None = None + self._fh = None # file handle opened in "wb" + self._opened_at: float = 0.0 + self._current_path: str = "" + self._rotate_min: int = 20 + self._capture_dir: str = "." + self._exclude: set = set() + + # ------------------------------------------------------------------ + # mitmproxy lifecycle hooks + # ------------------------------------------------------------------ + + def load(self, loader) -> None: + """Register addon options.""" + loader.add_option( + name="rotate_min", + typespec=int, + default=20, + help="Minutes per capture file before rolling over.", + ) + loader.add_option( + name="capture_dir", + typespec=str, + default=".", + help="Directory where rotating .mitm files are written.", + ) + loader.add_option( + name="exclude_hosts", + typespec=str, + default="", + help=( + "Comma-separated hosts or host:port that must never be " + "recorded (e.g. the proxy's own web UI). Flows matching " + "either the host alone or host:port are dropped silently." + ), + ) + + def configure(self, updated) -> None: + """Read option values and ensure the capture directory exists.""" + self._rotate_min = ctx.options.rotate_min + self._capture_dir = ctx.options.capture_dir + self._exclude = { + h.strip() for h in ctx.options.exclude_hosts.split(",") if h.strip() + } + if self._capture_dir: + os.makedirs(self._capture_dir, exist_ok=True) + + def response(self, flow) -> None: + """Called for every completed HTTP response. + + Rolls the file over if the rotation interval has elapsed, then + records the flow. Flows whose host (or host:port) is in the + exclude list are dropped without being written. + """ + if self._exclude: + host = flow.request.pretty_host + if host in self._exclude or f"{host}:{flow.request.port}" in self._exclude: + return + now = time.time() + if self._writer is None or (now - self._opened_at) >= self._rotate_min * 60: + self._roll() + self._writer.add(flow) + # Flush after every flow so the capture survives an abrupt kill + # (SIGKILL, crash, power loss). A capture proxy must never lose its + # window of traffic just because the process died without cleanup. + if self._fh is not None: + self._fh.flush() + + def done(self) -> None: + """Clean up when mitmdump shuts down.""" + self._close() + + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + + def _roll(self) -> None: + """Close the current file (if any) and open a new one.""" + self._close() + + timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") + filename = f"traffic-{timestamp}.mitm" + self._current_path = os.path.join(self._capture_dir, filename) + + self._fh = open(self._current_path, "wb") + self._writer = mitmproxy.io.FlowWriter(self._fh) + self._opened_at = time.time() + + ctx.log.info(f"rotate_capture_flows: opened new capture file {self._current_path}") + + def _close(self) -> None: + """Flush and close the current writer and file handle.""" + if self._fh is not None: + try: + self._fh.flush() + self._fh.close() + except Exception: + pass + finally: + self._fh = None + self._writer = None + + +addons = [Rotator()]