環境 :
Windows Vista / Windows 7
こんにちは。
日経ソフトウェアの 9 月号がリリースされましたが、この中で、以前も予告した通り、UAC (ユーザーアカウント制御) を完全解説しました。
日経ソフトウェア 2009 年 09 月号 「Windows 7 プログラミング 第2回 – UAC 徹底解説」
http://itpro.nikkeibp.co.jp/article/MAG/20090721/334111/
UAC については過去に こちら でも解説をおこなっていますが、上記では 「UAC における昇格のメカニズム (= 開発者は、コードでこれを乗り越えられるのか !?) 」、「ファイル / レジストリー仮想化の落とし穴」など、書ききれなかった「完全解説版」的な位置づけとして記載しました。(かつ、退屈しない分量で . . .)
実は上記は、「Windows 7 プログラミングの連載記事で、な~んで UAC なのさ !?」と思われる方も居られるかもしれませんが、次回は、第 1 回で記載した ” 7 らしい特徴” と、この第 2 回の内容を前提に、一般的なプログラミングの流れを書いていきますので、是非ご期待ください。
さて、「退屈せずに読める」ようにしたかったので長いコード説明などは入れなかったのですが、上記で解説している 「昇格まがいな処理」 について、以下に少し補足しておきましょう。
例えば、上記で説明している昇格の仕組みを応用して、下記のようなコードを書いたと仮定しましょう。
下記の処理は、Windows XP や、UAC を「オフ」にした Windows Vista / Windows 7 などでは、ちゃんと動作します。(すみません、XP では試してませんが、その「ハズ」です。) つまり、標準ユーザーでログインしていても、普通に、管理者のプロセスを起動できます !
と、その前に、こうした危険な処理が実行されないよう、ローカルポリシーが設定されているはずですので、動作確認の前に、OS の以下の設定をおこなっておいてください。(UAC が昇格をおこなう際は System が実行しているため、無論こうした設定がなくても動いています。。。)
事前設定
- mmc を起動します (コマンドプロンプトで、「mmc」 でも OK)。
- [ローカルポリシーオブジェクト] のスナップインを追加します。
- [コンピューターの構成] – [Windows の設定] – [セキュリティの設定] – [ローカルポリシー] – [ユーザー権利の割り当て] の [認証後にクライアントを偽装], [プロセスレベルトークンの置き換え], [プロセスのメモリクォータの増加] に、下記のコードを実行するユーザーを追加しておきます。
なお、Windows 7 の場合は、[トークンオブジェクトの作成] にもこのユーザーを追加しておきましょう。(既定ではここには空になっています。) - ログインをしなおしてください。
コードの実行
Windows Vista / Windows 7 では、UAC をオフにして下記を実行すると、標準ユーザーであっても管理者ユーザーのプロセス (メモ帳) がスイスイとあがってきます。
(※注意 : 下記のコードでは、読みやすさのためにエラー処理などはすべて省略していますので注意してください . . .)
int _tmain(int argc, _TCHAR* argv[]){ PROCESS_INFORMATION pi = {0}; STARTUPINFO si; HANDLE hUser=NULL; HANDLE hToken=NULL; _tsetlocale(LC_ALL, _T("")); ZeroMemory(&si,sizeof(si)); si.cb=sizeof(si); si.lpDesktop = L"winsta0\default"; // ユーザーID とパスワードを取得 wchar_t userid[255], password[255]; wprintf_s(L"何に化けますか? アカウント:"); wscanf_s(L"%ls", userid, 254); fflush(stdin); wprintf_s(L"パスワード:"); wscanf_s(L"%ls", password, 254); fflush(stdin); // 別ユーザーでのログイン(トークン取得) LogonUser(userid, NULL, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hUser); // このプロセスに、偽装特権を有効化 HANDLE hProcSelf = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); HANDLE hTokenSelf; OpenProcessToken(hProcSelf, TOKEN_ADJUST_PRIVILEGES, &hTokenSelf); LUID luid; LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid); TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hTokenSelf, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); // ウィンドウステーションのオープン HWINSTA hwinstaSave = NULL; HWINSTA hwinsta = NULL; HDESK hdesk = NULL; PSID pSid = NULL; hwinstaSave = GetProcessWindowStation(); hwinsta = OpenWindowStation(L"winsta0", FALSE, READ_CONTROL | WRITE_DAC); hdesk = OpenDesktop( L"default", // interactive window station 0, // no interaction with other desktop processes FALSE, // handle is not inheritable READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS); SetProcessWindowStation(hwinsta); // ウィンドウステーション/ デスクトップへの DACL 設定 GetLogonSID(hUser, &pSid); // <- 下記参照 AddAceToWindowStation(hwinsta, pSid); // <- 下記参照 AddAceToDesktop(hdesk, pSid); // <- 下記参照 // 偽装実行 ImpersonateLoggedOnUser(hUser); // CreateProcessAsUser !! (メモ帳を起動) CreateProcessAsUser(hUser, L"C:\Windows\System32\notepad.exe", L"", NULL, NULL,FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE , NULL, NULL, &si, &pi); // 偽装を戻す RevertToSelf();END: if (hwinstaSave) SetProcessWindowStation(hwinstaSave); if(pi.hProcess) CloseHandle(pi.hProcess); if(hUser!=NULL) CloseHandle(hUser); if(hTokenSelf) CloseHandle(hTokenSelf); if(hwinsta) CloseWindowStation(hwinsta); if(hdesk) CloseDesktop(hdesk); return 0;}
ところが、UAC をオンにすると、記事の中でも記載しているように、「ある箇所 ?」 で立派にエラー (error) となります。(記事を読まれた方はどこかわかりますよね ? . . . 内緒です)
また、上記のコードで、GetLogonSID, AddAceToWindowStation, AddAceToDesktop は、以下の関数になります。(Windows API ではありません。MSDN などにも載っています . . .)
なお、ハッカー養成のために書いているのではありません。記事の中でも記載しているように、今後の Windows プログラミングの 「前提」 となる知識ですので、是非ポイントと考え方をおさえておいてください。
///////////////// 下記関数は、MSDN から抜粋 (エラー処理などは省略してます)///////////////void GetLogonSID(HANDLE hToken, PSID *ppsid){ DWORD dwIndex; DWORD dwLength; PTOKEN_GROUPS ptg; // トークン情報を取得 // (サイズ取得-> 割り当て-> 情報取得) GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwLength); ptg = (PTOKEN_GROUPS) HeapAlloc(GetProcessHeap(), 0, dwLength); GetTokenInformation(hToken, TokenGroups, (LPVOID) ptg, dwLength, &dwLength); // SE_GROUP_LOGON_ID を取得 for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) { if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) { dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid); *ppsid = (PSID) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid); break; } }END: if (ptg != NULL) HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);}
#define GENERIC_ACCESS (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)#define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | WINSTA_READSCREEN | STANDARD_RIGHTS_REQUIRED)void AddAceToWindowStation(HWINSTA hwinsta, PSID psid){ SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; // ウィンドウステーションのSecurity Descriptor を取得 // (サイズ取得-> 割り当て-> 情報取得) PSECURITY_DESCRIPTOR psd; DWORD dwSdSize; GetUserObjectSecurity(hwinsta, &si, NULL, 0, &dwSdSize); psd = (PSECURITY_DESCRIPTOR) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSdSize); GetUserObjectSecurity(hwinsta, &si, psd, dwSdSize, &dwSdSize); // 新規にSecurity Descriptor を作成 PSECURITY_DESCRIPTOR psdNew; psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSdSize); InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION); // 既存のSecurity Descriptor からACL を取得 PACL pacl; BOOL bDaclPresent, bDaclExist; GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclExist); // 必要なACL サイズ情報の取得 DWORD dwNewAclSize; ACL_SIZE_INFORMATION aclSizeInfo; GetAclInformation(pacl, (LPVOID)&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation); dwNewAclSize = aclSizeInfo.AclBytesInUse + (2*sizeof(ACCESS_ALLOWED_ACE)) + (2*GetLengthSid(psid)) - (2*sizeof(DWORD)); // 新規にACL を作成(初期化) PACL pNewAcl; pNewAcl = (PACL)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwNewAclSize); InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION); for (unsigned int i=0; i < aclSizeInfo.AceCount; i++) { // i 番目のACE を取得 PVOID pTempAce; GetAce(pacl, i, &pTempAce); // ACL に、既存のACE を設定 AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, ((PACE_HEADER)pTempAce)->AceSize); } // 新しい権限のACE を追加(1 つ目) ACCESS_ALLOWED_ACE *pace; pace = (ACCESS_ALLOWED_ACE *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD)); pace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pace->Header.AceFlags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE; pace->Header.AceSize = sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD); pace->Mask = GENERIC_ACCESS; CopySid(GetLengthSid(psid), &pace->SidStart, psid); AddAce(pNewAcl, ACL_REVISION, MAXDWORD, (LPVOID)pace, pace->Header.AceSize); // 新しい権限のACE を追加(2 つ目) pace->Header.AceFlags = NO_PROPAGATE_INHERIT_ACE; pace->Mask = WINSTA_ALL; AddAce(pNewAcl, ACL_REVISION, MAXDWORD, (LPVOID)pace, pace->Header.AceSize); // ウィンドウステーションにSecurity Descriptor とACL を設定 SetSecurityDescriptorDacl(psdNew, TRUE, pNewAcl, FALSE); SetUserObjectSecurity(hwinsta, &si, psdNew);END: if (pace != NULL) HeapFree(GetProcessHeap(), 0, (LPVOID)pace); if (pNewAcl != NULL) HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl); if (psd != NULL) HeapFree(GetProcessHeap(), 0, (LPVOID)psd); if (psdNew != NULL) HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);}
#define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)void AddAceToDesktop(HDESK hdesk, PSID psid){ SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; // デスクトップのSecurity Descriptor を取得 // (サイズ取得-> 割り当て-> 情報取得) PSECURITY_DESCRIPTOR psd; DWORD dwSdSize; GetUserObjectSecurity(hdesk, &si, NULL, 0, &dwSdSize); psd = (PSECURITY_DESCRIPTOR) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSdSize); GetUserObjectSecurity(hdesk, &si, psd, dwSdSize, &dwSdSize); // 新規にSecurity Descriptor を作成 PSECURITY_DESCRIPTOR psdNew; psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSdSize); InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION); // 既存のSecurity Descriptor からACL を取得 PACL pacl; BOOL bDaclPresent, bDaclExist; GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclExist); // 必要なACL サイズ情報の取得 DWORD dwNewAclSize; ACL_SIZE_INFORMATION aclSizeInfo; GetAclInformation(pacl, (LPVOID)&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation); dwNewAclSize = aclSizeInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD); // 新規にACL を作成(初期化) PACL pNewAcl; pNewAcl = (PACL)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwNewAclSize); InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION); for (unsigned int i=0; i < aclSizeInfo.AceCount; i++) { // i 番目のACE を取得 PVOID pTempAce; GetAce(pacl, i, &pTempAce); // ACL に、既存のACE を設定 AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, ((PACE_HEADER)pTempAce)->AceSize); } // 新しい権限のACE を追加 AddAccessAllowedAce(pNewAcl, ACL_REVISION, DESKTOP_ALL, psid); // デスクトップにSecurity Descriptor とACL を設定 SetSecurityDescriptorDacl(psdNew, TRUE, pNewAcl, FALSE); SetUserObjectSecurity(hdesk, &si, psdNew);END: if(pNewAcl) HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl); if(psd) HeapFree(GetProcessHeap(), 0, (LPVOID)psd); if(psdNew) HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);}
Categories: Uncategorized