#!/usr/bin/perl -w # # $Id: //pentools/main/aimpass/aimpass.p#2 $ # # written by : Stephen J. Friedl # Software Consultant # Tustin, California USA # steve@unixwiz.net / www.unixwiz.net # # This program is a front-end to the AOL Instant Messenger # password decoder library routine: given an obscured password # presumably taken from the registry (or perhaps from the wire, # we're not sure how that works), it presents the cleartext # password. # # Under Windows, the "saved passwords" are found in the registry # under the string value: # # HKEY_CURRENT_USER\ # Software\ # America Online\ # AOL Instant Messenger (TM)\ # CurrentVersion\ # Users\ # (username)\ # Login\ # Password # # Note that each AIM user has his own (username) key, and the password # itself is always two bytes of FF followed by regular ASCII. The # FF bytes can be ignored, and the rest are all passed to the # --decode # # The password itself is two bytes of FF followed by regular ASCII, # and the FF bytes can be ignored. The rest are given to this program # after the "--decode=" parameter. # use strict; # use aimpass; my $version = '1.0 (2002/03/01) - steve@unixwiz.net'; my $seen_any = 0; foreach ( @ARGV ) { if ( m/^--help/i ) { print STDERR < {$decpass}\n"; } else { print "ERROR {$encpass} cannot be decoded.\n"; } ++$seen_any; } elsif ( m/^--encode=(.+)/ ) { my $decpass = $1; if ( my $encpass = encode_AIM_password( $decpass ) ) { print "ENCODE {$decpass} -> {$encpass}\n"; } else { print "ERROR {$decpass} cannot be encoded.\n"; } ++$seen_any; } else { die "ERROR: {$_} is an invalid cmdline param (try --help)\n"; } } die "ERROR: no passwords given to decode/encode (try --help)\n" if $seen_any == 0; # # $Id: //pentools/main/libperl/aimpass.pm#6 $ # # written by : Stephen J. Friedl # Software Consultant # Tustin, California USA # steve@unixwiz.net / www.unixwiz.net # 2002/02/18 # # AOL Instant Messenger stores its saved passwords in the registry # in an obscured format, and it's not difficult to reverse the hash # and produce the plaintexe password. There are applications on the # net that will perform this decoding, such as "Aim Recover" from # Dark Eclipse (http://www.dark-e.com), but these only work on the # local system's registry. In our work doing penetration testing, # we needed to apply this to a *remote* registry, and this program # doesn't seem to do that. So we figured out the obscuring mechanism # and have reduced it to perl code. # # REGISTRY LOCATIONS # ------------------ # # AIM is configired on a per-user basis in the registry, but for # the current user the location is # # HKEY_CURRENT_USER\ # Software\ # America Online\ # AOL Instant Messenger (TM)\ # CurrentVersion\ # Users\ # # Under this key are sub-keys for each user, and under each user # is a "Login" key that contains a "Password" string value: this # is what's encrypted. There are other fields there, but those are # not for this discussion. # # When attaching to a remote registry, we can also enumerate the # users HKEY_USERS hive and extract the AIM info from each one. # # DECODING ALGORITHM # ------------------ # # Each AIM obscured password from the registry is laid out as pairs # of bytes that represent individual bytes, plus a pair of "FF" # bytes at the beginning. These FF bytes might just be to make # them harder to type, but they don't appear to participate in # the password so we generally ignore them. # # The FF bytes are followed by alphabetic characters that are # all mapped into "regular" hex digits using this little translate # table: # # A -> 0 E -> 4 I -> 8 M -> C # B -> 1 F -> 5 J -> 9 N -> D # C -> 2 G -> 6 K -> A O -> E # D -> 3 H -> 7 L -> B P -> F # # The result is pairs of hex bytes, and by way of example we show # the longest possible encrypted password (16 characters) and the # decoded hex bytes. # # xFF xFF CB OH GL HD EC CB OG GI HE EN DO NJ BH IL LD MC # 21 E7 6B 7e 42 21 E6 68 74 4D e# D9 17 8B B3 C2 # # This string is XOR'd with a fixed pattern: # # 42 84 08 10 21 42 85 0B 17 2E 5D BA 74 E8 D0 A1 XOR HASH # # To yield the result: # # 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 (hex) # c c c c c c c c c c c c c c c c (bytes) # # So "\xFF\xFFCBOHGLHDECCBOGGIHEENDONJBHILLDMC" is "cccccccccccccccc" # # # USING THIS MODULE # ----------------- # # Simply call "AIM_unhash" with the obscured password, and it returns # the unobscured password. We delete the leading 0xFF (if found) and # do not care if it's not found in the first place. For testing, we # can also include whitespace in the obscured password for testing # (we don't expect to see this in real code). # # Just to be complete, we also provide an obscuring function as well. # # BUGS & TODO LIST # ---------------- # # We should really figure out if passwords are allowed to be longer # than 16 chars. # # HISTORY # ------- # # This was written by observing AIM version 4.7.2480 on a # Windows 2000 system. Mileage on other systems may vary. # use strict; # ------------------------------------------------------------------------ # The hash was determined by observation, and it seems to be 16 bytes # long. We don't know if AIM allows longer passwords than 16, so the # code below "recyles" the @AIMHASH values after it runs out: this gives # us as long # # NOTE: the BEGIN { } business allows # my @AIMHASH; # DO NOT INITILIAZE THIS! BEGIN { @AIMHASH = ( 0x42, 0x84, 0x08, 0x10, 0x21, 0x42, 0x85, 0x0B, 0x17, 0x2E, 0x5D, 0xBA, 0x74, 0xE8, 0xD0, 0xA1 ); } sub decode_AIM_password { my $hash = shift; # ---------------------------------------------------------------- # Do a bit of cleanup on the string: dump the leading 0xFF (if any) # and delete all the whitespace. We don't normally find whitespace # in a real obscured password, but we might use this in our test # fixtures. # # Then we return failure if the encoded password is not in the # special alphabet or it's not an even number of bytes long. # $hash =~ s/^\xff+//; # dump leading 0xFF $hash =~ s/\s+//g; # dump spaces return undef unless $hash =~ m/^ ( [A-P][A-P] )+ $/x; # convert the alphabet into the proper hex bytes $hash =~ tr/ABCDEFGHIJKLMNOP/0123456789ABCDEF/; # now start decoding my $pass = ""; # password my @H = @AIMHASH; # while ( $hash =~ m/(..)/g ) { $pass .= chr( hex($1) ^ shift @H ); # reuse the hash @H = @AIMHASH if @H == 0; } return $pass; } sub encode_AIM_password { my $cleartext = shift; my $encpass = ""; my @H = @AIMHASH; foreach my $ch ( split( m//, $cleartext) ) { $ch = ord($ch); # turn into binary (from char) $ch ^= shift @H; # hash it $encpass .= sprintf("%02X", $ch); # reuse the hash @H = @AIMHASH if @H == 0; } $encpass =~ tr/0123456789ABCDEF/ABCDEFGHIJKLMNOP/; return $encpass; } 1;