mttaggart / wtfbins

WTF are these binaries doing?! A list of benign applications that mimic malicious behavior.
MIT License
150 stars 12 forks source link

[New WTFBin]: IBM Personal Communications pcsnp.exe #58

Closed 59e5aaf4 closed 1 month ago

59e5aaf4 commented 1 month ago

1 - The tool.

I don't know where to start. This binary is part of some https://www.ibm.com/support/pages/ibm-personal-communications IBM "Personal Communications" tool, some sort of SSH.IBM.EXE relying on java and .exe to do script-like jobs.

It's commonly found on sysadmins workstations who work on any IBM thingies.

2 - The code.

pcsnp.exe ( has a lot of pcs*.exe friends in C:\Program Files (x86)\ibm\Personal Communications\ ) has a weird forced mkdir subcommand ( presumably written by Neelabh

int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
  char CurrentProcessId; // al
  char TickCount; // [esp+0h] [ebp-4h]

  TickCount = GetTickCount();
  GetEnvironmentVariable_PCOMM_Root();
  system_mkdir_c_Temp_pcsnp_init_log(); // Make that system("mkdir ...") call
  WriteLog((wchar_t *)L"cmdLine parse\n", TickCount);
  if ( ParseCommandLine(lpCmdLine) )
  {
    ReadFile_wrapper(&logfile);
    CryptProtectData_Neelabh_Credentials(&logfile); // Some fun
  }
  __RTDynamicCast();
  CreateProcessA_tpam_exe(); // More fun
  GetTickCount();
  CurrentProcessId = GetCurrentProcessId();
  WriteLog((wchar_t *)L"pcsnp(%d) %d sec\n\n", CurrentProcessId);
  SetLastError(0);
  return 0;
}

The first function does this : system("mkdir c:\\Temp");, effectively causing a subprocess cmd.exe /c mkdir c:\Temp to be launched.

wchar_t *system_mkdir_c_Temp_pcsnp_init_log()
{
  system("mkdir c:\\Temp");
  return wcscpy(pcsnp_init_log_path, L"c:\\Temp\\pcsnp_init.log");
}

3 - The behavior.

Sometimes, this is done from OTHER binaries than pcsnp.exe like mpnotify.exe ( at user login ? suspected since pcsnp parent is usually userinit.exe ), and on very rare occasions, by lsass.exe, causing CrowdStrike to pop a LsassToChild alert for valid reasons.

When this second, rare, behavior happens, CrowdStrike fires a LsassToChild alert stating : "Lsass launched a child process. If this child process is unexpected, it might indicate malware hijacked lsass to evade detection or dump credentials. Investigate the process tree."

Correlation between these mkdir commands and that tool being launched. Timestamps are missing but I swear it's really close.

image

IDA screenshot for fun and giggles; this appends forever to a log file (C:\Program Files (x86)\ibm\Personal Communications\private\pcsnp.log) without any timestamps fksdjhfqkljh. While we're at it, it has the capability (and forcibly tries) to launch tpam.exe, which I didn't find anywhere in my infra haha. So plant "tpam.exe" in system32 and boom, magic persistence whenever some admin launches that IBM thingy or just opens a session. BRB filing papers to open a crime consulting company.

image

int CreateProcessA_tpam_exe() { // redacted for brevity // read that churchill memo on brevity, it's good.
  WriteLog((wchar_t *)L"Tpam \n", v1);
  result = CreateProcessA(0, (LPSTR)"tpam.exe
    return WriteLog((wchar_t *)L"Tpam(%d) started\n", ProcessInformation.dwProcessId);

I still have NO idea why these mkdir commands appear as children of other processes, but I couldn't find another binary containing that specific string.


4 - Why Neelabh ?

Why Neelabh ? Here it is, that string is used as a test vector for encryption with Crypt(Un)ProtectData.

  v7 = RegCreateKeyExW(HKEY_CURRENT_USER, aSoftwareIbmPer, 0, 0, 1u, 0xF003Fu, 0, &hKey, &dwDisposition);// Software\IBM\Personal Communications\CurrentVersion\Credentials
    if ( !v7 && hKey && (dwDisposition == 1 || dwDisposition == 2) )
    {
      RegDeleteValueA(hKey, "Data");
                                        /* here */
      if ( CryptProtectData(&pDataIn, L"Neelabh", &pOptionalEntropy, 0, 0, 0, &pDataOut) )      {
        if ( !RegSetValueExA(hKey, "Data", 0, 3u, pDataOut.pbData, pDataOut.cbData) )        {
          if ( CryptUnprotectData(&pDataOut, 0, &pOptionalEntropy, 0, 0, 0, &v5) )          {
            if ( !memcmp(v5.pbData, pDataIn.pbData, v5.cbData) && v5.cbData == pDataIn.cbData )            {
              log((wchar_t *)L"Encryption validation success\n", v2);              v10 = 1;
            }            else            {
              log((wchar_t *)L"Encryption validation failed\n", v2);              v10 = 0;
            }            LocalFree(v5.pbData);
          }
          else
          {            RegDeleteValueA(hKey, "Data");            v10 = 0;
59e5aaf4 commented 1 month ago

( also gg )

mttaggart commented 1 month ago

Holy wow, what a great writeup and a great find! Thanks for this! Added in b5b8815