/*
   SL-ADV-2026-WP-001 v15.3 — Remus Stealer (WP-002)
   ClickFix / EtherHiding delivery chain
   Dispensight / SecureLeaf — secureleaf.dispensight.com — TLP:CLEAR
   2026-09-02

   Consolidated ruleset. Supersedes and fully contains v15.1 and v15.2 —
   every v15.2 rule is carried forward unchanged or improved; none were dropped.

   CHANGES SINCE v15.1
     Sources added
       * Corpus.a3x decompiled and its obfuscation resolved (loader stage).
       * Terminal native core recovered by RC4+LZNT1 unwrapping of the a3x blob.
       * Live detonation capture, 2026-09-02, 23,343 frames with embedded TLS DSB.

     Corrections
       * Build/campaign tag is 32 hex chars, not 33. The leading 'd' seen in an
         earlier pass was adjacent binary data caught by a strings run, not part
         of the constant. The corrected value is transmitted verbatim as the
         "tag=" field of the C2 registration beacon, which ties the a3x-recovered
         image directly to the cdire.shop C2.
       * WP002_Remus_native_core_hint renamed to ..._buildA_..., anchored at
         pe.entry_point (the floating 4-byte match was FP-prone) and documented as
         covering the earlier build only. It does not match the a3x-embedded build.
       * C2 body schemas are message types of one protocol, not wave variants.

     Rules added
       * Native core build B (a3x-embedded, compiled 2026-08-28).
       * Build/campaign tag, memory- and wire-scannable.
       * AutoIt loader source artefacts and the NXDOMAIN sentinel.
       * Obfuscator structural profile (family-level hunting rule).
       * C2 application-protocol frames.
       * PowerShell stager body and the stager ZIP.

   No rule in this file reproduces loader logic; all anchors are inert markers.
*/

import "hash"
import "pe"

rule WP002_Remus_IExpress_SFX_dropper
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "IExpress SFX wrapping AutoIt3.exe + compiled a3x"
        sha256 = "2a1d78fb02d047605ac2400b18e4bc902fe752bde24ccee0aa5c46def9787680"
        tlp = "CLEAR"
    strings:
        $sfx1 = "IntroductionChassis.exe" ascii
        $sfx2 = "Corpus.a3x" ascii
        $sfx3 = "AutoIt3.exe" ascii
        $cab  = "MSCF" ascii
    condition:
        uint16(0) == 0x5A4D and $cab and 2 of ($sfx*)
}

rule WP002_Remus_compiled_AutoIt_a3x
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Compiled AutoIt payload (AU3!EA06), packed"
        sha256 = "74a517b6b24b47a45101af70b9c5ca3572502e3e027fd69badc8dc3757a4a67f"
    strings:
        $au3 = "AU3!EA06" ascii
    condition:
        $au3 at 176 or (uint32be(0)==0xca087e26 and $au3)
}

rule WP002_Remus_ClickFix_stage2_config
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "asseload ClickFix stage-2 XOR/base64 loader"
    strings:
        $xorkey = "oipCQd0DIbeF" ascii
        $fn     = "_442b" ascii
        $atob   = ".charCodeAt(i%" ascii
    condition:
        2 of them
}

rule WP002_EtherHiding_BSC_injector
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "BSC-testnet EtherHiding retrieval of asseload injector"
    strings:
        $contract = "ae5d8eec6ea8366e7922c93abc908ff96eb08034" ascii nocase
        $selector = "0xe2179b8e" ascii nocase
        $rpc      = "data-seed-prebsc-1-s1.binance.org" ascii
    condition:
        any of them
}

rule WP002_Remus_C2_exfil_beacon
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Remus stealer HTTP exfil — POST /invoices, github.com host spoof, both param schemas"
    strings:
        $p   = "POST /invoices" ascii
        $h   = "Host: github.com" ascii
        $v1a = "access_token=" ascii
        $v1b = "&debug=" ascii
        $v2a = "tag=" ascii
        $v2b = "&exp=" ascii
        $v2c = "&hwid=" ascii
        $dom = "cdire.shop" ascii nocase
    condition:
        ($p and $h and (all of ($v1*) or all of ($v2*))) or $dom
}

/* --- native core, build A (earlier image) --- */

rule WP002_Remus_native_core_buildA_2026_08_pre
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Terminal native x64 Remus core, BUILD A (the image dumped from PID 5528 in v15.1). Prologue is now anchored at pe.entry_point; floating 4-byte matching in v15.1 was FP-prone. Does NOT match the a3x-embedded build B — see WP002_Remus_native_core_buildB_20260828."
        text_sha256 = "eba3f014bd57d992c69a56854c0f8ac07b665d2721053edd88db144e5814c138"
        image_base = "0x140000000"
        confidence = "medium"
    strings:
        // observed at EP 0x1400021c0 in build A: lea ecx,[rax+8]; jmp
        $ep_a = { 8D 48 08 E9 }
    condition:
        uint16(0) == 0x5A4D and pe.is_pe
        and pe.image_base == 0x140000000
        and $ep_a at pe.entry_point
}


rule WP002_Remus_native_core_buildB_20260828
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Terminal native x64 Remus core, BUILD B — recovered by RC4+LZNT1 unwrapping of the blob embedded in Corpus.a3x. Same ImageBase and same EP RVA (0x21c0) as build A but different code; build A's prologue anchor does not match this wave."
        sha256 = "6e3e5e0d169a79e148f0e73bfee7cad84370069ca9fbedd69e506ffb7780fce9"
        md5 = "3624cb665a45bdea14b3e5065121ab1b"
        text_sha256 = "11e2d6f7272175f6f2c4ed794c0d94c39b8b8a1ffe180bf3b1dedaa71584ff94"
        rdata_sha256 = "0231035731ceeb06826b433bbebeb2967f1289b1e9a5e51a511b9072bf04ed45"
        imphash = "754318d99f6bf9d00342a1a491eb8242"
        compile_ts = "2026-08-28T11:51:22Z"
        image_base = "0x140000000"
        entry_point_rva = "0x21c0"
        size = "227328"
        confidence = "high"
    strings:
        // EP prologue, build B: sub rsp,58h ; mov ecx,<seed imm32>
        $ep_b   = { 48 83 EC 58 B9 ?? ?? ?? ?? E8 }
        // inert .rdata markers, NUL-terminated
        $tag    = "355039cceb3bd77c4ef50905dc6c375f" ascii
        $bdate  = "28.08.2026" ascii
    condition:
        uint16(0) == 0x5A4D and pe.is_pe
        and pe.image_base == 0x140000000
        and (
              pe.imphash() == "754318d99f6bf9d00342a1a491eb8242"
              or ($ep_b at pe.entry_point and any of ($tag, $bdate))
              or all of ($tag, $bdate)
            )
}

rule WP002_Remus_core_build_tag_memory
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Build/campaign tag and build-date marker carried in the native core .rdata. v15.3: the tag is 32 hex chars (v15.2 said 33 — the leading 'd' was adjacent binary data) and is sent verbatim as the tag= field of the C2 registration beacon. Survives manual mapping — scan process memory dumps of hollowed AutoIt3.exe / TapiUnattend.exe."
        scope = "memory, network"
        verified = "2026-09-02 capture, frame 19879"
    strings:
        // NOT fullword: in .rdata the tag abuts preceding binary data
        $tag   = "355039cceb3bd77c4ef50905dc6c375f" ascii wide
        $bdate = "28.08.2026" ascii wide
    condition:
        $tag or all of them
}


rule WP002_AutoIt_loader_source_artifacts
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Corpus.a3x loader, source-level anchors. Scope: decompiled/deobfuscated AU3, AutoIt script memory, and unpacked script buffers. NOT expected to hit the packed a3x on disk (EA06 string table is encoded) — use WP002_Remus_compiled_AutoIt_a3x for that."
        scope = "source, memory"
        confidence = "high"
    strings:
        // NXDOMAIN sentinel: loader aborts if a wildcard resolver answers this label
        $sentinel = "BLKcXXxNNJsPfGATrtzbZZxCm" ascii
        // stage-decrypt key literal (ASCII digits, passed through Binary())
        $rc4key   = "57289866169362989668663919390480987" ascii
        // hollowing target fallback
        $target   = "\\TapiUnattend.exe" ascii
        // ntdll rebuild sources
        $kd1      = "\\KnownDlls\\ntdll.dll" ascii
        $kd2      = "\\KnownDlls32\\ntdll.dll" ascii
        // telemetry-blinding export names
        $etw1     = "EtwEventWriteFull" ascii
        $etw2     = "EtwEventWriteTransfer" ascii
        $amsi     = "AmsiScanBuffer" ascii
        // guard-rail process names
        $g1       = "SandboxieRpcSs.exe" ascii
        $g2       = "VboxTray.exe" ascii
        $g3       = "bdagent.exe" ascii
        $g4       = "avastui.exe" ascii
    condition:
        $sentinel
        or $rc4key
        or (all of ($kd*) and any of ($etw*) and $amsi)
        or ($target and 2 of ($g*) and any of ($etw*))
}

rule WP002_AutoIt_loader_nxdomain_sentinel
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Standalone high-confidence anchor: the loader resolves microsoft.com (must succeed) and a fixed nonsense label (must fail). A wildcard/sinkholing resolver answering the sentinel causes the loader to abort. The label is a fixed 25-char string reused as both labels of the FQDN."
        note = "Also deploy as a DNS-telemetry hunt — see Sigma rule secureleaf00009."
        confidence = "high"
    strings:
        $s1 = "BLKcXXxNNJsPfGATrtzbZZxCm.BLKcXXxNNJsPfGATrtzbZZxCm" ascii wide nocase
        $s2 = "BLKcXXxNNJsPfGATrtzbZZxCm" ascii wide nocase
    condition:
        $s1 or #s2 >= 2
}

