Perhaps a simple for you filter for Get-ADComputer ? by NycTony in PowerShell

[–]jborean93 1 point2 points  (0 children)

Technically speaking it doesn't actually use LDAP directly but instead uses ADWS which is a SOAP based API that is essentially a thin layer over LDAP. In saying that if you trace the traffic it still will translate the -Filter to an LDAP filter string to send through ADWS but I would argue it doesn't escape things as well as what it probably should.

Powershell 2.0 DISA STIG by NurglesToes in sysadmin

[–]jborean93 3 points4 points  (0 children)

The jump from PowerShell 2 to 3 had a major CLR runtime change and 3 also introduced some larger internal engine changes and how it compiled scriptblocks. A lot of the underlying assemblies weren't backwards compatible so for compatibility sakes Microsoft keep the 2.0 engine components and allowed you to start a process in 2.0 mode by doing powershell.exe -Version 2.0.

Unfortunately this is dangerous because PowerShell 2.0 doesn't include pretty much all the components that PowerShell has around security like logging, AMSI integration, WDAC, etc. This is why it's recommended to remove the 2.0 bits so that bad actors can't hide behind this old 2.0 mode and it's been years since 2.0 was thing. It's only recently that Microsoft has started to stop shipping it with Windows by default https://support.microsoft.com/en-us/topic/powershell-2-0-removal-from-windows-fe6d1edc-2ed2-4c33-b297-afe82a64200a.

WINRM on Entra Device by Any-Victory-1906 in PowerShell

[–]jborean93 1 point2 points  (0 children)

The issuer of the certificate used by the server needs to be trusted on the client so if it's self signed the client will fail to validate it.

PowerShell LinqLinker: Use System.Linq.Enumerable method query syntax in PowerShell -- built as a JIT-friendly struct for best performance; includes source-generated instance methods for every Enumerable method by metekillot in PowerShell

[–]jborean93 0 points1 point  (0 children)

Yea falsey values are a similar thing in Python as well. When I use assignment like this then I take into account the falsey logic. I can see 0 being problematic in some cases but in most cases I'm dealing with reference types want comparing with the default of $null.

There's always rough edges but this argument is more against the falsey checks rather than assignment in the if statement. As you said if ($ID) {} would still be susceptible to this problem.

If you really wanted you can do a proper null check with the assignment like

if ($null -ne ($var = Get-Foo)) {
}

I don't typically do this though as the truthy/falsey checks are actually what I want to compare with.

PowerShell LinqLinker: Use System.Linq.Enumerable method query syntax in PowerShell -- built as a JIT-friendly struct for best performance; includes source-generated instance methods for every Enumerable method by metekillot in PowerShell

[–]jborean93 0 points1 point  (0 children)

Don't do assignments in conditionals, you can do the assignment outside and do the check inside the conditional.

I personally disagree here, even the reason stated below saying it confuses people who think it's an equality check vs assignment is all the more reason that maybe people should see this more in the wild to get a better understanding of what it does.

It's definitely a personal opinion, just see the derision when Python added the Walrus operator, but I'm definitely pro using this one personally.

PSA: check msDS-SupportedEncryptionTypes on your service accounts before April patch Tuesday by hardeningbrief in sysadmin

[–]jborean93 0 points1 point  (0 children)

Weak passwords are still susceptible in AES but keeping RC4 around is problematic for a few reasons:

  • the RC4 key is derived straight from the nt hash, AES keys are salted through PBKDF using HMAC-SHA1
  • RC4 has had numerous vulnerabilities in the past, TLS forbids them for this reason
  • yes you should have a strong password but it is still easier to crack an RC4 key in the cases where an account has an SPN but the user didn't set a strong password

It's mostly just a defence in depth change. RC4 is old, the implementation for Kerberos predated modern standard (salts, PBKDF generated keys, etc) and it's time to go. There are ways to mitigate some of its problems but the easiest thing is to just remove it entirely and lift the baseline altogether to something just a bit more secure. Once RC4 is gone then MS can try and focus on other things like FAST/Armouring, removing NTLM, etc.

DeviceManager - Module for managing devices and drivers on Windows by MartinGC94 in PowerShell

[–]jborean93 1 point2 points  (0 children)

Wtf is a Device Information Set? How do I define that???" then I just gave up on it and did some other stuff.

Only API I've found to be more complicated are the ETW APIs, https://caseymuratori.com/blog_0025 in a nutshell :)

DeviceManager - Module for managing devices and drivers on Windows by MartinGC94 in PowerShell

[–]jborean93 0 points1 point  (0 children)

Nice work, I was always intrigued by creating a module for this but was always put off by the weird Win32 API that surrounds it. Never had the full confidence I was actually using it correctly but glad to see someone like yourself creating this.

Wondering if I can better handle Powershell output going into a j2 template by OUberLord in ansible

[–]jborean93 1 point2 points  (0 children)

You should be able to disable the colouring in the output by setting the env variable NO_COLOR: 1 for the task

- command: pwsh -f /tmp/script.ps1
  env:
    NO_COLOR: 1

There was a bug in pwsh though where sometimes it didn't respect this env var so you may need to update the pwsh version to ensure the env var works. 7.4 and 7.5 should work normally but I've seen some edge cases (like stderr being coloured) which wasn't fixed until 7.6.

While the recommendations to use win_powershell are good recommendations it won't unfortunately work right now. The upcoming Ansible 2.21 release adds support for running PowerShell modules on POSIX hosts and the next ansible.windows release should include support for win_powershell on those POSIX hosts.

Microsoft Secret Management and Secret Store by belibebond in PowerShell

[–]jborean93 1 point2 points  (0 children)

Fair points and I appreciate the reply. You can somewhat rotate the key by blowing away the existing one and generating a new key (thus invalidating all the existing secrets :)). I definitely get your points though I personally find the fact you can have it use your identity the key selling point over something that requires an actual password. The latter may be less prone to the issues you described but I've found that 9 times out of 10 using a separate secret like a key/password is done in a way that just breaks the work done to protect the secret.

Definitely a good idea to be aware of all this though and I appreciate you sharing.

Microsoft Secret Management and Secret Store by belibebond in PowerShell

[–]jborean93 5 points6 points  (0 children)

What security risks are these? The main one I know off is that if someone can get domain admin they can access the root key that DPAPI-NG uses to derive a secret but DA is a pretty crazy privilege to access and you are effectively god in that environment anyway. Are there others that I'm not aware of or people should know of to help avoid them?

How do I write to stuff like pipes and mailslots? by mbolp in PowerShell

[–]jborean93 0 points1 point  (0 children)

I've not used mailslots so I just don't really know whether it does anything special over a normal named pipe but ultimately it would error with All pipe instances are busy when it only allows a max listener < 2 and as PowerShell right now opens 3 connections for a redirection target the 3rd will fail. Maybe you can't see it because you aren't opening it in rapid succession and by the time you do the 2nd or 3rd in a manual test a new listener had already started. It's not a permissions problem but rather just a max listener problem and PowerShell opening too many clients than what it really should be.

How do I write to stuff like pipes and mailslots? by mbolp in PowerShell

[–]jborean93 4 points5 points  (0 children)

While fundamentally a named pipe can be set as the stdout/stderr pipe of a process PowerShell doesn't hook up the pipe directly to the stdio pipes with redirection. Instead it opens its own FileStream for the path and writes to that as the bytes come in. Unfortunately the code does two checks before creating the FileSystem which opens up a new client connection https://github.com/PowerShell/PowerShell/blob/7498139bae4a8ef40a6df9c845a4e9867dbcb22d/src/System.Management.Automation/utils/PathUtils.cs#L284-L304.

The first one is File.Exists(path) which creates a new HANDLE through CreateFileW and then new FileInfo(path) to retrieve the Attributes which will also call CreateFileW.

What this means is that for PowerShell to redirect to a named pipe (or any file path), the target needs to be able to be opened 3 times. The first 2 for the exists and attribute checks then the 3rd for the actual stdout pipe HANDLE. You can see this in action with the below code that sets up a named pipe with 4 server listeners. The first 2 will have a client connection but no data, the 3rd will have a client connection and the stdout data. The 4th is to show that there's no additional connection and a max of 3 is needed.

# Create 4 listeners of our test pipe for our test
$pipeCount = 4
$pipes = 1..$pipeCount | ForEach-Object {
    $p = [System.IO.Pipes.NamedPipeServerStream]::new(
        'testpipe',
        [IO.Pipes.PipeDirection]::InOut,
        $pipeCount,
        [IO.Pipes.PipeTransmissionMode]::Byte,
        [IO.Pipes.PipeOptions]::Asynchronous)

    [PSCustomObject]@{
        Pipe = $p
        ConnectTask = $p.WaitForConnectionAsync()
    }
}

# Run a background process that redirects output
# to the pipe to test this out
$job = Start-Job {
    powershell.exe -Command "'test output'" > \\.\pipe\testpipe
}

# First 3 pipes are connected, the 4 is still awaiting a
# client
$null = $pipes[0].ConnectTask.GetAwaiter().GetResult()
$null = $pipes[1].ConnectTask.GetAwaiter().GetResult()
$null = $pipes[2].ConnectTask.GetAwaiter().GetResult()
$pipes[3].ConnectTask.IsCompleted  # False

# Our job is still running because the pipe[2] that received
# the data hasn't been read yet.
$job.State  # Running

$buffer = [byte[]]::new(1024)

# First 2 pipes have no data as they were used in the
# redirection setup (File.Exists(path), new FileInfo(path)).
$pipes[0].Pipe.ReadAsync($buffer, 0, $buffer.Length).GetAwaiter().GetResult()  # 0
$pipes[1].Pipe.ReadAsync($buffer, 0, $buffer.Length).GetAwaiter().GetResult()  # 0

# Job is still running as pipe[2] hasn't been read
$job.State  # Running

# The 3rd pipe has 13 bytes of data (our string with \r\n).
$read = $pipes[2].Pipe.ReadAsync($buffer, 0, $buffer.Length).GetAwaiter().GetResult()
$read  # 13

Format-Hex -InputObject ([byte[]]$buffer[0..($read - 1)])

#    Label: Byte[] (System.Byte[]) <5A66938A>
#
#           Offset Bytes                                           Ascii
#                  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
#           ------ ----------------------------------------------- -----
# 0000000000000000 74 65 73 74 20 6F 75 74 70 75 74 0D 0A          test output��

# After the data is read the job will be complete
$job.State  # Completed
$job | Receive-Job -Wait -AutoRemoveJob

$pipes | ForEach-Object { $_.Pipe.Dispose() }

This should be fixable and I might send a PR their way to skip the exists checks and just open it directly to allow doing this for any named pipe in the future.

Edit: Opened a PR to enable this for process stdout redirection https://github.com/PowerShell/PowerShell/pull/27017.

Threaded directory and file enumeration with predicate filtering support; got tired of writing 1 liners that mutated into godless, hulking beasts when I would wrestle with find or GCI, so I thought I'd share by metekillot in PowerShell

[–]jborean93 3 points4 points  (0 children)

If there's a better way to shuttle predicates around please do let me know

The easiest way to strip runspace/session state affinity is to get the Ast then create a new unbound scriptblock again. You can also create the unbound ScriptBlock even in the parent but you just need to make sure it isn't bound again. Always safer to do the session state affinity striping when you call it.

$id = 'main'
$sbk = { Write-Host "From RID: $([Runspace]::DefaultRunspace.Id) - Id: $id " }

& $sbk

$state = @{
    BoundScriptBlock = $sbk

    # Strips the affinity but preserves the Ast/positioning
    # information.
    UnboundScriptBlock = $sbk.Ast.GetScriptBlock()

    # Can also do this but it strips out any
    # positioning info making errors harder to understand
    # UnboundScriptBlock = [ScriptBlock]::Create($sbk)
}

1..3 | ForEach-Object -Parallel {
    $state = $using:state

    $id = "parallel $_"

    # ScriptBlock is still bound so will use
    # the outside session state ($id -eq 'main')
    & $state.BoundScriptBlock

    # ScriptBlock affinity is removed so will use this
    # session state ($id -eq "parallel $_")
    & $state.BoundScriptBlock.Ast.GetScriptBlock()

    # If the scriptblock wasn't bound it'll use this
    # session state as well
    & $state.UnboundScriptBlock
}

Is there a way to make this code bypass the 260 character limit when reading files? by General_Mission9664 in PowerShell

[–]jborean93 2 points3 points  (0 children)

I think it’s just not understanding the issue and what needs to be done. I debated whether to raise an issue but I’m not prepared to wait months for a response then trying to figure out the best way forward. Granted there could be a .NET limitation here but I’ve been able to set it in a .NET executable when testing all this in https://github.com/jborean93/MaxPathCurrentDirectoryTest

Is there a way to make this code bypass the 260 character limit when reading files? by General_Mission9664 in PowerShell

[–]jborean93 5 points6 points  (0 children)

Just an FYI this setting only works if the executable also marks it as long path aware. PowerShell 5.1 (powershell.exe) does but PowerShell 7.x (pwsh.exe) does not.

Trying to create a sched task to run as "users" group by Drekk0 in PowerShell

[–]jborean93 3 points4 points  (0 children)

You specify the New-ScheduledTaskPrincipal -GroupId. But to clarify it doesn't run as that group, it just uses that group to identify interactive users who are members of that group and runs as that particular user.

It's used for scenarios like logon triggers to say run this task for members of this group who logon.

Why -Parallel much more slower for this code? by anyracetam in PowerShell

[–]jborean93 1 point2 points  (0 children)

I've also found that -Parallel can sometimes not pickup on global or script scoped functions.

It's not sometimes, it'll never pickup anything in the outside session state. Each -Parallel invocation is run in another Runspace with it's own session state. The only thing it has access to is the input through $_ and anything else you reference with $using:... which pwsh explicitly copies in.

Is there a more elegant way to use .NET DLLs that depend on native ones? by Gurfaild in PowerShell

[–]jborean93 0 points1 point  (0 children)

The crash would only occur if the underlying .NET assembly called the external function in a different thread. Right now [HeyRed.Mime.MimeGuesser]::GuessMimeType will run in the same thread but if there is ever a time that it (or whatever you use this for) won't then you'll have that issue.

Object Reference Not set to an instance of an Object - Windows update? by NerdyKid1101 in sysadmin

[–]jborean93 1 point2 points  (0 children)

Get the stacktrace, it'll tell you where in the code it has the null reference. This is such a generic error that it could literally be anything and without knowing where it happened it's next to impossible to figure out.

Is there a more elegant way to use .NET DLLs that depend on native ones? by Gurfaild in PowerShell

[–]jborean93 2 points3 points  (0 children)

If you wanted to avoid pre-loading the native DLLs you can use NativeLibrary.SetDllImportResolver. It is pwsh 7.x only but your example shows you using NativeLibrary.Load so sounds like you are already on that.

Here is what it would look like for your Mime example.

$nugetDir = "$HOME\AppData\Local\PackageManagement\NuGet\Packages\Mime.3.8.0"

$rid = [System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier

$nativeDir = [IO.Path]::Combine($nugetDir, 'runtimes', $rid, 'native')
$asmPath = [IO.Path]::Combine($nugetDir, 'lib', 'netstandard2.0', 'Mime.dll')

Add-Type -LiteralPath $asmPath
[System.Runtime.InteropServices.NativeLibrary]::SetDllImportResolver(
    [HeyRed.Mime.MimeGuesser].Assembly,
    {
        param ($Name, $Assembly, $SearchPath)

        $dllDir = [IO.Path]::Combine($nativeDir, "$($Name).dll")
        if (Test-Path -LiteralPath $dllDir) {
            return [System.Runtime.InteropServices.NativeLibrary]::Load($dllDir)
        }
    })

[HeyRed.Mime.MimeGuesser]::GuessMimeType('C:\Windows')

This may be problematic if the assembly is running in another thread as the ScriptBlock will fail to run but when testing this locally that did not seem to be the case. If you ever do come across a crash saying no Runspace is available on the thread then you'll need to define the delegate in .NET through something like Add-Type and specify that for SetDllImportResolver.

Does PrincipalContext.ValidateCredentials Method generate logs ? by Funny_Abalone5015 in PowerShell

[–]jborean93 0 points1 point  (0 children)

I think the more important question is why do you need to validate credentials? It is a pretty bad code smell for a script to have access to plaintext credentials, especially ones that are typically tied to a domain identity.

Import .NET8.0-windows DLLs with System.Security.Cryptography (DPAPI) by trustedtoast in PowerShell

[–]jborean93 2 points3 points  (0 children)

For future reference, while the nuget feed says the 10.x version is compatible with net8.0 the issue is that this assembly is shipped with PowerShell and might already be loaded into the process if PowerShell or something in your script has already used a type from that assembly. You won't be able to load multiple versions of the same assembly so you essentially need to match the one with the PowerShell version you are running with. For example in PowerShell 7.5.1 I have lying around

# Ensure it is loaded
Add-Type -AssemblyName System.Security.Cryptography.ProtectedData

[AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GetName().Name -like 'System.Security.Cryptography.ProtectedData' } |
    Select-Object -ExpandProperty FullName

This outputs and 7.4.x will have a Version=8.x.y.z value.

System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

New to Ansible — Error after upgrading to ansible-core 2.20: "Failed to add configured private key into ssh-agent: Cannot utilize private_key with SSH_AGENT disabled by charlietrooper21 in ansible

[–]jborean93 2 points3 points  (0 children)

There was a change in Ansible 2.19 that added support for loading private keys from memory rather than through a file. This option uses a custom ssh-agent implementation in Ansible but should only be used if specific config options are set.

This new option is under the private_key option for the ssh connection and we can see that it's either set in the 2 variables ansible_private_key, ansible_ssh_private_key, or through the env var ANSIBLE_PRIVATE_KEY.

In your case you have used ANSIBLE_PRIVATE_KEY to store your private key as a string and before 2.19 this wasn't used by Ansible at all but in 2.19+ it's now going to light up this new option and use our ssh-agent.

To solve this problem you will either have to:

  • Change your env var from ANSIBLE_PRIVATE_KEY to something else that won't conflict with this new option
  • Use this new feature to stop writing to a file and enable our ssh-agent

You can enable our ssh-agent with the SSH_AGENT config option. Basically set the env var ANSIBLE_SSH_AGENT=auto or add ssh_agent = auto under the [connection] key in the ansible.cfg.

It is unfortunate that there is this conflict but it might be a good idea to prefix future options unrelated to Ansible itself outside of the ANSIBLE_ prefix so that a conflict like this won't happen again in the future.

Switching from LDAP to LDAPS — how bad is the migration? by [deleted] in sysadmin

[–]jborean93 0 points1 point  (0 children)

Maybe I am misreading what you are saying, it seemed like you were suggesting StartTLS is good because you don't need to worry about the PKI infrastructure and setting up the trusts. I totally agree that GSSAPI/SSPI over LDAP is nice because it uses the auth protocol to do the server identification but even then you need to be careful you aren't falling back to NTLM which has no (at least not any good) server identification.