Listen to this Post

Introduction:
A sophisticated supply chain attack leveraging invisible Unicode characters has compromised over 150 GitHub repositories, npm packages, and at least one Visual Studio Code extension. Dubbed “GlassWorm,” this malware employs zero-width characters and Unicode variation selectors to hide malicious payloads inside code that appears empty to human reviewers and standard security scanners. This attack represents an evolution in steganographic code obfuscation, bypassing traditional static analysis by rendering payloads only during JavaScript runtime execution.
Learning Objectives:
- Understand how Unicode steganography is used to hide malware in source code
- Learn to detect invisible character injections using command-line tools and scripts
- Master mitigation techniques to protect repositories and package ecosystems from supply chain attacks
You Should Know:
1. Decoding the GlassWorm Injection Pattern
The attack injects a JavaScript decoder that processes thousands of invisible Unicode characters. The core obfuscation relies on Unicode variation selectors (U+FE00 to U+FE0F) and supplementary variation selectors (U+E0100 to U+E01EF).
Step‑by‑step analysis:
First, examine the decoder function injected into compromised repositories:
const s = v => [...v].map(w => (
w = w.codePointAt(0),
w >= 0xFE00 && w <= 0xFE0F ? w - 0xFE00 :
w >= 0xE0100 && w <= 0xE01EF ? w - 0xE0100 + 16 : null
)).filter(n => n !== null);
eval(Buffer.from(s(``)).toString('utf-8'));
What this does:
- The template literal
s(``)contains ~9,000 invisible Unicode characters - Each character is mapped to a numeric value (0‑15 for variation selectors, 16‑271 for supplementary selectors)
- These values are converted back to bytes, reconstructing the actual payload
– `eval()` executes the reconstructed malicious code
Detection with Linux command:
Find files containing variation selectors
grep -P "[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]" -r . --include=".js"
Count invisible characters in a suspicious file
perl -C -ne 'print if /[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]/' malicious.js | wc -c
Windows PowerShell equivalent:
Search for Unicode variation selectors
Get-ChildItem -Recurse -Filter .js | Select-String "[\uFE00-\uFE0F\uE0100-\uE01EF]" -List
Show hidden characters (PowerShell 7+)
Get-Content suspicious.js -Encoding UTF8 | % { $_ | ConvertTo-Json }
2. Manual Payload Extraction Technique
To safely extract the hidden payload without executing it, modify the decoder:
Create extractor.js:
const fs = require('fs');
// Original decoder logic (modified to return bytes)
const extractHiddenPayload = (invisibleString) => {
const values = [...invisibleString].map(c => {
let cp = c.codePointAt(0);
if (cp >= 0xFE00 && cp <= 0xFE0F) return cp - 0xFE00;
if (cp >= 0xE0100 && cp <= 0xE01EF) return cp - 0xE0100 + 16;
return null;
}).filter(n => n !== null);
return Buffer.from(values).toString('utf-8');
};
// Read the file containing invisible characters
const maliciousCode = fs.readFileSync('suspicious-file.js', 'utf8');
// Extract the part between backticks after 's(``)'
const match = maliciousCode.match(/s(<code>([^</code>])`)/);
if (match) {
console.log("Extracted payload:");
console.log(extractHiddenPayload(match[bash]));
}
Run safely:
node extractor.js > decoded_payload.txt
3. Identifying Compromised Repositories
The attacker used a specific pattern. Search GitHub using the provided query:
Search for the decoder pattern
curl -s "https://api.github.com/search/code?q=0xFE00%26%26w%3C%3D0xFE0F%3Fw-0xFE00%3Aw%3E%3D0xE0100%26%26w%3C%3D0xE01EF" \
| jq '.items[] | {repo: .repository.full_name, path: .path}'
Manual verification checklist:
- Check for unusually long template literals (e.g.,
s(``)with content) - Look for decoder functions named with single letters (s, v, w, n)
- Verify commit history for unexpected maintainer changes
- Compare file hashes with known clean versions
Generate file hash for integrity checking:
Linux sha256sum package/index.js Windows certutil -hashfile package\index.js SHA256
4. npm Package Ecosystem Protection
If you maintain npm packages, implement these security measures:
Pre-publish hook (.npmrc):
Enable 2FA for publishing
npm profile enable-2fa auth-and-writes
Add prepublish script to package.json
{
"scripts": {
"prepublishOnly": "node scripts/check-unicode-malware.js"
}
}
Check script (scripts/check-unicode-malware.js):
const fs = require('fs');
const path = require('path');
const suspiciousRanges = [
[0xFE00, 0xFE0F],
[0xE0100, 0xE01EF]
];
function checkFile(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
for (let [start, end] of suspiciousRanges) {
for (let cp = start; cp <= end; cp++) {
if (content.includes(String.fromCodePoint(cp))) {
console.error(<code>Malicious Unicode found in ${filePath}</code>);
process.exit(1);
}
}
}
}
function walkDir(dir) {
fs.readdirSync(dir).forEach(file => {
const fullPath = path.join(dir, file);
if (fs.statSync(fullPath).isDirectory()) {
walkDir(fullPath);
} else if (fullPath.endsWith('.js') || fullPath.endsWith('.ts')) {
checkFile(fullPath);
}
});
}
walkDir('.');
console.log('No invisible Unicode detected');
5. VS Code Extension Security Hardening
Since a VS Code extension was compromised, extension developers must audit their code:
VS Code extension manifest check:
{
"name": "your-extension",
"scripts": {
"vscode:prepublish": "npm run check-malware"
}
}
Extension runtime detection (for users):
Create a workspace setting to warn about suspicious extensions:
{
"files.watcherExclude": {
"/.git/objects/": true,
"/.git/subtree-cache/": true
},
"search.exclude": {
"/node_modules": true,
"/bower_components": true
}
}
User-side detection script:
Windows: Check all installed VS Code extensions for malicious patterns
$extensionsDir = "$env:USERPROFILE.vscode\extensions"
Get-ChildItem $extensionsDir -Recurse -Filter .js | ForEach-Object {
$content = Get-Content $<em>.FullName -Raw
if ($content -match "0xFE00.0xFE0F.eval(") {
Write-Host "Suspicious: $</em>"
}
}
6. Supply Chain Defense with Dependency Auditing
Implement automated scanning in CI/CD pipelines:
GitHub Actions workflow (.github/workflows/unicode-scan.yml):
name: Unicode Malware Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm install
- name: Scan for invisible Unicode
run: |
grep -r -P "[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]" . \
--include=".js" --include=".ts" --include=".json"
if [ $? -eq 0 ]; then
echo "::error::Malicious Unicode detected"
exit 1
fi
Git pre-commit hook (.git/hooks/pre-commit):
!/bin/sh
if grep -P "[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]" --include=".js" .; then
echo "ERROR: Invisible Unicode characters found. Commit rejected."
exit 1
fi
7. Enterprise Mitigation Strategies
For organizations managing multiple repositories:
Bulk detection script (Linux):
!/bin/bash
Scan all organization repos
ORG_NAME="your-org"
gh repo list $ORG_NAME --limit 1000 | while read repo _; do
git clone "https://github.com/$repo.git" temp-repo
cd temp-repo
if grep -r -P "[\x{FE00}-\x{FE0F}\x{E01000}-\x{E01EF}]" \
--include=".js" --include=".ts"; then
echo "ALERT: Malware pattern found in $repo"
Trigger incident response
fi
cd .. && rm -rf temp-repo
done
Splunk search query for log monitoring:
index=github_audit_logs action=push
| regex file_content="[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]"
| table repo, user, timestamp, file_path
Wazuh rule for detection:
<group name="github,supplychain,">
<rule id="100200" level="12">
<decoded>github_audit</decoded>
<field name="file_content" type="pcre2">[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]</field>
<description>GlassWorm Unicode malware detected in repository</description>
<mitre>
<id>T1195.001</id> <!-- Supply Chain Compromise -->
</mitre>
</rule>
</group>
What Undercode Say:
- Invisibility is the new camouflage – Attackers have moved beyond obvious obfuscation to techniques that exploit how humans and machines perceive data differently. Unicode steganography renders malware literally invisible to code review, demanding new detection paradigms.
- Trust, but verify – down to the byte – The attack succeeded because maintainers and security tools assumed visible characters equal complete code. Every character, including those that don’t render, must be inspected. Hash-based integrity checking and byte-level scanning are no longer optional.
The GlassWorm incident demonstrates that supply chain security must evolve to address semantic gaps between human-readable code and machine-executable content. Future attacks will likely extend this technique to other Unicode ranges, configuration files, and even binary formats. Organizations must treat code as data, not text, and implement multi-layered validation that includes entropy analysis, character set whitelisting, and runtime behavior monitoring. The days of trusting visible source code are over – we now audit the invisible.
Prediction:
This attack vector will proliferate across all text-based formats – Dockerfiles, YAML configurations, HTML templates, and even documentation. Expect to see Unicode steganography combined with AI-generated code, where poisoned training data teaches models to insert invisible backdoors. Security tools will need to render all Unicode characters visibly (e.g., using replacement glyphs) and implement strict character-set policies per file type. The next major supply chain attack won’t be hidden in code – it will be hidden in plain sight, unseen.
▶️ Related Video (86% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Advocatemack %F0%9D%9F%AD%F0%9D%9F%B1%F0%9D%9F%AC – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


