#!/usr/bin/env python3 """Cosecha el manual de las herramientas que no traen man page. ./manuales.py regenera conocimiento/09 - Manuales/ ./manuales.py --lista di que herramientas cosecharia y cuales estan Muchas herramientas de pentesting (ffuf, sqlmap, la suite impacket, netexec...) no instalan man page: su "manual" es lo que sueltan con --help. Este script lo captura, lo limpia de rutas y datos de la maquina, y lo escribe como markdown en el pack de conocimiento, donde indexa.py lo recoge como todo lo demas. Solo cosecha lo que ESTA instalado en la maquina. El resultado (los .md) se commitea, asi que crece con lo que cada quien tenga: quien instale una herramienta mas, regenera y manda el .md nuevo. Es el complemento verificado del cheatsheet escrito a mano (que da las invocaciones comunes; esto da la lista completa de opciones). """ import argparse import os import re import shutil import subprocess import sys AQUI = os.path.dirname(os.path.abspath(__file__)) DESTINO = os.path.join(AQUI, "conocimiento", "09 - Manuales") # Las herramientas cuyo manual vale la pena y que no suelen traer man page. # (comando, [argumentos para pedir la ayuda]). Se prueban en orden hasta que una # devuelva algo con sustancia. CATALOGO = [ ("ffuf", ["-h"]), ("feroxbuster", ["--help"]), ("gobuster", ["--help"]), ("sqlmap", ["-hh"]), ("nuclei", ["-h"]), ("hydra", ["-h"]), ("netexec", ["--help"]), ("crackmapexec", ["--help"]), ("evil-winrm", ["--help"]), ("john", []), # john sin args ya imprime el uso ("hashcat", ["--help"]), ("wpscan", ["--help"]), ("nikto", ["-Help"]), ("smbmap", ["-h"]), ("enum4linux-ng", ["-h"]), ("wfuzz", ["-h"]), ("dirsearch", ["-h"]), ("responder", ["-h"]), ("chisel", ["--help"]), ("ligolo-proxy", ["-h"]), ("bloodhound-python", ["-h"]), ("certipy", ["-h"]), ("kerbrute", ["-h"]), # La suite impacket: cada binario es una herramienta. ("impacket-secretsdump", ["-h"]), ("impacket-GetUserSPNs", ["-h"]), ("impacket-GetNPUsers", ["-h"]), ("impacket-getTGT", ["-h"]), ("impacket-psexec", ["-h"]), ("impacket-wmiexec", ["-h"]), ("impacket-smbexec", ["-h"]), ("impacket-smbserver", ["-h"]), ("impacket-mssqlclient", ["-h"]), ("impacket-ntlmrelayx", ["-h"]), ] ANSI = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") CASA = re.compile(re.escape(os.path.expanduser("~"))) USUARIO = re.compile(re.escape(os.environ.get("USER", "\0nadie\0"))) def limpia(texto): """Quita colores y cualquier rastro de la maquina de esta cosecha.""" texto = ANSI.sub("", texto) texto = CASA.sub("~", texto) # /home/quien-sea -> ~ texto = USUARIO.sub("usuario", texto) # el login real -> generico texto = re.sub(r"[ \t]+\n", "\n", texto) # espacios al final de linea texto = re.sub(r"\n{3,}", "\n\n", texto) # como mucho una linea en blanco return texto.strip() def ayuda(cmd, args): """La salida de ayuda de una herramienta, o None si no dice nada util.""" if not shutil.which(cmd): return None for intento in (args, ["--help"], ["-h"], []): try: r = subprocess.run([cmd, *intento], capture_output=True, text=True, timeout=15, errors="ignore") except (subprocess.TimeoutExpired, OSError): continue salida = (r.stdout or "") + (r.stderr or "") salida = limpia(salida) if len(salida) >= 120: # menos que esto no es un manual return salida return None def escribe(cmd, texto): md = (f"# {cmd} — manual\n\n" f"Salida de `{cmd} {'--help' if not cmd.startswith('impacket') else '-h'}`, " f"capturada de la propia herramienta. Referencia completa de opciones " f"(las invocaciones del dia a dia estan en el cheatsheet de comandos).\n\n" f"```\n{texto}\n```\n") os.makedirs(DESTINO, exist_ok=True) ruta = os.path.join(DESTINO, f"{cmd}.md") with open(ruta, "w", encoding="utf-8") as f: f.write(md) return ruta def main(): p = argparse.ArgumentParser() p.add_argument("--lista", action="store_true", help="di que cosecharia y que hay instalado, sin escribir") a = p.parse_args() if a.lista: for cmd, _ in CATALOGO: hay = "instalada" if shutil.which(cmd) else "-" print(f" {cmd:26s} {hay}") return 0 hechos = faltan = 0 for cmd, args in CATALOGO: texto = ayuda(cmd, args) if texto is None: faltan += 1 continue ruta = escribe(cmd, texto) print(f" {cmd:26s} -> {os.path.relpath(ruta, AQUI)} ({len(texto)} car.)") hechos += 1 print(f"\n {hechos} manuales cosechados, {faltan} no instalados en esta maquina") return 0 if __name__ == "__main__": sys.exit(main())