一键导入
deobfuscating-powershell-obfuscated-malware
使用 AST 分析、动态追踪以及 PSDecode 和 PowerDecode 等工具,系统地对多层 PowerShell 恶意软件进行去混淆,以揭示隐藏的载荷和 C2 基础设施。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
使用 AST 分析、动态追踪以及 PSDecode 和 PowerDecode 等工具,系统地对多层 PowerShell 恶意软件进行去混淆,以揭示隐藏的载荷和 C2 基础设施。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
通过分析 Zeek dns.log 中的高熵子域名查询、超量查询量、超长查询长度以及异常 DNS 记录类型,检测 DNS 隧道和数据外泄中的隐蔽通道通信。适用于:当需要狩猎基于 DNS 的 C2 或数据外泄通道、调查异常 DNS 查询模式、或响应涉及 DNS 隧道工具(iodine、dnscat2、DNSExfiltrator)的威胁情报时使用。
实施 Google 的 BeyondCorp 零信任访问模型,通过 IAP、Access Context Manager 和 Chrome Enterprise Premium,消除网络边界的隐式信任,强制执行基于身份的访问控制,实现无 VPN 的安全应用访问。适用于将传统 VPN 替换为零信任架构、部署 Identity-Aware Proxy、配置设备信任策略、或为远程办公实施上下文感知访问控制时使用。
在授权的安全评估过程中,使用 Burp Suite 的扫描器、Intruder 和 Repeater 工具识别和验证跨站脚本(XSS)漏洞。适用于 Web 应用渗透测试中检测反射型、存储型和 DOM 型 XSS,验证自动化扫描器报告的 XSS 发现,以及评估 CSP 和 XSS 过滤器的有效性时使用。
攻击活动溯源归因分析涉及系统性地评估证据,以确定哪个威胁行为者或组织对某次网络行动负责。本技能涵盖使用 Diamond Model 和 ACH(竞争假设分析)收集并加权溯源归因指标、分析基础设施重叠、TTP 一致性、恶意软件代码相似性、操作时序模式和语言痕迹,以构建置信度加权的溯源归因评估。
从 PE 文件和内存转储中提取并分析 Cobalt Strike beacon 配置,以识别 C2 基础设施、Malleable C2 配置文件和攻击者操作惯例。
使用 Ghidra 及专用脚本对 Go 编译的恶意软件进行逆向工程,包括函数恢复、字符串提取和去符号表 Go 二进制文件的类型重建。
| name | deobfuscating-powershell-obfuscated-malware |
| description | 使用 AST 分析、动态追踪以及 PSDecode 和 PowerDecode 等工具,系统地对多层 PowerShell 恶意软件进行去混淆,以揭示隐藏的载荷和 C2 基础设施。 |
| domain | cybersecurity |
| subdomain | malware-analysis |
| tags | ["powershell","deobfuscation","malware-analysis","scripting","obfuscation","ast-analysis","incident-response"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
PowerShell 因其与 Windows 的深度集成和强大的脚本功能而被恶意软件作者大量滥用。混淆技术包括:字符串拼接、Base64 编码、字符替换、Invoke-Expression 分层、SecureString 滥用、环境变量操控和反引号插入。现代恶意软件使用多层混淆,需要迭代式去混淆。PSDecode、PowerDecode 和 PowerPeeler 等工具可以自动化大部分这一过程,而手动 AST(抽象语法树)分析则可以处理自定义混淆。PowerPeeler 通过对表达式相关 AST 节点进行指令级动态分析,实现了 95% 的去混淆正确率。
base64、re、subprocess 模块Install-Module PSDecode)PowerShell 恶意软件采用分层混淆来规避静态检测。字符串拼接将命令拆分到多个变量中($a='In'+'voke')。Base64 编码将整个脚本包装在 -EncodedCommand 参数中。字符代码数组使用 [char] 转换([char[]](73,69,88)|%{$r+=$_})。环境变量滥用从 $env: 路径中读取子字符串。反引号插入在 PowerShell 会忽略的字符之间添加反引号(I`nv`oke-Exp`ression)。SecureString 转换使用 ConvertTo-SecureString 配合嵌入的密钥加密字符串。
PowerShell 的抽象语法树暴露了脚本的解析结构,与表面层面的混淆无关。通过遍历 AST 并评估表达式节点,分析人员可以解析拼接的字符串、解码编码值并重建原始命令。PowerPeeler 在指令级别使用这种方法,监控执行过程以将 AST 节点与其评估结果相关联。
通过将 Invoke-Expression(IEX)替换为 Write-Output,分析人员可以安全地捕获通常会被执行的去混淆脚本内容。这种技术通过迭代替换 IEX 调用跨越多层工作,直到最终载荷被揭示。
#!/usr/bin/env python3
"""识别并分类 PowerShell 混淆技术。"""
import re
import base64
import sys
def analyze_obfuscation(script_content):
"""识别 PowerShell 脚本中使用的混淆技术。"""
techniques = []
# 检查 Base64 编码命令
b64_pattern = re.compile(
r'-[Ee](?:nc(?:odedcommand)?)\s+([A-Za-z0-9+/=]{20,})',
re.IGNORECASE
)
if b64_pattern.search(script_content):
techniques.append("Base64 EncodedCommand")
# 检查 FromBase64String
if re.search(r'\[Convert\]::FromBase64String', script_content, re.IGNORECASE):
techniques.append("Base64 FromBase64String")
# 检查字符串拼接
concat_count = script_content.count("'+'") + script_content.count('"+"')
if concat_count > 3:
techniques.append(f"字符串拼接({concat_count} 次连接)")
# 检查字符数组构造
if re.search(r'\[char\]\s*\d+', script_content, re.IGNORECASE):
techniques.append("字符代码数组")
# 检查 Invoke-Expression 变体
iex_patterns = [
r'Invoke-Expression',
r'\bIEX\b',
r'\.\s*\(\s*\$',
r'&\s*\(\s*\$',
r'\|\s*IEX',
r'\|\s*Invoke-Expression',
]
for pattern in iex_patterns:
if re.search(pattern, script_content, re.IGNORECASE):
techniques.append(f"Invoke-Expression 变体:{pattern}")
# 检查反引号混淆
tick_count = script_content.count('`')
if tick_count > 5:
techniques.append(f"反引号插入({tick_count} 个反引号)")
# 检查环境变量滥用
if re.search(r'\$env:', script_content, re.IGNORECASE):
env_refs = re.findall(r'\$env:\w+', script_content, re.IGNORECASE)
if len(env_refs) > 2:
techniques.append(f"环境变量滥用({len(env_refs)} 处引用)")
# 检查 SecureString
if re.search(r'ConvertTo-SecureString', script_content, re.IGNORECASE):
techniques.append("SecureString 加密")
# 检查压缩
if re.search(r'IO\.Compression|DeflateStream|GZipStream',
script_content, re.IGNORECASE):
techniques.append("压缩(Deflate/GZip)")
# 检查 XOR 编码
if re.search(r'-bxor\s+\d+', script_content, re.IGNORECASE):
techniques.append("XOR 编码")
# 检查 Replace 链
replace_count = len(re.findall(r'\.Replace\(', script_content))
if replace_count > 2:
techniques.append(f"Replace 链({replace_count} 次替换)")
return techniques
def decode_base64_command(script_content):
"""提取并解码 Base64 编码的命令。"""
b64_match = re.search(
r'-[Ee](?:nc(?:odedcommand)?)\s+([A-Za-z0-9+/=]{20,})',
script_content, re.IGNORECASE
)
if b64_match:
encoded = b64_match.group(1)
try:
decoded = base64.b64decode(encoded).decode('utf-16-le')
return decoded
except Exception:
return None
return None
def remove_tick_marks(script_content):
"""移除 PowerShell 反引号混淆。"""
# 移除非转义序列的反引号
escape_chars = {'`n', '`r', '`t', '`a', '`b', '`f', '`v', '`0', '``'}
result = []
i = 0
while i < len(script_content):
if script_content[i] == '`' and i + 1 < len(script_content):
pair = script_content[i:i+2]
if pair in escape_chars:
result.append(pair)
i += 2
else:
# 跳过反引号,保留下一个字符
result.append(script_content[i+1])
i += 2
else:
result.append(script_content[i])
i += 1
return ''.join(result)
def resolve_string_concat(script_content):
"""解析简单的字符串拼接模式。"""
# 模式:'str1' + 'str2'
pattern = re.compile(r"'([^']*)'\s*\+\s*'([^']*)'")
while pattern.search(script_content):
script_content = pattern.sub(lambda m: f"'{m.group(1)}{m.group(2)}'",
script_content)
# 模式:"str1" + "str2"
pattern = re.compile(r'"([^"]*)"\s*\+\s*"([^"]*)"')
while pattern.search(script_content):
script_content = pattern.sub(lambda m: f'"{m.group(1)}{m.group(2)}"',
script_content)
return script_content
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"用法:{sys.argv[0]} <powershell_script>")
sys.exit(1)
with open(sys.argv[1], 'r', errors='replace') as f:
content = f.read()
print("[+] 混淆分析")
print("=" * 60)
techniques = analyze_obfuscation(content)
for t in techniques:
print(f" - {t}")
# 尝试自动去混淆
print("\n[+] 正在尝试去混淆")
print("=" * 60)
# 第 1 层:移除反引号
deobfuscated = remove_tick_marks(content)
# 第 2 层:解析字符串拼接
deobfuscated = resolve_string_concat(deobfuscated)
# 第 3 层:解码 Base64
b64_decoded = decode_base64_command(deobfuscated)
if b64_decoded:
print("[+] Base64 解码内容:")
print(b64_decoded[:2000])
deobfuscated = b64_decoded
print(f"\n[+] 去混淆后脚本长度:{len(deobfuscated)} 个字符")
output_file = sys.argv[1] + ".deobfuscated.ps1"
with open(output_file, 'w') as f:
f.write(deobfuscated)
print(f"[+] 已保存到 {output_file}")
import subprocess
import tempfile
import os
def iex_replacement_deobfuscate(script_content, max_layers=10):
"""迭代地将 IEX 替换为 Write-Output 以解包各层。"""
# IEX 替换模式
replacements = [
(r'\bInvoke-Expression\b', 'Write-Output'),
(r'\bIEX\b', 'Write-Output'),
(r'\|\s*IEX\b', '| Write-Output'),
]
current = script_content
layers = []
for layer_num in range(max_layers):
# 应用 IEX 替换
modified = current
for pattern, replacement in replacements:
modified = re.sub(pattern, replacement, modified, flags=re.IGNORECASE)
if modified == current and layer_num > 0:
print(f" [+] 在第 {layer_num} 层未发现更多 IEX 层")
break
# 写入临时文件并在受限的 PowerShell 中执行
with tempfile.NamedTemporaryFile(mode='w', suffix='.ps1',
delete=False) as tmp:
tmp.write(modified)
tmp_path = tmp.name
try:
result = subprocess.run(
['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass',
'-File', tmp_path],
capture_output=True, text=True, timeout=30
)
output = result.stdout.strip()
if output and output != current:
print(f" [+] 第 {layer_num + 1} 层:解包了 "
f"{len(output)} 个字符")
layers.append({
"layer": layer_num + 1,
"technique": "IEX 替换",
"content_length": len(output),
})
current = output
else:
break
except subprocess.TimeoutExpired:
print(f" [!] 第 {layer_num + 1} 层:执行超时")
break
finally:
os.unlink(tmp_path)
return current, layers
def extract_iocs_from_script(deobfuscated_content):
"""从去混淆后的 PowerShell 中提取攻陷指标。"""
iocs = {
"urls": [],
"ips": [],
"domains": [],
"file_paths": [],
"registry_keys": [],
"commands": [],
"base64_blobs": [],
}
# URL
url_pattern = re.compile(
r'https?://[^\s\'"<>)\]]+', re.IGNORECASE
)
iocs["urls"] = list(set(url_pattern.findall(deobfuscated_content)))
# IP 地址
ip_pattern = re.compile(
r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
)
iocs["ips"] = list(set(ip_pattern.findall(deobfuscated_content)))
# 文件路径
path_pattern = re.compile(
r'[A-Za-z]:\\[^\s\'"<>|]+|'
r'\\\\[^\s\'"<>|]+|'
r'%(?:APPDATA|TEMP|USERPROFILE|PROGRAMFILES)%[^\s\'"<>|]*',
re.IGNORECASE
)
iocs["file_paths"] = list(set(path_pattern.findall(deobfuscated_content)))
# 注册表键
reg_pattern = re.compile(
r'(?:HKLM|HKCU|HKCR|HKU|HKCC)(?:\\[^\s\'"<>|]+)+',
re.IGNORECASE
)
iocs["registry_keys"] = list(set(reg_pattern.findall(deobfuscated_content)))
# 可疑命令
suspicious_cmds = [
'New-Object Net.WebClient',
'DownloadString', 'DownloadFile', 'DownloadData',
'Start-Process', 'Invoke-WebRequest',
'New-Object IO.MemoryStream',
'Reflection.Assembly',
'Add-MpPreference -ExclusionPath',
'Set-MpPreference -DisableRealtimeMonitoring',
'New-ScheduledTask', 'Register-ScheduledTask',
]
for cmd in suspicious_cmds:
if cmd.lower() in deobfuscated_content.lower():
iocs["commands"].append(cmd)
return iocs