From: pgut01@cs.auckland.ac.nz (Peter Gutmann) Newsgroups: alt.security,comp.security.misc,sci.crypt Subject: How Windows encrypts .PWL files Date: 28 Nov 1995 13:13:36 GMT Organization: University of Auckland Sender: pgut01@cs.auckland.ac.nz (Peter Gutmann) Message-ID: <49f1u0$6ai@net.auckland.ac.nz> NNTP-Posting-Host: cs26.cs.auckland.ac.nz Windows for Workgroups can assign passwords to shared resources such as shared directories, printer queues, and network DDE shares. To save having to enter a password for each resource each time you log on, it keeps encrypted copies of the passwords in password (.PWL) files and decrypts them with the password you use initially to log on. The details of this are mostly undocumented "for security reasons". They're about to become documented... When you log on for the first time or when you delete your .PWL file, Windows will ask you whether you want it to create a password file for you. This contains the encrypted passwords. You add passwords to the file using the semi-documented WNetCachePassword() function and retrieve them using the equally semi-documented WNetGetCachedPassword() function. Anything running on the machine at any time a user is logged on can use WNgetGetCachedPassword() to retrieve any of the passwords in the .PWL file. When you add a password, you supply the name of the resource it pertains to (eg "\\NTSERVER\PRINTER1"), the password itself, and a number from 1 to 254 for the resource type (more about 0 and 255 later). Windows reserves resource types 1, 2, and 3 for its own use for network resources. The .PWL file is not protected from modification. To use a new password to access the network, simply delete the .PWL file and Windows will ask you if you want to create a new one the next time you log on. There can be no more than 255 passwords with types 1 to 127. If more than 256 are added, Windows starts throwing existing ones away to make room. You can avoid this by giving the resource a type from 128 to 254, but once you've used up this range all further attempts to add passwords will fail. By adding too many passwords of either type, you can either cause all existing passwords to be discarded by Windows or saturate the password file so no new passwords can be added. Most users (if they can figure out what's going wrong) will respond by deleting the password file and starting again with a new file, giving you a chance to recover from any "mistakes" you may have made with their .PWL file. The .PWL file contains a small header, two blocks of 256 byte-fields, and then the encrypted password resources. The header consists of a 4-byte magic ID "\xB0MFN", a 4-byte count of the number of password resources, 256 fields containing resource numbers or 0 if this resource is unused (which is why resource type 0 isn't allowed), and 256 fields containing a resource key or 0xFF is this field is unused (which is why resource type 255 isn't allowed). When you add a new password resource, Windows scans through the second array looking for the first 0xFF entry and then uses this to store the password information. Following this data come the encrypted password resources. Windows stores the logon password in memory at all times, performing a simple transformation on a copy of it whenever it's needed to encrypt or decrypt resources. The password is copied around to other memory locations as needed. Since the original password location isn't in a VxD (Windows virtual device driver), it can be swapped to disk at any point, and given that it's in memory for the whole time Windows is in use but is only rarely used, it probably does end up in the swapfile on numerous occasions. The transformation performed on the in-memory password, which reduces a variable-length password to 32 bits, is as follows: unsigned long result = 0L; for( i = 0; i < passwordLen + 1; i++ ) { int tmp = ( int ) ( result >> 25 ); result += toupper( password[ i ] ); result = ( result << 7 ) | tmp; } Note the use of passwordLen + 1, which includes the null terminator at the end of the string as part of the password. The effect is simply to rotate the final result by 7 bits as toupper( password[ i ] ) == 0. Here's the output from the password "blem": After After add rotate i = 0 00000042 00002100 i = 1 0000214C 0010A600 i = 2 0010A645 08532280 i = 3 085322CD 29916684 i = 4 29916684 C8B34214 This 32-bit key is then passed to RC4 which is used to encrypt or decrypt the resources. The above key would be passed to RC4 as { 0x14, 0x42, 0xB3, 0xC8 }. The RC4 implementation was licensed by MS in object form and probably comes from the BSAFE toolkit. I assume everyone has seen RC4 so I won't include it here (in any case the implementation used in Windows is fairly grotty, I'd recommend using the asm version I posted to sci.crypt a few weeks ago). The RC4 key usually doesn't contain anywhere near 32 bits of entropy, although as a whole it's not spectacularly ugly (you just keep rotating the 32-bit value, adding in one letter of the password each time). It's probably easier to brute-force the 32-bit key than to try anything clever based on the fact that the password will generally consist entirely of uppercase letters. There have been numerous posts in the past on the insecurity of Windows networking. This one shows that even the very basic access control mechanisms are highly insecure, and what's more can't really be made secure: You can patch Windows to use the whole logon password instead of the mangled 32-bit version as the RC4 key, but then you need to contend with the fact that any program can get a password with WNetGetCachedPassword() at any time (think of the possibilities with a machine running arbitrary apps and a live internet connection to send the results over) and that the original password is sitting in memory (and in the swapfile) most of the time. In conclusion: The Windows password/.PWL encryption is quite weak and should not be relied upon to keep any information secure, or to secure access to sensitive resources. Peter.