Skip to content

Instantly share code, notes, and snippets.

@robocoder
Last active December 2, 2023 04:25
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save robocoder/024442d06b8a75d292d58c5884be4642 to your computer and use it in GitHub Desktop.
Save robocoder/024442d06b8a75d292d58c5884be4642 to your computer and use it in GitHub Desktop.
PHP Decoding MySQL's .mylogin.cnf
<?php // tested with PHP 8.0.11
$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]);
$login = '';
$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('/^\[(.+?)\]/', $plain, $matches)) {
$login = $matches[1];
$settings[$login] = [];
} elseif (preg_match('/^(\w+) = "(.*?)"/', $plain, $matches)) {
$settings[$login][$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);
@robocoder
Copy link
Author

Generate the .cnf file using mysql_config_editor and copy to the current folder.

@robocoder
Copy link
Author

Refactored here for reuse: https://github.com/vipsoft/mysql-login

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment