Privilege Escalation and Access Token Rights Modification - c++

I have started to learn more and more about Windows access token and privilege adjustment but as I progress, I am getting confused. I ask some questions here to better understand this security mechanism of Windows.
In windows, when I run whoami /priv command for non-administrator users, it gives me the following result:
C:\Users\light>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
But when I run this command with administrator account it gives me a long list of privilege names which most of them are disabled. Now my first question is: Why some of these privileges are disabled? When a privilege disabled, it means user couldn't take the right to use that privilege?
My second question: How should I exactly modify an access token to get Se_Debug privilege? I do the following steps, but my application has not modify the token:
Call OpenProcessToken to open a process for access token query.
Initialized TOKEN_PRIVILEGES for querying access token.
Call LookupPrivilegeValue to check privilege
Call AdjustTokenPrivileges to adjust that privilege
My source code:
#include <Windows.h>
#include <iostream>
#pragma comment(lib, "advapi32.lib")
class privilege
{
private:
BOOL m_ok;
LUID m_luid;
LUID m_privilege_id;
HANDLE m_handle_token;
BOOL m_has_priviliege;
PRIVILEGE_SET m_privilege_set;
TOKEN_PRIVILEGES m_token_privilege;
public:
BOOL check(LPCWSTR arg_privilege)
{
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &m_handle_token))
m_ok = LookupPrivilegeValue(NULL, arg_privilege, &m_privilege_id);
if (m_ok)
{
m_privilege_set.PrivilegeCount = 1;
m_privilege_set.Control = PRIVILEGE_SET_ALL_NECESSARY;
m_privilege_set.Privilege[0].Luid = m_privilege_id;
m_privilege_set.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
m_ok = PrivilegeCheck(m_handle_token, &m_privilege_set, &m_has_priviliege);
}
if (m_ok && !m_has_priviliege)
return FALSE;
else
return TRUE;
}
BOOL set(HANDLE arg_token, LPCTSTR arg_privilege, BOOL arg_enable_privilege)
{
if (!LookupPrivilegeValue(NULL, arg_privilege, &m_luid))
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
m_token_privilege.PrivilegeCount = 1;
m_token_privilege.Privileges[0].Luid = m_luid;
if (arg_enable_privilege)
m_token_privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
m_token_privilege.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(arg_token, FALSE, &m_token_privilege, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
};
int main(int argc, wchar_t* argv)
{
HANDLE process_handle;
PHANDLE token_handle = NULL;
privilege o_privilege;
BOOL open_token_status = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token_handle);
if (open_token_status != NULL)
{
std::wcout << L"OpenProcessToken has been failed." << std::endl;
return EXIT_FAILURE;
}
else
{
std::wcout << L"Process has Backup privilege: " << std::boolalpha << o_privilege.check(L"SE_BACKUP_NAME") << std::endl;
BOOL privilege_adjust_status = o_privilege.set(token_handle, L"SeBackupPrivilege", TRUE);
std::wcout << L"Process has Backup privilege: " << std::boolalpha << o_privilege.check(L"SE_BACKUP_NAME") << std::endl;
return privilege_adjust_status;
}
return 0;
}
But this program couldn't set backup privilege for the process. Somebody could explain token enumeration and adjustment in Windows OS with a step by step approach?
Also I have a real problem with impersonation concept. Why we need such thing in Windows OS?

How should I exactly modify an access token to get Se_Debug privilege?
According to Changing Privileges in a Token,
You can change the privileges in either a primary or an impersonation
token in two ways:
Enable or disable privileges by using the AdjustTokenPrivileges function.
Restrict or remove privileges by using the CreateRestrictedToken function.
To assign privileges to a user account, according to Assigning Privileges to an Account
You can assign privileges to accounts either by using the Local
Security Policy Microsoft Management Console (MMC) snap-in
(Secpol.msc) or by calling the LsaAddAccountRights function.

Related

Delete folder for which every API call fails with ERROR_ACCESS_DENIED

I have created a folder with CreateDirectoryW to which I haven't got access. I used nullptr as a security descriptor, but for some reason it didn't copy the parent folder's ACLs, but instead made the folder inaccessible.
I can't view or change the owner. takeown, icacls, SetNamedSecurityInfoW, all from elevated processes or command prompts, fail with ERROR_ACCESS_DENIED.
Do I have any chance of deleting this folder in Windows (Shell or C++) before trying a Linux live CD which hopefully doesn't care about the ACLs?
You just need to enable backup (or restore) privilege:
#include <Windows.h>
#include <stdio.h>
int wmain(int argc, wchar_t ** argv)
{
// argv[1] must contain the directory to remove
HANDLE hToken;
struct
{
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[1];
} tkp;
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE,
(PTOKEN_PRIVILEGES)&tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
{
printf("AdjustTokenPrivileges: %u\n", GetLastError());
return 1;
}
if (GetLastError() != ERROR_SUCCESS)
{
// This happens if you don't have the privilege
printf("AdjustTokenPrivileges: %u\n", GetLastError());
return 1;
}
CloseHandle(hToken);
}
if (!RemoveDirectory(argv[1]))
{
printf("RemoveDirectory: %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
Note that in the interests of conciseness, some error handling has been omitted. Also note that AdjustTokenPrivileges() is one of the few special cases where calling GetLastError() is meaningful even though the call succeeded; it will return ERROR_SUCCESS or ERROR_NOT_ALL_ASSIGNED depending on whether you actually have all of the privileges you were attempting to enable.
This is a fairly general solution for bypassing security permissions on files. It works for most API calls, although in some cases (most notably CreateFile) you must provide a special flag in order to make use of backup privilege. As well as deleting files or removing directories that you don't have permission to, you can change attributes, change permissions, and even assign ownership to somebody else, which is not otherwise permitted.
assume that file not open with incompatible to delete share flags and no section on file.
for delete file enough 2 things - we have FILE_DELETE_CHILD on parent folder. and file is not read only. in this case call ZwDeleteFile (but not DeleteFile or RemoveDirectory - both this api will fail if file have empty DACL) is enough. in case file have read-only attribute - ZwDeleteFile fail with code STATUS_CANNOT_DELETE. in this case we need first remove read only. for this need open file with FILE_WRITE_ATTRIBUTES access. we can do this if we have SE_RESTORE_PRIVILEGE and set FILE_OPEN_FOR_BACKUP_INTENT option in call to ZwOpenFile. so code for delete file can be next:
NTSTATUS DeleteEx(POBJECT_ATTRIBUTES poa)
{
NTSTATUS status = ZwDeleteFile(poa);
if (status == STATUS_CANNOT_DELETE)
{
BOOLEAN b;
RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &b);
HANDLE hFile;
IO_STATUS_BLOCK iosb;
if (0 <= (status = NtOpenFile(&hFile, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT)))
{
static FILE_BASIC_INFORMATION fbi = {{},{},{},{}, FILE_ATTRIBUTE_NORMAL};
status = ZwSetInformationFile(hFile, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
NtClose(hFile);
if (0 <= status)
{
status = ZwDeleteFile(poa);
}
}
}
return status;
}

C++ Alternative to OpenProcess() (Program not willing to grant me permission)

I'm fairly new to C++ so my question is: Is there an alternative to OpenProcess()?The game does not let me get access to it, tried asking for smaller access than PROCESS_ACCESS_ALL since that might be a huge red flag. But here's my source code:
#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <Windows.h>
int main()
{
HWND hWnd = FindWindow(NULL, "Growtopia");
if (hWnd == NULL)
{
std::cout << "ERROR: Unable to find window process" << std::endl;
Sleep(2500);
exit(-1);
}
DWORD pID;
GetWindowThreadProcessId(hWnd, &pID);
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, false, pID);
if (!hProc)
{
std::cout << "Error: Unable to gain access to window" << std::endl;
Sleep(2500);
exit(-1);
}
return 0;
}
PS: The game I'm trying to gain access to is Growtopia if you couldn't find it out by the FindWindow() function :)
Thanks in advance.
Edit: My purpouse of the program is to edit a value of an adress
My purpouse of the program is to edit a value of an adress
That requires the use of WriteProcessMemory(), which requires PROCESS_VM_WRITE and PROCESS_VM_OPERATION permissions to the target process:
HANDLE hProc = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, pID);
To open a process with those permissions, the calling thread needs the "SeDebugPrivilege" privilege enabled, which means it has to be running as a user that is allowed to obtain debug privileges, such as an admin.
Run your app as a debug user (or dynamically impersonate such a user), then use OpenThreadToken() to open the calling thread's current access token and adjust it with AdjustTokenPrivileges() to make sure the "SeDebugPrivilege" privilege is active, before then calling OpenProcess():
Changing Privileges in a Token
How to obtain a handle to any process with SeDebugPrivilege

Running Process in system context

Is it possible to launch process in system context from a parent process thats running under administrator account with elevation(say a command prompt). The problem is similar to what psexec does but more of how it actually implements this.
I was thinking opening the crss.exe/winlogon.exe process duplicating the token and launching a new process using that process token. But I fail to even open the process handle (Getlasterror return 5). Can someone let me know if this is the right approach or the process should be launched differently ?
HANDLE hWinLogonProcess;
for(const auto& ps : running_processes)
{
if(ps.id == GetCurrentProcessId() ||
0 != ps.short_name.CompareNoCase(L"winlogon.exe"))
{
continue;
}
DWORD dwWinLogonSessionId(0);
if(FALSE == ProcessIdToSessionId(GetCurrentProcessId(), &dwWinLogonSessionId))
{
std::wcerr<<"Could not get Winlogon process session id"<<std::endl;
continue;
}
if(dwWinLogonSessionId != dwCurSessionId)
{
continue;
}
hWinLogonProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ps.id);
if(FALSE == hWinLogonProcess)
{
std::wcerr<<"Failed to get winlogon process handle"<<std::endl;
return;
}
else
{
std::wcout<<"Able to open process "<<ps.short_name.GetString()<<" handle"<<std::endl;
break;
}
}
I am sure its possible as there is a working tool (psexec) but I couldnt find any reference online to do this.
Also this is similar to question, but posting separately as there was details on how it had to be achieved.
Yes, this is possible (without any service help).
But I fail to even open the process handle
Does your process have the SE_DEBUG_PRIVILEGE privilege enabled?
With this privilege, you can open a system process with all access if it is not protected (smss.exe, csrss.exe, services.exe), and use that handle in CreateProcessAsUser(), or with UpdateProcThreadAttribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS) if you also have SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_TCB_PRIVILEGE privileges enabled (for setting the token's SessionId to 0), which you can get in 2 ways:
open a thread from an unprotected system process and impersonate it, then open your own thread token and adjust privileges on it.
open a token from any system process (this works even for protected processes), duplicate the token, adjust privileges on it, and then impersonate with this token.
To "launch a process in the system context", if you want to run the process:
with the LocalSystem token.
in the System terminal session (0)
Both, as I say, are possible. And all you need is SE_DEBUG_PRIVILEGE.
more simply - open some system process with PROCESS_CREATE_PROCESS access right. Use this handle with UpdateProcThreadAttribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS). As a result, your started process inherits a token from the system process. This will be not work on XP, but there it is possible to hook NtCreateProcess/Ex() to replace HANDLE ParentProcess with your opened handle.
Another way is to use CreateProcessAsUser(). Before creating the process, you will be need SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_TCB_PRIVILEGE privileges to set the token's TokenSessionId (if you want to run in session 0).
Thanks to RbMm answer I figured a way to accomplish this task.
For any of you who did not succeed, I leave below something that might help:
//First we need to add debug privilege to this process
HANDLE hToken;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
std::cout << "OpenProcessToken failed: " << GetLastError();
return 0;
}
TOKEN_PRIVILEGES tk;
tk.PrivilegeCount = 1;
tk.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tk.Privileges[0].Luid))
{
std::cout << "LookupPrivilegeValue failed: " << GetLastError();
return 0;
}
AdjustTokenPrivileges(hToken, FALSE, &tk, 0, NULL, 0);
if((DWORD res = GetLastError()) != ERROR_SUCCESS)
{
std::cout << "AdjustTokenPrivileges failed: " << res;
}
CloseHandle(hToken);
//Now we need a handle to a process that already runs as SYSTEM.
//You can choose any process that is not protected (if OpenProcess fails try with other process)
//pid of chosen process (you can get this by opening task manager and go to
//Details tab or by enumerating all processes and extract that one you need)
DWORD pid;
HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);
if (!hProcess)
{
std::cout << "OpenProcess with pid " << pid << "failed: " << GetLastError();
return 0
}
//We need to initialize a list that contains PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
//to specify that parent process of the process we are going to start is the
//process we opened earlier (this will make the child process inherit the system context).
//This list will be specified in a STARTUPINFOEX object that CreateProcess will get
STARTUPINFOEX siex = { sizeof(STARTUPINFOEX) };
siex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
//We need to initialize our list. To do this we call InitializeProcThreadAttributeList
//with a NULL list to get how big our list needs to be to store all attributes
//we want to specify, then we allocate our list with the size we got from first call
//and we call again the function to initialize the list.
SIZE_T cbAttributeListSize = 0;
if(!InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize))
{
std::cout << "InitializeProcThreadAttributeList failed: " << GetLastError();
return 0
}
siex.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize));
if(!InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &cbAttributeListSize))
{
std::cout << "InitializeProcThreadAttributeList failed: " << GetLastError();
return 0
}
if(!UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), NULL, NULL))
{
std::cout << "UpdateProcThreadAttribute failed: " << GetLastError();
return 0
}
//path to program we want to run in system context
LPWSTR szCmdline = _wcsdup(TEXT("C:\\Windows\\System32\\notepad.exe"));
PROCESS_INFORMATION pi = { 0 };
if(!CreateProcess(NULL, szCmdline, nullptr, nullptr, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOW>(&siex), &pi))
{
std::cout << "CreateProcess failed: " << GetLastError();
return 0
}

Retrieving currently logged in user SID in a multiple logged in users system

I'm facing an issue where user X, which is a non-admin user, runs an elevated program, retrieves the wrong SID information while fetching and querying the token associated with the current process.
My main limitation here is that I must use winXP compatible code, so WSTx functions are out of the question.
Methods I've tried:
I tried extracting the SID from the interactive desktop / main window station, but these yield odd results.
Expanded env variables: %USERPROFILE%
Used GetUserName()
The last 2 actually retrieved the elevated user.
my code:
HANDLE hTok = NULL;
if (false == OpenProcessToken(/*hProcess*/GetCurrentProcess(), TOKEN_QUERY, &hTok))
{
LOG_ERROR(L"Failed obtaining process' token");
return false;
}
// get user info size
LPBYTE pBuf = nullptr;
DWORD dwSize = 0;
bool bSuccess = false;
if (false == GetTokenInformation(hTok, TokenUser, NULL, 0, &dwSize))
{
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
LOG_ERROR(L"Failed getting token information");
return false;
}
}
do
{
pBuf = (BYTE*)::LocalAlloc(LPTR, dwSize);
if (nullptr == pBuf)
{
LOG_ERROR(L"Failed allocating buffer for token information");
break;
}
WCHAR* pSid = nullptr;
if (GetTokenInformation(
hTok,
TokenUser,
pBuf,
dwSize,
&dwSize))
{
PTOKEN_USER pUserToken = reinterpret_cast<PTOKEN_USER>(pBuf);
if (false == ConvertSidToString(pUserToken->User.Sid, &pSid))
{
LOG_ERROR(L"Failed converting sid to string");
break;
}
bSuccess = true;
::LocalFree(pSid);
}
} while (false);
if (pBuf)
::LocalFree(pBuf);
if (hTok && INVALID_HANDLE_VALUE != hTok)
::CloseHandle(hTok);
return bSuccess;
Another idea I had in mind, is to open explorer.exe's token, but I encountered another issue when 2 users are logged in, how can I differentiate between the running explorer.exes instances now?
Edit: If i retrieve Active desktop's SID using GetUserObjectInformation with UOI_USER_SID, i get the Logon Session which is 20 in length, is it possible to somehow translate this logon session into user session?
The WTS functions have been around since Windows 2000.
I can think of a few approaches to your issue. Your elevated process can either:
use WTSQuerySessionInformation() to query the current session for WTSUserName/WTSDomainName, or WTSSessionInfo (which also provides domain/user), then pass those values to LookupAccountName() to get that user's SID.
use OpenDesktop() to open the current desktop, then use
GetUserObjectInformation() to query it for UOI_USER_SID.
create a separate service running in the LocalSystem account that uses WTSQueryUserToken() to get the current session's user token (your app could pass its current SessionID to the service so it knows which session to query), then use GetTokenInformation() to query for its TokenLogonSid, and pass the SID back to your app.

