Skip to content

Instantly share code, notes, and snippets.

@thecliguy thecliguy/decode.php

forked from robocoder/decode.php
Last active Feb 10, 2020
Embed
What would you like to do?
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);
@mivk

This comment has been minimized.

Copy link

mivk commented Feb 10, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.