mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
mysql: Handle server connection phase separately from command phase
This avoids interpreting an AuthSwitchRequest (0xfe) during the command phase as EOF_Packet. Thanks @AmazingPP. Closes #3880
This commit is contained in:
parent
44a3ed676b
commit
b1c63ae4e0
6 changed files with 99 additions and 15 deletions
|
@ -153,7 +153,7 @@ refine flow MySQL_Flow += {
|
|||
return true;
|
||||
%}
|
||||
|
||||
function proc_auth_switch_request(msg: AuthSwitchRequest): bool
|
||||
function proc_auth_switch_request_payload(msg: AuthSwitchRequestPayload): bool
|
||||
%{
|
||||
zeek::BifEvent::enqueue_mysql_auth_switch_request(connection()->zeek_analyzer(),
|
||||
connection()->zeek_analyzer()->Conn(),
|
||||
|
@ -201,8 +201,8 @@ refine typeattr Resultset += &let {
|
|||
proc = $context.flow.proc_resultset(this);
|
||||
};
|
||||
|
||||
refine typeattr AuthSwitchRequest += &let {
|
||||
proc = $context.flow.proc_auth_switch_request(this);
|
||||
refine typeattr AuthSwitchRequestPayload += &let {
|
||||
proc = $context.flow.proc_auth_switch_request_payload(this);
|
||||
};
|
||||
|
||||
refine typeattr AuthMoreData += &let {
|
||||
|
|
|
@ -296,7 +296,7 @@ type EmptyOrNUL_String = RE/([^\0]*\0)?/;
|
|||
type MySQL_PDU(is_orig: bool) = record {
|
||||
hdr : Header;
|
||||
msg : case is_orig of {
|
||||
false -> server_msg: Server_Message(hdr.seq_id, hdr.len);
|
||||
false -> server_msg: Server_Message(hdr.seq_id, hdr.len, state);
|
||||
true -> client_msg: Client_Message(state);
|
||||
} &requires(state);
|
||||
} &let {
|
||||
|
@ -310,17 +310,17 @@ type Header = record {
|
|||
len : uint32 = to_int()(le_len) + 4;
|
||||
} &length=4;
|
||||
|
||||
type Server_Message(seq_id: uint8, pkt_len: uint32) = case is_initial of {
|
||||
true -> initial_handshake: Initial_Handshake_Packet;
|
||||
false -> command_response : Command_Response(pkt_len);
|
||||
} &let {
|
||||
type Server_Message(seq_id: uint8, pkt_len: uint32, state: int) = case state of {
|
||||
CONNECTION_PHASE -> connection_phase: Server_Connection_Phase(is_initial);
|
||||
COMMAND_PHASE -> command_response: Command_Response(pkt_len);
|
||||
} &requires(is_initial) &let {
|
||||
is_initial : bool = (seq_id == 0) && ($context.connection.get_previous_seq_id() != 255);
|
||||
update_seq_id : bool = $context.connection.set_previous_seq_id(seq_id);
|
||||
};
|
||||
|
||||
type Client_Message(state: int) = case state of {
|
||||
CONNECTION_PHASE -> connection_phase: Connection_Phase_Packets;
|
||||
COMMAND_PHASE -> command_phase : Command_Request_Packet;
|
||||
type Server_Connection_Phase(is_initial: bool) = case is_initial of {
|
||||
true -> initial_handshake: Initial_Handshake_Packet;
|
||||
false -> subsequent_handshake: Server_Connection_Phase_Packets;
|
||||
};
|
||||
|
||||
# Handshake Request
|
||||
|
@ -371,6 +371,19 @@ type Handshake_v9 = record {
|
|||
scramble : NUL_String;
|
||||
};
|
||||
|
||||
# While in the CONNECTION_PHASE, handle the following packets. Note that
|
||||
# this is subtly different from Command_Response_Status which interprets
|
||||
# 0xfe as EOF packet and also has does not support AuthMoreData.
|
||||
type Server_Connection_Phase_Packets = record {
|
||||
pkt_type: uint8;
|
||||
packet: case pkt_type of {
|
||||
0x00 -> data_ok: OK_Packet;
|
||||
0x01 -> auth_more_data: AuthMoreData(false);
|
||||
0xfe -> auth_switch_request: AuthSwitchRequestPayload;
|
||||
0xff -> data_err: ERR_Packet;
|
||||
};
|
||||
};
|
||||
|
||||
# Handshake Response
|
||||
|
||||
type Handshake_Response_Packet = case $context.connection.get_version() of {
|
||||
|
@ -451,6 +464,11 @@ type Handshake_Response_Packet_v9 = record {
|
|||
|
||||
# Connection Phase
|
||||
|
||||
type Client_Message(state: int) = case state of {
|
||||
CONNECTION_PHASE -> connection_phase: Connection_Phase_Packets;
|
||||
COMMAND_PHASE -> command_phase : Command_Request_Packet;
|
||||
};
|
||||
|
||||
type Connection_Phase_Packets = case $context.connection.get_conn_expectation() of {
|
||||
EXPECT_HANDSHAKE -> handshake_resp: Handshake_Response_Packet;
|
||||
EXPECT_AUTH_DATA -> auth_data: AuthMoreData(true);
|
||||
|
@ -530,10 +548,6 @@ type Command_Response_Status = record {
|
|||
pkt_type: uint8;
|
||||
response: case pkt_type of {
|
||||
0x00 -> data_ok: OK_Packet;
|
||||
# When still in the CONNECTION_PHASE, the server can reply
|
||||
# with AuthMoreData which is 0x01 stuffed opaque payload.
|
||||
# https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_auth_more_data.html
|
||||
0x01 -> auth_more_data: AuthMoreData(false);
|
||||
0xfe -> data_eof: EOF_Packet(EOF_END);
|
||||
0xff -> data_err: ERR_Packet;
|
||||
default -> unknown: empty;
|
||||
|
@ -635,6 +649,10 @@ type AuthMoreData(is_orig: bool) = record {
|
|||
|
||||
type AuthSwitchRequest = record {
|
||||
status: uint8 &enforce(status==254);
|
||||
payload: AuthSwitchRequestPayload;
|
||||
};
|
||||
|
||||
type AuthSwitchRequestPayload = record {
|
||||
name : NUL_String;
|
||||
data : bytestring &restofdata;
|
||||
} &let {
|
||||
|
@ -875,6 +893,7 @@ refine connection MySQL_Conn += {
|
|||
expected_ = EXPECT_STATUS;
|
||||
break;
|
||||
case COM_CHANGE_USER:
|
||||
// XXX: Could we switch into CONNECTION_PHASE instead?
|
||||
expected_ = EXPECT_AUTH_SWITCH;
|
||||
break;
|
||||
case COM_BINLOG_DUMP:
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
#separator \x09
|
||||
#set_separator ,
|
||||
#empty_field (empty)
|
||||
#unset_field -
|
||||
#path mysql
|
||||
#open XXXX-XX-XX-XX-XX-XX
|
||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd arg success rows response
|
||||
#types time string addr port addr port string string bool count string
|
||||
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.21.179.53 58227 10.21.20.70 3306 login dsm1 F - Access denied for user 'dsm1'@'10.21.179.53' (using password: YES)
|
||||
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -0,0 +1,9 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
mysql auth plugin, F, caching_sha2_password, "\x037#\x03-\x17\x02B28\x0e4;y\x7f\x01Q\x08\x09\x00, 21
|
||||
mysql handshake, dsm1
|
||||
mysql auth plugin, T, caching_sha2_password, \x9dh\x1f\xbb\x8c+\x90\xbe\x06.\x18j\xe3\x90\xa6\x95M\x0c\xc5\x04c\xf4\xfa\xff\xfaJF\x88\x17\xdfu+, 32
|
||||
mysql auth switch request, sha256_password, (:1\x01tK7wV-BA.\x17hf)D\x0b(\x00, 21
|
||||
mysql auth more data, T, \x01, 1
|
||||
mysql auth more data, F, -----BEGIN PUBLIC KEY-----\x0aMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwp+ZvMw7i8dEUQK43WIz\x0aB1F7rnvXmrw+awePuJu2Syn+aqHU/pL92WP/zpbFPbliaNUcrEf9dZXgYmW96zcV\x0aUaJTwf2RtV9PcZVurYLjG0LDjiigAl5p3PehRwxFgNGiqO44MtTj+KFm0AjBZesD\x0aSPiwa/2+AamXvzWb6KzLRyICciNfA0gMCXJpxPHsq3pJrob0FimviRiaTfTyYe3n\x0aDNOlvtmoUeD4nv3Jg22+mwlHd5qdc04wKJDzaeDgQqRx9O5FTwQ5LGCydyUMXFtl\x0a03PnlGJ+UZqTrsyo3oXwhVmrHOuzd64Oz6lO74uNWcpSpsEV9EOW9IVidca1uxeA\x0aXQIDAQAB\x0a-----END PUBLIC KEY-----\x0a, 451
|
||||
mysql auth more data, T, }[\xdc\x7f\xca\xc1\x83\xb7\x14\x00%\xb6\x8fG\x89r\x935\xd54\x0c<9\xb2N\x10\x1d\x03\xcc\xb9u\x1f\xf0`\xad\x86\xcc*p\xefs\x8a\xac\x03\xbe\xe1\xb6 1\xd4\xd7'\xa7\x89\xf7\xed\xdbu\xad\x85W\x96\xd1\xfe\x89D+\x90\xc7/\x1b\xb0h\xadKM\x8c\xd9Z\x9c\x9c.\x0aJ\xcd)9\x1d&{5p\x1e\xd3\xa5\x1b\xdf\x1a\x8a\x82\xb6\x0cGm<\xbfw~\xc4\x9a\x17\x09\xca\xc0J\x01\x8d\xbe\x06o\xddo\xe12\xc5L\x80\x0b\xe0_nk\x1c\xac)\xcd\x02\xc7\xc3\x80af\xe3\xd7\xd1\x7f\xdb\x00\xa0u\xd2\xad\x0e\x17\x14\xc7K\x06\x05\xb02\xc2+\xda\xac\x13=)YJ;]\xe2\xf3\xd7\xa034\x8f\xcd\xbf\x13R\x1c\xdd\x8e\xe2\x93\x9f\x0f8$\xfb\x9d\x02\xe9.\xeco\xb2U\x80\xd0\x12\xf3\xec\xaa\xedx\x87\x0cq\x8aE\\x8c\x8f\x1d\x84\x07\x0e\xc1\xb0Y<\xf7\x01b5\x9fm\x8dSB\x8a\xaf\xa3\xc5\x0dD\x88fL6b\x05\x06\x8c.\xcag\xaf\xae\xd0\xb0\xf8, 256
|
||||
mysql error, 1045, Access denied for user 'dsm1'@'10.21.179.53' (using password: YES)
|
BIN
testing/btest/Traces/mysql/mysql8-navicat-login-failed.pcapng
Normal file
BIN
testing/btest/Traces/mysql/mysql8-navicat-login-failed.pcapng
Normal file
Binary file not shown.
|
@ -0,0 +1,45 @@
|
|||
# @TEST-EXEC: zeek -b -C -r $TRACES/mysql/mysql8-navicat-login-failed.pcapng %INPUT >out
|
||||
# @TEST-EXEC: btest-diff out
|
||||
# @TEST-EXEC: btest-diff mysql.log
|
||||
|
||||
@load base/protocols/mysql
|
||||
|
||||
event mysql_ok(c: connection, affected_rows: count)
|
||||
{
|
||||
print "mysql ok", affected_rows;
|
||||
}
|
||||
|
||||
event mysql_eof(c: connection, is_intermediate: bool)
|
||||
{
|
||||
print "mysql eof", is_intermediate;
|
||||
}
|
||||
|
||||
event mysql_error(c: connection, code: count, msg: string)
|
||||
{
|
||||
print "mysql error", code, msg;
|
||||
}
|
||||
|
||||
event mysql_command_request(c: connection, command: count, arg: string)
|
||||
{
|
||||
print "mysql request", command, arg;
|
||||
}
|
||||
|
||||
event mysql_handshake(c: connection, username: string)
|
||||
{
|
||||
print "mysql handshake", username;
|
||||
}
|
||||
|
||||
event mysql_auth_plugin(c: connection, is_orig: bool, name: string, data: string)
|
||||
{
|
||||
print "mysql auth plugin", is_orig, name, data, |data|;
|
||||
}
|
||||
|
||||
event mysql_auth_switch_request(c: connection, name: string, data: string)
|
||||
{
|
||||
print "mysql auth switch request", name, data, |data|;
|
||||
}
|
||||
|
||||
event mysql_auth_more_data(c: connection, is_orig: bool, data: string)
|
||||
{
|
||||
print "mysql auth more data", is_orig, data, |data|;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue