Windows黑客编程之突破Session 0隔离创建用户进程

描述

  • 通过CreateProcessAsUser函数,在一个服务进程中创建用户进程,实现ui交互

准备知识

  • CFF Explorer:PE查看器,可查看PE文件的导入导出表等信息
  • Windows窗口回调
    • Windows操作系统向应用程序发送一系列消息,如左键按下和左键抬起,应用程序将通过GetMessage等方法,从消息队列中取出消息,将其提交到窗口过程(WndProc)指向一个应用程序定义的窗口过程的指针
    • win32 api编写窗口程序例程:链接
  • Qt自己有一套消息处理机制(信号槽),但是也可以使用Windows原生的消息机制,链接

代码

注册服务程序入口函数

int _tmain(int argc, _TCHAR* argv[])
{
	SERVICE_TABLE_ENTRY stDispatchTable[] = { {g_szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL, NULL} };
	::StartServiceCtrlDispatcher(stDispatchTable);
	return 0;
}

服务入口函数

  • 注册服务控制处理函数ServiceCtrlHandle
  • 完成服务主体工作
void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv)
{
	g_ServiceStatus.dwServiceType = SERVICE_WIN32;
	g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
	g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwServiceSpecificExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;
	g_ServiceStatus.dwWaitHint = 0;

	g_ServiceStatusHandle = ::RegisterServiceCtrlHandler(g_szServiceName, ServiceCtrlHandle);
	g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
	g_ServiceStatus.dwCheckPoint = 0;
	g_ServiceStatus.dwWaitHint = 0;
	::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);

	DoTask();
}

服务控制处理函数

  • 接收来自ControlService发出的服务控制请求,按照基本控制码进行状态判断,通过SetServiceStatus通知SCM对服务状态进行更新
void __stdcall ServiceCtrlHandle(DWORD dwOperateCode)
{
	switch (dwOperateCode)
	{
	case SERVICE_CONTROL_PAUSE:
	{
		// 暂停
		g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
		break;
	}
	case SERVICE_CONTROL_CONTINUE:
	{
		// 继续
		g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
		break;
	}
	case SERVICE_CONTROL_STOP:
	{
		// 停止
		g_ServiceStatus.dwWin32ExitCode = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		g_ServiceStatus.dwCheckPoint = 0;
		g_ServiceStatus.dwWaitHint = 0;
		::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
		break;
	}
	case SERVICE_CONTROL_INTERROGATE:
	{
		// 询问
		break;
	}
	default:
		break;
	}
}

服务主体工作

  • 调用CreateProcessAsUser函数,创建用户态进程
  • 通过WTSSendMessage弹出一个用户态提示窗口
void DoTask()
{
	ShowMessage("Hello Z5onk0\nThis Message Is From Session 0 Service!\n", "Session 0");
	CreateUserProcess("E:\\Project\\Cpp\\BlackHat\\ResourceFree\\result\\ResourceFree.exe");
}

void ShowMessage(TCHAR* lpszMessage, TCHAR* lpszTitle)
{
	DWORD dwSessionId = ::WTSGetActiveConsoleSessionId();
	DWORD dwResponse = 0;
	::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSessionId,
		lpszTitle, (1 + ::lstrlen(lpszTitle)),
		lpszMessage, (1 + ::lstrlen(lpszMessage)),
		0, 0, &dwResponse, FALSE);
}

BOOL CreateUserProcess(char* lpszFileName)
{
	BOOL bRet = TRUE;
	DWORD dwSessionID = 0;
	HANDLE hToken = NULL;
	HANDLE hDuplicatedToken = NULL;
	LPVOID lpEnvironment = NULL;
	STARTUPINFO si = { 0 };
	PROCESS_INFORMATION pi = { 0 };
	si.cb = sizeof(si);

	do
	{
		// 获得当前Session ID
		dwSessionID = ::WTSGetActiveConsoleSessionId();

		// 获得当前Session的用户令牌
		if (FALSE == ::WTSQueryUserToken(dwSessionID, &hToken))
		{
			ShowMessage("WTSQueryUserToken", "ERROR");
			bRet = FALSE;
			break;
		}

		// 复制令牌
		if (FALSE == ::DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
			SecurityIdentification, TokenPrimary, &hDuplicatedToken))
		{
			ShowMessage("DuplicateTokenEx", "ERROR");
			bRet = FALSE;
			break;
		}

		// 创建用户Session环境
		if (FALSE == ::CreateEnvironmentBlock(&lpEnvironment,
			hDuplicatedToken, FALSE))
		{
			ShowMessage("CreateEnvironmentBlock", "ERROR");
			bRet = FALSE;
			break;
		}

		// 在复制的用户Session下执行应用程序,创建进程
		if (FALSE == ::CreateProcessAsUser(hDuplicatedToken,
			lpszFileName, NULL, NULL, NULL, FALSE,
			NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
			lpEnvironment, NULL, &si, &pi))
		{
			ShowMessage("CreateProcessAsUser", "ERROR");
			bRet = FALSE;
			break;
		}

	} while (FALSE);
	// 关闭句柄, 释放资源
	if (lpEnvironment)
	{
		::DestroyEnvironmentBlock(lpEnvironment);
	}
	if (hDuplicatedToken)
	{
		::CloseHandle(hDuplicatedToken);
	}
	if (hToken)
	{
		::CloseHandle(hToken);
	}
	return bRet;
}

结果

  • 用服务加载器来加载和启动服务
  • 服务一启动后,立马弹出用户窗口,并且成功运行带ui的用户进程
  • 查看ProcMon,可以看到位于session 0的服务进程成功创建了位于session 9的用户进程
posted @ 2023-02-24 22:04  z5onk0  阅读(129)  评论(0编辑  收藏  举报