Malicious code in cyber security is any script, binary, or embedded instruction written to damage a system, steal data, or hand control to an attacker without the owner's consent. That definition is broad on purpose. The question of what is malicious code in cyber security used to have a tidy answer built around standalone viruses, but the interesting cases now arrive inside things you asked for: a package update, a build plugin, a browser extension, a container base image.
I spend most of my week reading through dependency trees and CI logs, and the pattern that keeps repeating is that malicious code rarely announces itself. It looks like ordinary code until it does one specific thing at one specific moment.
The Main Families and What They Actually Do
It helps to sort malicious code by behavior rather than by scary name, because the names overlap and marketing has muddied them.
A virus attaches itself to a legitimate file and spreads when that file runs. A worm self-propagates across a network without needing a host file. A trojan presents as something useful, a game or a utility, and carries a hidden payload. Ransomware encrypts data and demands payment. Spyware and keyloggers quietly exfiltrate what you type and where you go. A rootkit buries itself below the level where normal tools can see it, hooking the operating system so that a compromised machine reports itself as clean.
Two categories matter more than they used to. Fileless malware lives in memory and abuses legitimate tools already on the box, like PowerShell or WMI, so there is no file on disk for a scanner to flag. And logic bombs sit dormant until a trigger fires, a date, a missing employee record, a specific config value. The infamous case pattern is a contractor who plants code that wipes systems if their own account is ever disabled.
Where Malicious Code Hides in a Software Supply Chain
The distribution method has shifted. A decade ago you worried about an email attachment. Today the higher-leverage path is the software you build on.
Typosquatted packages are the everyday version of this. An attacker publishes reqeusts or python-sqlite or a lookalike npm scope, waits for a fatigued developer to fat-finger an install, and runs an install script that scrapes environment variables. Dependency confusion pushes a public package with the same name as your internal one and a higher version number, so the resolver reaches for the attacker's copy. Then there is the compromise of a legitimate, popular package, where an attacker takes over a maintainer account and ships a poisoned point release to everyone who trusts it.
The uncomfortable truth is that most of this malicious code executes with the same permissions as your build. A postinstall hook or a Gradle plugin runs arbitrary code on your CI runner, which usually has cloud credentials sitting right there. This is why dependency review is a security control and not a nicety. An SCA tool can flag a package that suddenly gained an install script or started reaching out to a network, but the discipline of reviewing what enters your tree matters just as much as the tooling.
Static and Dynamic Detection
There are two complementary ways to find malicious code, and mature programs use both.
Static analysis inspects code or binaries without running them. Signature-based detection matches known-bad byte patterns, which is fast and reliable for cataloged threats but useless against anything novel or lightly obfuscated. Heuristic and structural analysis looks for suspicious constructs: a "utility" library that base64-decodes a blob and passes it to eval, an image-processing package that opens a socket, obfuscated strings that decode into URLs.
Dynamic analysis runs the suspect code in a sandbox and watches what it does. This is where fileless and heavily obfuscated samples reveal themselves, because behavior is harder to fake than structure. You look for the actions that malicious code cannot avoid taking: writing to autostart locations, spawning shells, contacting command-and-control infrastructure, enumerating credentials. For web-facing code, dynamic application security testing exercises the running application to surface injected or backdoored behavior that a source scan would miss.
Neither approach is complete on its own. Static analysis is blind to runtime behavior, and dynamic analysis only sees the paths you actually trigger.
Obfuscation Is the Tell, Not the Problem
Attackers obfuscate to slip past static detection, but obfuscation is itself a strong signal. Legitimate open source is overwhelmingly readable. When a small npm package ships minified code with no corresponding source, or a Python file constructs function calls out of character-code arithmetic, that mismatch between claimed purpose and observed complexity is worth investigating on its own.
I treat any of these as a reason to stop and read carefully: dynamic code execution built from decoded strings, network calls in a package that has no business making them, install-time scripts in a library that should be pure data, and sudden additions of native binaries to a previously pure-source project.
Practical Defenses That Hold Up
Prevention beats detection when you can get it. A few controls carry most of the weight:
- Pin and lock dependencies. Commit a lockfile with hashes so the exact bytes you reviewed are the bytes you ship. A floating version range is an open door to a poisoned release.
- Least privilege on CI. Build runners should not hold long-lived cloud admin credentials. Scope tokens tightly and rotate them, so a malicious postinstall script steals something short-lived and narrow.
- Egress filtering. If your build machines cannot reach arbitrary internet hosts, a lot of exfiltration simply fails.
- Code signing and verification. Verify signatures on artifacts and base images so a swapped binary is rejected before it runs.
- A generated SBOM. Knowing exactly what is in your build means that when a package is later revealed as malicious, you can answer "are we affected" in minutes instead of days.
None of these is exotic. The failures I investigate almost always trace back to a skipped basic, not a missing advanced control. If you want to go deeper on threat modeling and detection, our security academy walks through several of these in more detail.
FAQ
What is the difference between malicious code and a vulnerability?
A vulnerability is an unintentional flaw in otherwise legitimate code that an attacker can abuse. Malicious code is written with harmful intent from the start. A buffer overflow is a vulnerability; a package that ships a credential-stealing install script is malicious code. Both can lead to compromise, but you handle them differently: you patch a vulnerability, and you remove and report malicious code.
Can antivirus software detect all malicious code?
No. Signature-based antivirus reliably catches known, cataloged threats but struggles with novel samples, obfuscated payloads, and fileless techniques that abuse legitimate system tools. Effective defense layers signature detection with behavioral analysis, dependency review, and least-privilege controls rather than relying on any single scanner.
How does malicious code get into open source dependencies?
Common paths include typosquatting a popular package name, dependency confusion attacks that exploit resolver behavior, and takeover of a legitimate maintainer's account to publish a poisoned release. Because build tools execute install scripts and plugins with full permissions, this code often runs directly on CI runners that hold sensitive credentials.
What is fileless malware and why is it hard to catch?
Fileless malware runs entirely in memory and abuses tools already present on the system, such as PowerShell, WMI, or shell interpreters, rather than dropping an executable to disk. Because there is no file to scan, signature-based detection misses it, and finding it requires watching runtime behavior and monitoring how legitimate tools are being invoked.