rule WP002_durable_RSA1024_C2_key
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Durable WP-002 RSA-1024 CAPI PUBLICKEYBLOB (e=65537). Runtime-resolved in PowerShell stage; modulus SHA-256 dc4cb858…febfac6f. Confirmed absent from the a3x static image in v15.3 — scan process memory dumps."
    strings:
        // CAPI PUBLICKEYBLOB header: bType=06 bVer=02 reserved=0000 aiKeyAlg=0000a400 "RSA1" bitlen=0400 exp=00010001
        $blob = { 06 02 00 00 00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00 }
    condition:
        $blob
}

rule WP002_AutoIt_obfuscator_profile
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Structural profile of the obfuscator applied to Corpus.a3x, matched on the STILL-OBFUSCATED decompiled AU3 (before string recovery). Family-level, not campaign-level: control-flow flattening into While+Switch state machines with arithmetic-computed case labels, every literal routed through one repeating-key XOR decode function, and dense junk DllCall/Execute filler."
        scope = "source (decompiled a3x), memory"
        confidence = "medium"
        note = "Hunting rule. Expect hits on unrelated samples sharing this commodity AutoIt obfuscator; triage with WP002_AutoIt_loader_source_artifacts after string recovery."
    strings:
        // literal + key pair fed to the single decode routine
        $decode  = /\("0x[0-9A-Fa-f]{8,}", "[A-Za-z]{4,20}"\)/
        // arithmetic-computed switch dispatch
        $case1   = "Case BitXOR(0x" ascii
        $case2   = "Case BitShift(0x" ascii
        // junk-call filler characteristic of this packer
        $junk1   = "\"IsProcessorFeaturePresent\"" ascii
        $junk2   = "\"PathIsDirectoryW\", \"wstr\"" ascii
        $junk3   = "Execute(\"StringIsFloat('" ascii
        $junk4   = "Execute(\"DriveGetSerial('" ascii
        $au3     = "@AutoItX64" ascii
    condition:
        $au3
        and #decode > 200
        and (#case1 + #case2) > 200
        and 3 of ($junk*)
}


rule WP002_Remus_C2_protocol_frames
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "Remus C2 application protocol, corrected in v15.3. The two body schemas are NOT wave variants as stated in v15.1 — they are distinct message types inside one session: a single tag/exp/hwid registration, numbered access_token/step markers, access_token/debug status frames, and multipart bulk uploads with a fixed part layout. Observed 83 frames in one 90-second run."
        scope = "pcap, proxy body capture, memory"
        verified = "2026-09-02 capture — 1x register, 7x step, 69x debug, 6x multipart"
    strings:
        $uri     = "POST /invoices" ascii
        $spoof   = "Host: github.com" ascii
        // registration frame
        $reg1    = "tag=" ascii
        $reg2    = "&exp=" ascii
        $reg3    = "&hwid=" ascii
        // session frames
        $sess1   = "access_token=" ascii
        $sess2   = "&debug=" ascii
        $sess3   = "&step=" ascii
        // multipart bulk upload part layout
        $mp1     = "name=\"access_token\"" ascii
        $mp2     = "name=\"type\"" ascii
        $mp3     = "name=\"file\"; filename=\"data\"" ascii
        // non-browser header order: Host appears AFTER Content-Type
        $hdr     = "Cache-Control: no-cache\r\nConnection: Keep-Alive\r\nPragma: no-cache\r\nContent-Type: " ascii
    condition:
        ($uri and $spoof)
        or all of ($reg*)
        or all of ($mp*)
        or ($hdr and $spoof)
        or ($sess1 and any of ($sess2, $sess3) and $uri)
}

rule WP002_Remus_PowerShell_stage_coms
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "The 243-byte PowerShell body served from 193.221.200.66:4139/tools/soft/coms. Recovered in full from the capture; it supplies the host artefacts the advisory previously lacked (%APPDATA%\\c.zip, %APPDATA%\\c\\)."
        sha256 = "64aa3e37acb9efe05122614e0c1a63f85c264d8adecb845059fad7dbb60c7426"
        scope = "script, memory, proxy body capture"
    strings:
        $u1 = "193.221.200.66:4139/tools/soft/file" ascii wide nocase
        $u2 = "/tools/soft/coms" ascii wide nocase
        $a1 = "$env:APPDATA" ascii wide nocase
        $a2 = "c.zip" ascii wide nocase
        $a3 = "Expand-Archive" ascii wide nocase
        $a4 = "IntroductionChassis.exe" ascii wide nocase
    condition:
        any of ($u*) or (3 of ($a*))
}

rule WP002_Remus_stage_archive
{
    meta:
        author = "Dispensight/SecureLeaf"
        advisory = "SL-ADV-2026-WP-001 v15.3"
        description = "ZIP delivered from /tools/soft/file (served with Content-Type: text/plain — mismatch is itself a proxy signal). Contains only IntroductionChassis.exe."
        sha256 = "97860fba4e4d1928154390dd04762f3566e3e5b72300fc7ed7f9e7219faf45b5"
        size = "1621425"
        contains_sha256 = "2a1d78fb02d047605ac2400b18e4bc902fe752bde24ccee0aa5c46def9787680"
    strings:
        $zip  = { 50 4B 03 04 }
        $name = "IntroductionChassis.exe" ascii
    condition:
        $zip at 0 and $name and filesize < 3MB
}
