Skip to content

Library

Detection rules

Rules published with the articles. Each card shows what has and has not been tested; test a rule on your own data before relying on it.

7 of 7 rules

  • From PolinRider: A Supply-Chain Loader in 40,136 GitHub Repositories and How the GitHub Investigation Was Verified

    VS Code task that runs node on a disguised file when the folder opens· yaraDownload

    Matches .vscode/tasks.json files with the PolinRider trigger: a folderOpen task whose command runs node on a file with a font, image or made-up extension (.woff2, .ttf, .orc, .lkf and others). Proof additionally requires the target to be a non-empty JavaScript file.

    Status
    experimental
    Family
    PolinRider
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Rewritten on 2 November: the first version matched none of 199 proven tasks, because it expected node and the target as separate JSON strings and knew only font extensions. The proven tasks run node inside one shell command string, and many target made-up extensions such as .orc. This version looks for node followed by a path with a font, image or made-up extension in any folderOpen task. It matched all 199 files it was written against, all 300 held-out files, and none of 88 benign tasks.json files. The proof pass chose the proven files with a similar check (a folderOpen task running node on a non-JavaScript file), so these runs show that the rule finds the proven set; they do not show that it finds variants without those markers.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0Proven PolinRider .vscode/tasks.json files the rule was written against (199)199 of 199 matched
    2026-11-02YARA 4.5.0Proven tasks.json files not used to write the rule, random sample (300)300 of 300 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (1,194 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files, 88 of them tasks.json)0 hits
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits
    rule CMPC_PolinRider_VSCode_Task_Autorun
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "PolinRider trigger: VS Code folderOpen task that runs node on a file with a font, image or made-up extension"
            status = "experimental"
            date = "2026-11-02"
            scope = "Run on .vscode/tasks.json; confirm the target file is JavaScript before acting"
            targets = "woff2 woff ttf otf eot orc lkf lfk llf glb fml svg png jpeg sol: every extension in the proven tasks"
        strings:
            $open = "folderOpen" ascii
            $node = /\bnode(\.exe)?\s+(\\?["'])?(\.{0,2}[\/\\]{1,2})?[\w.\-\/\\\[\]()@]+\.(woff2?|ttf|otf|eot|orc|lkf|lfk|llf|glb|fml|sol|svg|png|jpe?g|gif|ico|bmp|webp)\b/ ascii
        condition:
            filesize < 200KB and $open and $node
    }
  • From PolinRider: A Supply-Chain Loader in 40,136 GitHub Repositories and How the GitHub Investigation Was Verified

    Obfuscated loader appended to a build configuration file· yaraDownload

    Matches files that carry the PolinRider loader on one long line: the global['_V'] or global['!'] marker, or an _$_xxxx obfuscator name, followed by at least 300 more characters on the same line. In build configurations the line is usually pushed off-screen by spaces or tabs; the rule does not depend on that padding.

    Status
    experimental
    Family
    PolinRider
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Rewritten on 2 November: the first version found 8 of 200, because it required files over 20 KB and a 400-character run of spaces, and it hit 13 benign files. This version drops both conditions and requires the marker to be followed by at least 300 characters on the same line. It matched all 200 files it was written against, all 297 held-out files, and no benign file, 1,685 of them configuration files. The proof pass chose the proven files with the same markers plus a long line or a long run of spaces, so these runs show that the rule finds the proven set; they do not show that it finds variants without those markers.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0Proven PolinRider files carrying the appended loader, mostly build configurations (200; the files the rule was written against)200 of 200 matched
    2026-11-02YARA 4.5.0Proven files of the same set not used to write the rule, random sample (297; 3 no longer available)297 of 297 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (1,196 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files, 1,673 of them *.config.* files)0 hits
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits
    rule CMPC_PolinRider_BuildConfig_Appended_Loader
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "PolinRider loader appended to a file: global['_V'] / global['!'] or an _$_xxxx name, then the rest of the loader on the same line"
            status = "experimental"
            date = "2026-11-02"
            scope = "Run on *.config.(js|ts|mjs|cjs), postcss/tailwind/vite/next/eslint configurations and font-named files that read as text"
        strings:
            $g = /global\[\s*['"](_V|!)['"]\s*\][^\n\r]{300}/ ascii
            $s = /_\$_[0-9a-f]{4}[^\n\r]{300}/ ascii
        condition:
            filesize < 5MB and any of them
    }
  • From How the GitHub Investigation Was Verified

    atob on an environment variable, evaluated with eval or Function· yaraDownload

    Matches server or build files that decode AUTH_API_KEY (or a sibling variable) from the environment and evaluate the result. The decoded value in .env is a URL; the article lists the hosts seen.

    Status
    experimental
    Family
    AUTH_API_KEY loader
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Matched every proven file in both samples (200 and 172), no other proven set and no benign file. The proof pass chose the proven files with the same strings (atob on process.env plus eval or Function), so these runs show that the rule finds the proven set; they do not show that it finds variants without those markers.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0Proven AUTH_API_KEY loader files, random sample (200)200 of 200 matched
    2026-11-02YARA 4.5.0Proven AUTH_API_KEY loader files not in that sample (172)172 of 172 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (1,321 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files)0 hits
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits
    rule CMPC_AuthApiKey_Env_Eval_Loader
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "AUTH_API_KEY loader: atob(process.env.<VAR>) passed to eval or Function"
            status = "experimental"
            date = "2026-11-01"
            scope = "Run on *.ts, *.js, *.mjs, *.cjs in application and build-config paths"
        strings:
            $decode = /atob\(\s*process\.env\.[A-Z_]*API_KEY[A-Z_]*\s*\)/ ascii
            $eval = /\beval\s*\(/ ascii
            $fn = /\bnew\s+Function\s*\(|\bFunction\s*\(/ ascii
        condition:
            filesize < 512KB and $decode and ($eval or $fn)
    }
    
  • From How the GitHub Investigation Was Verified

    Semgrep rule: code fetched from an environment-variable URL and evaluated (JavaScript/TypeScript)· semgrepDownload

    Taint rule: follows the value of atob(process.env.<VAR>) through the request that uses it and the response it returns, and reports when that data reaches eval, new Function or Function. For CI pipelines that scan source before build.

    Status
    experimental
    Family
    AUTH_API_KEY loader
    Platforms
    none recorded
    Engines tested
    Semgrep 1.183.0
    Known benign cases
    none recorded
    Testing
    Rewritten on 2 November: the first version matched none of 200, because the decoded value is a URL: it is fetched, and eval runs the response, not the decoded value. This version is a taint rule that follows the value through the request and its response to eval, new Function or Function. It matched 199 of the 200 files it was written against (the miss has the loader commented out, so not flagging it is right) and 166 of 172 held-out files (2 misses are Markdown files, 3 did not parse fully, 1 parsed file with the usual structure was missed for a reason not identified). No benign hits, but only 17,474 benign JavaScript and TypeScript files were scanned: Semgrep's default ignore list skips node_modules. The proof pass chose the proven files with atob on process.env plus eval or Function, so these runs show that the rule finds the proven set; they do not show that it finds variants without those markers.

    Check results

    DateEngineCorpusResult
    2026-11-02Semgrep 1.183.0Proven AUTH_API_KEY loader files the rule was written against (200; 3 parsed only partially)199 of 200 matched; the miss has the loader commented out
    2026-11-02Semgrep 1.183.0Proven AUTH_API_KEY loader files not used to write the rule (172)166 of 172 matched: 2 are Markdown files Semgrep does not scan, 3 did not parse fully, 1 missed for a reason not identified
    2026-11-02Semgrep 1.183.0The other proven sets of this investigation (1,321 files; 560 of them JavaScript or TypeScript that Semgrep scanned)0 matches
    2026-11-02Semgrep 1.183.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (Semgrep scanned 17,088 JavaScript/TypeScript files; 8 timed out)0 hits
    2026-11-02Semgrep 1.183.0Benign files in the analysis VM (Semgrep scanned 386: its default ignore list skips node_modules; 3 timed out or failed to parse)0 hits
    rules:
      - id: cmpc-auth-api-key-env-fetch-eval
        message: Code fetched from a URL decoded out of an environment variable reaches eval or Function (AUTH_API_KEY loader pattern)
        severity: ERROR
        languages: [javascript, typescript]
        metadata:
          author: Cristian Chiriac / CMPC Research
          status: experimental
          date: 2026-11-02
        mode: taint
        pattern-sources:
          - pattern: atob(process.env.$VAR)
        pattern-sinks:
          - pattern: eval(...)
          - pattern: new Function(...)
          - pattern: Function(...)
    
  • From How the GitHub Investigation Was Verified

    Repository-shipped post-checkout hook that pipes a download into a shell· yaraDownload

    Matches .githooks/post-checkout and .github/githooks/post-checkout files that fetch a script with curl or wget and run it. Git does not run hooks from these paths by itself; the repositories set core.hooksPath or ship a .git directory in an archive.

    Status
    experimental
    Family
    Contagious Interview
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Matched every available proven hook and no benign file, including git's own sample hooks. Six files is a small set. YARA warns that $bg may slow scanning.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0Proven repository-shipped post-checkout hooks (6 of the 7 sampled; 1 no longer available)6 of 6 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (917 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files)0 hits (includes the clones' .git/hooks samples)
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits
    rule CMPC_Git_Hook_PostCheckout_Pipe_To_Shell
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "Committed post-checkout hook downloading and executing a remote script"
            status = "experimental"
            date = "2026-11-01"
            scope = "Run on files named post-checkout under .githooks/ or .github/githooks/"
        strings:
            $fetch = /\b(curl|wget)\b[^\n]{0,200}(https?:\/\/|\/settings\/)/ ascii
            $pipe = /\|\s*(sh|bash|zsh)\b/ ascii
            $flag = "flag=" ascii
            $bg = /(\bnohup\b|&\s*$|setsid)/ ascii
        condition:
            filesize < 8KB and $fetch and $pipe and ($flag or $bg)
    }
    
  • From How the GitHub Investigation Was Verified

    Published Discord token stealer reading local storage and posting to a webhook· yaraDownload

    Matches source files that hold a complete Discord webhook URL and read the Local Storage leveldb folder of Discord or a browser, where Discord keeps its login token. These are published tools; the owner is not necessarily a victim or an operator.

    Status
    experimental
    Family
    Discord token stealer
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Rewritten on 2 November: the first version found 2 of 196, because it required a literal token-shaped string; most of these tools hold the token pattern as regular-expression source instead. This version requires a complete webhook URL and a Local Storage or leveldb read. It matched all 196 files it was written against, the one remaining proven file, and no benign file. With one held-out file, this is close to a test on the development set only. The proof pass chose the proven files with the same strings (a webhook URL plus Local Storage or leveldb), so these runs show that the rule finds the proven set; they do not show that it finds variants without those markers.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0Proven Discord token stealer files the rule was written against (196)196 of 196 matched
    2026-11-02YARA 4.5.0The one other proven Discord token stealer file1 of 1 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (1,496 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files)0 hits
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits
    rule CMPC_Discord_Token_Stealer_LevelDB_Webhook
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "Discord token stealer: hard-coded webhook URL plus a read of Discord or browser Local Storage (leveldb)"
            status = "experimental"
            date = "2026-11-02"
            scope = "Run on source files (py, js, ps1, cs, java, rs); expect hits in research corpora too"
        strings:
            $hook = /discord(app)?\.com\/api\/webhooks\/\d{15,}\/[\w\-]{30,}/ ascii
            $ls1 = "Local Storage" ascii
            $ls2 = "leveldb" ascii nocase
        condition:
            filesize < 5MB and $hook and any of ($ls*)
    }
    
  • From How the GitHub Investigation Was Verified

    Python file that decrypts an embedded Fernet stage and executes it· yaraDownload

    Matches Python sources that call exec on Fernet(key).decrypt(ciphertext) with the key in the same file. One proven repository; the same shape is common in sample collections, so check the repository is not a research corpus.

    Status
    experimental
    Family
    Python Fernet loader
    Platforms
    none recorded
    Engines tested
    YARA 4.5.0
    Known benign cases
    none recorded
    Testing
    Matched the only proven file and no benign file. With one proven file, recall cannot be estimated.

    Check results

    DateEngineCorpusResult
    2026-11-02YARA 4.5.0The one proven Python Fernet loader file1 of 1 matched
    2026-11-02YARA 4.5.0The other proven sets of this investigation (922 files)0 matches
    2026-11-02YARA 4.5.0Benign source code: shallow clones of next.js, microsoft/vscode and vscode-extension-samples (56,864 files)0 hits
    2026-11-02YARA 4.5.0Benign files in the analysis VM: npm tools (19,474), Python standard library and packages (29,360), git sample hooks (14)0 hits, including 29,360 Python library files
    rule CMPC_Python_Fernet_Exec_Stage
    {
        meta:
            author = "Cristian Chiriac / CMPC Research"
            description = "exec(Fernet(<literal key>).decrypt(b'<ciphertext>')) in a Python source file"
            status = "experimental"
            date = "2026-11-01"
            scope = "Run on *.py; exclude test, fixture, sample and dataset paths"
        strings:
            $import = "from cryptography.fernet import Fernet" ascii
            $exec = /exec\s*\(\s*Fernet\s*\(\s*b?['\"][A-Za-z0-9_\-=]{40,48}['\"]\s*\)\s*\.decrypt\s*\(\s*b?['\"][A-Za-z0-9_\-=]{200,}/ ascii
        condition:
            filesize < 4MB and $import and $exec
    }