Babax stealer rebrands to Osno, installs rootkit

Babax stealer rebrands to Osno, installs rootkit

Original text Karsten Hahn

Babax not only changes its name but also adds a Ring 3 rootkit and lateral spreading capabilities. Furthermore it has a ransomware component called OsnoLocker. Is this combination as dangerous as it sounds?

Emergence of Babax and Osno

Babax stealer is at least around since June 2019. At that time a user named ForlayPy gave away the source code for free after being dissatisfied with a customer they sold the source to.

A colleague of mine discovered the first Osno stealer sample[1] on 5th October 2020. The sample[1] is a packed .NET assembly with the module name FallGuysStats. The module name indicates that it is using a statistics generator for the Steam game Fall Guys as a lure. The config shows version Osno 2.1.5 and has placeholders for some of the functions, including FTP and Telegram settings.

By the end of October researcher @backsla3h noted that the stealer is sold on forums. The advertisment comparison of Babax and Osno shows not only an increased price but also four more features or «Benefits» for Osno: r77 and network spreading, Anti-AV and evasion of WindowsDefender via allowlist, AnarchyGrabber and microphone records. Additionally there is a ransomware module which is not advertised (yet). Most of these features are described in the following sections.

Rootkit r77

Although the advertisment calls this an exploit, it is actually an open source rootkit by bytecode77. The Github repo provides DLL’s for this rootkit as well as an installer. Osno does the installation itself. Just like the rootkit installer, Osno registers the rootkit DLL to AppInit_DLLs and enables LoadAppInit_DLLs so it is loaded with every process. Because the rookit DLLs are not signed, it sets RequireSignedAppInit_DLLs to 0.

The rootkit uses MinHook to redirect WinAPI calls, so that it hides processes and file names, e.g., from explorer and taskmanager. The proof-of-concept binaries for the rootkit hide files and processes that start with «$77». Since Osno uses the proof-of-concept binaries, it needs to add the prefix «$77» to its own files to make it work.

File hiding demonstration by r77 rootkit. Image from

The Github for r77 states that the rootkit is still work in progress. Because of that hiding files for x86 is currently unstable and disabled in the proof-of-concept files.

Lateral movement via SharpExec

Osno collects all accessible IP adresses in the local network, then downloads SharpExec binaries from Github. SharpExec is a tool with various commands for lateral movement. Osno executes the following command for every collected IP and domain:

<sharpexec> -m=psexec -i=<collected-ip> -d=<collected-domain> -f=<path-in:%TEMP%/gpustats.bx> -e=%TEMP%/<randomname>

The file gpustats.bx contains the path to the Osno executable. This command attempts to upload Osno to the given IP into the %TEMP% folder and executes it. That way Osno is able to spread to all accessible computers within the network.


The following Anti-AV features are those of the unpacked Osnoe sample[2]. The packer stub itself has also Anti-AV which is beyond the scope of this article.

Windows Defender

Osno adds its own path and the root of drive C: as exclusion folder for Windows Defender using the following PowerShell commands:

Add-MpPreference -ExclusionPath C:\
Add-MpPreference -ExclusionPath <path-to-malware>

This can only be successful if the malware has already gotten foothold on the system and obtained administrator privileges.

Other AVs

Osno searches for Window titles and process names to kill the processes of Antivirus software. It does this for the following window titles and process names:

  • Window title «Malwarebytes Anti-Malware» and process name either mbamgui or mbam
  • Process names: avgidsagent, avgfws, avgtray, avgemcx, avgwdsvc, avgnsx, avgcsrvx, avgrsx, Toolbar Updater

That means only Malwarebytes Anti-Malware and AVG are affected.

Osno ransomware is a wiper

Osno ransomware, or OsnoLocker as it is called in the code, has an implementation for XXTEA. However, it is not used in the current sample. Instead it overwrites the original content of the files with a marker. The marker is the string «OsnoRansom» appended by a randomly created string of length 50-200 characters consisting of uppercase letters A-Z and digits 0-9. Described as a regex this would be ^OsnoRansom([A-Z0-9]){50,200}$. The randomly created string will be different for every affected file.

OsnoLocker adds the .osnoed extension to these files.

OsnoLocker puts a ransom message into a file called RecInstruct.osnoned (sic!). It proceeds to write an executable to disk named Osno Decryptor.exe[3] whose purpose is to lock the screen, display the ransom message that was placed in RecInstruct.osnoned and ask for a decryption code. This Osno Decryptor.exe has the module name FakeRansomware.

Osno is a wiper in its current form and payment will not help to get any files back. Recovery of files via shadow volumes copies can work, though. Future versions of Osno might use the already implemented XXTEA to encrypt files.

AnarchyGrabber and other copied tools

Osno stealer implements the code of AnarchyGrabber 3. An article by Bleepingcomputer describes the additional features of the latest AnarchyGrabber version. Just like that version, Osno will force Discord to load JScript files inject.js and discordmod.js. To do that it puts the JScript files into the folder %AppData%\Discord\<version>\modules\discord_desktop_core\osno. Discord will then act as a stealer.

Another tool that this Osno sample uses is Da pure C++ Clipper[4]. A native binary for clipbanking.

Furthermore, @backsla3h pointed out that the RunPE method and VM/Debugger/Sandbox detection code are taken from CSharp-RunPE and Anti-Analysis by NYAN-x-CAT.

So we have already identified six different copied sources and tools that are deployed by Osno: SharpExec, AnarchyGrabber, Da Pure C++ Clipper, CSharp-RunPE, Anti-Analysis, r77 rootkit. It is likely that there are more copied sources in those functions I didn’t look at as they are beyond the scope of this article.


Osno is not just a stealer anymore. Although that is still the main focus, the added capabilities pose a more serious threat, especially RDP access, lateral movement and file destruction.

However, none of that seems particularly scary.

Firstly, most of the serious sounding features are only possible after the malware sucessfully accessed the system and gained administrator privileges. That includes the rootkit and the anti-AV. The lateral movement portion depends on an external tool that needs to be downloaded first. It is only successful if network adminstrators disregard security measures alltogether, thus, unlikely to cause serious outbreaks.

Secondly, many of the stealer’s features have been taken from public respositories and are known to defenders, making detection of the malware easier. Osno seems to have been worked around some of those tools. E.g., it uses the r77 rootkit binaries as is, although they are unfinished and only work with drawbacks. Osno renames its files to make them work for the rootkit binaries instead of implementing a rootkit that works for the Osno files.

The ransomware, which may have been self-implemented, seems not finished yet, which is confirmed by existence of non-implemented XXTEA code and the fact that this feature is not advertised. Later versions will likely use encryption instead of destroying files.

Due to the mishmash of open-source code and tools from other malware Osno is best described as a patchwork Frankenstein’s monster .

Indicators of compromise

[1] Osno/Babax stealer3bb9f55514122071824320091030f517a2809c140d86791275037569b26f53f1
[2] Unpacked Osno4fd221c89030a1fe1c2396a957990693ec8e6330ed79c63bde24abdbc0b8b166
[3] Screenlocker «Osno Decryptor.exe»40e4fffa431378e9f09310bba5ff4b8bcec1e11e2b9a606d15f123b696bdb697
[3] Da pure C++ Clipper1412516d5f9e43e9c797bbeb3872ef2ff0f68cf51d66288cfd257bb0b56a0e54
DescriptionIndicator of compromise
Regex for names of downloaded and executed files by Osno
located in %APPDATA% folder
Used as marker for download and execute, contains «0x15» after successful execution.%APPDATA%\system.infox
Data file that indicates if autorun was set, contains either «False» or «01010»C:\ProgramData\ar.xdg
Contains the path of the Osno executable base64 encoded or after lateral movement the string «0x14»%TEMP%/gpustats.bx
Discord JScript files placed by AnarchyGrabber%AppData%\Discord\<version>\modules\discord_desktop_core\osno\inject.js
Additional user for RDP access, password «5Z6aW8qRhLWEwS»Defaultuzer
Ransom message to be displayed by screenlockerRecInstruct.osnoned
Regex for content of overwritten files with .osnoed extension^OsnoRansom([A-Z0-9]){50,200}$
Stolen data, placed in%TEMP%\<md5(username)>Osno\<md5(machinename)>-LogsChromium Logins.txt
Gecko Logins.txt
Direct Login Cookies.txt
Hardware & Soft.txt

Bypass AMSI in PowerShell

Bypass AMSI in PowerShell #bypass #amsi #powershell

Original text by Aidin Naserifard

In one of the RedTeam projects, I was looking to use BloodHoundAD Script. BloodHound is a single page JavaScript web application, built on top of Linkurious, compiled with Electron, with a Neo4j database fed by a C# data collector. BloodHound uses graph theory to reveal the hidden and often unintended relationships within an Active Directory environment. Attackers can use BloodHound to easily identify highly complex attack paths that would otherwise be impossible to quickly identify. Defenders can use BloodHound to identify and eliminate those same attack paths. Both blue and red teams can use BloodHound to easily gain a deeper understanding of privilege relationships in an Active Directory environment. [].

Let me explain the Scenario…

I want to use SharpHound. SharpHound is the C# Rewrite of the BloodHound Ingestor.

Image for post

When you run the SharpHound.ps1 directly in PowerShell, the latest version of AMSI prevents it from running:

Image for post

Because this script is known as a malicious payload, Microsoft AMSI has its signature and prevented it from running.

Well, I fragmented this script and ran each part separately and directly in PowerShell. The reason for this is that I wanted to find out which parts of the malicious payload can detect by AMSI exactly.

OMG! AMSI cannot detect value of this parameter : $EncodedCompressedFile. This is the main part of malicious code. Now you need to use some other part of the script to execute it correctly in PowerShell.

Image for post

You can bypass AMSI by base64 encoding and deflate compressing a malicious payload and then executing it. Here is the POC video.

How I found a Tor vulnerability in Brave Browser, reported it, watched it get patched, got a CVE (CVE-2020-8276) and a small bounty, all in one working day

Original text by sickcodes

Recently, I discovered a small but potentially devastating vulnerability in the new Tor feature of the Brave browser.

As of November 2nd 2020, Brave monthly users 9 have massively increased their browser market share to 20 Million Monthly Active Users + 7 Million Daily Active Users.

Brave is a unique browser, created by Brendan Eich, who is also the inventor/creator of JavaScript. He is also the former CEO of the Firefox browser’s parent company, Mozilla.

Brave is based on Chromium, which is the Open Source version of the well known Chrome browser. Being an Open Source project, this allows any developer, anywhere, the ability to inspect the source code of the project. You can inspect the code yourself here: 12

In 2018 Brave added the ability to integrate Tor sessions into their Incognito mode. Essentially, it is Private Window browsing inside Chrome, with an added Tor network traffic routing circuit, for a whole range of added privacy bonuses: 9

On October 30th 2020, I updated my local version of Brave to the latest version.

Specifically, I was running brave-bin 1:1.16.72-1, from the Arch User Repository (AUR) btw. 3

After some light digging, there is a transient session information file located in the folder that contains your Brave configurations.

~/.config/BraveSoftware/Brave-Browser/Local\ State

The file is named “Local State” and annoying has a space in the filename, so you must always quote the file or escape the space with a backslash.

It is a JSON file that stores core metrics, particularly, the feature in which Brave makes money from.

Without going into too much detail, Brave makes money off users enabling the Brave rewards program that involves cryptocurrency, sponsored background images, and referral links to big crypto trading sites like Binance, Gemini,, Coinbase etc


Once pretty-printed, you can see the sites in which Brave will send a “partner” header to:

    "referral": {
      "headers": &#91;
          "cookieNames": &#91;],
          "domains": &#91;
          "expiration": 31536000000.0,
          "headers": {
            "X-Brave-Partner": "coinbase"
          "cookieNames": &#91;],
          "domains": &#91;
          "expiration": 31536000000.0,
          "headers": {
            "X-Brave-Partner": "softonic"
          "cookieNames": &#91;],
          "domains": &#91;
          "expiration": 31536000000.0,
          "headers": {
            "X-Brave-Partner": "dowjones"

If you visit these websites while using Brave Browser, the browser will automatically add a referral header. This is the same as clicking an affiliate link to Amazon, except that you can’t really turn it off.

You can modify the source code, of course, but below I will demonstrate the difference and why it initially raised my eyebrows…

Here is the request for a normal website:

curl '' \
  -H 'authority:' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'dnt: 1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-user: ?1' \
  -H 'sec-fetch-dest: document' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'sec-gpc: 1' \

Here is the request for a Brave Partner website:

curl '' \
  -H 'authority:' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'dnt: 1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'x-brave-partner: coinbase' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-user: ?1' \
  -H 'sec-fetch-dest: document' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'sec-gpc: 1' \

As you can see, Brave inserts the header:

  -H 'x-brave-partner: coinbase' \

Obviously Brave has to make money. But I immediately wanted to see if it showed up on Tor sessions.

To enable the Tor feature, you need to visit 

, or just go to settings in the menu.

Enable this:

Private Window with Tor
Tor hides your IP address from the sites you visit.

Once enabled, you can open Tor windows in the menu bar:


As Tor Project users will understand, there are a number of obvious issues while using Tor outside of the original Tor Browser.

The list of ongoing leakproofing for the Brave Tor experience can be found in the Brave Core git issues: 8

Tor browser users know about the window size fingerprinting technique, which already has an issue opened for the Brave rendition of Tor: 6

To answer the question above, yes, Brave sends the “x-brave-partner: coinbase” header when visiting the coinbase website.

Even when Tor is on:

curl '' \
  -H 'authority:' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'dnt: 1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
  -H 'x-brave-partner: coinbase' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-user: ?1' \
  -H 'sec-fetch-dest: document' \
  -H 'accept-language: en-US,en;q=0.9' \

I reported this small issue to the security team at Brave, via email, rather than HackerOne.

Along with this issue, I mentioned to the team that there is a folder that is generated during Tor sessions:


I suggested to the Brave security team that they should clear or shred this folder on exit.

Brave has a super responsive security team that excellently run by a well-known security icon named Yan Zhu, who you can follow on Twitter here: 31

This is the original security report in full:

Re: [Security]

Vulnerability Details

A vulnerability in the Brave Web Browser allows remote websites to bypass an intended privacy feature of the Brave Web Browser. Even while using Tor, Brave sends a referral partner HTML header to specific partner websites, for example, “X-Brave-Partner”: “coinbase”, and “X-Brave-Partner”: “softonic”.
A targeted attack could trivially de-anonymize a user of the Brave browser. Since Oct 5, 2020 Brave offers a “private window” feature that supports The Onion Router (Tor) Network. The purpose of the Tor Browser from Tor project is to isolate each website you visit so that third-parties cannot follow you.

The Brave private window feature with Tor does not sufficiently anonymize users visiting Brave’s partner websites.

While using Tor, Brave stores the count, attempt count, and absolute timestamp of the above HTML request:
“tor_used”: true
“referral_attempt_count”: 25,
“referral_attempt_timestamp”: “13248492282530685”,
“timestamp”: “13244344644287168”
“incognito_used_timestamp”: “13248493693576042”,
“last_used”: “Tor Profile”,
“profile_counts_reported”: “13248492279614953”

After the user ends the Tor session, the data is not cleared and users should be aware that the Tor feature of Brave browser is not secure as intended and the browser can leak, or send usage statistics, of critical information to their partner websites that could be used by an attacker to triangulate a user.

After a few emails back and forth discussing the issue with the persistent Tor folder and various Tor metrics in the Local State file, Yan @ Brave confirmed in the following message:

Monday Morning, 2nd November 2020:

Re: [Security]

…we found that indeed we were accidentally logging the when any incognito session was used, when we were supposed to only log the timestamp that a non-Tor incognito session was used: 21.
We consider this a valid security issue and would be happy to issue a bounty via HackerOne if you resubmit it there: 14. I think this is a sec-low issue given that someone who has access to the local disk cannot tell if the timestamp corresponds to a Tor session (rather than a regular incognito session). Also, the value that actually gets sent to the Brave metrics server is bucketed into the following values (so the timestamp isn’t a fingerprinting vector):

Used in last 24h
Used in last week but not 24h
Used in last 28 days but not week
Ever used but not in last 28 days
Never used

Before I had even submitted the report to HackerOne, the team had already patched the 21

12452: Fix incognito used timestamp for P3A. 21

opened  Nov 2, 2020 iefremov+2 -1HackerOne 25

Brave Software disclosed on HackerOne: Brave Browser potentially… 25

## Summary: A vulnerability in the Brave Browser allows an attacker to view the last time a Tor session was used in incognito mode. A local, on-disk attacker could read the Brave Browser’s «Local State» json file and identify the last time a Tor…

GitHub has an amazing feature where you can add 

 to a pull request URL and it will give you the Pull Request changes, in a patch file. 8

From 2b690a354e42ee8cd2aaa9a7f0b4375dc91d5e01 Mon Sep 17 00:00:00 2001
From: Ivan Efremov &lt;>
Date: Mon, 2 Nov 2020 15:43:05 +0700
Subject: &#91;PATCH] 12452: Fix incognito used timestamp for P3A.

 browser/p3a/ | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/browser/p3a/ b/browser/p3a/
index 9befca08bf6..4a0c2d691d9 100644
--- a/browser/p3a/
+++ b/browser/p3a/
@@ -43,7 +43,8 @@ enum class WindowUsageStats {
 const char* GetPrefNameForProfile(Profile* profile) {
-  if (profile->IsIncognitoProfile()) {
+  if (profile->IsIncognitoProfile() &amp;&amp;
+      !brave::IsTorProfile(profile)) {
     return kLastTimeIncognitoUsed;
   return nullptr;

It’s hard to follow the timestamps correctly, because there are lots of timezones and stamps involved, but the speed at which this was fixed can be emphasized by the following fact:

In the reply where Brave confirmed it was indeed a security issue, they had already patched the vulnerability.

I would like to thank Brave security for being crystal clear in all emails, very open and discussive about security issues, and for the quick turn around.

Had this not been over the weekend, I think Brave would have patched and pushed this fix same-day.

Open Source Code Programs

I speak to a lot of security researchers, every day.

Mostly on my Twitter @sickcodes 24, sometimes on Discord, and also researcher forums like here, 1.

For other researchers out there, I’d just like to highlight a few things about this disclosure process.

This bug is an Open Source Code program on HackerOne, and you can find the Brave project’s HackerOne program here: 5

Although Brave is designed for Desktops, there are plenty of security programs out there that are meant to run on servers.

The reason I reported the vulnerability through email first, was that I wasn’t really interested in the bounty, I was interested in the CVE.

What actually ended up happening was that MITRE rejected the CVE Request, which is at the top of this post, due to some flaws. They actually contacted Brave first to verify the report which was interesting.

After brainstorming with Brave through email, the bug was actually chiseled out to be the core_p3a_metrics timestamping in the JSON file.

What I would like to show other researchers is that this bug involved no actual code review.

Because this is an Information Exposure vulnerability, it is contextual rather than seeing an XSS alert show up on your screen.

One major advantage of Open Source projects is that there are no duplicates.

When a bug is found, usually within a day or two, there is either a Pull Request, that anyone can view, or a new issue posted, that anyone can view publicly.

If you’re having trouble with duplicates, see if.

Even though a simple timestamp might not appear to be a vulnerability, with some critical thinking, you can imagine some scenarios.

For example, consider this scenario

  • a Human Right’s activist has their disk seized by local authorities.
  • Brave browser’s Local State file is read
  • file shows the last time Tor was used, with nanosecond accuracy.
  • the ISP also has a corresponding timestamp for the person’s Tor usage.

As you can imagine, this could be absolutely devastating for some.

Since this is a physical access vulnerability, the actual exploitability is quite low.

While I consider it medium loss of software integrity on the CVSS calculator, I didn’t want to hype this vulnerability up.

I entered the vulnerability as low security and was awarded a bounty of $100

Here are some Critical Weaknesses I encourage other researchers to consider when looking for bugs, like in programs that they use every day:

  • CWE-200: Information Exposure
  • CWE-201: Information Exposure Through Sent Data
  • CWE-202: Exposure of Sensitive Data Through Data Queries
  • CWE-203: Information Exposure Through Discrepancy
  • CWE-209: Information Exposure Through an Error Message
  • CWE-211: Information Exposure Through Externally-Generated Error Message
  • CWE-212: Improper Cross-boundary Removal of Sensitive Data
  • CWE-213: Intentional Information Exposure
  • CWE-214: Information Exposure Through Process Environment
  • CWE-215: Information Exposure Through Debug Information
  • CWE-226: Sensitive Information Uncleared Before Release
  • CWE-497: Exposure of System Data to an Unauthorized Control Sphere
  • CWE-524: Information Exposure Through Caching
  • CWE-526: Information Exposure Through Environmental Variables
  • CWE-532: Information Exposure Through Log Files
  • CWE-538: File and Directory Information Exposure
  • CWE-598: Information Exposure Through Query Strings in GET Request
  • CWE-612: Information Exposure Through Indexing of Private Data

I’m always happy to chat to researchers, and even collaborate, on vulnerabilities together.

There’s only so many hours in each day and more than one brain is almost always stronger than one.

Hope this shed some light on this simple process of finding this vulnerability.

In essence:

  • Thinking about Brave partners made me look at the partner headers.
  • Looking at partner headers in Tor made me look inside the Tor folder.
  • After exiting the Tor folder, and observing no deletion of the folder, I considered this a vulnerability starting block.
  • Further looking inside the .config led me to find Tor timestamps.
  • Since Tor is a security feature, any bugs in security features are bigger than they might seem.

If you want to ask me any questions about finding bugs, or hacking in general, you can follow me on Twitter, my username there is and the URL is:

@sickcodes 24

Attack of the clones: Git clients remote code execution

Attack of the clones: Git clients remote code execution

Original text


This post is a rather unusual story of a vulnerability that could be leveraged as a supply chain attack and used to attack millions of software developers around the world. It is also a tale of a bug collision that paid a bounty to one reporter and assigned the CVE to another!

The main focus of this blog post is GitHub Desktop. Other Git clients such as GitKraken, Git-Tower and SourceTree were also found to be vulnerable, however these have different exploitation scenarios that require user interaction.

Brief description of the issue

As part of GitHub Desktop’s default repository cloning process, among other actions it calls the executable git-lfs.

From git-lfs’s official page «Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like or GitHub Enterprise.»

git-lfs is then called on the current cloned repository, already present in disk.

A vulnerability was discovered when cloning a repository with a especially crafted file in the root directory. Upon cloning such malicious repository, code execution is achieved with the same privileges as the affected user running GitHub Desktop.

When inspecting the desktop application under Proccess Monitor, it was noticed several actions of git-lfs called a git executable from inside the newly cloned repository.


By placing a malicious git in the root of the malicious repo, a user that clones it will have the malicious git executed by git-lfs, all of this behind the scenes and transparent to the user.

The cloning process happens normally and there is no visual indication that a malicious binary was ran instead of the original git executable.

Root cause analysis

We attempted to understand the root cause of this vulnerability by reading git-lfs’s code. The authors of this write-up tried to put together as much information as possible in order to understand the cause of the problem.


From line 23:

func cloneCommand(cmd *cobra.Command, args &#91;]string) {

    if git.IsGitVersionAtLeast("2.15.0") {
        msg := &#91;]string{
            "WARNING: 'git lfs clone' is deprecated and will not be updated",
            "          with new flags from 'git clone'",
            "'git clone' has been updated in upstream Git to have comparable",
            "speeds to 'git lfs clone'.",

        fmt.Fprintln(os.Stderr, strings.Join(msg, "\n"))

The function above seems to be called when a clone action takes place. Right in the beginning the function calls requireGitVersion() and then checks whether the version is at least 2.15.0.


Line 537:

func requireGitVersion() {
    minimumGit := "1.8.2"

    if !git.IsGitVersionAtLeast(minimumGit) {
        gitver, err := git.Version()
        if err != nil {
            Exit("Error getting git version: %s", err)
        Exit("git version >= %s is required for Git LFS, your version: %s", minimumGit, gitver)

It calls !git.IsGitVersionAtLeast(minimumGit) which can be found in

From line 28:

func IsGitVersionAtLeast(ver string) bool {
    gitver, err := Version()
    if err != nil {
        tracerx.Printf("Error getting git version: %v", err)
        return false
    return IsVersionAtLeast(gitver, ver)

Version() can be found in the same file version.go on line 18:

func Version() (string, error) {
    gitVersionOnce.Do(func() {
        gitVersion, gitVersionErr =
            subprocess.SimpleExec("git", "version")
    return gitVersion, gitVersionErr

The function calling chain is the following:
cloneCommand() -> requireGitVersion() -> IsGitVersionAtLeast() -> Version()

We can see that in line 21 in version.go, there is inside the function Version() a call to subprocess.SimpleExec() — it executes the git binary with the argument «version».

There is an implicit assumption from git-lfs that by executing an external binary, the operating system will first look it up from the system’s PATH environment variable. This is the true root cause of the vulnerability.

Calling the subprocess ‘git’ works as intended in GitHub Desktop for MacOS. In Windows, however, the search order favors the current directory first and only then it searches in the order specified in PATH.


By copying calc.exe, renaming it to git.exe and committing/pushing it into the root of the repository, the attacker sets up the crafted repository that will be used to later exploit users.

When a developer clones this repository using GitHub Desktop for Windows, its subcomponent git-lfs will work on the cloned folder as its current directory, meaning all subsequent calls to the binary ‘git’ will be instead calling the malicious git.exe not the one present in the PATH.


Below is a the video of exploiting this issue on GitHub Desktop for Windows:

Other clients

During our research we noticed SourceTree

  • SourceTree


The vulnerability was simultaneously discovered and researched by Vitor Fernandes and Julio Fort of Blaze Information Security. The independent researcher Dawid Golunski (dobra robota, amigo!) apparently discovered the issue a few days earlier and submitted it to MITRE, while Vitor submitted it to GitHub’s bug bounty.

Vitor collected the bounty from GitHub’s program on HackerOne as his report arrived earlier and Dawid was assigned the CVE.


[3] (Collision with Dawid Golunski)