RegLoadKey is giving error code 5 (Access Denied)

Hi I'm trying to load a key from HKLM\\SYSTEM\\CurrentControlSet\\Services\\Fax but i'm getting error 5 (Access Denied). I'm not able to figure it out what is wrong with my code.
Here is my code
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if ( !AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
void _tmain(int argc, TCHAR *argv[])
{
HKEY hKey;
LONG lErrorCode;
HANDLE ProcessToken;
LPCWSTR subkey = L"SYSTEM\\CurrentControlSet\\Services\\Fax";
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &ProcessToken))
{
SetPrivilege(ProcessToken, SE_BACKUP_NAME, TRUE);
SetPrivilege(ProcessToken, SE_RESTORE_NAME, TRUE);
SetPrivilege(ProcessToken, SE_RESTORE_NAME, TRUE);
}
lErrorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey ,
0, KEY_ALL_ACCESS, &hKey);
if (lErrorCode != ERROR_SUCCESS)
{
_tprintf(TEXT("Error in RegOpenKeyEx (%d).\n"), lErrorCode);
return;
}
else
{
_tprintf(TEXT("Key is successfully Opened\n"));
}
lErrorCode = RegSaveKey(hKey,L"c:\\load.reg",0);
if (lErrorCode != ERROR_SUCCESS)
{
_tprintf(TEXT("Error in RegSaveKey (%d).\n"), lErrorCode);
return;
}
else
{
_tprintf(TEXT("Key is successfully Saved \n"));
}
lErrorCode = RegLoadKey(HKEY_LOCAL_MACHINE,subkey,L"c:\\load.reg");
if (lErrorCode != ERROR_SUCCESS)
{
_tprintf(TEXT("Error in RegLoadKey (%d).\n"), lErrorCode);
return;
}
else
{
_tprintf(TEXT("Key is successfully loaded \n"));
}
lErrorCode = RegCloseKey(hKey);
if (lErrorCode != ERROR_SUCCESS)
{
_tprintf(TEXT("Error in closing the key (%d).\n"), lErrorCode);
return;
}
else
{
_tprintf(TEXT("Key is successfully closed \n"));
}
}
This is the output
Key is successfully Opened
Key is successfully Saved
Error in RegLoadKey (5).
RegLoadKey can only be used to load a new hive into the registry. You can't use it to overwrite a subkey of an existing hive.
You probably want to use RegRestoreKey instead.
Additional:
To the best of my knowledge, hives can only be loaded at a registry root, i.e., they must be HKEY_LOCAL_MACHINE\foo or HKEY_USERS\foo, never HKEY_LOCAL_MACHINE\foo\bar. Also, I don't think you can load a hive with a name that already exists, e.g., you can't load a hive into HKEY_LOCAL_MACHINE\SOFTWARE. Even if you could do these things, you'd be changing your view of the content, not merging it, and when the system was rebooted the original content would reappear. As previously mentioned, if you want to insert data into an existing hive, use RegRestoreKey rather than RegLoadKey.
You ask about the use cases for RegLoadKey: there aren't many. Mostly, it's used by the operating system; for example, that's how your personal hive is loaded into HKEY_USERS\username when you log in. There are some oddball cases, such as resetting a password offline or otherwise modifying the registry of another Windows instance. The process I use for the unattended installation of Windows on the computers in my teaching lab depends on modifying the registry of the Windows installation image to disable the keyboard and mouse.