Add enhanced urlf4ck3r
This commit is contained in:
parent
dc9e81f06e
commit
21c7e1e6c6
7
catch-all/08_urlf4ck3r/.dockerignore
Normal file
7
catch-all/08_urlf4ck3r/.dockerignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
README.md
|
||||||
|
docker-compose.yaml
|
||||||
|
docker-compose.yml
|
||||||
|
Dockerfile
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.vscode
|
24
catch-all/08_urlf4ck3r/Dockerfile
Normal file
24
catch-all/08_urlf4ck3r/Dockerfile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Usa una imagen base de Python oficial
|
||||||
|
FROM python:3.9-slim
|
||||||
|
ARG maintaner="manuelver"
|
||||||
|
|
||||||
|
# Establece el directorio de trabajo dentro del contenedor
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copia los archivos necesarios al contenedor
|
||||||
|
COPY requirements.txt /app
|
||||||
|
|
||||||
|
# Instala las dependencias del script
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copia el script al contenedor
|
||||||
|
COPY urlf4ck3r.py /app
|
||||||
|
|
||||||
|
# Da permisos de ejecución al script
|
||||||
|
RUN chmod +x urlf4ck3r.py
|
||||||
|
|
||||||
|
# Define el comando predeterminado para ejecutar el script con argumentos
|
||||||
|
ENTRYPOINT ["./urlf4ck3r.py"]
|
||||||
|
|
||||||
|
# Especifica el comando por defecto, que puede ser sobreescrito al correr el contenedor
|
||||||
|
CMD ["-h"]
|
116
catch-all/08_urlf4ck3r/README.md
Normal file
116
catch-all/08_urlf4ck3r/README.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# 🕸️ URLf4ck3r 🕵️♂️
|
||||||
|
|
||||||
|
> Repositorio original: [URLf4ck3r](https://github.com/n0m3l4c000nt35/urlf4ck3r)
|
||||||
|
|
||||||
|
URLf4ck3r es una herramienta de reconocimiento diseñada para escanear y extraer URLs del código fuente de sitios web.
|
||||||
|
|
||||||
|
📝 **Tabla de contenidos**
|
||||||
|
- [🕸️ URLf4ck3r 🕵️♂️](#️-urlf4ck3r-️️)
|
||||||
|
- [🚀 Características principales](#-características-principales)
|
||||||
|
- [📋 Requisitos](#-requisitos)
|
||||||
|
- [🛠️ Instalación](#️-instalación)
|
||||||
|
- [💻 Uso](#-uso)
|
||||||
|
- [Con docker](#con-docker)
|
||||||
|
- [Construir la imagen de Docker](#construir-la-imagen-de-docker)
|
||||||
|
- [Ejecutar el contenedor](#ejecutar-el-contenedor)
|
||||||
|
- [Ejecutar con Docker Compose:](#ejecutar-con-docker-compose)
|
||||||
|
|
||||||
|
|
||||||
|
## 🚀 Características principales
|
||||||
|
|
||||||
|
- 🔍 Escaneo recursivo de URLs
|
||||||
|
- 🌐 Detección de subdominios
|
||||||
|
- ✍️ Detección de palabras sensibles en los comentarios
|
||||||
|
- 🔗 Clasificación de URLs absolutas y relativas
|
||||||
|
- 💠 Detección de archivos JavaScript
|
||||||
|
- 🎨 Salida colorida para una fácil lectura
|
||||||
|
- ⏱️ Interrumpible en cualquier momento
|
||||||
|
|
||||||
|
|
||||||
|
## 📋 Requisitos
|
||||||
|
|
||||||
|
- Python 3.x
|
||||||
|
- Bibliotecas: `requests`, `beautifulsoup4`, `pwntools` (Ver en el fichero [requirements.txt](requirements.txt))
|
||||||
|
|
||||||
|
|
||||||
|
## 🛠️ Instalación
|
||||||
|
|
||||||
|
1. Descarga esta carpeta
|
||||||
|
2. Instalá las dependencias:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Haz el script ejecutable:
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod +x urlf4ck3r.py
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Para ejecutar el script desde cualquier ubicación:
|
||||||
|
|
||||||
|
- Mueve el script a un directorio que esté en el PATH, por ejemplo:
|
||||||
|
```
|
||||||
|
sudo mv urlf4ck3r.py /usr/bin/urlf4ck3r
|
||||||
|
```
|
||||||
|
- O añade el directorio del script al PATH editando el archivo `.bashrc` o `.zshrc`:
|
||||||
|
```
|
||||||
|
echo 'export PATH=$PATH:/ruta/al/directorio/de/urlf4ck3r' >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 💻 Uso
|
||||||
|
|
||||||
|
Si seguiste el paso 4 de la instalación, puedes ejecutar el script desde cualquier ubicación simplemente con:
|
||||||
|
|
||||||
|
```
|
||||||
|
urlf4ck3r -u <URL> -o output.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
De lo contrario, desde el directorio donde se encuentra ubicado el script:
|
||||||
|
|
||||||
|
```
|
||||||
|
./urlf4ck3r.py -u <URL> -o output
|
||||||
|
```
|
||||||
|
|
||||||
|
Ejemplo:
|
||||||
|
|
||||||
|
```
|
||||||
|
urlf4ck3r -u https://ejemplo.com -o output.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Con docker
|
||||||
|
|
||||||
|
### Construir la imagen de Docker
|
||||||
|
|
||||||
|
Para construir la imagen desde el Dockerfile, navega al directorio donde se encuentra tu Dockerfile y ejecuta el siguiente comando:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker build -t urlf4ck3r .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ejecutar el contenedor
|
||||||
|
|
||||||
|
Después de construir la imagen, puedes ejecutar tu script dentro de un contenedor de la siguiente manera:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run --rm urlf4ck3r -u https://ejemplo.com -o output.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
El flag --rm asegura que el contenedor se elimina automáticamente después de que se complete su ejecución.
|
||||||
|
|
||||||
|
|
||||||
|
### Ejecutar con Docker Compose:
|
||||||
|
|
||||||
|
En la línea de comandos, navega hasta el directorio donde guardaste estos archivos.
|
||||||
|
|
||||||
|
Ejecuta el siguiente comando para construir la imagen y ejecutar el contenedor usando Docker Compose:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker-compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Esto ejecutará urlf4ck3r y generará el archivo output.txt en el directorio ./output de tu máquina local.
|
||||||
|
|
9
catch-all/08_urlf4ck3r/docker-compose.yaml
Normal file
9
catch-all/08_urlf4ck3r/docker-compose.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
urlf4ck3r:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
command: ["-u", "https://vergaracarmona.es", "-o", "output.txt"]
|
||||||
|
volumes:
|
||||||
|
- ./output:/app/output
|
||||||
|
container_name: urlf4ck3r
|
3
catch-all/08_urlf4ck3r/requirements.txt
Normal file
3
catch-all/08_urlf4ck3r/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
beautifulsoup4==4.12.3
|
||||||
|
pwntools==4.13.0
|
||||||
|
Requests==2.32.3
|
444
catch-all/08_urlf4ck3r/urlf4ck3r.py
Normal file
444
catch-all/08_urlf4ck3r/urlf4ck3r.py
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup, Comment
|
||||||
|
from collections import defaultdict
|
||||||
|
from urllib.parse import urljoin, urlparse
|
||||||
|
from typing import Optional, Tuple, Dict, List, Set
|
||||||
|
|
||||||
|
|
||||||
|
class URLf4ck3r:
|
||||||
|
"""
|
||||||
|
URLf4ck3r es una herramienta que extrae las URL's del código fuente de una
|
||||||
|
página web. Además, puede extraer comentarios sensibles del código fuente
|
||||||
|
y guardar las URL's en archivos de texto.
|
||||||
|
"""
|
||||||
|
|
||||||
|
RED = "\033[91m"
|
||||||
|
GREEN = "\033[92m"
|
||||||
|
YELLOW = "\033[93m"
|
||||||
|
BLUE = "\033[94m"
|
||||||
|
GRAY = "\033[90m"
|
||||||
|
PURPLE = "\033[95m"
|
||||||
|
END_COLOR = "\033[0m"
|
||||||
|
|
||||||
|
SENSITIVE_KEYWORDS = [
|
||||||
|
# Palabras clave originales
|
||||||
|
"password", "user", "username", "clave", "secret", "key", "token",
|
||||||
|
"private", "admin", "credential", "login", "auth", "api_key", "apikey",
|
||||||
|
"administrator",
|
||||||
|
|
||||||
|
# # Criptografía y Seguridad
|
||||||
|
# "encryption", "decrypt", "cipher", "security", "hash", "salt", "ssl",
|
||||||
|
# "tls", "secure", "firewall", "integrity",
|
||||||
|
|
||||||
|
# # Gestión de Usuarios y Autenticación
|
||||||
|
# "auth_token", "session_id", "access_token", "oauth", "id_token",
|
||||||
|
# "refresh_token", "csrf", "sso", "two_factor", "2fa",
|
||||||
|
|
||||||
|
# # Información Personal Identificable (PII)
|
||||||
|
# "social_security", "ssn", "address", "phone_number", "email", "dob",
|
||||||
|
# "credit_card", "card_number", "ccv", "passport", "tax_id", "personal_info",
|
||||||
|
|
||||||
|
# # Configuración de Sistemas
|
||||||
|
# "config", "database", "db_password", "db_user", "connection_string",
|
||||||
|
# "server", "host", "port",
|
||||||
|
|
||||||
|
# # Archivos y Rutas
|
||||||
|
# "filepath", "filename", "root_path", "home_dir", "backup", "logfile",
|
||||||
|
|
||||||
|
# # Llaves y Tokens de API
|
||||||
|
# "aws_secret", "aws_key", "api_secret", "secret_key", "private_key",
|
||||||
|
# "public_key", "ssh_key",
|
||||||
|
|
||||||
|
# # Finanzas y Pagos
|
||||||
|
# "payment", "transaction", "account_number", "iban", "bic", "swift",
|
||||||
|
# "bank", "routing_number", "billing", "invoice",
|
||||||
|
|
||||||
|
# # Cuentas y Roles de Administrador
|
||||||
|
# "superuser", "root", "sudo", "admin_password", "admin_user",
|
||||||
|
|
||||||
|
# # Otros
|
||||||
|
# "jwt", "cookie", "session", "bypass", "debug", "exploit"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Inicializa las variables de instancia.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.all_urls: Dict[str, Set[str]] = defaultdict(set)
|
||||||
|
self.comments_data: Dict[str, List[str]] = defaultdict(list)
|
||||||
|
self.base_url: Optional[str] = None
|
||||||
|
self.urls_to_scan: List[str] = []
|
||||||
|
self.flag = self.Killer()
|
||||||
|
self.output: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
def banner(self) -> None:
|
||||||
|
"""
|
||||||
|
Muestra el banner de la herramienta.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("""
|
||||||
|
|
||||||
|
█ ██ ██▀███ ██▓ █████▒▄████▄ ██ ▄█▀ ██▀███
|
||||||
|
██ ▓██▒▓██ ▒ ██▒▓██▒ ▓██ ▒▒██▀ ▀█ ██▄█▒ ▓██ ▒ ██▒
|
||||||
|
▓██ ▒██░▓██ ░▄█ ▒▒██░ ▒████ ░▒▓█ ▄ ▓███▄░ ▓██ ░▄█ ▒
|
||||||
|
▓▓█ ░██░▒██▀▀█▄ ▒██░ ░▓█▒ ░▒▓▓▄ ▄██▒▓██ █▄ ▒██▀▀█▄
|
||||||
|
▒▒█████▓ ░██▓ ▒██▒░██████▒░▒█░ ▒ ▓███▀ ░▒██▒ █▄░██▓ ▒██▒
|
||||||
|
░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░ ▒░▓ ░ ▒ ░ ░ ░▒ ▒ ░▒ ▒▒ ▓▒░ ▒▓ ░▒▓░
|
||||||
|
░░▒░ ░ ░ ░▒ ░ ▒░░ ░ ▒ ░ ░ ░ ▒ ░ ░▒ ▒░ ░▒ ░ ▒░
|
||||||
|
░░░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ░ ░░ ░ ░░ ░
|
||||||
|
░ ░ ░ ░ ░ ░ ░ ░ ░
|
||||||
|
░
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
"""
|
||||||
|
Ejecuta la herramienta.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.banner()
|
||||||
|
|
||||||
|
args, parser = self.get_arguments()
|
||||||
|
|
||||||
|
if not args.url:
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.output:
|
||||||
|
self.output = args.output
|
||||||
|
|
||||||
|
self.base_url = args.url
|
||||||
|
self.all_urls["scanned_urls"] = set()
|
||||||
|
self.urls_to_scan = [self.base_url]
|
||||||
|
|
||||||
|
_, domain, _ = self.parse_url(self.base_url)
|
||||||
|
|
||||||
|
print(f"\n[{self.GREEN}DOMAIN{self.END_COLOR}] {domain}\n")
|
||||||
|
|
||||||
|
while self.urls_to_scan and not self.flag.exit():
|
||||||
|
url = self.urls_to_scan.pop(0)
|
||||||
|
self.scan_url(url)
|
||||||
|
|
||||||
|
print()
|
||||||
|
self.show_lists()
|
||||||
|
self.save_files()
|
||||||
|
|
||||||
|
print(f"\n[{self.GREEN}URLS TO SCAN{self.END_COLOR}]:")
|
||||||
|
|
||||||
|
if self.flag.exit():
|
||||||
|
print(
|
||||||
|
f"[{self.RED}!{self.END_COLOR}] Quedaron por escanear {self.RED}{len(self.urls_to_scan)}{self.END_COLOR} URLs"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif not self.urls_to_scan:
|
||||||
|
print(
|
||||||
|
f"[{self.GREEN}+{self.END_COLOR}] Se escanearon todas las URLs posibles"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"[{self.RED}!{self.END_COLOR}] Quedaron por escanear {self.RED}{len(self.urls_to_scan)}{self.END_COLOR} URLs"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_arguments(self) -> Tuple[argparse.Namespace, argparse.ArgumentParser]:
|
||||||
|
"""
|
||||||
|
Obtiene los argumentos proporcionados por el usuario.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="urlf4ck3r",
|
||||||
|
description="Extraer las URL's del código fuente de una web",
|
||||||
|
epilog="Creado por https://github.com/n0m3l4c000nt35 y modificado por gitea.vergaracarmona.es/manuelver"
|
||||||
|
)
|
||||||
|
parser.add_argument("-u", "--url", type=str, dest="url",
|
||||||
|
help="URL a escanear", required=True)
|
||||||
|
parser.add_argument("-o", "--output", type=str,
|
||||||
|
dest="output", help="Nombre del archivo de salida")
|
||||||
|
|
||||||
|
return parser.parse_args(), parser
|
||||||
|
|
||||||
|
def scan_url(self, url: str) -> None:
|
||||||
|
"""
|
||||||
|
Escanea una URL en busca de URLs, comentarios sensibles y archivos JS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.flag.exit():
|
||||||
|
return
|
||||||
|
|
||||||
|
if url in self.all_urls["scanned_urls"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.all_urls["scanned_urls"].add(url)
|
||||||
|
print(f"[{self.GREEN}SCANNING{self.END_COLOR}] {url}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = requests.get(url, timeout=5)
|
||||||
|
soup = BeautifulSoup(res.content, 'html.parser')
|
||||||
|
|
||||||
|
self.extract_js_files(soup, url)
|
||||||
|
self.extract_comments(soup, url)
|
||||||
|
self.extract_hrefs(soup, url, res)
|
||||||
|
|
||||||
|
except requests.Timeout:
|
||||||
|
print(f"[{self.RED}REQUEST TIMEOUT{self.END_COLOR}] {url}")
|
||||||
|
self.all_urls['request_error'].add(url)
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
print(f"{self.RED}[REQUEST ERROR]{self.END_COLOR} {url}")
|
||||||
|
self.all_urls['request_error'].add(url)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"[{self.RED}UNEXPECTED ERROR{self.END_COLOR}] {url}: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_hrefs(self, soup: BeautifulSoup, url: str, res: requests.Response) -> None:
|
||||||
|
"""
|
||||||
|
Extrae las URL's del código fuente de una página web.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for link in soup.find_all("a", href=True):
|
||||||
|
href = link.get("href")
|
||||||
|
scheme, domain, path = self.parse_url(href)
|
||||||
|
schemes = ["http", "https"]
|
||||||
|
|
||||||
|
if href:
|
||||||
|
full_url = urljoin(url, path) if not scheme else href
|
||||||
|
|
||||||
|
if full_url not in self.all_urls["all_urls"]:
|
||||||
|
self.all_urls["all_urls"].add(full_url)
|
||||||
|
|
||||||
|
if not scheme:
|
||||||
|
self.all_urls["relative_urls"].add(full_url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.all_urls["absolute_urls"].add(full_url)
|
||||||
|
|
||||||
|
if self.is_jsfile(url, res):
|
||||||
|
self.all_urls["javascript_files"].add(url)
|
||||||
|
|
||||||
|
if (self.is_internal_url(self.base_url, full_url) or
|
||||||
|
self.is_subdomain(self.base_url, full_url)):
|
||||||
|
|
||||||
|
if full_url not in self.all_urls["scanned_urls"] and full_url not in self.urls_to_scan:
|
||||||
|
self.urls_to_scan.append(full_url)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_js_files(self, soup: BeautifulSoup, base_url: str) -> None:
|
||||||
|
"""
|
||||||
|
Extrae los archivos JS del código fuente de una página web.
|
||||||
|
"""
|
||||||
|
|
||||||
|
js_files = set()
|
||||||
|
|
||||||
|
for script in soup.find_all('script', src=True):
|
||||||
|
|
||||||
|
js_url = script['src']
|
||||||
|
|
||||||
|
if not urlparse(js_url).netloc:
|
||||||
|
|
||||||
|
js_url = urljoin(base_url, js_url)
|
||||||
|
|
||||||
|
js_files.add(js_url)
|
||||||
|
|
||||||
|
self.all_urls["javascript_files"].update(js_files)
|
||||||
|
|
||||||
|
|
||||||
|
def is_jsfile(self, url: str, res: requests.Response) -> bool:
|
||||||
|
"""
|
||||||
|
Verifica si un archivo es un archivo JS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return url.lower().endswith(('.js', '.mjs')) or 'javascript' in res.headers.get('Content-Type', '').lower()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_subdomain(self, url: str) -> str:
|
||||||
|
"""
|
||||||
|
Extrae el subdominio de una URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
netloc = urlparse(url).netloc.split(".")
|
||||||
|
|
||||||
|
return ".".join(netloc[1:] if netloc[0] == "www" else netloc)
|
||||||
|
|
||||||
|
|
||||||
|
def is_subdomain(self, base_url: str, url: str) -> bool:
|
||||||
|
"""
|
||||||
|
Verifica si una URL es un subdominio del dominio base.
|
||||||
|
"""
|
||||||
|
|
||||||
|
base_domain = self.extract_subdomain(base_url)
|
||||||
|
sub = self.extract_subdomain(url)
|
||||||
|
|
||||||
|
return sub.endswith(base_domain) and sub != base_domain
|
||||||
|
|
||||||
|
|
||||||
|
def is_internal_url(self, base_url: str, url: str) -> bool:
|
||||||
|
"""
|
||||||
|
Verifica si una URL es interna (pertenece al mismo dominio).
|
||||||
|
"""
|
||||||
|
|
||||||
|
return urlparse(base_url).netloc == urlparse(url).netloc
|
||||||
|
|
||||||
|
|
||||||
|
def extract_comments(self, soup: BeautifulSoup, url: str) -> None:
|
||||||
|
"""
|
||||||
|
Extrae los comentarios del código fuente de una página web.
|
||||||
|
"""
|
||||||
|
|
||||||
|
comments = soup.find_all(string=lambda text: isinstance(text, Comment))
|
||||||
|
|
||||||
|
for comment in comments:
|
||||||
|
|
||||||
|
comment_str = comment.strip()
|
||||||
|
|
||||||
|
if any(keyword in comment_str.lower() for keyword in self.SENSITIVE_KEYWORDS):
|
||||||
|
self.comments_data[url].append(comment_str)
|
||||||
|
print(
|
||||||
|
f"{self.YELLOW}[SENSITIVE COMMENT FOUND]{self.END_COLOR} {comment_str}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_url(self, url: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
||||||
|
"""
|
||||||
|
Parsea una URL y devuelve el esquema, dominio y path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parsed_url = urlparse(url)
|
||||||
|
|
||||||
|
return parsed_url.scheme, parsed_url.netloc, parsed_url.path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_directory_exists(self, directory: str) -> None:
|
||||||
|
"""
|
||||||
|
Asegura que el directorio existe, y lo crea si no es así.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
|
||||||
|
def save_file(self, data: List[str], filename: str) -> None:
|
||||||
|
"""
|
||||||
|
Guarda los datos en un archivo.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Asegurarse de que el directorio 'output' existe
|
||||||
|
self.ensure_directory_exists("output")
|
||||||
|
|
||||||
|
if self.output:
|
||||||
|
filename = f"{self.output}_{filename}"
|
||||||
|
|
||||||
|
filepath = os.path.join("output", filename)
|
||||||
|
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
f.write("\n".join(data))
|
||||||
|
|
||||||
|
print(f"[{self.GREEN}+{self.END_COLOR}] Guardado en {filepath}")
|
||||||
|
|
||||||
|
except IOError as e:
|
||||||
|
print(
|
||||||
|
f"{self.RED}[FILE WRITE ERROR]{self.END_COLOR} No se pudo guardar el archivo {filename}: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def save_files(self) -> None:
|
||||||
|
"""
|
||||||
|
Guarda las URLs y los comentarios extraídos en archivos.
|
||||||
|
"""
|
||||||
|
self.save_file(
|
||||||
|
sorted(self.all_urls["all_urls"]),
|
||||||
|
"all_urls.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.save_file(
|
||||||
|
sorted(self.all_urls["absolute_urls"]),
|
||||||
|
"absolute_urls.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.save_file(
|
||||||
|
sorted(self.all_urls["relative_urls"]),
|
||||||
|
"relative_urls.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.save_file(
|
||||||
|
sorted(self.all_urls["javascript_files"]),
|
||||||
|
"javascript_files.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.comments_data:
|
||||||
|
sensitive_comments = []
|
||||||
|
|
||||||
|
for url, comments in self.comments_data.items():
|
||||||
|
sensitive_comments.append(f"\n[ {url} ]\n")
|
||||||
|
sensitive_comments.extend(comments)
|
||||||
|
|
||||||
|
self.save_file(sensitive_comments, "sensitive_comments.txt")
|
||||||
|
|
||||||
|
|
||||||
|
def show_lists(self) -> None:
|
||||||
|
"""
|
||||||
|
Muestra el resumen de las URLs extraídas.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"\n[{self.GREEN}ALL URLS{self.END_COLOR}]: {len(self.all_urls['all_urls'])}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{self.GREEN}ABSOLUTE URLS{self.END_COLOR}]: {len(self.all_urls['absolute_urls'])}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{self.GREEN}RELATIVE URLS{self.END_COLOR}]: {len(self.all_urls['relative_urls'])}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{self.GREEN}JAVASCRIPT FILES{self.END_COLOR}]: {len(self.all_urls['javascript_files'])}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"[{self.GREEN}SENSITIVE COMMENTS{self.END_COLOR}]: {len(self.comments_data)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Killer:
|
||||||
|
"""
|
||||||
|
Clase utilizada para manejar la interrupción del script con Ctrl+C.
|
||||||
|
"""
|
||||||
|
|
||||||
|
kill_now = False
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, self.exit_gracefully)
|
||||||
|
|
||||||
|
|
||||||
|
def exit_gracefully(self, signum, frame) -> None:
|
||||||
|
"""
|
||||||
|
Método llamado cuando se recibe la señal de interrupción.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.kill_now = True
|
||||||
|
|
||||||
|
def exit(self) -> bool:
|
||||||
|
"""
|
||||||
|
Retorna True si el script debe terminar.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.kill_now
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
tool = URLf4ck3r()
|
||||||
|
tool.run()
|
@ -7,7 +7,7 @@ Aquí iré dejando scripts y ejercicios que se me ocurran, con lo que no hay un
|
|||||||
## Índice de ejercicios
|
## Índice de ejercicios
|
||||||
|
|
||||||
| Nombre | Descripción | Nivel |
|
| Nombre | Descripción | Nivel |
|
||||||
| -------------------------------------------------------: | :------------------------------------------------------- | :--------: |
|
| -------------------------------------------------------: | :--------------------------------------------------------------- | :--------: |
|
||||||
| [Words Linux](./01_scripts_words_linux/README.md) | Script con el fichero: `/usr/share/dict/words` | fácil |
|
| [Words Linux](./01_scripts_words_linux/README.md) | Script con el fichero: `/usr/share/dict/words` | fácil |
|
||||||
| [Descifrador wargame](./02_scripts_descifrador_wargame/) | Script descifrador de código al estilo wargame | fácil |
|
| [Descifrador wargame](./02_scripts_descifrador_wargame/) | Script descifrador de código al estilo wargame | fácil |
|
||||||
| [Clima España](./03_clima/) | Script conectado a API AEMET | intermedio |
|
| [Clima España](./03_clima/) | Script conectado a API AEMET | intermedio |
|
||||||
@ -15,3 +15,4 @@ Aquí iré dejando scripts y ejercicios que se me ocurran, con lo que no hay un
|
|||||||
| [Pruebas de infraestructuras](./05_infra_test/README.md) | Redis, RabbitMQ, Kafka, Prometheus, etc | intermedio |
|
| [Pruebas de infraestructuras](./05_infra_test/README.md) | Redis, RabbitMQ, Kafka, Prometheus, etc | intermedio |
|
||||||
| [Bots Telegram](./06_bots_telegram/README.md) | Bots de Telegram con Python | avanzado |
|
| [Bots Telegram](./06_bots_telegram/README.md) | Bots de Telegram con Python | avanzado |
|
||||||
| [Diagram as code](./07_diagrams_as_code/README.md) | Diagramas de infraestructuras con Python | fácil |
|
| [Diagram as code](./07_diagrams_as_code/README.md) | Diagramas de infraestructuras con Python | fácil |
|
||||||
|
| [urlf4ck3r](./08_urlf4ck3r/README.md) | Script para buscar enlaces en una web y guardarlos en un fichero | intermedio |
|
||||||
|
Loading…
Reference in New Issue
Block a user