模拟用户登录
--张佩
1. 用户模拟
如果将文件test.exe的安全属性设置成Guest用户无法访问的话,以Guest用户登录后,访问者就不能访问test.exe文件了。但有一个办法可以让访问者在使用Guest账户登录的情况下也能够访问此文件,就是使用用户模拟的方式,暂时以高权用户的身分访问这个文件!下面的代码实现了用户模拟:
HANDLEhToken; DWORD dwError; STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); si.cb = sizeof(si); if(0 ==LogonUser("administ", ".", "password", LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, &hToken)) { printf("Failed to call LogonUser/r/n"); dwError = GetLastError(); } else { if(CreateProcessAsUserW(hToken,L"c://test.exe",NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {/×成功×/} }
代码调用了两个关键函数:LogonUser获取高权用户的令牌(Token),令牌代表了用户所能拥有的系统权限,使用这个API需要将用户名称和密码作为参数输入;CreateProcessAsUser利用此用户令牌创建进程,从而执行有安全保护的文件。
如果要提权的不是可执行程序,而是一个文档(比如:text.txt),比如要读取它里面的数据,应怎么办呢?其实很简单,代码如下:
bRet = ImpersonateLoggedOnUser(hToken); if(bRet) { FILE* hFile = fopen("c://test.txt", "r"); RevertToSelf(); }
ImpersonateLoggedOnUser的作用是模拟用户,就是让当前执行进程暂时以被模拟用户的身份来运行,并完全使用被模拟用户的权限级别来操作。输入参数hToken就是前面已经获取的用户令牌。调用RevertToSelf让当前进程恢复到本身的用户环境。
2. 问题和改进
上面的方法有一个缺点,通过LogonUser打开用户令牌的时候,必须输入用户名和密码作为必备参数,可移植性太差。如果有更好的办法获取高权用户Token就好了。每个进程在运行的时候都保存一个用户令牌,而用户令牌又是可以获取的,从这一点出发,我们能想到更好的办法。winlogon.exe进程总是以System用户权限运行,下面的代码将从Winlogon中提取System用户令牌(代码作者是我的同事jiurl):
pid = FindWinlogon();//取得winlogon.exe的进程ID,从略 if(pid<0 ) { return 0; } hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); if(hProcess==NULL ) { return 0; } rc = OpenProcessToken( hProcess, TOKEN_ALL_ACCESS, &hOldToken ); if(rc==0 ) { CloseHandle(hProcess); return 0; } rc = DuplicateTokenEx(hOldToken,NULL, NULL, SecurityIdentification, TokenPrimary, &hNewToken); if(rc==0 ) { CloseHandle(hProcess); CloseHandle(hOldToken); return 0; }
(注:上面的代码一般应在单独的服务中运行,否则代码本身可能会因为权限问题而中途失败)
代码分成了三个部分,首先是找到Winlogon进程,具体方法很多,代码从略。然后获取进程令牌,通过调用OpenProcessToken函数实现。最后复制一个令牌给当前进程使用,从而生成新的令牌句柄,这一步通过调用DuplicateToken/DuplicateTokenEx实现。得到新的令牌句柄hNewToken后,我们就可以继续第一节中的用户模拟,从而实施高权操作了。