fix: PDF/Office/压缩包关键词误报 — 两层修复
【regex 层收紧】policy.yaml keyword_rules
1) ReverseShellNetcat: \\bnc\\b → \\bnc\\s+-[elpvs]\\w*
nc 后必须跟参数 (-e/-l/-p/-v/-s), 单独 nc 在二进制里必然误报
ncat 同理: \\bncat\\b → \\bncat\\s+-\\w+
保留 \\bnetcat\\b (8 字母, 碰撞概率极低)
2) CredentialStealer: 全部关键词加 \\b 词边界
wce 单独加后续约束: wce(\\.exe\\b|\\s|\\/)
要求 wce 后跟 .exe / 空格 / / (二进制随机字节里 WCE 不再触发)
【引擎层架构修复】keywords.py
新增 BINARY_SKIP_EXTS (48 个扩展名):
- PDF / Office (.pdf/.docx/.xlsx/.pptx/...)
- 压缩包 (.rar/.zip/.7z/.tar/.gz/...)
- 图片 (.png/.jpg/.gif/.svg/...)
- 视频 (.mp4/.avi/.mkv/...)
- 音频 (.mp3/.wav/.flac/...)
KeywordEngine.scan() 开头检查:
如果 declared_ext / path ext 在 BINARY_SKIP_EXTS → 直接返回空
根因:
PDF/DOCX/ZIP 等文件是 DEFLATE 压缩流 (伪随机字节)
短关键词 (nc=2w/wce=3w) 在压缩流里必然出现
误报导致: 云启智能体平台生成效果.pdf 被拦截
ReverseShellNetcat: 'g%ew;V6Z?)Nc{>72z}*&96'
CredentialStealer: '2>>~:pa}w*WCE|2MVB85t/$'
验证:
✅ 新 regex: 真实 nc -e / wce.exe 命中, PDF 压缩流字节不命中
✅ BINARY_SKIP_EXTS: .pdf/.docx/.zip 跳过, .sh/.ps1/.txt 扫描
✅ scan() 方法已含跳过逻辑
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
@@ -24,6 +25,26 @@ logger = logging.getLogger(__name__)
|
|||||||
class KeywordEngine(Engine):
|
class KeywordEngine(Engine):
|
||||||
"""内网特征词表匹配引擎。"""
|
"""内网特征词表匹配引擎。"""
|
||||||
|
|
||||||
|
# ---- 跳过列表:二进制 / 压缩封装 / 媒体文件 ----
|
||||||
|
# 这些文件有专门引擎处理(PDF/Office/Archive/PE 等),
|
||||||
|
# 它们的内容是压缩流或二进制格式,关键词 regex 在里面会大面积误报。
|
||||||
|
# 短关键词 (nc/wce/curl/...) 在压缩流里等于伪随机字节必然碰撞。
|
||||||
|
BINARY_SKIP_EXTS = frozenset({
|
||||||
|
# PDF / Office
|
||||||
|
".pdf", ".docx", ".docm", ".doc", ".xlsx", ".xlsm", ".xls",
|
||||||
|
".pptx", ".pptm", ".ppt", ".ppsx", ".pps", ".potx",
|
||||||
|
# 压缩包
|
||||||
|
".rar", ".zip", ".7z", ".tar", ".gz", ".bz2", ".xz", ".tgz",
|
||||||
|
".iso", ".img", ".dmg",
|
||||||
|
# 图片
|
||||||
|
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".tif", ".webp",
|
||||||
|
".ico", ".svg",
|
||||||
|
# 视频
|
||||||
|
".mp4", ".avi", ".mkv", ".mov", ".flv", ".wmv", ".webm",
|
||||||
|
# 音频
|
||||||
|
".mp3", ".wav", ".flac", ".ogg", ".aac", ".wma", ".m4a",
|
||||||
|
})
|
||||||
|
|
||||||
name = "keywords"
|
name = "keywords"
|
||||||
layer = "L2B"
|
layer = "L2B"
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
@@ -57,6 +78,14 @@ class KeywordEngine(Engine):
|
|||||||
|
|
||||||
findings: List[Finding] = []
|
findings: List[Finding] = []
|
||||||
|
|
||||||
|
# ---- 跳过二进制/压缩封装文件 ----
|
||||||
|
# 这些文件内容是压缩流或纯二进制,短关键词 regex 在里面必然误报
|
||||||
|
ext = (target.declared_ext or "").lower()
|
||||||
|
if not ext and target.path:
|
||||||
|
ext = os.path.splitext(target.path)[1].lower()
|
||||||
|
if ext in self.BINARY_SKIP_EXTS:
|
||||||
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = self._extract_text(target.path, target.declared_ext)
|
text = self._extract_text(target.path, target.declared_ext)
|
||||||
except Exception: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
|
|||||||
@@ -357,7 +357,8 @@ keyword_rules:
|
|||||||
# ---- 反弹 Shell ----
|
# ---- 反弹 Shell ----
|
||||||
- name: ReverseShellNetcat
|
- name: ReverseShellNetcat
|
||||||
match: regex
|
match: regex
|
||||||
pattern: "(\\bnc\\b|\\bncat\\b|\\bnetcat\\b|/dev/tcp/|bash\\s+-i\\s+>&|sh\\s+-i\\s+>&)"
|
# nc/ncat 后必须跟参数 (如 -e/-l/-p/-v/-s),单独 nc 在二进制里必然误报
|
||||||
|
pattern: "(\\bnc\\s+-[elpvs]\\w*|\\bncat\\s+-\\w+|\\bnetcat\\b|/dev/tcp/|bash\\s+-i\\s+>&|sh\\s+-i\\s+>&)"
|
||||||
ignore_case: true
|
ignore_case: true
|
||||||
weight: 40
|
weight: 40
|
||||||
severity: critical
|
severity: critical
|
||||||
@@ -372,7 +373,8 @@ keyword_rules:
|
|||||||
# ---- 凭据窃取工具 ----
|
# ---- 凭据窃取工具 ----
|
||||||
- name: CredentialStealer
|
- name: CredentialStealer
|
||||||
match: regex
|
match: regex
|
||||||
pattern: "(mimikatz|sekurlsa|wce|gsecdump|fgdump|lsadump|dcsync|dcsyncer|rubeus)"
|
# 全部加 \b 词边界, wce 要求后跟 .exe / 空格 / / (二进制随机字节里的 WCE 不会触发)
|
||||||
|
pattern: "(\\bmimikatz\\b|\\bsekurlsa\\b|\\bwce(\\.exe\\b|\\s|\\/)|\\bgsecdump\\b|\\bfgdump\\b|\\blsadump\\b|\\bdcsync\\b|\\bdcsyncer\\b|\\brubeus\\b)"
|
||||||
ignore_case: true
|
ignore_case: true
|
||||||
weight: 45
|
weight: 45
|
||||||
severity: critical
|
severity: critical
|
||||||
|
|||||||
Reference in New Issue
Block a user