On OS-X, there is an option to allow the machine to automatically log a user in on boot. Obviously, this is pretty insecure - not only does it let any new 'owner' of your machine straight in, it also leaves an obfuscated copy of that password in /etc/kcpassword. (It is protected as root, but that is no help if the machine is booted single user or from CD)
Now there are (a very few) reasons you might want to use this feature. In our case, we want to bypass the login screen since we are passing through authentication from another system. To achieve this, we want to write the user's password to /etc/kcpassword, set a few loginwindow settings and restart loginwindow.
As I mentioned, kcpassword is just obfuscated. The format is simple and it only took an evening to decode. The password is XOR'ed with a series of 11 bytes (not sure if they have any hidden meaning). If the password is longer than 11 bytes, the pattern is repeated. Interestingly OS-X writes the file in multiples of 12 bytes. Any excess seems to be random data. I hope this is intentional obfuscation, not accidental reading of unallocated memory!
The following code just encodes the password, but since it is XORed, decoding is the same function. Just remember to strip the garbage after the null in the decoded string. That said, I can't think of any valid reason to decode a kcpassword.
Have fun, and remember "sudo rm -f /etc/kcpassword" ...
#!/usr/bin/perl
# Gavin Brock (http://brock-family.org/gavin/perl) - June 2007
#==============================================================================#
use strict;
use warnings;
use Foundation;
#==============================================================================#
sub kcpassword_xor {
my ($pass) = @_;
### The magic 11 bytes - these are just repeated
# 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F
my @key = qw( 125 137 82 35 210 188 221 234 163 185 31 );
my $key = pack "C*", @key;
my $key_len = length $key;
for (my $n=0; $n<length($pass); $n+=$key_len) {
substr($pass,$n,$key_len) ^= $key;
}
return $pass;
}
#==============================================================================#
sub write_pass {
my ($enc) = @_;
my $file = '/etc/kcpassword';
open(my $kcp, '>', $file) || die;
print $kcp $enc;
close($kcp);
chmod(0600, $file);
}
#==============================================================================#
sub set_loginwindow {
my ($user) = @_;
my $uid = getpwnam($user) || die;
my $file = '/Library/Preferences/com.apple.loginwindow.plist';
$user = NSString->stringWithCString_($user);
$uid = NSNumber->numberWithLong_($uid);
my $plist = NSMutableDictionary->dictionaryWithContentsOfFile_($file);
die unless $plist && $$plist;
$plist->setObject_forKey_($user, "lastUserName");
$plist->setObject_forKey_($user, "autoLoginUser");
$plist->setObject_forKey_($uid, "autoLoginUserUID");
$plist->writeToFile_atomically_($file, 0);
}
#==============================================================================#
my $user = "someuser";
my $pass = "mysecretpassword";
my $enc = kcpassword_xor($pass);
write_pass($enc);
set_loginwindow($user);
system('killall','loginwindow');
exit 0;
#==============================================================================#
|