xbast1x
Goto Top

Windows Tresor per GPO leeren?

Hallo zusammen,

bei uns tritt häufig das Problem auf, dass veraltete Anmeldeinformationen (Outlook) im Tresor vorhanden sind und es dadurch zu Authentifizierungsproblemen kommt. Die Lösung ist den Tresor zu leeren und Outlook erneut zu starten.

Gibt es eine Möglichkeit das ganze zeitabhängig per Skript oder geplanten Task laufen zu lassen?

Gruß xbast1x

Content-Key: 318373

Url: https://administrator.de/contentid/318373

Printed on: April 16, 2024 at 06:04 o'clock

Member: emeriks
emeriks Oct 19, 2016 at 06:55:43 (UTC)
Goto Top
Hi,
Die Lösung ist den Tresor zu leeren
Wie? Mit CMDKEY ?

Gibt es eine Möglichkeit das ganze zeitabhängig per Skript oder geplanten Task laufen zu lassen?
Ja, als Benutzer-Anmeldescript per GPO.
Oder per GPO pro User einen Scheduled Task einrichten.

E.
Member: xbast1x
xbast1x Oct 19, 2016 at 09:02:04 (UTC)
Goto Top
Momentan mache ich es händisch wenn das Problem auftritt.

Bezüglich Skript habe ich leider keine Ahnung wie ich das ganze realisieren soll. Hast du eine Syntax? Ich habe bei meinen Recherchen leider nichts gefunden..
Member: emeriks
Solution emeriks Oct 19, 2016 at 09:10:07 (UTC)
Goto Top
Momentan mache ich es händisch wenn das Problem auftritt.
Mit Löschpapier?

Bezüglich Skript habe ich leider keine Ahnung wie ich das ganze realisieren soll. Hast du eine Syntax? Ich habe bei meinen Recherchen leider nichts gefunden..
Das glaube ich nicht.
GPO Logon Script
Scheduled Task
CMDKEY Syntax
Batch File
Mitglied: 131223
Solution 131223 Oct 19, 2016 updated at 09:16:47 (UTC)
Goto Top
CMD
for /f "tokens=1,* delims=: " %%a in ('cmdkey /list ^| findstr /i "Ziel:"') do @cmdkey /delete:"%%b"  
Powershell
cmdkey /list | ?{$_ -match 'Ziel: (.*)'} | %{cmdkey /delete:"$($matches[1])"}  
Member: xbast1x
xbast1x Oct 19, 2016 at 09:22:04 (UTC)
Goto Top
Danke euch beiden. Das Stichwort was mir gefehlt hat war CMDKey.

Merci
Mitglied: 131223
131223 Oct 19, 2016 updated at 09:30:45 (UTC)
Goto Top
Zitat von @xbast1x:
Danke euch beiden. Das Stichwort was mir gefehlt hat war CMDKey.
Tomaten auf den Augen.

Wie? Mit CMDKEY ?
Member: colinardo
colinardo Oct 19, 2016 updated at 15:53:25 (UTC)
Goto Top
Oder wenn man cmdkey nicht nutzen will oder kann, warum auch immer, so gehts auch per ADVAPI mit etwas C# als Powershell um alle Credentials im Store zu löschen:
$code = @"  
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

public static class CredentialManager
{
    public static Credential ReadCredential(string applicationName, CredentialType credType)
    {
        IntPtr nCredPtr;
        bool read = CredRead(applicationName, credType, 0, out nCredPtr);
        if (read)
        {
                CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(nCredPtr, typeof(CREDENTIAL));
                return ReadCredential(cred);
        }

        return null;
    }

    private static Credential ReadCredential(CREDENTIAL credential)
    {
        string applicationName = Marshal.PtrToStringUni(credential.TargetName);
        string userName = Marshal.PtrToStringUni(credential.UserName);
        string secret = null;
        if (credential.CredentialBlob != IntPtr.Zero)
        {
            secret = Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2);
        }

        return new Credential(credential.Type, applicationName, userName, secret);
    }

   
    public static IReadOnlyList<Credential> EnumerateCredentials()
    {
        List<Credential> result = new List<Credential>();

        int count;
        IntPtr pCredentials;
        bool ret = CredEnumerate(null, 0, out count, out pCredentials);
        if (count > 0) { 
            if (ret)
            {
                for (int n = 0; n < count; n++)
                {
                    IntPtr credential = Marshal.ReadIntPtr(pCredentials, n * Marshal.SizeOf(typeof(IntPtr)));
                    result.Add(ReadCredential((CREDENTIAL)Marshal.PtrToStructure(credential, typeof(CREDENTIAL))));
                }
            }
            else
            {
                int lastError = Marshal.GetLastWin32Error();
                throw new Win32Exception(lastError);
            }
            return result;
        }
        return result;
    }

    public static bool DeleteCredential(string Applicationname,CredentialType credType)
    {
        Credential cred = ReadCredential(Applicationname, credType);
        if (cred != null)
        {
            bool result = CredDelete(cred.ApplicationName, cred.CredentialType, 0);
            if (!result)
            {
                int lastError = Marshal.GetLastWin32Error();
                throw new Win32Exception(lastError);
            }
            return result;
        }
        else
        {
            throw new Exception();
        }
    }
    
    public static void DeleteAllCredentials() {
        IReadOnlyList<Credential> all = EnumerateCredentials();
        if (all.Count > 0) {
            foreach (Credential cred in all) {
                DeleteCredential(cred.ApplicationName, cred.CredentialType);
            }
        }
    }

    [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]  
    static extern bool CredRead(string target, CredentialType type, int reservedFlag, out IntPtr credentialPtr);

    [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]  
    static extern bool CredEnumerate(string filter, int flag, out int count, out IntPtr pCredentials);

    [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]  
    static extern bool CredFree([In] IntPtr cred);

    [DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]  
    private static extern bool CredDelete(string target, CredentialType type, int flags);


    private enum CredentialPersistence : uint
    {
        Session = 1,
        LocalMachine,
        Enterprise
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct CREDENTIAL
    {
        public uint Flags;
        public CredentialType Type;
        public IntPtr TargetName;
        public IntPtr Comment;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
        public uint CredentialBlobSize;
        public IntPtr CredentialBlob;
        public uint Persist;
        public uint AttributeCount;
        public IntPtr Attributes;
        public IntPtr TargetAlias;
        public IntPtr UserName;
    }
}

public enum CredentialType
{
    Generic = 1,
    DomainPassword,
    DomainCertificate,
    DomainVisiblePassword,
    GenericCertificate,
    DomainExtended,
    Maximum,
    MaximumEx = Maximum + 1000,
}

public class Credential
{
    private readonly string _applicationName;
    private readonly string _userName;
    private readonly string _password;
    private readonly CredentialType _credentialType;

    public CredentialType CredentialType
    {
        get { return _credentialType; }
    }

    public string ApplicationName
    {
        get { return _applicationName; }
    }

    public string UserName
    {
        get { return _userName; }
    }

    public string Password
    {
        get { return _password; }
    }

    public Credential(CredentialType credentialType, string applicationName, string userName, string password)
    {
        _applicationName = applicationName;
        _userName = userName;
        _password = password;
        _credentialType = credentialType;
    }

    public override string ToString()
    {
        return string.Format("CredentialType: {0}, ApplicationName: {1}, UserName: {2}, Password: {3}", CredentialType, ApplicationName, UserName, Password);  
    }
}

"@  
Add-Type -TypeDefinition $code
[CredentialManager]::DeleteAllCredentials()
Falls es jemand gebrauchen kann.

Grüße Uwe

p.s. Code ist etwas länger da er auch noch mehr kann als nur den Store komplett zu leeren face-wink
Member: xbast1x
xbast1x Oct 20, 2016 at 12:30:53 (UTC)
Goto Top
Noch eine Frage:

Per Logonskript leert er den Tresor nicht. Ich vermute, da noch andere Logonskripts laufen (ich habe testweise die Batch für den Tresor an die oberste Stelle der Reihenfolge verschoben)

Per Aufgabenplanung funktioniert es wunderbar, jedoch muss ich auch hier 30 Sekunden Delay einbauen. Ich würde gern das DOS Fenster welches erscheint unterdrücken doch bekomme es so nicht hin. Das Argument /min bzw. direkt vor dem Pfad der Batch Datei "start /min" "Pfad zur Batch" funktioniert auch nicht.

Gibt es eine Möglichkeit ohne VBS Skript ein DOS Fenster nicht sichtbar durch die Aufgabenplanung starten zu lassen?
Member: emeriks
emeriks Oct 20, 2016 updated at 12:35:29 (UTC)
Goto Top
Gibt es eine Möglichkeit ohne VBS Skript ein DOS Fenster nicht sichtbar durch die Aufgabenplanung starten zu lassen?
Ja. Aber nur wenn man es "unabhängig von der Anmeldung" (o.ä.) einrichtet, also am Task das Passwort hinterlegt. Und das kann man per GPO/GPP nicht.
Edit: Oder per Powershell-Einzeiler.

Per Logonskript leert er den Tresor nicht. Ich vermute, da noch andere Logonskripts laufen
Andere Scripte sind da irrelevant. Stell mal per GPO ein, dass die Loginscripte sichtbar laufen sollen und lass die Batch mit "pause" am Ende wartet. Dann kannst Du sehen, ob und was er macht bzw. warum nicht.
Member: xbast1x
xbast1x Oct 20, 2016 at 13:09:17 (UTC)
Goto Top
GPO Einstellungen + Skript habe ich angepasst. Ich sehe kein CMD Fenster.


Wie würde das ganz per Powershell aussehen. Wäre das Atribut dann "-windowstyle hidden" ?
cmdkey /list | ?{$_ -match 'Ziel: (.*)'} | %{cmdkey /delete:"$($matches[1])"}

Gruß
Member: emeriks
emeriks Oct 20, 2016 at 13:23:12 (UTC)
Goto Top
Powershell --> Da wäre @colinardo ne gute Adresse ...
Member: colinardo
Solution colinardo Oct 20, 2016 updated at 13:31:25 (UTC)
Goto Top
-windowstyle hidden
Ja

Aber GPO-Logon-Scripts werden doch schon unsichtbar ausgeführt ??!
http://www.gruppenrichtlinien.de/artikel/anmelde-skripte/

Man kann das PS Fenster aber auch aus dem Skript selber heraus ohne diesen Parameter verstecken:
$showWindowAsync = Add-Type –memberDefinition @"   
[DllImport("user32.dll")]   
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); 
"@ -name "Win32ShowWindowAsync" -namespace Win32Functions –passThru  
[void]$showWindowAsync::ShowWindowAsync((Get-Process –id $pid).MainWindowHandle, 0) 

# und hier der Rest
Member: xbast1x
xbast1x Oct 21, 2016 at 06:23:33 (UTC)
Goto Top
Vielen Dank colinardo.

Skript funktioniert super (lokal getestet). Ich muss nur unter der GPO sagen, dass Powershell Skripte erlaubt sind. Ich habe jedoch folgenden Eintrag nicht "Policies\Administrative Templates\Windows Components\Windows PowerShell."

Die PowerShellExecutionPolicy.admx dafür ist vorhanden und liegt auch im %systemroot%/PolicyDefinitions, wird aber im GPO Editor nicht angezeigt.
Member: emeriks
emeriks Oct 21, 2016 at 06:33:13 (UTC)
Goto Top
Die PowerShellExecutionPolicy.admx dafür ist vorhanden und liegt auch im %systemroot%/PolicyDefinitions, wird aber im GPO Editor nicht angezeigt.
Habt Ihr einen Central Store eingerichtet? Falls ja, dann muss die ADMX dort rein.

E.
Member: xbast1x
xbast1x Oct 21, 2016 at 06:44:08 (UTC)
Goto Top
Danke emeriks. Nun passt es.