Skip to content

Instantly share code, notes, and snippets.

@Hashbrown777
Last active April 17, 2024 09:14
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Hashbrown777/081e57ff9673a1f457e1c3a71b55cfaf to your computer and use it in GitHub Desktop.
Save Hashbrown777/081e57ff9673a1f457e1c3a71b55cfaf to your computer and use it in GitHub Desktop.
A set of functions that enable you to make use of multiple local SMB servers (eg ssh -L), or get explorer to interact with samba shares of other machines on non-standard ports
<#
#Required:
Install-Module -Name LoopbackAdapter -MinimumVersion 1.2.0.0
#run in admin terminal
#you do NOT need to disable/remove SMB 1.0/CIFS
#Troubleshooting:
#You can check [attempted] forwardings and [successful] listeners here, respectively
netsh interface portproxy show v4tov4
netstat -an | sls ':445'
#With the server running (or `ssh -L` tunnel to the SMB on another network listening)
#you can check whether both the unforwarded and forwarded ports are operating with
Test-NetConnection -ComputerName IP -Port PORT
#This might be needed if the above shows forwardings are present, server is accessible, yet the port isn't listening.
#I'm only including it because it was mentioned in a tutorial I came across;
#like the SMB1.0 note above, I did not need to do it at all for my script to work
#and it runs fine (on my Win10 19044) with a stock/opposing setup.
#
#lanmanserver (local SMB) may be stealing ports (all interfaces) before iphlpsvc (netsh portproxy) can
#So add iphlpsvc as a dependant, takes effect upon reboot
sc.exe `
config `
lanmanserver `
depend= `
((&{ Get-Service -Name lanmanserver -RequiredServices | %{ $_.Name }; 'iphlpsvc'}) -join '/')
#similarly apparently workstation might be problematic
sc.exe `
config `
lanmanworkstation `
depend= `
((&{ Get-Service -Name lanmanworkstation -RequiredServices | %{ $_.Name }; 'iphlpsvc'}) -join '/')
#To Undo
sc.exe `
config `
lanmanserver `
depend= `
((Get-Service -Name lanmanserver -RequiredServices | %{ $_.Name } | ?{ $_ -ne 'iphlpsvc' }) -join '/')
sc.exe `
config `
lanmanworkstation `
depend= `
((Get-Service -Name lanmanworkstation -RequiredServices | %{ $_.Name } | ?{ $_ -ne 'iphlpsvc' }) -join '/')
#>
Import-Module -Name LoopbackAdapter
#Creates a link between this new hostname's ip's SMB port and the given destination ip/port
#Effectively making it so that you can visit \\newhost
#this is achieved by
#-adding a loopback device with the given IP
#-using netsh portproxy to link this IP's 445 with the destination
#-calling Add-Host so you no longer need to rememeber this IP
# (I attempt to detect clashes, but be careful of subnet intrusion,
# and arbituary windows limitations like 127.x.x.x not being available)
#
#The server does not need to be available at time of creation, it will merely be inaccessible
Function Create-Host { Param(
[Parameter(Mandatory=$true)]$Name,
[Parameter(Mandatory=$true)]$Ip,
[Parameter(Mandatory=$true)]$Dest,
[Parameter(Mandatory=$true)]$Port,
[switch]$Force
)
if (
(Get-NetIPAddress -IPAddress $Ip -ErrorAction SilentlyContinue) -or
(Test-Connection $Ip -Quiet)
) {
throw "$Ip exists"
}
netsh `
interface portproxy `
add v4tov4 `
listenaddress=$Ip `
listenport=445 `
connectaddress=$Dest `
connectport=$Port
if (!$?) {
return
}
Add-Host `
-Name $Name `
-Ip $Ip `
-Comment 'Loopback machine for custom SMB' `
-Force:$Force
#the -PassThru of the cmdlets following this one return absolute junk so we cannot do it in one chain
$adapter = New-LoopbackAdapter -Name $Name -Force:$Force
$adapter `
| Disable-NetAdapterBinding `
-ComponentID ms_msclient,ms_pacer,ms_server,ms_lltdio,ms_rspndr
$adapter `
| Set-DnsClient `
-RegisterThisConnectionsAddress $False `
-PassThru `
| Set-NetIPInterface `
-InterfaceMetric '254' `
-WeakHostSend Enabled `
-WeakHostReceive Enabled `
-Dhcp Disabled
#this breaks hostname resolution, it also doesnt seem necessary as everything works
#(just keeping it here as it was present in a different tutorial I saw)
# -SkipAsSource $True
$adapter `
| New-NetIPAddress `
-IPAddress $Ip `
-PrefixLength 32 `
-AddressFamily IPv4 `
| Out-Null
'Reboot your machine'
}
#cleans up after Create-Host
#NB Create-Host is persistent across reboots, you only need to call it once
#this is for when you will no longer be using the host at all
Function Retire-Host { Param(
[Parameter(Mandatory=$true)]$Name,
[switch]$Force
)
netsh `
interface portproxy `
delete v4tov4 `
listenaddress=$(Reach-Host -Name $Name -Local) `
listenport=445
if (!$?) {
return
}
Remove-Host -Name $Name -Force:$Force
Remove-LoopbackAdapter -Name $Name -Force:$Force
}
#internal function for grabbing hosts from the hosts file
Function Local-Hosts { Param(
[Parameter(Mandatory=$true)]$Name,
[switch]$NotMatch
)
Select-String `
-Path "$([Environment]::SystemDirectory)\drivers\etc\hosts" `
-Pattern "^\s*([a-f0-9:.]+)\s+$([regex]::Escape($Name))(\s|$)" `
-NotMatch:$NotMatch
}
#detects whether a hostname resolves on your machine
#-Local forces use only of the hosts file, ignoring DNS
Function Reach-Host { Param(
[Parameter(Mandatory=$true)]$Name,
[switch]$Local
)
if ($Local) {
Local-Hosts -Name $Name `
| %{ $_.Matches.Groups[1].Value }
return
}
try {
Resolve-DnsName $Name -ErrorAction SilentlyContinue `
| %{ $_.IPAddress }
}
catch {
}
}
#Adds the hostname to your hosts file with the given IP, adding any comment alongside
#without -Force I make sure you're not blocking any reachable hostname
Function Add-Host { Param(
[Parameter(Mandatory=$true)]$Name,
[Parameter(Mandatory=$true)]$Ip,
[switch]$Force,
$Comment=''
)
$exists = Reach-Host -Name $Name
if ($exists) {
if (!$Force) {
throw $exists
}
if (Local-Hosts -Name $Name) {
Remove-Host -Name $Name -Force
}
}
if ($Comment) {
$Comment = '#' + $Comment
}
[System.IO.File]::AppendAllText(
"$([Environment]::SystemDirectory)\drivers\etc\hosts",
("`r`n" + $Ip,$Name,$Comment -join "`t"),
[System.Text.Encoding]::ASCII
)
}
#Removes the hostname from the hosts file
#-Force skips the check that the host is actually present there
Function Remove-Host { Param(
[Parameter(Mandatory=$true)]$Name,
[switch]$Force
)
if ($Force) {
}
elseif (Local-Hosts -Name $Name) {
}
else {
throw "$Name not present"
}
[System.IO.File]::WriteAllLines(
"$([Environment]::SystemDirectory)\drivers\etc\hosts",
(Local-Hosts -Name $Name -NotMatch | %{ $_.Line }),
[System.Text.Encoding]::ASCII
)
}
#if you had created some hosts with ports like so
#Create-Host -Name 'frodo' -Ip '10.254.0.1' -Dest '127.0.0.1' -Port 21112
#Create-Host -Name 'bilbo' -Ip '10.254.0.2' -Dest '127.0.0.1' -Port 21212
#and there are some other services like RDP and SOCKS you want
#you can run this to attach an array of disks
# @{
# H = '\\frodo\USER'
# G = '\\bilbo\shared$'
# I = '\\frodo\dbapps$'
# J = '\\frodo\joint$'
# P = '\\bilbo\public$'
# }.GetEnumerator() `
# | ConnectSMB `
# -server middle.earth `
# -domain shire `
# -account USER `
# -socks 21012 `
# -forwards @{
# 21112 = 'frodo:445'
# 21212 = 'bilbo:445'
# 21312 = 'eyeofsauron:3389'
# }
#see the hardcoded.ps1 for a discription of what the function does (that was actually written first, before I generalised it here)
Function ConnectSMB { Param(
[Parameter(Mandatory=$true)][string]$server,
[string]$user,
[int]$socks,
[Parameter(Mandatory=$true)][System.Collections.Hashtable]$forwards,
[string]$domain,
[string]$account,
[Parameter(ValueFromPipeline=$true)][System.Collections.DictionaryEntry[]]$Input
)
Begin {
$success = @($socks)
$ssh = Start-Process `
-NoNewWindow `
-PassThru `
ssh `
(
$forwards.GetEnumerator() `
| &{
Begin {
if ($user) {
$server = "$user@$server"
}
$args = [System.Collections.ArrayList]@($server)
if ($socks) {
$args.AddRange(('-D',$socks))
}
}
Process {
$args.AddRange(('-L', "$($_.Key):$($_.Value)"))
if (!$success[0]) {
$success[0] = ($_.Value -split ':')[-1]
}
}
End {
$args.AddRange((
'-o', 'ExitOnForwardFailure=yes',
'-q',
'read; kill $PPID'
))
$args
}
}
)
if (!$success[0]) {
throw 'Add some forwardings'
}
$tmp = $Global:ProgressPreference
$Global:ProgressPreference = 'SilentlyContinue'
try {
while (!(Test-NetConnection `
-InformationLevel Quiet `
-ComputerName localhost `
-Port $success[0] `
-WarningAction SilentlyContinue
)) {
if ($ssh.HasExited) {
throw 'SSH failed to connect!'
}
}
$ErrorActionPreference = 'Stop'
if (!$account) {
$account = $user
}
if ($domain) {
$account = "$domain\$account"
}
$credential = Get-Credential $account
}
catch {
if (!$ssh.HasExited) {
$ssh.Kill()
}
throw $_
}
finally {
$Global:ProgressPreference = $tmp
}
}
Process {
try {
New-PSDrive `
-PSPRovider FileSystem `
-Name $_.Key `
-Root $_.Value `
-Credential $credential `
-Persist
}
catch {
if (!$ssh.HasExited) {
$ssh.Kill()
}
throw $_
}
}
End {
'Drives connected! Press enter when done.'
$ssh | Wait-Process
}
}
#runs an ssh instance in the background, whilst still asking for ssh authentication if needed
#attaching SMB hosts on the other side to ports previously expected via Create-Host in attachsmb.ps1
#as well as SOCKS and an RDP server
#when connected it will prompt for smb credentials and use these to hook up a number of network drives
#when done you simply hit enter in the terminal and the drives all detach and the ssh session is closed
#
#this is a hardcoded version of the example given in the generalised sshsmb script
#as you can see it's much simpler and more appropriate to use something like this if only ever using one configuration
#especially if passing around this script in an organisation (where users would really only be changing their username)
Function ConnectSMB { Param([Parameter(Mandatory=$true)]$user)
$ssh = Start-Process -NoNewWindow -PassThru `
ssh ("$user@middle.earth",
'-D', '21012',
'-L', '21112:frodo:445',
'-L', '21212:bilbo:445',
'-L', '21312:eyeofsauron:3389',
'-o', 'ExitOnForwardFailure=yes',
'-q',
'read; kill $PPID')
$tmp = $Global:ProgressPreference
$Global:ProgressPreference = 'SilentlyContinue'
try {
while (!(Test-NetConnection `
-InformationLevel Quiet `
-ComputerName localhost `
-Port 21012 `
-WarningAction SilentlyContinue
)) {
if ($ssh.HasExited) {
throw 'SSH failed to connect!'
}
}
}
finally {
$Global:ProgressPreference = $tmp
}
try {
$ErrorActionPreference = 'Stop'
$cred = Get-Credential "shire\$user"
New-PSDrive -Name H -PSPRovider FileSystem -Root "\\frodo\$user" -Credential $cred -Persist
New-PSDrive -Name G -PSPRovider FileSystem -Root '\\bilbo\shared$' -Credential $cred -Persist
New-PSDrive -Name I -PSPRovider FileSystem -Root '\\frodo\dbapps$' -Credential $cred -Persist
New-PSDrive -Name J -PSPRovider FileSystem -Root '\\frodo\joint$' -Credential $cred -Persist
New-PSDrive -Name P -PSPRovider FileSystem -Root '\\bilbo\public$' -Credential $cred -Persist
'Drives connected! Press enter when done.'
$ssh | Wait-Process
}
finally {
if (!$ssh.HasExited) {
$ssh.Kill()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment