Yubikey Manager and PowerShell: Static Passwords

Title image of Yubikey and Powershell logos

In writing this, I hope I am not the only one who has encountered this scenario. You get the call or ticket: “Can you check on the Windows device XXXXX? It is not normal and performing strangely.” Being a diligent systems administrator, you log into device XXXXX to take a look. As the desktop background renders, you start to see warnings and notifications from your antivirus/antimalware solution: “infections detected, cannot be quarantined: Trojan.Emotet …” (or some other credential-stealing malware variant). Yikes! And you just logged in with your privileged account!

Probably best to assume your privileged credentials are now compromised or otherwise in the wild.

Although this specific scenario may not occur a lot, I believe it is advisable to change or rotate your privileged account password more frequently than you would other accounts. If you use a Yubikey (and the good old static password configuration), you can script the reset process with Powershell to save yourself some keystrokes, time and panic if you must reset your password in a hurry.

If you do not have a Yubikey already, you need one. Or two so you have a backup.

In this post, I will share a PowerShell based approach to quickly generate a new random, static password on a Yubikey and subsequently change your local or domain account password.

Install the YubiKey Manager (ykman) command-line interface (CLI)

It is worth noting, there are 2 Yubikey command line interfaces.

There is some overlap between the tools but after the valuable comment by Dag Heyman, the tool’s maintainer, I prefer using ykman.exe (YubiKey Manager) for simplicity.

There is an install for YubiKey Manager on Windows which includes ykman.exe by default - simply download the appropriate version from Yubico, run the installer and follow the prompts.

Use PowerShell to call ykman and generate a random, static password in your Yubikey

Under Windows, ykman does not require elevation to interact with the Yubikey. That being the case, I wanted to create a function or script I could run to quickly do the following:

1) Generate a random, static password in the Yubikey that complies with most password policy requirements (note: the generated portion should comprise only part of your complete password - more specifically, the end of it. For security, prepend a password of your choosing to the generated password to create a “something you know” and “something you have” scenario.)

2) Set the password on my local Windows or domain account to the new static password in the Yubikey

You can generate a new static password on the Yubikey with these PowerShell commands (via ykman):

# Create an alias for ykman pointing to the install location
set-alias ykman "$env:programfiles\Yubico\YubiKey Manager\ykman.exe"

# Run ykman to generate a random static password in Yubikey's config slot 2
ykman otp static 2 --generate --length 16 --force --keyboard-layout US

If you run the sample above, you should end up with a random, 16 character password generated on your Yubikey in configuration slot 2. You can long-press the button to release and view the password (assuming you used configuration slot 2 as I did).

To flesh things out further, I put together this snippet (also available as a gist).

It uses the base logic of the original command to generate a new static password on the Yubikey, then reset the password of the user running the PowerShell session to the new static password (requires pressing the button on the Yubikey to release it twice).

# Create an alias for ykman pointing the the install location
set-alias ykman "$env:programfiles\Yubico\YubiKey Manager\ykman.exe"

# Provide an opportunity to insert the yubikey before continuing
Read-Host -Prompt "Ensure Yubikey is inserted then press Enter to continue"

# Add a new line for formatting/tidiness
write-host " "

# Run ykman to generate the static password on the Yubikey (in slot 2)
ykman otp static 2 --generate --length 16 --force --keyboard-layout US

# Wait a second, then add a new line for formatting/tidiness
sleep 1
write-host " "

# Have the user enter their own password to prepend the Yubikey random, static password (improves security)
# See https://support.yubico.com/support/solutions/articles/15000006480-understanding-core-static-password-features
write-host "Resetting password for $env:username - enter a personal password and without pressing enter, long-press the button on your Yubikey to append the generated static password when prompted (twice) ..."

# Determine if the account is a local account or domain account and run the respective "net use"
# command to reset the password. In most cases, the account is local if %userdomain% and %computername% match
if($env:userdomain -like "*$env:computername") {
     net user "$env:username" *
} else {
     net user "$env:username" * /domain
}

# Zero out variables
$NewPassword = ""

# Add new lines and output for formatting/tidiness
write-host " "
write-host "DONE"
write-host " "

Once done, the account password is reset and you can login by typing your personal password, then long pressing the button on your Yubikey to release the generated password.

For a more detailed look at the construction of a secure, static password on Yubikey, see:

Example of combining a "known" personal password with a Yubikey randomly generated, static password (something you "have") to improve static password security

In this example, the personal portion (something I “know”) of the static password is Abc123. The random (generated) portion of the static password is LNtr45ucdhdtlril (something I “have” - this is emitted from the Yubikey).

Closing thoughts

In case you are curious, a static password can be generated in the Yubikey using ykpersonalize (YubiKey Personalization Tool) as follows:

# Create an alias for ykpersonalize pointing the the location it is downloaded to
set-alias ykpersonalize "$env:localappdata\Programs\Yubico\bin\ykpersonalize.exe"

# Generate a random hex (AES key) to serve as entropy (randomness source) for ykpersonalize
$RandomHex = (((32)|%{((1..$_)|%{('{0:X}' -f (random(16)))})}) -Join "").ToLower()

# Run ykpersonalize to generate a random static password in Yubikey's config slot 2
ykpersonalize -2 -a"$RandomHex" -ostatic-ticket -oshort-ticket -ostrong-pw1 -ostrong-pw2 -y

The magic with ykpersonalize comes from the -a switch - this specifies a “randomness source” in the form of a hex AES key that the Yubikey uses to generate the static password. Supply a different hex key to ykpersonalize each time you run it to generate a unique password each time. If you fail to specify an AES key (-a), ykpersonalize will throw an error like this:

Yubikey personalization error: no randomness source available

Speaking of which, the $RandomHex variable is generated based on the awesome work of Forty3 from Code Golf on Stack Exchange. I made a minor adjustment to Forty3’s PowerShell code that generates a random uuid string.

As you may have guessed, I actually started with ykpersonalize first but now prefer ykman because the latter eliminates the need to generate a randomness source (hex) in PowerShell.

It is worth noting, static passwords on the Yubikey are not the most secure authentication option. Be that as it may, I have found that the convenience and versatility of static passwords outweigh the risks. I would argue that frequently changing or rotating static passwords (concatenated with your own secure password) on the Yubikey offers added protection to this approach.

Here are a few examples of where Yubikey static passwords shine:

  • Across platforms and devices (Windows, macOS, Linux, etc.)
  • Physical servers in a datacenter (with USB ports)
  • Virtual servers via SSH, RDP, etc. access
  • The VMware Remote Console (no copy/paste)
  • Windows UAC (User Account Control) elevation screens (no copy/paste)
  • Broken devices where smartcard authentication, FIDO, etc. are no longer working properly
  • Air-gapped devices with no network access

However you choose to proceed, stay secure (or at least more secure than your slowest friends) to avoid being eaten by the compromised credential bear!