|
|
@@ -0,0 +1,233 @@
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
+
|
|
|
+import socket
|
|
|
+import threading
|
|
|
+import select
|
|
|
+import sys
|
|
|
+import time
|
|
|
+import itertools
|
|
|
+import os
|
|
|
+import ssl # 🛡️ NUEVO: Motor criptográfico nativo
|
|
|
+
|
|
|
+# --- CONFIGURACIÓN BASE ---
|
|
|
+LISTENING_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 443 # Cambiado al 443 (Puerto Oficial HTTPS)
|
|
|
+SSH_HOST = '127.0.0.1'
|
|
|
+SSH_PORT = 22
|
|
|
+LOG_FILE = "/root/proxy.log"
|
|
|
+MAX_LOG_SIZE = 10 * 1024 * 1024
|
|
|
+
|
|
|
+# --- CONFIGURACIÓN SSL/TLS (STUNNEL EMBEBIDO) ---
|
|
|
+USE_SSL = True # Cambia a False si quieres volver al modo texto plano
|
|
|
+CERT_FILE = "/root/cert.pem"
|
|
|
+KEY_FILE = "/root/key.pem"
|
|
|
+
|
|
|
+# --- CONFIGURACIÓN DE SEGURIDAD AVANZADA ---
|
|
|
+MAX_CONNECTIONS = 100
|
|
|
+CONNECTION_COOLDOWN = 0.7
|
|
|
+TIMEOUT = 60
|
|
|
+BUFLEN = 16384
|
|
|
+
|
|
|
+# --- FILTRADO DE ACCESO Y AUTO-BANEO ---
|
|
|
+ALLOWED_IPS = []
|
|
|
+BLOCKED_HOSTS = ['ads.doubleclick.net', 'telemetry.microsoft.com']
|
|
|
+AUTO_BAN_STRIKES = 3
|
|
|
+BAN_TIME = 3600
|
|
|
+banned_ips_memory = {}
|
|
|
+ip_strikes = {}
|
|
|
+
|
|
|
+# --- CUSTOM HEADERS ---
|
|
|
+CUSTOM_HEADERS = {
|
|
|
+ "Server": "nginx/1.21.0",
|
|
|
+ "X-Forwarded-For": "127.0.0.1",
|
|
|
+ "Content-Type": "text/html; charset=UTF-8",
|
|
|
+ "Proxy-Connection": "keep-alive",
|
|
|
+ "Cache-Control": "no-cache",
|
|
|
+ "X-Proxy-Agent": "Gemini-Ultra-Robust-v4-TLS",
|
|
|
+ "X-Forwarded-For-Proxy": "True"
|
|
|
+}
|
|
|
+
|
|
|
+# --- MENSAJES ROTATIVOS ---
|
|
|
+MENSAJES = [
|
|
|
+ "🚀 CONEXION TLS ESTABLECIDA",
|
|
|
+ "🛡️ CIFRADO MILITAR ACTIVO",
|
|
|
+ "🔋 MODO SIGILO SSL OK",
|
|
|
+ "🌐 BYPASS DE FIREWALL OK"
|
|
|
+]
|
|
|
+mensaje_cycle = itertools.cycle(MENSAJES)
|
|
|
+cycle_lock = threading.Lock()
|
|
|
+
|
|
|
+def log(msg, addr=None):
|
|
|
+ try:
|
|
|
+ if os.path.exists(LOG_FILE) and os.path.getsize(LOG_FILE) > MAX_LOG_SIZE:
|
|
|
+ with open(LOG_FILE, 'w') as f:
|
|
|
+ f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] LOG REINICIADO\n")
|
|
|
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
+ client_info = f" [{addr[0]}]" if addr else ""
|
|
|
+ log_entry = f"[{timestamp}]{client_info} {msg}\n"
|
|
|
+ with open(LOG_FILE, 'a') as f: f.write(log_entry)
|
|
|
+ print(log_entry.strip())
|
|
|
+ except: pass
|
|
|
+
|
|
|
+active_connections = 0
|
|
|
+conn_lock = threading.Lock()
|
|
|
+ip_cooldowns = {}
|
|
|
+
|
|
|
+class ConnectionHandler(threading.Thread):
|
|
|
+ def __init__(self, client_socket, addr):
|
|
|
+ super().__init__(daemon=True)
|
|
|
+ self.client = client_socket
|
|
|
+ self.addr = addr
|
|
|
+ self.target = None
|
|
|
+ self.tx_bytes = 0
|
|
|
+ self.rx_bytes = 0
|
|
|
+
|
|
|
+ def build_http_response(self, status_msg):
|
|
|
+ headers_str = "".join([f"{k}: {v}\r\n" for k, v in CUSTOM_HEADERS.items()])
|
|
|
+ response = (
|
|
|
+ f"HTTP/1.1 101 {status_msg}\r\n"
|
|
|
+ f"{headers_str}"
|
|
|
+ f"Connection: Upgrade\r\n"
|
|
|
+ f"Upgrade: websocket\r\n\r\n"
|
|
|
+ )
|
|
|
+ return response.encode('utf-8')
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ global active_connections
|
|
|
+ client_ip = self.addr[0]
|
|
|
+
|
|
|
+ try:
|
|
|
+ if client_ip in banned_ips_memory:
|
|
|
+ if time.time() > banned_ips_memory[client_ip]:
|
|
|
+ del banned_ips_memory[client_ip]
|
|
|
+ if client_ip in ip_strikes: del ip_strikes[client_ip]
|
|
|
+ log(f"🔓 Baneo expirado. Acceso restaurado.", self.addr)
|
|
|
+ else: return
|
|
|
+
|
|
|
+ if ALLOWED_IPS and client_ip not in ALLOWED_IPS: return
|
|
|
+
|
|
|
+ now = time.time()
|
|
|
+ if client_ip in ip_cooldowns and (now - ip_cooldowns[client_ip]) < CONNECTION_COOLDOWN:
|
|
|
+ ip_strikes[client_ip] = ip_strikes.get(client_ip, 0) + 1
|
|
|
+ if ip_strikes[client_ip] >= AUTO_BAN_STRIKES:
|
|
|
+ banned_ips_memory[client_ip] = time.time() + BAN_TIME
|
|
|
+ log(f"⛔ IP Baneada (Flood/Spam)", self.addr)
|
|
|
+ return
|
|
|
+
|
|
|
+ ip_cooldowns[client_ip] = now
|
|
|
+ ip_strikes[client_ip] = 0
|
|
|
+
|
|
|
+ self.client.settimeout(5)
|
|
|
+ try:
|
|
|
+ payload = self.client.recv(BUFLEN)
|
|
|
+ if not payload: return
|
|
|
+ except: return
|
|
|
+
|
|
|
+ log(f"Intentando conectar a {SSH_HOST}:{SSH_PORT}...", self.addr)
|
|
|
+ self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=10)
|
|
|
+
|
|
|
+ with cycle_lock: current_status = next(mensaje_cycle)
|
|
|
+
|
|
|
+ self.client.sendall(self.build_http_response(current_status))
|
|
|
+ log(f"✅ Túnel cifrado activo: {current_status}", self.addr)
|
|
|
+
|
|
|
+ self.tunnel()
|
|
|
+
|
|
|
+ except Exception as e: log(f"❌ Error: {e}", self.addr)
|
|
|
+ finally:
|
|
|
+ with conn_lock: active_connections -= 1
|
|
|
+ self.cleanup()
|
|
|
+
|
|
|
+ def tunnel(self):
|
|
|
+ self.client.settimeout(None)
|
|
|
+ self.target.settimeout(None)
|
|
|
+ sockets = [self.client, self.target]
|
|
|
+ while True:
|
|
|
+ readable, _, error = select.select(sockets, [], sockets, 300)
|
|
|
+ if error or not readable: break
|
|
|
+ for s in readable:
|
|
|
+ try:
|
|
|
+ data = s.recv(BUFLEN)
|
|
|
+ if not data: return
|
|
|
+ if s is self.client:
|
|
|
+ self.target.sendall(data)
|
|
|
+ self.tx_bytes += len(data)
|
|
|
+ else:
|
|
|
+ self.client.sendall(data)
|
|
|
+ self.rx_bytes += len(data)
|
|
|
+ except: return
|
|
|
+
|
|
|
+ def cleanup(self):
|
|
|
+ total_mb = (self.tx_bytes + self.rx_bytes) / (1024 * 1024)
|
|
|
+ if total_mb > 0.01: log(f"[*] Conexión finalizada. Tráfico consumido: {total_mb:.2f} MB", self.addr)
|
|
|
+ for s in [self.client, self.target]:
|
|
|
+ if s:
|
|
|
+ try: s.close()
|
|
|
+ except: pass
|
|
|
+
|
|
|
+def main():
|
|
|
+ global active_connections
|
|
|
+
|
|
|
+ # 🛡️ PREPARAR EL MOTOR SSL/TLS
|
|
|
+ ssl_context = None
|
|
|
+ if USE_SSL:
|
|
|
+ try:
|
|
|
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
|
+ ssl_context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
|
|
|
+ log(f"🔒 Motor SSL/TLS Inicializado correctamente.")
|
|
|
+ except Exception as e:
|
|
|
+ log(f"❌ Error crítico cargando certificados SSL: {e}")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ try:
|
|
|
+ addr_info = socket.getaddrinfo(None, LISTENING_PORT, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
|
|
|
+ addr_info.sort(key=lambda x: x[0] == socket.AF_INET6, reverse=True)
|
|
|
+ af, socktype, proto, canonname, sa = addr_info[0]
|
|
|
+
|
|
|
+ server = socket.socket(af, socktype, proto)
|
|
|
+ server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
+ if af == socket.AF_INET6:
|
|
|
+ try: server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
|
+ except: pass
|
|
|
+
|
|
|
+ server.bind(sa)
|
|
|
+ server.listen(200)
|
|
|
+
|
|
|
+ log(f"=====================================================")
|
|
|
+ estado_ssl = "ACTIVADO (Invisible a DPI)" if USE_SSL else "DESACTIVADO (Texto Plano)"
|
|
|
+ log(f"🔥 Servidor Robusto Iniciado en Puerto {LISTENING_PORT}")
|
|
|
+ log(f"🛡️ Modo Sigilo SSL/TLS: {estado_ssl}")
|
|
|
+ log(f"🎯 Destino Interno: {SSH_HOST}:{SSH_PORT}")
|
|
|
+ log(f"=====================================================")
|
|
|
+
|
|
|
+ while True:
|
|
|
+ client, addr = server.accept()
|
|
|
+
|
|
|
+ # 🛡️ ENVOLVER LA CONEXIÓN EN TLS AL INSTANTE
|
|
|
+ if USE_SSL:
|
|
|
+ try:
|
|
|
+ # server_side=True le dice al script que actúe como un servidor HTTPS
|
|
|
+ client = ssl_context.wrap_socket(client, server_side=True)
|
|
|
+ except ssl.SSLError as e:
|
|
|
+ # Si alguien intenta conectarse sin encriptar (escáneres), falla en silencio
|
|
|
+ log(f"⚠️ Intento de conexión no encriptada rechazado: {addr[0]}")
|
|
|
+ client.close()
|
|
|
+ continue
|
|
|
+ except Exception as e:
|
|
|
+ client.close()
|
|
|
+ continue
|
|
|
+
|
|
|
+ with conn_lock:
|
|
|
+ if active_connections >= MAX_CONNECTIONS:
|
|
|
+ client.close()
|
|
|
+ continue
|
|
|
+ active_connections += 1
|
|
|
+
|
|
|
+ ConnectionHandler(client, addr).start()
|
|
|
+
|
|
|
+ except Exception as e: log(f"❌ Error crítico en servidor: {e}")
|
|
|
+ finally: server.close()
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|
|
|
+
|
|
|
+
|