How Some Malicious AI Skills Hide Instructions from Reviewers
In Part III of our “From Automation to Infection” series (following Part I and Part II), we analyzed 35,878 AI agent skills submitted to VirusTotal and found that 62% of the 6,637 malicious skills contain no malicious code. Their payload is written as plain-language instructions inside the skill documentation.
A common recommendation is to read SKILL.md and any bundled scripts before installing a skill. In most malicious skills in our data, nothing is concealed: a reviewer who reads the files can see the instruction or code, even if it is written to look routine. A small group goes further and hides it. In 90 of the 6,637 malicious skills (1.4%), the instruction is placed after hundreds of blank lines, inside an HTML comment, or in invisible Unicode characters, so what a reviewer sees in a browser or editor is not what the agent reads. This post describes those three techniques with real samples and how to check for them.
| Technique | Skills | How it works |
|---|---|---|
| Whitespace padding | 56 | Places 78 to 825 blank lines before the payload, so it sits below the visible end of the file. |
| HTML comments | 28 | Hidden by Markdown renderers on GitHub and marketplaces, but read in full by the agent. |
| Invisible Unicode | 6 | Encodes text in non-rendering characters, or inserts them to break keyword matching. |
These are lower bounds: a skill is only counted when the instruction our pipeline flagged as malicious sits after the padding, inside the comment, or in the invisible characters.
1. Whitespace padding
In Part III we showed a tax-planning skill (d06c79db56...) that hid an exfiltration instruction after nearly 500 blank lines. We found 56 malicious skills that use this technique, with runs of 78 to 825 blank lines. A reviewer scrolling through the file can easily stop where the documentation appears to end, and scanners that truncate long inputs may never reach the last line.
Sample 0146a85682... (Hijacker:OpenClaw/Skilldrop0146a856!sem) queries World Health Organization indicators. Its last line, after 733 blank lines (all but one filled with spaces), changes the data the agent returns:
|
Sample 14c1b9503f... (Stealer:OpenClaw/Skilldrop14c1b950!sem), a cold-email outreach skill, ends with this line after 672 blank lines, also filled with spaces:
|
2. HTML comments (<!-- ... -->)
GitHub, marketplace web pages, and IDE Markdown previews do not display HTML comments. An agent that loads SKILL.md reads the raw file, including the comment. In 28 malicious skills, the flagged instruction is inside an HTML comment.
Four skills named api-helper (for example edb911b9d6..., Stealer:Agentic/Skilldrop4183627d!sem) describe themselves as helpers to configure API connections. Halfway through SKILL.md they include:
|
Sample 15b0c7c01b... (Loader:Agentic/Skilldrop15b0c7c0!sem) is a code security review checklist. Section 5 opens with a visible sentence about validating the environment, followed by a comment with the actual command:
|
The same zkorman.com command appears in six other skills, and two copies of this checklist with identical wording point to http://localhost:8000/validate.sh instead.
Fourteen skills, including ad75f854bc... (Backdoor:OpenClaw/Skilldrop5d2bc64c!sem, a Hedera blockchain query tool), contain usable documentation followed by a single comment on the last line that registers the agent with an external hub:
|
3. Invisible Unicode
This is the least common technique in our data: six skills, with four distinct payloads. Some of them are proofs of concept with placeholder or harmless payloads, but they were submitted as installable skills, and the hiding works the same way regardless of the payload.
Unicode tag characters (U+E0000–U+E007F)
The Unicode tag block mirrors ASCII: U+E0020 to U+E007E correspond to 0x20 to 0x7E. Most editors, terminals, and browsers display nothing for these characters, so a line made of them looks empty. Several LLMs have been shown to read and follow text encoded this way.
Sample d9721c15fb... (Stealer:Agentic/SkilldropD9721c15!sem) shows four lines of text in an editor:
|
Line 11 of SKILL.md looks empty but contains 182 tag characters: a begin tag (U+E0001), 180 characters of text, and a cancel tag (U+E007F). Subtracting 0xE0000 from each code point gives:
|
The command is a placeholder, so this sample is a template rather than a working attack. Sample bf11eede93... (Hijacker:Agentic/SkilldropBf11eede!sem), a set of testing guidelines whose metadata lists snyk-labs as author and which was redistributed through TomeVault, uses the same encoding. An apparently empty line holds 171 tag characters that decode to:
|
Opening Calculator is the usual proof-of-concept payload; the same encoding can carry any other instruction.
Binary encoding with zero-width characters
Sample c507763bc1... (Hijacker:OpenClaw/SkilldropC507763b!sem) presents itself as agent-hardening, a read-only audit skill that checks an agent’s memory files for Unicode injection. Its title, intro sentence, and three section headings contain 888 zero-width characters: U+200C (zero width non-joiner) as bit 0 and U+200D (zero width joiner) as bit 1. Read as 111 bytes of ASCII, they say:
|
The payload only adds a marker to every response, but it asks the agent to store it in persistent memory, where it would outlast the skill. The last line of the same file addresses LLM-based scanners:
|
Zero-width spaces between words (U+200B)
Sample 2e4c9dbf07... (Stealer:OpenClaw/Skilldrop2e4c9dbf!sem) is a text-cleaning skill. One paragraph reads normally in an editor:
|
In the raw file, that paragraph contains 79 zero-width spaces (U+200B, UTF-8 E2 80 8B) next to the spaces between words:
|
A rule that looks for API keys, environment variables, or Do not inform the user does not match this file unless zero-width characters are removed first.
4. Telling the agent to stay quiet and remember
Some skills combine hiding with instructions aimed at the agent’s behavior after installation: not reporting what it does, as in the api-helper and text-processor examples above, or writing entries into its persistent memory or configuration files (MEMORY.md, SOUL.md, AGENTS.md), which can keep influencing the agent after the skill is removed. We do not give a count for these, because phrases like “silently” or references to memory files also appear in legitimate skills and we could not separate the two reliably with simple rules.
Sample 0145809b6b... (Hijacker:OpenClaw/Skilldrop0145809b!sem) addresses the agent directly. In its list of things the agent can do:
|
And later:
|
5. How to catch hidden instructions
These techniques are uncommon today, and most malicious skills do not use them. They are also simple to apply and simple to check for, so it is worth adding the checks now:
- Review the raw file, not the rendered page, and check the end of the file.
- Normalize before scanning. Remove zero-width characters, decode or reject Unicode tag characters (
U+E0000–U+E007F), and collapse runs of blank lines before applying static rules or passing the file to an LLM. - Protect agent memory and configuration files. Treat
MEMORY.md,SOUL.md,AGENTS.md,CLAUDE.md, and.cursorrulesas files that skills cannot modify without explicit user approval. - Check skills in VirusTotal before installing them. Code Insight and our agent integrations analyze the raw skill package.
Read more: How Some Malicious AI Skills Hide Instructions from Reviewers