4

Scenario:

  • User A logs into Windows (8 upwards, Server 2008R2 upwards).
  • The user accesses a 3rd party service through software with username and passwort.
  • I need to provide a "Keep logged in"-experience, hence I need a symetrically obfuscated PW stored password locally (user specific - inside %localappdata%/software/....)

Problem:

So far my predecessor used Symetric Rijndael with Cipher Block Chaining and hardcoded digest to obfuscate the password. Any user & any maschine wich got the %localappdata% file copied over could authenticate successfully with User A's stored creds. I don't like.

Constraints for obfuscation:

  • I want to tie the password obfuscation somehow to the logged in windows user/maschine
  • changing f.e. the windows users password should not invalidate the obfuscated password
  • moving the file over to a different maschine with a (cloned) User A should invalidate the deobfuscation
  • moving the file from User A to User B should invalidate the deobfuscation

Jon Galloway's blog which is from 2008 but made me look into DPAPI: as I understand it, System.Security.Cryptography.ProtectedData with DataProtectionScope.CurrentUser should disable other Users to deobfustcate the password.

Question:

What do I provide as entropy value to disable access for a cloned User A on a different maschine or should I go another route altogether?


Some code to test the ProtectedData stuff from MSDN with a string passphrase:

// modified from https://docs.microsoft.com/de-de/dotnet/api/system.security.cryptography.protecteddata
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

public class DataProtectionSample
{
  static string GetHexString (byte [] data)
    => string.Join (" ", data.Select (d => d.ToString ("x2")));

  static string GetUnicodeString (byte [] data)
    => Encoding.Unicode.GetString (data);

  static byte [] GetBytesFromUnicode (string data)
    => Encoding.Unicode.GetBytes (data);

  public static void Main ()
  {
    // Create byte array for additional entropy when using Protect method.
    byte [] entropy = { 9, 8, 7, 6, 5 };
    byte [] secret = GetBytesFromUnicode ("Cräzy obfuscated paß phrâse");

    //Encrypt the data.
    byte [] encryptedSecret = Protect( secret , entropy );

    Console.WriteLine ("The encrypted byte array is:");
    Console.WriteLine (GetHexString (encryptedSecret));

    // Decrypt the data and store in a byte array.
    byte [] originalData = Unprotect( encryptedSecret, entropy );
    Console.WriteLine ("{0}The original data is:", Environment.NewLine);
    Console.WriteLine (GetUnicodeString (originalData));

    Console.ReadLine ();
  }

  static byte [] Protect (byte [] data, byte [] entropy)
  {
    try
    {
      return ProtectedData.Protect (data, entropy, DataProtectionScope.CurrentUser);
    }
    catch (CryptographicException e)
    {
      Console.WriteLine (e.ToString ());
      return null;
    }
  }

  static byte [] Unprotect (byte [] data, byte [] entropy)
  {
    try
    {
      return ProtectedData.Unprotect (data, entropy, DataProtectionScope.CurrentUser);
    }
    catch (CryptographicException e)
    {
      Console.WriteLine (e.ToString());
      return null;
    }
  }
}

I am aware that impersonating User A will make the obfuscation void - hence obfuscation not encryption. What I am after, is a kindof tougher version of "can't read plaintext pw from file" with a second factor of "need to be logged in with same windows user" and "on the same maschine".

I also looked into How should I ethically approach user password storage for later plaintext retrieval? wich does not quite help as it's answers mostly assume some kind of web scenario with password reset mechanism which I do not have.

Disclaimer: My passwords are random generated Keypass stored values. I do not use "Keep logged in" features. Storing passwords in plaintext is evil, storing passwords symetrically encrypted can be broken and has to be avoided. Still need to provide above feature ...

https://www.harmj0y.net/blog/redteaming/operational-guidance-for-offensive-user-dpapi-abuse/ suggests that DPAPI has several exploitable "flaws" as soon as you are able to impersonate the user in any way or form.

Patrick Artner
  • 43,256
  • 8
  • 36
  • 57
  • You do not need to change the entropy - different user cannot decrypt even with the same entropy data – Sir Rufo Feb 19 '20 at 12:05
  • Do the accounts have admin rights? If they do then `DataProtectionScope` wont do you any good. – Charles Feb 20 '20 at 07:24
  • @charles Maybe. Why would being a local administrator make any difference to this? If logged in as administrator I would still not be the same user and not able to `ProtectedData.Unprotect` some other users stored data? – Patrick Artner Feb 20 '20 at 07:54
  • 1
    @PatrickArtner an administrator can create services in session 0, session 0 or the `System` account can impersonate any user. With `PsExec` for example i can spawn a process under any user of the computer. So im pretty sure `Only threads running under the current user context can unprotect the data` would be useless if im admin. Plus a program in session 0 can easily read your memory. Alternatively as admin you can grab ownership of files and folders. – Charles Feb 20 '20 at 08:17
  • 1
    As a Service you can get a user token with `WTSQueryUserToken` and then use `CreateProcessAsUser` to spawn a process under the user of your choice. – Charles Feb 20 '20 at 08:43

0 Answers0