How to check if current user is password protected?

Suppose you run PS as Administrator…

What command would tell you if that Administrator has password set?

There seems to be solution for AD as explained here:
windows - Check whether user account has password set - Server Fault

But is it possible to do it on workstation machine with pure powershell?

If you’re referring to local accounts. Get-LocalUser has a property PasswordRequired

1 Like

I can’t believe it’s that simple, I’ve been searching around for some time but couldn’t find a solution that is so simple.

However not having ability to determine if remote account has a password set is not an issue since PS remoting via WinRM doesn’t support login with blank password - it results in “access denied” error even if remote login requires no password.

Also Get-Credential can be used to generate an error upfront to be able to write error in more descriptive way before that even happens.

Thanks a lot!

Oh, a problem though…

I’ve just tested the following code:

$WindowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
Get-LocalUser -Name ($WindowsIdentity.Name | Split-Path -Leaf) | Select-Object -ExpandProperty PasswordRequired

And it says that password is not required for my account which is false because my account is password protected.

So something is wrong about that, you can test it.
Anyone knows why?

EDIT:
I found out why…

if “require password” is false the user can set an empty password. It is not “currently has an empty password”.

New-LocalUser , PasswordRequired property · Issue #11965 · PowerShell/PowerShell (github.com)

Still looking for some other solution if possible, to my best search results it seems PS doesn’t let one determine if password is blank since this would be some kind of security issue.

So, I would think this is two parts. The only way one could set an empty password is if PassWordReguired is set to False. Then, you could try to authenticate to that account using an empty password and if that succeeds, then you have your answer?

1 Like

I used Test-WSMan to attempt to authenticate with blank password, problem is that WinRM doesn’t support passwordless authentication, resulting in “access is denied” error even if user has no password.

I’m not sure what other method could be used to authenticate?

I dont know about “sure fire” but one could attempt to map a drive without a password. If I were going to test this, which I cant as our environment does not support the lack of a password.

I did some googling on this and it would appear there is really no way to detect if the password is actually blank, just the PassWordRequired setting. If you do find a way, please post.

1 Like

Metablaster, I am making the assumption you are trying to figure this out on a non AD joined Windows workstation, such as Windows 10 pro or enterprise. Based on that, I configured a standalone, non-domain joined Windows 10 Enterprise 21h2, fully patched, dot net 4.8 and created two test accounts.

Clean install, NO settings changed, just what comes out of the box. In this state, you can create a local account and NOT set a password and still be able to login to the pc/workstation.

If you create 1 test account without a password, and another with a password then look at them with Get-LocalUser, they look identical.

AccountExpires         : 
Description            : no password
Enabled                : True
FullName               : test.user01
PasswordChangeableDate : 
PasswordExpires        : 12/26/2022 8:55:33 PM
UserMayChangePassword  : True
PasswordRequired       : True
PasswordLastSet        : 11/14/2022 8:55:33 PM
LastLogon              : 11/15/2022 7:22:30 PM
Name                   : test.user01
SID                    : S-1-5-21-497985456-3804842693-4194089287-1002
PrincipalSource        : Local
ObjectClass            : User

AccountExpires         : 
Description            : Has password
Enabled                : True
FullName               : test.user02
PasswordChangeableDate : 11/14/2022 8:55:52 PM
PasswordExpires        : 12/26/2022 8:55:52 PM
UserMayChangePassword  : True
PasswordRequired       : True
PasswordLastSet        : 11/14/2022 8:55:52 PM
LastLogon              : 
Name                   : test.user02
SID                    : S-1-5-21-497985456-3804842693-4194089287-1003
PrincipalSource        : Local
ObjectClass            : User

Note that both indicate a password required and password last set. The no password account can login locally. Key word there is locally. Not remote. In back of the brain, a fuzzy memory from my PC support days wants to say there is an explanation about this in the MS docs somewhere.

The following will ensure users can’t have blank passwords.

  1. open gpedit.msc, go to computer configuration > windows setting > security settings > account policies > password policy

  2. in password policy change the policy “Minimum password length” to anything 1 or greater.

  3. on each local account set “user must change password at next logon” to true.

  4. make sure no one but a trusted admin has admin on the box so a clever user (never underestimate determined and clever users) can’t reverse these changes.

Now you can be sure everyone has a password. The other option is to join the workstation to an AD domain and users must login with a domain account that is subject to domain password policies that prevent blank passwords.

While doesn’t answer your original question of how to use PowerShell to find local accounts without a password, it some ideas for how you can ensure user do have a password.

The link you shared in your opening post did have one script that seemed to be onto something, I am playing with it and if anything comes of it will share.

1 Like

Ok, this is probably as close as there is to finding if a local only to a PC user account does not have a password. This is a modified script from the link in the OP.

run as Admin on the PC described in my other post, it will check all local accounts that are enabled on the PC.


C:\PSS\checklocalpwd.ps1
test.user01's Password is blank

If you run the script without the Try Catch, you will get a false positive on accounts that have passwords. Which is expected since we have not included the passwords anywhere in the script. But the account that truly does not have a password set throws an error.

I know that Administrator and test.user02 both have passwords but note the message that the password is blank. But there is an error in there that is related to test.user01, which I know doesn’t have a password. By catching this error, we can now identify which account doesn’t have a password.

I confirmed that test.user01 was the source of the error by disabling test.user01 taking it out of scope. No error. Reenabled test.user01, and the error returned.

C:\PSS\checklocalpwd.ps1
Administrator's Password is blank
Exception calling "ValidateCredentials" with "2" argument(s): "Account restrictions are preventing this user from 
signing in. For example: blank passwords aren't allowed, sign-in times are limited, or a policy restriction has been 
enforced.
"
At C:\PSS\checklocalpwd.ps1:9 char:13
+             $myPasswordIsBlank = $PrincipalContext.ValidateCredential ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : PrincipalOperationException
 
test.user01's Password is blank
test.user02's Password is blank

Script.


Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('Machine')

Get-LocalUser | Where-Object Enabled -eq $true | ForEach-Object {
    
    Try{
             $myUsername = $_.Name
             $myPasswordIsBlank = $PrincipalContext.ValidateCredentials($myUserName, $null)
     
             If ($myPasswordIsBlank) {
        
                 }
         }
    Catch {
             Write-Host "$($myUsername)'s Password is blank"
          }
   
}

1 Like

Thank you guys for suggestions, I’ll do some testing later today when I get time.

I had to do some digging through a ton of scripts, but finally found the one I had used previously to determine if a local account is using a blank password.

Add-Type -AssemblyName System.DirectoryServices.AccountManagement

$script = {
    Param($cred)

    try{
        $obj = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine',$env:ComputerName)

        $obj.ValidateCredentials($cred.username, $cred.GetNetworkCredential().password)
    }
    catch {
        if($_.Exception.InnerException -like "*blank passwords aren't allowed*"){
            $true
        }
        else{
            Write-Warning $_.exception.message
            $false
        }
    }
}

$userlist = Get-WmiObject win32_useraccount -Filter "LocalAccount=True AND disabled=False"

[securestring]$blankpassword = New-Object securestring

$nopassword = foreach($user in $userlist){
    $credential = New-Object System.Management.Automation.PSCredential -ArgumentList $user.Name,$blankpassword
    if(. $script $credential){
        [PSCustomObject]@{
            Username = $user.Name
            BlankPassword = $true
        }
    }
}
1 Like

@Matt
@krzydoug

Hey guys, your solution works without a problem!

This will be very useful for my project because it will help to identify a problem to the root instead of just saying “access is denied”.

Now I guess I’ll leave this thread without a marked solution since both of your examples are correct.
Thanks a lot!

I have tried but problem is that New-PSDrive doesn’t work for passwordless authentication, ex:

$Domain = "VM-PRO"
$Credential = Get-Credential -Credential "$Domain\Admin2"
$SystemDrive = Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty SystemDrive

New-PSDrive -Credential $Credential -PSProvider FileSystem -Root "\\$Domain\$SystemDrive$" -Name Test

This results in the following error:

New-PSDrive : Index was outside the bounds of the array.

And I followed the suggestion from here:
New-PSDrive: Index was outside the bounds of the array. (microsoft.com)

Similar problem is also on serverfault with no answer:
networking - Create a New-PSDrive with username and a blank password - Server Fault

So I guess it’s just not possible to test credentials with a blank password.

What do you mean with this statement?

I mean that neither net command nor New-PSDrive can be used to test credentials with a blank password on another (remote) computer because authentication over network require a password to be set.

I didn’t test authentication to share for local computer since the solution you posted works.

1 Like

Ahh I see. Thanks for clarifying. :slight_smile:

Well, technically, it is possible, using a local account, but you have to modify a GPO setting that you will NEVER find modified. I tested with “net use” …

Computer Configuration/Windows Settings/Security Settings/Local Policies/Security Options/Accounts:
“Limit local account use of blank passwords to console logon only”

So, in reality, you are correct as this would never be the case in any sane environment :slight_smile:

And thanks Krazy Doug for the solution. Well done.

1 Like

Now that’s something good to know, thanks for sharing!

Interestingly, in the last couple of days Jordan Borean (one of my powershell idols) posted this function in the powershell discord help channel. It is a more sophisticated password checker.

Function Test-Credential {
    param(
        [Parameter(Mandatory)]
        [PSCredential]
        $Credential
    )

    Add-Type -Namespace PInvoke -Name NativeMethods -MemberDefinition @'
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LogonUserW(
    string lpszUsername,
    string lpszDomain,
    IntPtr lpszPassword,
    UInt32 dwLogonType,
    UInt32 dwLogonProvider,
    out Microsoft.Win32.SafeHandles.SafeWaitHandle phToken);

public static Microsoft.Win32.SafeHandles.SafeWaitHandle LogonUser(string username, string domain,
    System.Security.SecureString password, uint logonType, uint logonProvider)
{   
    IntPtr passPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
    try
    {
        Microsoft.Win32.SafeHandles.SafeWaitHandle token;
        if (!LogonUserW(username, domain, passPtr, logonType, logonProvider, out token))
        {
            throw new System.ComponentModel.Win32Exception();
        }
        
        return token;
    }
    finally
    {
        Marshal.ZeroFreeGlobalAllocUnicode(passPtr);
    }
}
'@

    $user = $Credential.UserName
    $domain = $null
    if ($user.Contains('\')) {
        $domain, $user = $user -split '\\', 2
    }

    try {
        $token = [PInvoke.NativeMethods]::LogonUser(
            $user,
            $domain,
            $Credential.Password,
            3,  # LOGON32_LOGON_NETWORK
            0  # LOGON32_PROVIDER_DEFAULT
        )
        $token.Dispose()
        return $true
    }
    catch [System.ComponentModel.Win32Exception] {
        # following errors indicate the creds are correct but the user was
        # unable to log on for other reasons, which we don't care about
        $successCodes = @(
            0x0000052F, # ERROR_ACCOUNT_RESTRICTION
            0x00000530, # ERROR_INVALID_LOGON_HOURS
            0x00000531, # ERROR_INVALID_WORKSTATION
            0x00000569  # ERROR_LOGON_TYPE_GRANTED
        )
        $failedCodes = @(
            0x0000052E, # ERROR_LOGON_FAILURE
            0x00000532, # ERROR_PASSWORD_EXPIRED
            0x00000773, # ERROR_PASSWORD_MUST_CHANGE
            0x00000533  # ERROR_ACCOUNT_DISABLED
        )

        if ($_.Exception.NativeErrorCode -in $failedCodes) {
            return $false
        }
        elseif ($_.Exception.NativeErrorCode -in $successCodes) {
            return $true
        }
        else {
            # an unknown failure, reraise exception
            throw $_
        }
    }
}
1 Like

Ah power++ ha :smiley:

There is a similar function on gist which I found some time ago:
Test-WinCredential: PowerShell function for validating Windows domain / local user credentials. (github.com)

But there is one problem in that this doesn’t work over network outside AD if not currently logged on to, from function comment:

IRRESPECTIVE OF THE DOMAIN NAME SPECIFIED, VALIDATION IS
ONLY EVER PERFORMED AGAINST THE CURRENT USER’S LOGON DOMAIN.

Otherwise it works perfectly for local computer and computers in AD.

Btw. I must admit, last time I used it I did some changes to code and when I figured out it doesn’t work remotely I never used it again, some months later I forgot my tests and thought the function doesn’t work at all due to my changes but it turns out it can be used for empty password check on local computer, so that’s something.