Skip to content

Latest commit

 

History

History
1510 lines (1185 loc) · 48.7 KB

File metadata and controls

1510 lines (1185 loc) · 48.7 KB

🛡️ Playbook de Pentesting | Altrupets Monorepo

Proyecto: altrupets/monorepo
Tecnologías: Flutter (Mobile), NestJS (Backend), Kubernetes (Minikube), Terraform, PostgreSQL
Metodología: Cyber Kill Chain + OWASP Testing Guide


📋 Fase 1: Reconocimiento (Reconnaissance)

1.1 Recolección de Información Pasiva

Actividad Herramienta Objetivo
OSINT del repositorio GitHub, Repogrinder URLs expuestas, emails, secretos filtrados
指纹识别 Wappalyzer, WhatWeb Tecnologías detectables
DNS Enumeration dnsenum, dig Subdominios, registros MX/SRV

1.2 Recolección Activa

# Escaneo de puertos
nmap -sV -sC -p- <target-ip>

# Descubrimiento de servicios
curl -I http://<backend-url>/health
curl -I http://<mobile-api>/api

# Enumeración de endpoints
ffuf -u http://<target>/FUZZ -w wordlist.txt

1.3 Targets Identificados

Componente URL/Puerto Tecnología
Backend API :3000 NestJS
Frontend Web :4200 Angular/Django
Mobile App API Gateway Flutter
Kubernetes :6443 Minikube
PostgreSQL :5432 Database
Redis :6379 Cache

📋 Fase 2: Análisis de Vulnerabilidades (Vulnerability Assessment)

2.1 Escaneo de Dependencias

# Node.js/NPM
npm audit
npm audit --json > npm-audit.json

# Dart/Flutter
flutter pub deps
dart analyze

# Containers
trivy image <backend-image>
grype <backend-image>

2.2 Escaneo de Configuración

# SAST - Static Application Security Testing
# Backend NestJS
npm run lint
eslint --ext .ts ./apps/backend

# Mobile Flutter
flutter analyze
dart analyze

# IaC Security
tfsec ./infrastructure/terraform
checkov -d ./infrastructure/terraform

2.3 Análisis de Secretos

# TruffleHog
trufflehog filesystem .
trufflehog github --repo altrupets/monorepo

# GitLeaks
gitleaks detect --source .

2.4 Vulnerabilidades Comunes en Este Proyecto

OWASP Top 10 Componente Afectado Severidad Check
A01:2021 Broken Access Control Backend API Alta Verificar RBAC, JWT validation
A02:2021 Cryptographic Failures Secrets Management Alta Revisar .env, Infisical config
A03:2021 Injection Backend API, DB Crítica SQLi, NoSQLi, Command Injection
A04:2021 Insecure Design Mobile App Media Binary patching, SSL pinning bypass
A05:2021 Security Misconfiguration K8s, Terraform Alta RBAC, network policies, secrets
A06:2021 Vulnerable Components Dependencies Media npm audit, trivy
A07:2021 Auth Failures Backend, Mobile Alta JWT, OAuth, MFA
A08:2021 Data Integrity Failures CI/CD Media Pipeline security
A09:2021 Logging Failures Backend Media Log injection, PII exposure
A10:2021 SSRF Backend API Alta URL parsing, file access

📋 Fase 3: Explotación (Exploitation)

3.1 Web Application Testing

# SQL Injection
sqlmap -u "http://api.example.com/user?id=1" --batch

# XSS
# Reflected
curl "http://api.example.com/search?q=<script>alert(1)</script>"
# Stored - analizar campos de input

# IDOR
# Manipular IDs en requests REST
GET /api/users/1 -> GET /api/users/2

# JWT Attacks
jwt_tool.py <token> -T

3.2 Mobile App Testing (Flutter)

# Extracción de APK
apktool d app.apk

# Análisis estático
jadx app.apk

# SSL Pinning Bypass
# Using Frida
frida -U -f com.altrupets.app -l ssl-pinning-bypass.js

# Root/Jailbreak Detection Bypass
frida -U -f com.altrupets.app -l root-bypass.js

3.3 API Testing

# Enumeración de endpoints
ffuf -u https://api.altrupets.com/FUZZ -w raft-large-words.txt -mc 200,401,403

# Auth bypass
# JWT None Algorithm
{"alg":"none"}

# Mass assignment
# Modificar parámetros JSON
{"role": "admin", "user_id": 1}

3.4 Kubernetes Pentesting

# Enumeración
kubectl get pods --all-namespaces
kubectl get secrets --all-namespaces

# Pod escape
# If privileged: mount host filesystem
kubectl get pod <pod-name> -o yaml | grep -i privileged

# Service Account tokens
kubectl exec <pod-name> -- cat /run/secrets/kubernetes.io/serviceaccount/token

📋 Fase 4: Análisis de Tráfico (Traffic Analysis)

4.1 Captura de Tráfico

# Wireshark
# Capturar tráfico HTTP/HTTPS
# Filter: http.request.method == POST

# TCPdump
tcpdump -i any -w capture.pcap port 3000 or port 5432

# Mitmproxy para tráfico mobile
mitmproxy -p 8080

4.2 Análisis de Tráfico Cifrado

# Extraer certificados
# Android: /system/etc/security/cacerts

