Skip to content

Detection request: undocumented JAR infector "Solar" (s0lar.net), active Feb-Aug 2026 #778

Description

@Plattio

Describe the bug

Hi — we run a mid-sized network that was hit by a JAR infector we have not found documented anywhere. We know of five affected networks in Germany alone, all found independently. Full write-up with IoCs, structural detection criteria and the decrypted second stage:

https://gist.github.com/Plattio/d40f53c2e9f962746f3c01a1a244b9e4

Short version

Injects a method getTelemetryId()I with flags 0x1041 (ACC_PUBLIC|ACC_BRIDGE|ACC_SYNTHETIC) plus a field telemetryId. Loads an AES-encrypted second stage from https://s0lar.net/routes/0/d.php and starts it via PluginManager.loadPlugin(). Falls back to a local cache in ./logs if the C2 is unreachable, so blocking the domain does not stop it.

String signatures (base64 in the constant pool, plaintext never appears)

czBsYXIubmV0                                  s0lar.net
aHR0cHM6Ly9zMGxhci5uZXQvcm91dGVzLzAvZC5waHA=  https://s0lar.net/routes/0/d.php
bmV0LnMwbGFyLlNvbGFyTG9hZGVy                  net.s0lar.SolarLoader
cC5Bc3luY1RpbWluZ3M=                          p.AsyncTimings

Structural signatures (these survive a domain change)

  • method getTelemetryId()I with access_flags == 0x1041 on a plain int getter
  • field telemetryId
  • 9 nop bytes at the start of a method (javac never emits nop)
  • this starting at offset 9 instead of 0 in LocalVariableTable
  • large method without LineNumberTable while sibling methods in the same class have debug info

Cross-checked against the JDK's own jrt-fs.jar — zero false positives.

Two things that broke our first detection attempts

  1. The injection is not reliably in the main class from plugin.yml. Across 13 infected JARs the host class varied (ConfigManager classes, constructors, printSimpleLogo(), loadConfigValues()). Only 1 of 13 sat in onEnable() of the main class.
  2. The server core can be infected too, with a different style. Our Paperclip jar carried a hand-written patchLibs() in io/papermc/paperclip/Main.class, called as the first instruction of main() — same base64 strings, but no getTelemetryId, no synthetic+bridge, no nop padding.

Persistence outside the plugin folder

Cache file in ./logs, named like a rotated log (<date>-<n>.log.gz) with an invisible U+200B appended. A full plugin replacement does not touch it.

find /path/to/server -name "*$(printf '\xe2\x80\x8b')" -ls

How to reproduce

Not a bug report — this is a detection request for a malware family the scanner does not currently catch.

To reproduce the detection gap: run MCAntiMalware against a plugin JAR infected with this family. It passes clean, because the telling strings are base64-encoded in the constant pool and the injected method is flagged ACC_SYNTHETIC|ACC_BRIDGE, which hides it from most analysis paths.

Details, signatures and structural criteria are in the description above.

Screenshots / Videos

No response

Server Log

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: bugSomething doesn't work as it was intended to.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions