|
|
@@ -9,231 +9,212 @@ import itertools
|
|
|
import os
|
|
|
import ssl
|
|
|
|
|
|
-# --- CONFIGURACIÓN BASE ---
|
|
|
+# ==============================================================================
|
|
|
+# CONFIGURACIÓN DEL SISTEMA
|
|
|
+# ==============================================================================
|
|
|
LISTENING_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 443
|
|
|
SSH_HOST = '127.0.0.1'
|
|
|
-SSH_PORT = 22 # Asegúrate de que este es tu puerto SSH/Dropbear
|
|
|
+SSH_PORT = 22 # Cambiar a 223 si usas Dropbear modificado
|
|
|
LOG_FILE = "/root/proxy.log"
|
|
|
-MAX_LOG_SIZE = 10 * 1024 * 1024
|
|
|
+MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB antes de rotar
|
|
|
|
|
|
-# --- CONFIGURACIÓN SSL/TLS ---
|
|
|
-USE_SSL = True
|
|
|
+# RUTAS DE CERTIFICADOS SSL (Deben existir en el servidor)
|
|
|
CERT_FILE = "/root/cert.pem"
|
|
|
KEY_FILE = "/root/key.pem"
|
|
|
|
|
|
-# --- CONFIGURACIÓN DE SEGURIDAD AVANZADA ---
|
|
|
-MAX_CONNECTIONS = 150 # Ligeramente aumentado
|
|
|
-CONNECTION_COOLDOWN = 0.5
|
|
|
+# ==============================================================================
|
|
|
+# PARÁMETROS DE RESILIENCIA Y SEGURIDAD
|
|
|
+# ==============================================================================
|
|
|
+MAX_CONNECTIONS = 200
|
|
|
+CONNECTION_COOLDOWN = 0.7
|
|
|
BUFLEN = 16384
|
|
|
+TIMEOUT_HTTP = 3.0
|
|
|
+
|
|
|
+# SISTEMA DE AUTO-BANEO TEMPORAL
|
|
|
AUTO_BAN_STRIKES = 3
|
|
|
-BAN_TIME = 3600
|
|
|
-banned_ips_memory = {}
|
|
|
+BAN_TIME = 3600 # 1 hora de baneo
|
|
|
+banned_ips = {}
|
|
|
ip_strikes = {}
|
|
|
-ALLOWED_IPS = []
|
|
|
|
|
|
-# --- RESPUESTA FAKE WEB (ANTI ACTIVE PROBING) ---
|
|
|
-FAKE_WEB_RESPONSE = (
|
|
|
+# RESPUESTA WEB FALSA (Para engañar a escáneres como Shodan)
|
|
|
+FAKE_WEB = (
|
|
|
b"HTTP/1.1 200 OK\r\n"
|
|
|
- b"Server: nginx/1.21.0\r\n"
|
|
|
+ b"Server: nginx/1.24.0\r\n"
|
|
|
b"Content-Type: text/html; charset=UTF-8\r\n"
|
|
|
b"Connection: close\r\n\r\n"
|
|
|
- b"<!DOCTYPE html>\n<html>\n<head><title>Bienvenido</title></head>\n"
|
|
|
- b"<body style='text-align:center; padding:50px; font-family:sans-serif;'>\n"
|
|
|
- b"<h1>Hola</h1>\n<p>Servicio en funcionamiento.</p>\n"
|
|
|
- b"</body>\n</html>\n"
|
|
|
+ b"<!DOCTYPE html><html><head><title>Welcome</title></head>"
|
|
|
+ b"<body style='font-family:sans-serif;text-align:center;padding:50px;'>"
|
|
|
+ b"<h1>404 Not Found</h1><p>The requested resource was not found.</p>"
|
|
|
+ b"</body></html>"
|
|
|
)
|
|
|
|
|
|
-# --- CUSTOM HEADERS PARA VPN ---
|
|
|
-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-v7-HA",
|
|
|
- "X-Forwarded-For-Proxy": "True"
|
|
|
-}
|
|
|
-
|
|
|
+# MENSAJES PARA LA INYECCIÓN HTTP (NetMod)
|
|
|
MENSAJES = [
|
|
|
"🚀 CONEXION TLS ESTABLECIDA",
|
|
|
"🛡️ CIFRADO MILITAR ACTIVO",
|
|
|
"🔋 MODO SIGILO SSL OK",
|
|
|
+ "Pfsense",
|
|
|
+ "OPNsense",
|
|
|
+ "VyOS",
|
|
|
+ "Claro",
|
|
|
+ "Windows Server",
|
|
|
+ "BSD Free",
|
|
|
+ "VyOS",
|
|
|
+ "Altice",
|
|
|
+ "Viva",
|
|
|
+ "Google",
|
|
|
+ "VyOS",
|
|
|
+ "TNSR",
|
|
|
"🌐 BYPASS DE FIREWALL OK"
|
|
|
]
|
|
|
mensaje_cycle = itertools.cycle(MENSAJES)
|
|
|
cycle_lock = threading.Lock()
|
|
|
|
|
|
-def log(msg, addr=None):
|
|
|
+# ==============================================================================
|
|
|
+# FUNCIONES DE SOPORTE
|
|
|
+# ==============================================================================
|
|
|
+def write_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)
|
|
|
+ with open(LOG_FILE, 'w') as f: f.write("--- LOG REINICIADO ---\n")
|
|
|
+
|
|
|
+ ts = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
+ ip_info = f" [{addr[0]}]" if addr else ""
|
|
|
+ log_line = f"[{ts}]{ip_info} {msg}\n"
|
|
|
+
|
|
|
+ with open(LOG_FILE, 'a') as f: f.write(log_line)
|
|
|
+ print(log_line.strip())
|
|
|
except: pass
|
|
|
|
|
|
-active_connections = 0
|
|
|
-conn_lock = threading.Lock()
|
|
|
+active_conn_count = 0
|
|
|
+conn_count_lock = threading.Lock()
|
|
|
|
|
|
-class ConnectionHandler(threading.Thread):
|
|
|
+class ProxyHandler(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()])
|
|
|
- return (f"HTTP/1.1 101 {status_msg}\r\n{headers_str}Connection: Upgrade\r\nUpgrade: websocket\r\n\r\n").encode('utf-8')
|
|
|
|
|
|
def run(self):
|
|
|
- global active_connections
|
|
|
+ global active_conn_count
|
|
|
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]
|
|
|
- else: return
|
|
|
-
|
|
|
- now = time.time()
|
|
|
- if client_ip in ip_strikes and (now - ip_strikes.get('last_time', 0)) < 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 por Flood/Escaneo: {client_ip}", self.addr)
|
|
|
- return
|
|
|
-
|
|
|
- ip_strikes['last_time'] = now
|
|
|
- ip_strikes[client_ip] = 0
|
|
|
|
|
|
- self.client.settimeout(2.0)
|
|
|
- payload = b""
|
|
|
- try:
|
|
|
- payload = self.client.recv(BUFLEN)
|
|
|
- except socket.timeout:
|
|
|
- pass # NetMod en silencio
|
|
|
- except Exception:
|
|
|
- return
|
|
|
+ try:
|
|
|
+ # 1. Verificar Baneo
|
|
|
+ if client_ip in banned_ips:
|
|
|
+ if time.time() < banned_ips[client_ip]: return
|
|
|
+ else: del banned_ips[client_ip]
|
|
|
|
|
|
+ # 2. Leer Petición inicial
|
|
|
+ self.client.settimeout(TIMEOUT_HTTP)
|
|
|
try:
|
|
|
- self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=10)
|
|
|
- except Exception as e:
|
|
|
- log(f"❌ Error interno destino SSH: {e}", self.addr)
|
|
|
- return
|
|
|
-
|
|
|
- if payload:
|
|
|
- if payload.startswith(b"SSH-"):
|
|
|
- self.target.sendall(payload)
|
|
|
- elif b"HTTP/" in payload and b"Upgrade: websocket" not in payload:
|
|
|
- log(f"🕵️ Escáner detectado. Respondiendo Fake Web.", self.addr)
|
|
|
- self.client.sendall(FAKE_WEB_RESPONSE)
|
|
|
- return
|
|
|
+ data = self.client.recv(BUFLEN)
|
|
|
+ except: data = b""
|
|
|
+
|
|
|
+ # 3. Lógica de Detección de Protocolo
|
|
|
+ if data:
|
|
|
+ # Si es un navegador/escáner buscando web
|
|
|
+ if b"HTTP/" in data and b"Upgrade: websocket" not in data:
|
|
|
+ write_log("🕵️ Escáner detectado. Enviando Fake Web.", self.addr)
|
|
|
+ self.client.sendall(FAKE_WEB)
|
|
|
+ return
|
|
|
+
|
|
|
+ # Si es tráfico SSH directo (sin inyector)
|
|
|
+ if data.startswith(b"SSH-"):
|
|
|
+ self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=5)
|
|
|
+ self.target.sendall(data)
|
|
|
else:
|
|
|
- with cycle_lock: current_status = next(mensaje_cycle)
|
|
|
- self.client.sendall(self.build_http_response(current_status))
|
|
|
-
|
|
|
- self.tunnel()
|
|
|
+ # Es un Inyector (NetMod/NekoBox)
|
|
|
+ with cycle_lock: current_msg = next(mensaje_cycle)
|
|
|
+ resp = f"HTTP/1.1 101 {current_msg}\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n"
|
|
|
+ self.client.sendall(resp.encode('utf-8'))
|
|
|
+ self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=5)
|
|
|
+ else:
|
|
|
+ # Conexión vacía (posible probe silencioso)
|
|
|
+ self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=5)
|
|
|
+
|
|
|
+ # 4. Iniciar Túnel
|
|
|
+ self.bridge()
|
|
|
|
|
|
- except Exception as e: pass
|
|
|
+ except Exception as e:
|
|
|
+ pass
|
|
|
finally:
|
|
|
- with conn_lock: active_connections -= 1
|
|
|
+ with conn_count_lock: active_conn_count -= 1
|
|
|
self.cleanup()
|
|
|
|
|
|
- def tunnel(self):
|
|
|
+ def bridge(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:
|
|
|
+ r, _, e = select.select(sockets, [], sockets, 300)
|
|
|
+ if e or not r: break
|
|
|
+ for s in r:
|
|
|
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)
|
|
|
+ (self.target if s is self.client else self.client).sendall(data)
|
|
|
except: return
|
|
|
|
|
|
def cleanup(self):
|
|
|
- total_mb = (self.tx_bytes + self.rx_bytes) / (1024 * 1024)
|
|
|
- if total_mb > 0.05: log(f"[*] Tráfico finalizado: {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
|
|
|
- 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)
|
|
|
- except Exception as e:
|
|
|
- print(f"Error crítico cargando certificados SSL: {e}")
|
|
|
- sys.exit(1)
|
|
|
+ global active_conn_count
|
|
|
+
|
|
|
+ # Configuración de Contexto SSL Flexible (Compatible con NetMod)
|
|
|
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
|
+ try:
|
|
|
+ context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
|
|
|
+ except Exception as e:
|
|
|
+ print(f"Error cargando certificados: {e}")
|
|
|
+ sys.exit(1)
|
|
|
|
|
|
+ # Creación del Socket Servidor (Dual Stack IPv4/IPv6)
|
|
|
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 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
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(500) # Aumentado el backlog para soportar ráfagas de bots
|
|
|
+ server.bind(('', LISTENING_PORT))
|
|
|
+ server.listen(500)
|
|
|
|
|
|
print(f"=====================================================")
|
|
|
- print(f"🔥 Servidor Robusto INMORTAL Iniciado - Puerto {LISTENING_PORT}")
|
|
|
- print(f"🛡️ Motor SSL/TLS & Anti-Crash: ACTIVADO")
|
|
|
+ print(f"🚀 PROXY V7 RESILIENTE INICIADO - PUERTO {LISTENING_PORT}")
|
|
|
+ print(f"🛡️ MOTOR ANTI-CRASH Y FAKE WEB ACTIVADOS")
|
|
|
print(f"=====================================================")
|
|
|
|
|
|
- # EL BUCLE PRINCIPAL AHORA ES BLINDADO
|
|
|
while True:
|
|
|
try:
|
|
|
- client, addr = server.accept()
|
|
|
+ raw_client, addr = server.accept()
|
|
|
|
|
|
- if USE_SSL:
|
|
|
- try:
|
|
|
- client = ssl_context.wrap_socket(client, server_side=True)
|
|
|
- except Exception:
|
|
|
- # Si el bot manda basura en lugar de un handshake SSL, se cierra y se ignora silenciosamente.
|
|
|
- client.close()
|
|
|
- continue
|
|
|
+ # 5. Envolver conexión en SSL
|
|
|
+ try:
|
|
|
+ client = context.wrap_socket(raw_client, server_side=True)
|
|
|
+ except:
|
|
|
+ raw_client.close()
|
|
|
+ continue
|
|
|
|
|
|
- with conn_lock:
|
|
|
- if active_connections >= MAX_CONNECTIONS:
|
|
|
+ with conn_count_lock:
|
|
|
+ if active_conn_count >= MAX_CONNECTIONS:
|
|
|
client.close()
|
|
|
continue
|
|
|
- active_connections += 1
|
|
|
-
|
|
|
- ConnectionHandler(client, addr).start()
|
|
|
+ active_conn_count += 1
|
|
|
|
|
|
- except socket.error as e:
|
|
|
- # Si Linux se queda sin recursos por 1 segundo por un ataque DDoS,
|
|
|
- # esperamos 50ms y volvemos a intentarlo en lugar de apagar el script.
|
|
|
- time.sleep(0.05)
|
|
|
+ ProxyHandler(client, addr).start()
|
|
|
+
|
|
|
+ except socket.error:
|
|
|
+ time.sleep(0.05) # Pausa ante saturación de sockets
|
|
|
continue
|
|
|
- except Exception as e:
|
|
|
- time.sleep(1)
|
|
|
+ except Exception:
|
|
|
+ time.sleep(0.5)
|
|
|
continue
|
|
|
-
|
|
|
- except Exception as e:
|
|
|
- print(f"Error fatal: {e}")
|
|
|
- finally:
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ print(f"Error crítico: {e}")
|
|
|
+ finally:
|
|
|
server.close()
|
|
|
|
|
|
if __name__ == "__main__":
|