PHP Decoding MySQL's .mylogin.cnf
<?php | |
// 2019-01-28: Tested with PHP 7.0.32 (AR) | |
// tested with PHP 7.1.7 | |
$start = microtime(true); | |
const LOGIN_KEY_LEN = 20; | |
const MY_LOGIN_HEADER_LEN = 24; | |
const MAX_CIPHER_STORE_LEN = 4; | |
$raw = file_get_contents(__DIR__ . '/.mylogin.cnf'); | |
$fp = 4; // skip null bytes | |
$b = substr($raw, $fp, LOGIN_KEY_LEN); | |
$fp = MY_LOGIN_HEADER_LEN; | |
// extract key | |
$key = array_pad([], 16, 0); | |
for ($i = 0; $i < LOGIN_KEY_LEN; $i++) { | |
$key[$i % 16] ^= ord($b[$i]); | |
} | |
$key = pack('C*', $key[0], $key[1], $key[2], $key[3], | |
$key[4], $key[5], $key[6], $key[7], | |
$key[8], $key[9], $key[10], $key[11], | |
$key[12], $key[13], $key[14], $key[15]); | |
$settings = []; | |
while ($fp < strlen($raw)) { | |
$b = substr($raw, $fp, MAX_CIPHER_STORE_LEN); | |
$fp += MAX_CIPHER_STORE_LEN; | |
$cipher_len = unpack('V', $b); | |
$b = substr($raw, $fp, $cipher_len[1]); | |
$fp += $cipher_len[1]; | |
$plain = trim(openssl_decrypt($b, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING)); | |
if (preg_match('/^(\w+) = (.*)/', $plain, $matches)) { | |
$settings[$matches[1]] = $matches[2]; | |
} | |
} | |
// in my VM, this took about 1/10th of a millisecond, float(0.00011301040649414) | |
$end = microtime(true); | |
var_dump($end - $start); | |
var_dump($settings); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Nice, thank you. I started with this and it works. But it seems much more complicated than needed. See http://mysqldump.azundris.com/archives/104-.mylogin.cnf-password-recovery.html or a Perl version : https://github.com/mivk/decode-mylogin/blob/master/decode-mylogin