fix: L0-011 双扩展名逻辑反转 + L3-009 跨行关键词预处理
- _has_double_ext(): 逻辑从 SUSPICIOUS_DOUBLE_EXT 白名单改为 DANGEROUS_EXTS 黑名单(单一事实源),以最后一个扩展名 是危险可执行 + 倒数第二个是安全/文档类作为判定条件 - _check_extension_mismatch(): safe_then_dangerous 硬编码链 同样用 DANGEROUS_EXTS 替代,同时删掉冗余的局部 import - KeywordEngine.scan(): 匹配前预处理换行符 (\r\n/\r/\n → 空串) 解决 Invoke-Web\r\nRequest 被 CRLF 拆分导致关键词漏匹配 对账结果: 159/168 通过 (94.6%),剩余 9 条为真引擎缺失 - L0-026 假损坏 zip、L1-013 大文件内嵌 EICAR、L1-014 占位符 - L4-005/007/008/010/013 YARA 样本占位或规则缺失 - L5-004 假 PE 结构 pefile 解析失败
This commit is contained in:
@@ -647,19 +647,10 @@ class StructureEngine(Engine):
|
||||
filename = (target.filename or "").lower()
|
||||
|
||||
# 双扩展名:安全扩展名 + 危险扩展名结尾(如 invoice.pdf.exe)
|
||||
safe_then_dangerous = (
|
||||
filename.endswith(".exe") or filename.endswith(".dll") or filename.endswith(".scr")
|
||||
or filename.endswith(".com") or filename.endswith(".pif") or filename.endswith(".cpl")
|
||||
or filename.endswith(".msi") or filename.endswith(".lnk") or filename.endswith(".chm")
|
||||
or filename.endswith(".bat") or filename.endswith(".cmd") or filename.endswith(".ps1")
|
||||
or filename.endswith(".vbs") or filename.endswith(".hta")
|
||||
or filename.endswith(".elf") or filename.endswith(".so")
|
||||
or filename.endswith(".desktop") or filename.endswith(".run") or filename.endswith(".bin")
|
||||
or filename.endswith(".deb") or filename.endswith(".appimage")
|
||||
)
|
||||
safe_exts = {".txt", ".pdf", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".doc", ".docx",
|
||||
".xls", ".xlsx", ".ppt", ".pptx", ".rtf", ".csv", ".xml", ".html", ".htm",
|
||||
".zip", ".rar", ".7z", ".wps", ".et", ".dps", ".wpt", ".ett", ".dpt"}
|
||||
# 用 DANGEROUS_EXTS 做单一事实源,避免手工维护 endswith 链漏覆盖
|
||||
_dangerous_tuple = tuple(sorted(DANGEROUS_EXTS, key=len, reverse=True))
|
||||
safe_then_dangerous = filename.endswith(_dangerous_tuple)
|
||||
safe_exts = _SAFE_PREFIX_EXTS
|
||||
|
||||
if safe_then_dangerous:
|
||||
# 取最后两个扩展名
|
||||
@@ -685,7 +676,7 @@ class StructureEngine(Engine):
|
||||
return findings
|
||||
|
||||
# 真实类型危险可执行,声明安全类型 → 伪装
|
||||
from app.engines.structure import DANGEROUS_EXTS
|
||||
# DANGEROUS_EXTS 是模块级常量,此处直接引用
|
||||
if real_ext in DANGEROUS_EXTS and ext not in DANGEROUS_EXTS and ext:
|
||||
findings.append(
|
||||
Finding(
|
||||
@@ -1063,17 +1054,33 @@ def _is_path_traversal(name: str) -> bool:
|
||||
return ".." in parts
|
||||
|
||||
|
||||
# 安全/文档类扩展名集合——双扩展名伪装中作为"前缀"出现
|
||||
_SAFE_PREFIX_EXTS = DOCUMENT_EXTS | {
|
||||
".txt", ".log", ".csv", ".json", ".xml", ".html", ".htm",
|
||||
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".svg",
|
||||
".zip", ".rar", ".7z", ".tar", ".gz", ".bz2",
|
||||
".wps", ".et", ".dps", ".wpt", ".ett", ".dpt", ".rtf", ".odt",
|
||||
}
|
||||
|
||||
|
||||
def _has_double_ext(filename: str) -> bool:
|
||||
"""判断文件名是否包含两个以上扩展名。"""
|
||||
"""判断文件名是否为双扩展名伪装。
|
||||
|
||||
正确逻辑:最后一个扩展名是 DANGEROUS_EXTS 里的危险可执行类型(黑名单基准,
|
||||
单一事实源),倒数第二个是安全/文档类(可能被攻击者故意放在前面伪装)。
|
||||
例如 invoice.docx.js → .js 危险 + .docx 安全 → True。
|
||||
"""
|
||||
name = os.path.basename(filename)
|
||||
parts = name.split(".")
|
||||
if len(parts) < 3:
|
||||
return False
|
||||
prefix_ext = "." + parts[-2].lower()
|
||||
suffix_ext = "." + parts[-1].lower()
|
||||
if suffix_ext not in SUSPICIOUS_DOUBLE_EXT:
|
||||
# 核心:以 DANGEROUS_EXTS 为单一事实源
|
||||
if suffix_ext not in DANGEROUS_EXTS:
|
||||
return False
|
||||
return prefix_ext in DANGEROUS_EXTS or prefix_ext in SUSPICIOUS_DOUBLE_EXT
|
||||
# 前缀必须是安全/文档类,才算"伪装"
|
||||
return prefix_ext in _SAFE_PREFIX_EXTS
|
||||
|
||||
|
||||
def _compatible_exts(real_ext: str, real_type: str) -> set[str]:
|
||||
|
||||
Reference in New Issue
Block a user