# SSL/TLS Analysis
# Verificar configuración TLS
testssl.sh https://api.altrupets.com

# heartbeat/heartbleed
nmap -p 443 --script ssl-heartbleed <target>

4.3 Indicators of Compromise (IoC)

Tipo Indicador Acción
DNS *.evil.com Bloquear en firewall
IP 192.168.1.100 Investigar origen
Hash a1b2c3d4... Agregar a blacklist
URL /admin/exploit.php Remover/delist

📋 Fase 5: Post-Explotación (Post-Exploitation)

5.1 Escalación de Privilegios

# Linux
# Enumeration
linpeas.sh
linenum.sh

# Kernel exploits
uname -a
searchsploit linux kernel <version>

# Sudo misconfigurations
sudo -l

5.2 Movimiento Lateral

# SSH pivoting
ssh -J jump-server target-host

# Kubernetes lateral movement
# Between pods
kubectl exec -it <pod-a> -- bash
# Service account abuse

5.3 Persistencia

# Kubernetes
# Backdoor via Deployment
kubectl expose deployment <name> --port=4444

# SSH keys
# Agregar public key a authorized_keys

5.4 Exfiltración

# Data
# Identificar datos sensibles
find / -type f -name "*.env" -o -name "*.key"

# DNS Exfiltration
# Review DNS queries to attacker domain

📋 Fase 6: Reporte y Remediación

6.1 Severidad (CVSS 3.1)

Rating Score Descripción
Critical 9.0-10.0 Immediate action required
High 7.0-8.9 Urgent remediation
Medium 4.0-6.9 Plan remediation
Low 0.1-3.9 Monitor
None 0.0 No action

6.2 Plantilla de Hallazgos

## Finding #X: [Título]

### Severity: [Critical|High|Medium|Low]
### CVSS: [Score]
### Component: [Backend|Mobile|K8s|Terraform]

### Description
[Descripción detallada]

### Proof of Concept
[Código/explotación]

### Impact
[Impacto negocio/técnico]

### Remediation
[Recomendación específica]

6.3 Priorización de Remediación

Prioridad Tipo Timeline
P1 RCE, SQLi, Auth bypass 24-48 hrs
P2 IDOR, XXE, SSRF 1 semana
P3 XSS, Info disclosure 2 semanas
P4 Best practices Sprint actual

🔧 Herramientas Recomendadas

Recopilación

Herramienta Uso
Nmap Escaneo de puertos
ffuf Fuzzing web
Wappalyzer Fingerprinting

Análisis

Herramienta Uso
Burp Suite Web proxy
OWASP ZAP Web testing
sqlmap SQL Injection
jwt_tool JWT attacks
Metasploit Exploitation

Mobile

Herramienta Uso
Frida Dynamic analysis
Jadx Decompilation
MobSF Mobile SAST

Kubernetes

Herramienta Uso
kube-hunter K8s penetration
kube-bench CIS benchmark
kubectl Enumeration

📊 Checklist de Auditoría

  • Fase 1: Reconocimiento completado
  • Fase 2: Vulnerabilidades documentadas
  • Fase 3: Explotación autorizada (scope defined)
  • Fase 4: Tráfico analizado
  • Fase 5: Post-explotación ejecutada (si autorizado)
  • Fase 6: Reporte generado
  • Remediation plan entregado

📋 Fase 7: Automatización DevSecOps (CI/CD Pipeline)

Objetivo: Automatizar las fases de Reconocimiento (Fase 1) y Análisis de Vulnerabilidades (Fase 2) dentro del pipeline de CI/CD para detectar vulnerabilidades base antes de cada despliegue a Staging/Producción.

7.1 Arquitectura Conceptual

┌─────────────────────────────────────────────────────────────────────────────┐
│                        DEVSECOPS PIPELINE ARCHITECTURE                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────┐    ┌─────────┐    ┌─────────────┐    ┌──────────────────┐    │
│  │  BUILD  │───▶│  TEST   │───▶│SECURITY SCAN │───▶│      REPORT      │    │
│  └─────────┘    └─────────┘    └─────────────┘    └──────────────────┘    │
│                                         │                                   │
│                                         ▼                                   │
│                               ┌─────────────────┐                         │
│                               │   STAGE GATES    │                         │
│                               ├─────────────────┤                         │
│                               │ Critical: HALT   │                         │
│                               │ High:    HALT    │                         │
│                               │ Medium:  WARN    │                         │
│                               │ Low:     INFO    │                         │
│                               └─────────────────┘                         │
│                                                                             │
│  SECURITY SCAN STAGES:                                                      │
│  ├── 1. SCA (Trivy)       → Dependency vulnerabilities                    │
│  ├── 2. DAST (OWASP ZAP)  → Web application scanning                      │
│  ├── 3. Infra (Nmap)      → Port/service discovery                        │
│  └── 4. Secrets (Gitleaks)│ Hardcoded secrets                            │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

7.2 GitHub Actions Workflow

# .github/workflows/security-scan.yml
name: 🛡️ DevSecOps Security Scan

on:
  push:
    branches: [main, develop, staging, production]
  pull_request:
    branches: [main, develop]
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM

env:
  TRIVY_CACHE_DIR: .trivy-cache
  ZAP_REPORT_DIR: .zap-reports
  SARIF_DIR: .sarif-reports

jobs:
  # ═══════════════════════════════════════════════════════════════════════
  # STAGE 1: BUILD
  # ═══════════════════════════════════════════════════════════════════════
  build:
    name: Build Application
    runs-on: ubuntu-latest
    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build Backend Image
        uses: docker/build-push-action@v5
        with:
          context: ./apps/backend
          push: false
          tags: altrupets/backend:${{ github.sha }}
          cache-from: type=registry,ref=altrupets/backend:buildcache
          cache-to: type=registry,ref=altrupets/backend:buildcache,mode=max

      - name: Generate image metadata
        id: meta
        run: |
          echo "tags=altrupets/backend:${{ github.sha }}" >> $GITHUB_OUTPUT

  # ═══════════════════════════════════════════════════════════════════════
  # STAGE 2: TEST
  # ═══════════════════════════════════════════════════════════════════════
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm test -- --coverage

      - name: Run linter
        run: npm run lint

  # ═══════════════════════════════════════════════════════════════════════
  # STAGE 3: SECURITY SCANS
  # ═══════════════════════════════════════════════════════════════════════

  # ─────────────────────────────────────────────────────────────────────────
  # 3A: SCA - Dependency Vulnerability Scanning (Trivy)
  # ─────────────────────────────────────────────────────────────────────────
  trivy-sca:
    name: Trivy SCA Scan
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy vulnerability scanner (fs mode)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH,MEDIUM'
          ignore-unfixed: true

      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
          category: 'trivy-sca'

      - name: Check Trivy results
        run: |
          python3 .github/scripts/check-trivy.py trivy-results.sarif

  # ─────────────────────────────────────────────────────────────────────────
  # 3B: DAST - Dynamic Application Security Testing (OWASP ZAP)
  # ─────────────────────────────────────────────────────────────────────────
  zap-scan:
    name: OWASP ZAP Scan
    runs-on: ubuntu-latest
    needs: test
    if: github.event_name != 'schedule'
    steps:
      - uses: actions/checkout@v4

      - name: Start application in background
        run: |
          # For staging/production URLs, use actual URL
          if [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
            echo "SCAN_URL=${{ secrets.STAGING_URL }}" >> $GITHUB_ENV
          elif [[ "${{ github.ref }}" == "refs/heads/production" ]]; then
            echo "SCAN_URL=${{ secrets.PRODUCTION_URL }}" >> $GITHUB_ENV
          else
            # Start local server for dev branches
            echo "SCAN_URL=http://localhost:3000" >> $GITHUB_ENV
          fi

      - name: Wait for application to be ready
        run: sleep 10

      - name: Run OWASP ZAP Spider
        uses: zaproxy/action-baseline@v0.9.0
        with:
          target: '${{ env.SCAN_URL }}'
          spider: 'true'

      - name: Run OWASP ZAP Active Scan
        uses: zaproxy/action-full-scan@v0.9.0
        with:
          target: '${{ env.SCAN_URL }}'
          max_spider_duration: 300
          ajax_spider: true

      - name: Generate ZAP Report
        run: |
          python3 .github/scripts/parse-zap.py zap_report.html

      - name: Upload ZAP results
        uses: actions/upload-artifact@v4
        with:
          name: zap-report
          path: zap_report.html

  # ─────────────────────────────────────────────────────────────────────────
  # 3C: Infrastructure Scanning (Nmap)
  # ─────────────────────────────────────────────────────────────────────────
  nmap-scan:
    name: Nmap Port Scan
    runs-on: ubuntu-latest
    needs: test
    if: github.event_name == 'schedule' || contains(github.event.head_commit.message, '[infra-scan]')
    steps:
      - uses: actions/checkout@v4

      - name: Install Nmap
        run: sudo apt-get install -y nmap

      - name: Run Nmap scan
        run: |
          # Scan only staging/production endpoints
          NMAP_TARGETS="${{ secrets.STAGING_IP }} ${{ secrets.PRODUCTION_IP }}"
          nmap -sV -sC -p- -oN nmap-results.txt $NMAP_TARGETS

      - name: Check for critical open ports
        run: |
          python3 .github/scripts/check-nmap.py nmap-results.txt

      - name: Upload Nmap results
        uses: actions/upload-artifact@v4
        with:
          name: nmap-results
          path: nmap-results.txt

  # ─────────────────────────────────────────────────────────────────────────
  # 3D: Secrets Detection (Gitleaks)
  # ─────────────────────────────────────────────────────────────────────────
  gitleaks:
    name: Gitleaks Secrets Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITLEAKS_CONFIG_PATH: .gitleaks.toml
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE_KEY: ${{ secrets.GITLEAKS_LICENSE }}

  # ═══════════════════════════════════════════════════════════════════════
  # STAGE 4: REPORT & GATEKEEPING
  # ═══════════════════════════════════════════════════════════════════════
  security-report:
    name: Security Report
    runs-on: ubuntu-latest
    needs: [trivy-sca, zap-scan, nmap-scan, gitleaks]
    if: always()
    outputs:
      total-critical: ${{ steps.summary.outputs.critical }}
      total-high: ${{ steps.summary.outputs.high }}
    steps:
      - uses: actions/checkout@v4

      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Generate summary report
        id: summary
        run: |
          python3 .github/scripts/generate-report.py

      - name: Post summary to PR
        if: github.event_name == 'pull_request'
        run: |
          echo "## 🛡️ Security Scan Summary" >> $GITHUB_STEP_SUMMARY
          echo "- Critical: ${{ steps.summary.outputs.critical }}" >> $GITHUB_STEP_SUMMARY
          echo "- High: ${{ steps.summary.outputs.high }}" >> $GITHUB_STEP_SUMMARY

      - name: Fail on Critical/High findings
        if: needs.trivy-sca.result == 'failure' || needs.gitleaks.result == 'failure'
        run: |
          echo "❌ Critical security issues found - blocking deployment"
          exit 1

  # ═══════════════════════════════════════════════════════════════════════
  # STAGE GATE CONFIGURATION
  # ═══════════════════════════════════════════════════════════════════════
  # ═══════════════════════════════════════════════════════════════════════
  # STAGE GATE RULES:
  # - CRITICAL: FAIL BUILD immediately
  # - HIGH:     FAIL BUILD unless explicitly acknowledged
  # - MEDIUM:  WARN only - allow deployment with notification
  # - LOW:     INFO only - no action required
  # ═══════════════════════════════════════════════════════════════════════

7.3 Scripts Auxiliares

#!/usr/bin/env python3
# .github/scripts/check-trivy.py
"""
Parse Trivy SARIF results and enforce stage gate policy.
CRITICAL/HIGH: Fail build
MEDIUM: Warn only
"""

import sys
import json
from pathlib import Path


def check_trivy_sarif(sarif_path: str):
    with open(sarif_path, 'r') as f:
        data = json.load(f)

    severity_counts = {'CRITICAL': 0, 'HIGH': 0, 'MEDIUM': 0, 'LOW': 0}
    findings = []

    for run in data.get('runs', []):
        for result in run.get('results', []):
            level = result.get('level', 'warning')
            rule_id = result.get('ruleId', 'unknown')
            message = result.get('message', {}).get('text', '')

            severity = 'MEDIUM'
            if 'CRITICAL' in message.upper():
                severity = 'CRITICAL'
            elif 'HIGH' in message.upper():
                severity = 'HIGH'

            severity_counts[severity] += 1
            findings.append(f"[{severity}] {rule_id}: {message}")

    print("\n=== Trivy Scan Results ===")
    for sev, count in severity_counts.items():
        print(f"{sev}: {count}")

    print("\n=== Findings ===")
    for f in findings[:10]:
        print(f)

    # Stage gate enforcement
    if severity_counts['CRITICAL'] > 0:
        print("\n❌ BLOCKED: Critical vulnerabilities found!")
        sys.exit(1)

    if severity_counts['HIGH'] > 0:
        print("\n⚠️ WARNING: High vulnerabilities found!")
        print("   Set TRIVY_FAIL_ON_HIGH=false to allow with warning")
        if os.getenv('TRIVY_FAIL_ON_HIGH', 'true').lower() == 'true':
            sys.exit(1)

    print("\n✅ Scan passed - no blocking issues")
    sys.exit(0)


if __name__ == '__main__':
    import os
    sarif_file = sys.argv[1] if len(sys.argv) > 1 else 'trivy-results.sarif'
    check_trivy_sarif(sarif_file)
#!/usr/bin/env python3
# .github/scripts/generate-report.py
"""
Aggregate all security scan results into a unified report.
"""

import json
import os
from pathlib import Path


def generate_unified_report():
    report = {
        'timestamp': os.getenv('GITHUB_RUN_ID', 'unknown'),
        'scans': {},
        'summary': {'critical': 0, 'high': 0, 'medium': 0, 'low': 0}
    }

    # Parse Trivy results
    trivy_path = Path('artifacts/trivy-results.sarif')
    if trivy_path.exists():
        with open(trivy_path) as f:
            data = json.load(f)
            count = len(data.get('runs', [{}])[0].get('results', []))
            report['scans']['trivy'] = {'findings': count}

    # Generate output
    output = f"""
# 🛡️ Security Scan Report

## Summary
- Critical: {report['summary']['critical']}
- High: {report['summary']['high']}
- Medium: {report['summary']['medium']}
- Low: {report['summary']['low']}

## Scans Executed
{chr(10).join(f"- {k}: {v['findings']} findings" for k, v in report['scans'].items())}
"""

    print(output)

    # Set outputs for GitHub Actions
    with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
        f.write(f"critical={report['summary']['critical']}\n")
        f.write(f"high={report['summary']['high']}\n")


if __name__ == '__main__':
    generate_unified_report()

7.4 Gestión de Falsos Positivos

Strategy Implementation Use Case
Suppress file-level .trivyignore Ignore known safe dependencies
Suppress rule-level trivy.yaml / .zapignore Ignore specific CVE rules
Baseline file ZAP baseline Accept current findings, alert on new
Comment-based // trivy:ignore Suppress in code comments
Ticket integration Auto-create issue Track FP for review
# .trivyignore
# Format: CVE-ID
CVE-2021-44228
CVE-2021-45046

# Ignore dev dependencies
pkg:golang.org/x/net/http2
pkg:github.com/nats-io/nats-server/v2
<!-- .zap/baseline.xml -->
<!-- Baseline: Accept current findings -->
<OWASPZAPReport version="2.12.0">
  <site name="https://staging.altrupets.com">
    <alerts>
      <alertname>Missing Anti-CSRF Token">
        <risk>Medium</risk>
        <confidence>High</confidence>
        <accepted>true</accepted>
        <justification>API uses alternative CSRF protection</justification>
      </alertname>
    </alerts>
  </site>
</OWASPZAPReport>

7.5 Próximos Pasos (Roadmap)

Phase Task Effort Priority
v1 (This spike) Trivy + Gitleaks in CI 2 hrs P0
v1.1 Add OWASP ZAP baseline 4 hrs P1
v1.2 Add Nmap for infra 4 hrs P1
v2 Add SAST (Semgrep/CodeQL) 1 día P2
v2.1 Add container scanning (Clair) 1 día P2
v3 Integrate with Jira/Linear 2 días P3
v3.1 Add notification (Slack/Teams) 4 hrs P3
v4 Add interactive DAST (ZAP API) 1 semana P4
v5 Add runtime protection (Falco) 1 semana P4

7.6 Optimización de Rendimiento

# Tips para mantener < 15 minutos:

# 1. Cache de herramientas
- uses: actions/cache@v4
  with:
    path: .trivy-cache
    key: trivy-${{ hashFiles('**/package-lock.json') }}

# 2. Scan en paralelo (jobs independientes)
# 3. Skip scans en dev branches (solo main/staging/production)
# 4. Usar baseline mode (solo diffs)
# 5. Timeout apropiado (max 5-10 min por scan)
# 6. Early exit en Critical findings

📋 Fase 8: Automatización de Pentesting (Toolkit Script)

Objetivo: Crear un script orquestador que ejecute automáticamente las Fases 1 (Reconocimiento) y 2 (Análisis de Vulnerabilidades) consolidadas en un único informe.

8.1 Estructura del Proyecto

pentest-toolkit/
├── automator.py          # Script principal (entrypoint)
├── requirements.txt     # Dependencias Python
├── config/
│   ├── nuclei-templates # Plantillas Nuclei
│   └── wordlists/       # Listas de palabras
├── scans/               # Output generado
│   └── {target}/
│       ├── subdomains/
│       ├── nmap/
│       ├── nuclei/
│       └── summary.md
└── scripts/
    ├── parse_nmap.py
    └── generate_report.py

8.2 Script Principal (automator.py)

#!/usr/bin/env python3
"""
Pentest Automation Toolkit
==========================
Orquesta ejecución secuencial de herramientas de reconocimiento y escaneo.

Usage:
    python automator.py --target example.com
    python automator.py --target 192.168.1.1 --ports 1000
"""

import argparse
import subprocess
import os
import sys
import json
import time
import shutil
from pathlib import Path
from datetime import datetime
from typing import Optional
import concurrent.futures


class Colors:
    """Colores para output en consola."""
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    CYAN = '\033[96m'
    BOLD = '\033[1m'
    END = '\033[0m'


class PentestAutomator:
    """Orquestador principal de herramientas de pentesting."""

    def __init__(self, target: str, output_dir: str = "scans", fast: bool = False):
        self.target = target
        self.output_dir = Path(output_dir)
        self.target_dir = self.output_dir / target.replace('.', '_')
        self.fast = fast
        self.results = {
            'target': target,
            'timestamp': datetime.now().isoformat(),
            'subdomains': [],
            'open_ports': [],
            'vulnerabilities': [],
            'nuclei_findings': []
        }

        self._setup_directories()
        self._check_dependencies()

    def _setup_directories(self):
        """Crea estructura de directorios."""
        dirs = ['subdomains', 'nmap', 'nuclei', 'web']
        for d in dirs:
            (self.target_dir / d).mkdir(parents=True, exist_ok=True)
        print(f"{Colors.BLUE}[*] Output: {self.target_dir}{Colors.END}")

    def _check_dependencies(self):
        """Verifica herramientas requeridas."""
        tools = ['nmap', 'subfinder', 'nuclei', 'httpx']
        missing = []
        for tool in tools:
            if shutil.which(tool) is None:
                missing.append(tool)
        if missing:
            print(f"{Colors.YELLOW}[!] Missing tools: {', '.join(missing)}{Colors.END}")
            print(f"{Colors.CYAN}[*] Install with: apt-get install nmap && go install github.com/projectdiscovery/subfinder/... && go install github.com/projectdiscovery/nuclei/... && go install github.com/projectdiscovery/httpx/...{Colors.END}")

    def _run_command(self, cmd: list, timeout: int = 300, capture: bool = True) -> tuple:
        """Ejecuta comando con manejo de errores y timeout."""
        try:
            result = subprocess.run(
                cmd,
                capture_output=capture,
                text=True,
                timeout=timeout,
                cwd=self.target_dir
            )
            return result.returncode, result.stdout, result.stderr
        except subprocess.TimeoutExpired:
            print(f"{Colors.RED}[!] Timeout: {' '.join(cmd[:3])}...{Colors.END}")
            return -1, "", "Timeout"
        except Exception as e:
            print(f"{Colors.RED}[!] Error: {e}{Colors.END}")
            return -1, "", str(e)

    def _log_progress(self, stage: str, message: str):
        """Imprime progreso con formato."""
        timestamp = datetime.now().strftime("%H:%M:%S")
        print(f"{Colors.CYAN}[{timestamp}]{Colors.END} {Colors.BOLD}{stage}:{Colors.END} {message}")

    # ═══════════════════════════════════════════════════════════════════════
    # STAGE 1: SUBDOMAIN ENUMERATION
    # ═══════════════════════════════════════════════════════════════════════
    def enumerate_subdomains(self) -> list:
        """
        Fase 1: Enumeración de subdominios usando subfinder + amass.
        """
        self._log_progress("STAGE 1", "Enumerating subdomains...")
        subdomains = set()

        # Subfinder - DNS enumeration
        self._log_progress("STAGE 1", "Running subfinder...")
        cmd = ['subfinder', '-d', self.target, '-silent', '-recursive']
        if self.fast:
            cmd.extend(['-threads', '10'])

        code, stdout, _ = self._run_command(cmd, timeout=300)
        if code == 0 and stdout:
            found = [line.strip() for line in stdout.splitlines() if line.strip()]
            subdomains.update(found)
            self.results['subdomains'].extend(found)

        # Guardar resultados
        output_file = self.target_dir / 'subdomains' / 'all.txt'
        with open(output_file, 'w') as f:
            f.write('\n'.join(sorted(subdomains)))

        self._log_progress("STAGE 1", f"Found {len(subdomains)} subdomains")
        return list(subdomains)

    # ═══════════════════════════════════════════════════════════════════════
    # STAGE 2: PORT SCANNING
    # ═══════════════════════════════════════════════════════════════════════
    def scan_ports(self, hosts: list = None) -> list:
        """
        Fase 2a: Escaneo de puertos con Nmap.
        - Quick scan: puertos comunes
        - Full scan: todos los puertos (solo si no es fast)
        """
        if hosts is None:
            hosts = [self.target]

        # Agregar subdominios si son IPs
        resolved_hosts = []
        for h in hosts:
            if h.replace('.', '').isdigit():
                resolved_hosts.append(h)

        all_hosts = list(set(resolved_hosts))
        if not all_hosts:
            all_hosts = [self.target]

        self._log_progress("STAGE 2", f"Scanning ports on {len(all_hosts)} hosts...")

        # Quick scan primero
        nmap_args = [
            'nmap',
            '-T4',  # Fast timing
            '--max-retries', '2',
            '--min-rate', '1000' if self.fast else '500',
            '-oG', str(self.target_dir / 'nmap' / 'quick.gnmap'),
            '-oX', str(self.target_dir / 'nmap' / 'quick.xml')
        ]

        # Puertos a escanear
        if self.fast:
            nmap_args.extend(['-p', '22,80,443,3000,3306,5432,6379,8080,8443'])
        else:
            nmap_args.extend(['-p-'])

        nmap_args.append(','.join(all_hosts))

        self._log_progress("STAGE 2", "Running Nmap quick scan...")
        code, stdout, stderr = self._run_command(nmap_args, timeout=600)

        # Parse resultados Nmap
        open_ports = self._parse_nmap_results()
        self.results['open_ports'] = open_ports

        self._log_progress("STAGE 2", f"Found {len(open_ports)} open ports")
        return open_ports

    def _parse_nmap_results(self) -> list:
        """Parsea resultados Nmap en formato Grepable."""
        ports = []
        gnmap_file = self.target_dir / 'nmap' / 'quick.gnmap'

        if not gnmap_file.exists():
            return ports

        with open(gnmap_file, 'r') as f:
            for line in f:
                if '/Open/' in line:
                    parts = line.split()
                    for part in parts:
                        if '/' in part and 'Open' in part:
                            port_proto = part.split('/')[0]
                            ports.append(port_proto)

        return ports

    # ═══════════════════════════════════════════════════════════════════════
    # STAGE 3: WEB DISCOVERY
    # ═══════════════════════════════════════════════════════════════════════
    def discover_web_services(self) -> list:
        """
        Fase 2b: Descubrimiento de servicios web con httpx.
        """
        self._log_progress("STAGE 3", "Discovering web services...")

        # Usar subdomains encontrados
        subs_file = self.target_dir / 'subdomains' / 'all.txt'
        if not subs_file.exists():
            # Usar target directo
            targets = [self.target]
        else:
            with open(subs_file, 'r') as f:
                targets = [line.strip() for line in f if line.strip()]

        if not targets:
            targets = [self.target]

        # httpx para detectar servicios web
        cmd = ['httpx', '-silent', '-threads', '50', '-timeout', '10']
        cmd.extend(targets)

        code, stdout, _ = self._run_command(cmd, timeout=300)
        web_services = []

        if code == 0 and stdout:
            web_services = [line.strip() for line in stdout.splitlines() if line.strip()]

        # Guardar
        output_file = self.target_dir / 'web' / 'web_services.txt'
        with open(output_file, 'w') as f:
            f.write('\n'.join(web_services))

        self._log_progress("STAGE 3", f"Found {len(web_services)} web services")
        return web_services

    # ═══════════════════════════════════════════════════════════════════════
    # STAGE 4: VULNERABILITY SCANNING
    # ═══════════════════════════════════════════════════════════════════════
    def scan_vulnerabilities(self, targets: list = None) -> list:
        """
        Fase 2c: Escaneo de vulnerabilidades con Nuclei.
        """
        self._log_progress("STAGE 4", "Running Nuclei vulnerability scan...")

        if targets is None:
            web_file = self.target_dir / 'web' / 'web_services.txt'
            if web_file.exists():
                with open(web_file, 'r') as f:
                    targets = [line.strip() for line in f if line.strip()]
            else:
                targets = [self.target]

        if not targets:
            targets = [f"http://{self.target}"]

        # Nuclei scan
        nuclei_args = [
            'nuclei',
            '-silent',
            '-json',
            '-o', str(self.target_dir / 'nuclei' / 'results.json'),
            '-etags', 'brainstorm,dos',  # Excluir noisy
            '-severity', 'critical,high,medium'
        ]

        if self.fast:
            nuclei_args.extend(['-c', '25', '-rate-limit', '100'])
        else:
            nuclei_args.extend(['-c', '50', '-rate-limit', '200'])

        nuclei_args.extend(targets)

        self._log_progress("STAGE 4", f"Scanning {len(targets)} targets...")
        code, stdout, stderr = self._run_command(nuclei_args, timeout=900)

        # Parse resultados
        findings = self._parse_nuclei_results()
        self.results['nuclei_findings'] = findings

        self._log_progress("STAGE 4", f"Found {len(findings)} vulnerabilities")
        return findings

    def _parse_nuclei_results(self) -> list:
        """Parsea resultados Nuclei en formato JSON."""
        findings = []
        nuclei_file = self.target_dir / 'nuclei' / 'results.json'

        if not nuclei_file.exists():
            return findings

        with open(nuclei_file, 'r') as f:
            for line in f:
                try:
                    finding = json.loads(line.strip())
                    findings.append({
                        'target': finding.get('matched-at', ''),
                        'template': finding.get('template-id', ''),
                        'severity': finding.get('info', {}).get('severity', ''),
                        'name': finding.get('info', {}).get('name', ''),
                        'description': finding.get('info', {}).get('description', '')[:200]
                    })
                except json.JSONDecodeError:
                    continue

        return findings

    # ═══════════════════════════════════════════════════════════════════════
    # STAGE 5: REPORT GENERATION
    # ═══════════════════════════════════════════════════════════════════════
    def generate_summary(self):
        """
        Genera archivo summary.md consolidado.
        """
        self._log_progress("STAGE 5", "Generating summary report...")

        critical = [f for f in self.results['nuclei_findings'] if f['severity'].upper() == 'CRITICAL']
        high = [f for f in self.results['nuclei_findings'] if f['severity'].upper() == 'HIGH']
        medium = [f for f in self.results['nuclei_findings'] if f['severity'].upper() == 'MEDIUM']

        report = f"""# 📊 Pentest Scan Summary - {self.target}

> **Fecha:** {self.results['timestamp']}
> **Target:** {self.target}

---

## Executive Summary

| Métrica | Valor |
|---------|-------|
| Subdominios encontrados | {len(self.results['subdomains'])} |
| Puertos abiertos | {len(self.results['open_ports'])} |
| Servicios web | {len(self.results.get('web_services', []))} |
| **Vulnerabilidades críticas** | **{len(critical)}** |
| **Vulnerabilidades altas** | **{len(high)}** |
| Vulnerabilidades medias | {len(medium)} |

---

## 🔴 Critical Findings

"""
        if critical:
            for i, f in enumerate(critical, 1):
                report += f"""
### {i}. {f['name']}

- **Target:** `{f['target']}`
- **Template:** `{f['template']}`
- **Description:** {f['description']}

"""
        else:
            report += "*No critical vulnerabilities found.*\n\n"

        report += """
---

## 🟠 High Findings

"""
        if high:
            for i, f in enumerate(high, 1):
                report += f"""
### {i}. {f['name']}

- **Target:** `{f['target']}`
- **Template:** `{f['template']}`

"""
        else:
            report += "*No high vulnerabilities found.*\n\n"

        report += f"""
---

## 📋 Open Ports

{chr(10).join(self.results['open_ports'][:50])}


---

## 🌐 Web Services

{chr(10).join(self.results.get('web_services', []))}


---

## 📂 Files Generated

- `subdomains/all.txt` - All discovered subdomains
- `nmap/quick.gnmap` - Nmap grepable results
- `nuclei/results.json` - Nuclei JSON results

---

*Generated by PentestAutomator*
"""

        # Guardar reporte
        output_file = self.target_dir / 'summary.md'
        with open(output_file, 'w') as f:
            f.write(report)

        print(f"\n{Colors.GREEN}[✓] Report saved to: {output_file}{Colors.END}")

        # Print summary to console
        print(f"\n{Colors.BOLD}{'='*50}{Colors.END}")
        print(f"{Colors.RED}CRITICAL:{Colors.END} {len(critical)}  {Colors.YELLOW}HIGH:{Colors.END} {len(high)}  {Colors.BLUE}MEDIUM:{Colors.END} {len(medium)}")
        print(f"{Colors.BOLD}{'='*50}{Colors.END}")

        return output_file

    # ═══════════════════════════════════════════════════════════════════════
    # MAIN EXECUTION
    # ═══════════════════════════════════════════════════════════════════════
    def run(self):
        """Ejecuta el pipeline completo."""
        print(f"\n{Colors.BOLD}{'='*60}{Colors.END}")
        print(f"{Colors.BOLD}  PENTEST AUTOMATOR - {self.target}{Colors.END}")
        print(f"{Colors.BOLD}{'='*60}{Colors.END}\n")

        start_time = time.time()

        try:
            # Stage 1: Subdomain enumeration
            subdomains = self.enumerate_subdomains()

            # Stage 2: Port scanning
            open_ports = self.scan_ports()

            # Stage 3: Web discovery
            web_services = self.discover_web_services()

            # Stage 4: Vulnerability scanning
            if web_services:
                vulnerabilities = self.scan_vulnerabilities(web_services)
            else:
                vulnerabilities = []

            # Stage 5: Generate report
            report = self.generate_summary()

            elapsed = time.time() - start_time
            print(f"\n{Colors.GREEN}[✓] Completed in {elapsed/60:.1f} minutes{Colors.END}")

        except KeyboardInterrupt:
            print(f"\n{Colors.YELLOW}[!] Interrupted by user{Colors.END}")
            sys.exit(1)
        except Exception as e:
            print(f"\n{Colors.RED}[!] Error: {e}{Colors.END}")
            sys.exit(1)


def main():
    parser = argparse.ArgumentParser(
        description='Pentest Automation Toolkit',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python automator.py --target example.com
  python automator.py --target 192.168.1.1 --fast
  python automator.py --target subdomains.txt --input-type file
        """
    )

    parser.add_argument('--target', '-t', required=True,
                        help='Target domain or IP address')
    parser.add_argument('--output', '-o', default='scans',
                        help='Output directory (default: scans)')
    parser.add_argument('--fast', '-f', action='store_true',
                        help='Fast mode: reduced scan time')
    parser.add_argument('--ports', '-p', type=int, default=0,
                        help='Number of top ports to scan (0 = all)')
    parser.add_argument('--input-type', choices=['domain', 'ip', 'file'],
                        default='domain', help='Input type')

    args = parser.parse_args()

    automator = PentestAutomator(
        target=args.target,
        output_dir=args.output,
        fast=args.fast
    )
    automator.run()


if __name__ == '__main__':
    main()

8.3 Dependencias (requirements.txt)

# Python dependencies
requests>=2.28.0
colorama>=0.4.6
tqdm>=4.65.0

# Install system tools
# apt-get install nmap

# Install Go tools
# go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
# go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# go install github.com/projectdiscovery/httpx/cmd/httpx@latest
# go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest

# Install Nuclei templates
# nuclei -ut

8.4 Ejemplo de Output (summary.md)

# 📊 Pentest Scan Summary - altrupets.com

> **Fecha:** 2026-03-15T14:30:00.000000
> **Target:** altrupets.com

---

## Executive Summary

| Métrica | Valor |
|---------|-------|
| Subdominios encontrados | 12 |
| Puertos abiertos | 8 |
| Servicios web | 6 |
| **Vulnerabilidades críticas** | **2** |
| **Vulnerabilidades altas** | **4** |
| Vulnerabilidades medias | 7 |

---

## 🔴 Critical Findings

### 1. Remote Code Execution via API

- **Target:** `https://api.altrupets.com/v1/exec`
- **Template:** `cve-2024-XXXX-rce`
- **Description:** The application is vulnerable to RCE through...

---

## 🟠 High Findings

### 1. SQL Injection in /users endpoint

- **Target:** `https://api.altrupets.com/users`

### 2. JWT None Algorithm

- **Target:** `https://auth.altrupets.com`

---

## 📋 Open Ports

22/tcp OpenSSH 8.9 80/tcp Apache 2.4.52 443/tcp nginx 1.24.0 3000/tcp Node.js 5432/tcp PostgreSQL 15 6379/tcp Redis 7.0 8080/tcp Apache 2.4.52 8443/tcp nginx 1.24.0


---

## 🌐 Web Services

https://altrupets.com https://www.altrupets.com https://api.altrupets.com https://auth.altrupets.com https://staging.altrupets.com https://admin.altrupets.com


---

*Generated by PentestAutomator*

8.5 Uso del Script

# Instalación de dependencias
apt-get update && apt-get install -y nmap golang-go
go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
nuclei -ut

# Ejecución
python3 automator.py --target altrupets.com
python3 automator.py --target 192.168.1.1 --fast
python3 automator.py --target altrupets.com --output /pentest/results

# Output generado
scans/
└── altrupets_com/
    ├── subdomains/
    │   └── all.txt
    ├── nmap/
    │   ├── quick.gnmap
    │   └── quick.xml
    ├── nuclei/
    │   └── results.json
    ├── web/
    │   └── web_services.txt
    └── summary.md

8.6 Optimizaciones de Rendimiento

Técnica Implementación
Paralelización Ejecutar subfinder + nmap en paralelo
Fast flags Nmap -T4 --min-rate 1000, Nuclei -c 25
Early exit Detener si se encuentra RCE crítico
Cache Reutilizar resultados de subfinder
Timeout 5 min subfinder, 10 min nmap, 15 min nuclei

⚠️ Nota: Este playbook debe ejecutarse solo con autorización explícita por escrito del propietario del sistema. Toda prueba debe estar dentro del scope definido.