2

I'm in a test environment trying to use Wireshark to capture credentials being passed to MySQL. I've done some digging and I read that the MySQL client hashes the password before sending even when passing unencrypted. So, when I capture the packet containing the credentials, I'm expecting to see the username in the clear and the hashed password being passed, but that's not what I see. The username is in the clear, but the password doesn't equal the hashed password from the database. What's even weirder is the password changes in the packet, each time I log in.

WireShark Login Packet #1:

MAX Packet: 16777216
Charset: utf8 COLLATE utf8_general_ci (33)
Username: root
Password: ada5be054b6a9b44eaa0d86e33fb9442e8af7169
Client Auth Plugin: mysql_native_password

WireShark Login Packet #2:

MAX Packet: 16777216
Charset: utf8 COLLATE utf8_general_ci (33)
Username: root
Password: 78a85ed4ba56ae733057226fdc0a189b7672a0a7
Client Auth Plugin: mysql_native_password

WireShark Login Packet #3:

MAX Packet: 16777216
Charset: utf8 COLLATE utf8_general_ci (33)
Username: root
Password: f097e87cbba8f39cbaa3403dd5f7c966e3ed3969
Client Auth Plugin: mysql_native_password

I've looked at MySQL documentation and searched the Internet and can't seem to find anything on this. Does anyone have any thoughts/ideas?

Thanks for your help!

Eric P
  • 151
  • 2
  • 6

2 Answers2

2

Not an expert in MySQL, but typically password authorization is protected from replay attack, so sniffing network shouldn't allow attacker to authorize by sending exactly the same data. This is usually done with nonce: server sends unique data, so client must provide hash(nonce + password_hash) in order to authorize. On next connection different nonce is provided, different authorization data must be sent, so previous authorization data can't be reused.

Also note, that hash might be hash of hash of hash of hash and include salt on one or multiple steps, so comparing sent data with known password requires knowing exact algorithm.

nnovich-OK
  • 2,900
  • 2
  • 13
  • 16
  • I can't seem to find the use of nonce in any of MySQL's documentation? – Eric P Feb 15 '18 at 17:02
  • 3
    Ok, you made me googling MySQL protocol details. [This article](https://www.safaribooksonline.com/library/view/understanding-mysql-internals/0596009577/ch04s04.html) sheds some light on authentication. Server sends random seed string to client in greeting packet, so client builds authentication data by scrambling password with seed. – nnovich-OK Feb 15 '18 at 17:28
  • @nnovich-OK, FYI, The link of the article you have shared is not hitting a 404 Not Found – Danwand N S Sep 30 '21 at 10:02
2

Lets observe a wireshark dump of a mysql_native_password authentication of username "apoc" with the password "rockyou".

First we receive a MySQL Protocol Server Greeting (using MariaDB 10.3.31):

MySQL Protocol
    Packet Length: 111
    Packet Number: 0
    Server Greeting
        Protocol: 10
        Version: 5.5.5-10.3.31-MariaDB-1:10.3.31+maria~focal
        Thread ID: 10
        Salt: rC6%U]Tk
        Server Capabilities: 0xf7fe
        Server Language: latin1 COLLATE latin1_swedish_ci (8)
        Server Status: 0x0002
        Extended Server Capabilities: 0x81bf
        Authentication Plugin Length: 21
        Unused: 000000000000
        MariaDB Extended Server Capabilities: 0x00000007
        Salt: @;H8~5)orp]_
        Authentication Plugin: mysql_native_password

Our client responds with the Login Request:

MySQL Protocol
    Packet Length: 208
    Packet Number: 1
    Login Request
        Client Capabilities: 0xa684
        Extended Client Capabilities: 0x20ff
        MAX Packet: 16777216
        Charset: utf8 COLLATE utf8_general_ci (33)
        Unused: 00000000000000000000000000000000000000
        MariaDB Extended Client Capabilities: 0x00000005
        Username: apoc
        Password: e12a39bbf61a19c68413fa57fc65c7af3a86b85f
        Client Auth Plugin: mysql_native_password
        Connection Attributes

The way mysql_native_password is calculating the client password that is sent in the Login Request is using this formular (documented here):

SHA1( password ) XOR
    SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )

To reconstruct the password we received in the Login Request (e12a39bbf61a19c68413fa57fc65c7af3a86b85f) we need the 20 bytes of random data (the salts) in the server greeting request.

The seed is made up of the two Salt parameters received in the Server Welcome request, rC6%U]Tk and @;H8~5)orp]_ (20 bytes concatenated).

To calculate the client password hash the client uses this formular:

SHA1( "rockyou" ) XOR
    SHA1( "rC6%U]Tk@;H8~5)orp]_" <concat> SHA1( SHA1( "rockyou" ) ) )

= "e12a39bbf61a19c68413fa57fc65c7af3a86b85f"

You can use this python code to reconstruct the hash:

from binascii import hexlify
from hashlib import sha1 as sha1_

def sha1(data):
    h = sha1_()
    h.update(data)
    return h.digest()

def xor(x, y):
    return (int.from_bytes(x, 'little') ^ 
        int.from_bytes(y, 'little')).to_bytes(len(x), 'little')
    #  in numpy this is:
    # data = np.bitwise_xor(
    #     np.frombuffer(x, dtype=np.uint8), np.frombuffer(y, dtype=np.uint8))
    # return data.tobytes()

password = b'rockyou'
salt = b"rC6%U]Tk"
salt += b"@;H8~5)orp]_"

hashed = xor(sha1(password), sha1(salt + sha1(sha1(password))))

print(hexlify(hashed))
mattzq
  • 21
  • 2