关于 deobfuscating-javascript-malware
这是一项面向安全分析人员的 JavaScript 恶意软件混淆分析技能,涵盖安全提取、逐层解码和载荷检查,并强调在隔离环境中操作。
主要功能
- 01安全提取并检查脚本
- 02识别常见混淆技术
- 03移除反分析陷阱
- 04逐层解码字符串
- 05安全解析 eval 链
- 06提取恶意载荷指标
适用场景
- 01调查钓鱼页中的混淆脚本
- 02分析电商网站注入的网页盗刷程序
- 03检查含脚本的恶意邮件附件
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill deobfuscating-javascript-malware安装后在 Claude Code 中输入/deobfuscating-javascript-malware确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.py详细介绍
反混淆 JavaScript 恶意软件
何时使用
- 调查包含混淆 JavaScript 的钓鱼页面,该脚本执行凭据窃取或重定向
- 分析注入到电商网站中的网页窃密脚本(Magecart 式)
- 反混淆下载并执行二阶段恶意软件的 JavaScript 投放器
- 检查包含嵌入混淆脚本的 HTML 文件的恶意电子邮件附件
- 分析使用重度 JavaScript 混淆来隐藏漏洞利用投递的浏览器漏洞利用工具包
不要使用于仅仅是压缩过的生产代码的混淆 JavaScript;请改用标准的美化工具。
前提条件
- 已安装 Node.js 18+,用于在受控环境中执行和调试 JavaScript
- Python 3.8+ 及
jsbeautifier库,用于代码格式化 - 浏览器开发者工具(Chrome DevTools),用于在隔离浏览器中进行受控执行
- CyberChef(https://gchq.github.io/CyberChef/),用于编码/解码操作
- de4js 或 JStillery,用于自动化 JavaScript 反混淆
- 隔离的分析虚拟机,无法访问生产系统或敏感数据
工作流
步骤 1:安全提取并检查混淆脚本
在不执行的情况下隔离恶意 JavaScript:
# Extract JavaScript from HTML file
python3 << 'PYEOF'
from html.parser import HTMLParser
class ScriptExtractor(HTMLParser):
def __init__(self):
super().__init__()
self.in_script = False
self.scripts = []
self.current = ""
def handle_starttag(self, tag, attrs):
if tag == "script":
self.in_script = True
self.current = ""
def handle_endtag(self, tag):
if tag == "script":
self.in_script = False
if self.current.strip():
self.scripts.append(self.current)
def handle_data(self, data):
if self.in_script:
self.current += data
with open("malicious_page.html") as f:
parser = ScriptExtractor()
parser.feed(f.read())
for i, script in enumerate(parser.scripts):
with open(f"script_{i}.js", "w") as f:
f.write(script)
print(f"Extracted script_{i}.js ({len(script)} bytes)")
PYEOF
# Beautify the extracted JavaScript
npx js-beautify script_0.js -o script_0_pretty.js
步骤 2:识别混淆技术
对所使用的混淆方法进行分类:
Common JavaScript Obfuscation Techniques:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
String Encoding:
- Hex encoding: "\x68\x65\x6c\x6c\x6f" -> "hello"
- Unicode escapes: "\u0068\u0065\u006c\u006c\u006f" -> "hello"
- Base64: atob("aGVsbG8=") -> "hello"
- charCodeAt/fromCharCode: String.fromCharCode(104,101,108,108,111)
- Array-based lookup: var _0x1234 = ["hello","world"]; _0x1234[0]
Eval Chains:
- eval(atob("..."))
- eval(unescape("..."))
- new Function("return " + decoded)()
- document.write("<script>" + decoded + "</script>")
- setTimeout(decoded, 0)
Control Flow:
- Switch-case dispatcher with shuffled case order
- Opaque predicates (always-true/false conditions)
- Dead code insertion
- Variable name mangling (_0x4a3b, _0xab12)
Anti-Analysis:
- Debugger traps: setInterval(function(){debugger;}, 100)
- Console detection: overriding console.log
- Timing checks: performance.now() deltas
- DevTools detection: window.outerWidth - window.innerWidth > 100
步骤 3:移除反分析保护
消除反调试和反分析陷阱:
// Remove debugger traps before analysis
// Replace in the obfuscated script:
// Before:
setInterval(function() { debugger; }, 100);
// After (neutralized):
setInterval(function() { /* debugger removed */ }, 100);
// Neutralize DevTools detection
// Before:
if (window.outerWidth - window.innerWidth > 160) { window.location = "about:blank"; }
// After:
if (false) { window.location = "about:blank"; }
// Neutralize timing checks
// Override performance.now to return consistent values
const originalNow = performance.now;
performance.now = function() { return 0; };
步骤 4:解码字符串混淆层
逐步解码经过编码的字符串:
# Python script to decode common JS obfuscation patterns
import re
import base64
import urllib.parse
def decode_hex_strings(code):
"""Replace \\xNN sequences with ASCII characters"""
def hex_replace(match):
hex_str = match.group(0)
try:
return bytes.fromhex(hex_str.replace("\\x", "")).decode("ascii")
except:
return hex_str
return re.sub(r'(?:\\x[0-9a-fA-F]{2})+', hex_replace, code)
def decode_unicode_escapes(code):
"""Replace \\uNNNN sequences with characters"""
def unicode_replace(match):
return chr(int(match.group(1), 16))
return re.sub(r'\\u([0-9a-fA-F]{4})', unicode_replace, code)
def decode_charcode_arrays(code):
"""Resolve String.fromCharCode calls"""
def charcode_replace(match):
codes = [int(c.strip()) for c in match.group(1).split(",")]
return '"' + "".join(chr(c) for c in codes) + '"'
return re.sub(r'String\.fromCharCode\(([0-9,\s]+)\)', charcode_replace, code)
def decode_base64_strings(code):
"""Resolve atob() calls with static strings"""
def atob_replace(match):
try:
decoded = base64.b64decode(match.group(1)).decode("utf-8")
return f'"{decoded}"'
except:
return match.group(0)
return re.sub(r'atob\(["\']([A-Za-z0-9+/=]+)["\']\)', atob_replace, code)
# Apply all decoders
with open("script_0.js") as f:
code = f.read()
code = decode_hex_strings(code)
code = decode_unicode_escapes(code)
code = decode_charcode_arrays(code)
code = decode_base64_strings(code)
with open("script_0_decoded.js", "w") as f:
f.write(code)
print("Decoded strings written to script_0_decoded.js")
步骤 5:安全解析 Eval 链
在不执行的情况下展开 eval/Function 构造函数链:
// Node.js script to safely resolve eval chains
// Run in isolated environment: node --experimental-vm-modules deobfuscate.js
const vm = require('vm');
// Create sandboxed context with logging
const sandbox = {
eval: function(code) {
console.log("=== EVAL INTERCEPTED ===");
console.log(code.substring(0, 500));
console.log("========================");
return code; // Return the code instead of executing it
},
document: {
write: function(html) {
console.log("=== DOCUMENT.WRITE INTERCEPTED ===");
console.log(html.substring(0, 500));
},
getElementById: function() { return { innerHTML: "" }; }
},
window: { location: { href: "" } },
atob: function(s) { return Buffer.from(s, 'base64').toString(); },
unescape: unescape,
setTimeout: function(fn) { if (typeof fn === 'string') console.log("TIMEOUT CODE:", fn); },
console: console,
String: String,
Array: Array,
parseInt: parseInt,
RegExp: RegExp,
};
const context = vm.createContext(sandbox);
// Load and execute the obfuscated script in sandbox
const fs = require('fs');
const code = fs.readFileSync('script_0.js', 'utf8');
try {
vm.runInContext(code, context, { timeout: 5000 });
} catch(e) {
console.log("Execution error (expected):", e.message);
}
步骤 6:分析反混淆后的载荷
检查所揭示的恶意逻辑:
Deobfuscated Malware Categories and IOC Extraction:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Credential Harvester:
- Form action URLs (exfiltration endpoints)
- XMLHttpRequest/fetch destinations
- Targeted input field names (username, password, cc_number)
Web Skimmer (Magecart):
- Payment form overlay injection
- Card data exfiltration URLs
- Keylogger event listeners (onkeypress, oninput)
Redirect Script:
- Destination URLs in location.href assignments
- Conditional redirects based on user-agent or referrer
- Cloaking logic (show benign content to bots)
Exploit Kit Landing:
- Browser/plugin version checks
- Exploit payload URLs
- Shellcode embedded as arrays or encoded strings
关键概念
| 术语 | 定义 |
|---|---|
| Eval 链 | 嵌套的 eval()、Function() 或 document.write() 调用层,每一层在传递给下一层之前解码一层混淆 |
| 字符串数组轮转 | 一种混淆技术,将所有字符串存储在一个打乱顺序的数组中,并通过计算索引访问它们以隐藏字符串字面量 |
| 死代码插入 | 添加永远不会执行的非功能性代码块,以增加分析复杂度并干扰模式匹配 |
| 不透明谓词 | 结果预先确定但难以静态确定的条件表达式;用于模糊控制流 |
| 反调试 | 用于检测和阻止浏览器 DevTools 或调试器使用的 JavaScript 技术,包括 debugger 语句和计时检查 |
| 网页窃密脚本 | 注入到电商网站中,用于从结账表单窃取支付卡数据的恶意 JavaScript(Magecart 攻击) |
| CyberChef | GCHQ 的基于 Web 的工具,用于编码/解码转换,有助于展开多层混淆 |
工具与系统
- CyberChef:GCHQ 的基于 Web 的工具,用于编码/解码转换,有助于展开多层混淆
- de4js:在线 JavaScript 反混淆器,支持常见混淆工具(obfuscator.io、JScrambler)
- Node.js VM 模块:沙箱化的 JavaScript 执行环境,用于在拦截 API 的情况下安全评估混淆代码
- Chrome DevTools:浏览器开发者工具,用于通过断点和控制台访问单步调试 JavaScript 执行
- JSDetox:JavaScript 恶意软件分析工具,提供执行模拟和反混淆
常见场景
场景:反混淆 Magecart 网页窃取程序
背景:一个被入侵的电商网站,其结账页面被注入了混淆过的 JavaScript。需要对该脚本进行反混淆,以识别数据外传端点,并确定哪些客户数据被窃取。
方法:
- 从页面源代码中提取注入的脚本(通常附加在合法的 JS 文件末尾,或从外部域加载)
- 美化代码并识别混淆技术(通常是字符串数组 + 轮转 + 十六进制编码)
- 使用 Python 解码脚本解码各层字符串编码(十六进制 -> Unicode -> base64)
- 通过求值数组定义和轮转函数来还原字符串数组
- 识别表单定向逻辑(针对支付表单字段的 querySelector)
- 从 XMLHttpRequest 或 fetch 调用中提取外传 URL
- 记录被窃取的数据字段和外传端点,用于事件响应
陷阱:
- 在联网系统上执行混淆脚本(脚本可能在分析期间回连)
- 在使用浏览器 DevTools 之前未移除反调试陷阱(无限 debugger 循环)
- 遗漏从外部 URL 动态加载的额外混淆层
- 忽略可能包含额外脚本的 base64 编码内联图片或 data URI
输出格式
JAVASCRIPT MALWARE DEOBFUSCATION REPORT
=========================================
Source: checkout.js (injected into example-shop.com)
Obfuscation: obfuscator.io (string array + rotation + hex encoding)
Layers Removed: 3
OBFUSCATION TECHNIQUES IDENTIFIED
[1] String array with 247 entries, rotated by 0x1a3
[2] Hex-encoded string references (\x68\x65\x6c\x6c\x6f)
[3] Base64-wrapped eval chain (2 layers)
[4] Anti-debugging: setInterval debugger trap
DEOBFUSCATED FUNCTIONALITY
Type: Magecart Payment Card Skimmer
Target Forms: input[name*="card"], input[name*="cc_"]
Data Captured: Card number, expiration, CVV, cardholder name
Exfil Method: POST via XMLHttpRequest
Exfil URL: hxxps://analytics-cdn[.]com/collect
Exfil Format: JSON { "cn": card_number, "exp": expiry, "cv": cvv }
Trigger: Form submit event on checkout page
EXTRACTED IOCs
Domains: analytics-cdn[.]com
IPs: 185.220.101[.]42
URLs: hxxps://analytics-cdn[.]com/collect
hxxps://analytics-cdn[.]com/gate.js
评论
支持 Markdown