Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MySQL: parser v2 #11842

Closed
wants to merge 10 commits into from
29 changes: 29 additions & 0 deletions doc/userguide/output/eve/eve-json-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3057,3 +3057,32 @@ Example of ARP logging: request and response
"dest_mac": "00:1d:09:f0:92:ab",
"dest_ip": "10.10.10.1"
}

Event type: MySQL
---------------

Fields
~~~~~~

* "version": the MySQL protocol version offered by the server.
* "tls": protocol need to be upgrade to tls.
* "command": sql query statement or utility command like ping.
* "rows": zero or multi results from executing sql query statement, one row is splited by comma.

Examples
~~~~~~~~

Example of MySQL logging:

::

{
"mysql": {
"version": "8.0.32",
"tls": false,
"command": "SELECT VERSION()",
"rows": [
"8.0.32"
]
}
}
1 change: 1 addition & 0 deletions doc/userguide/rules/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Suricata Rules
nfs-keywords
smtp-keywords
websocket-keywords
mysql-keywords
app-layer
xbits
noalert
Expand Down
50 changes: 50 additions & 0 deletions doc/userguide/rules/mysql-keywords.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
MySQL Keywords
============

The MySQL keywords are implemented and can be used to match on fields in MySQL messages.

============================== ==================
Keyword Direction
============================== ==================
mysql.command Request
mysql.rows Response
============================== ==================

mysql.command
----------

This keyword matches on the query statement like `select * from xxx where yyy = zzz` found in a MySQL request.

Syntax
~~~~~~

::

mysql.command; content:<command>;

Examples
~~~~~~~~

::

mysql.commands; content:"select";

mysql.rows
-------

This keyword matches on the rows which come from query statement result found in a Mysql response.
row format: 1,foo,bar

Syntax
~~~~~~

::

mysql.rows; content:<rows>;

Examples
~~~~~~~~

::

mysql.rows; content:"foo,bar";
2 changes: 2 additions & 0 deletions doc/userguide/upgrade.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Major changes
- sip.content_length
- Napatech support has been moved to a capture plugin. See :doc:`Napatech plugin
<upgrade/8.0-napatech-plugin>`.
- MySQL parser and logger have been introduced.
- The MySQL keywords ``mysql.command`` and ``mysql.command`` have been introduced.

Removals
~~~~~~~~
Expand Down
41 changes: 41 additions & 0 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,35 @@
},
"additionalProperties": false
},
"mysql": {
"type": "object",
"optional": true,
"properties": {
"version": {
"type": "string",
"description": "Mysql server version"
},
"tls": {
"type": "boolean"
},
"command": {
"type": "string",
"description": "sql query statement or some utility commands like ping."
},
"affected_rows": {
"type": "integer"
},
"rows": {
"type": "array",
"optional": true,
"minItems": 1,
"items": {
"type": "string"
QianKaiLin marked this conversation as resolved.
Show resolved Hide resolved
},
"description": "Comma separated result from sql statement"
}
}
},
"ldap": {
"type": "object",
"optional": true,
Expand Down Expand Up @@ -4663,6 +4692,10 @@
"description": "Errors encountered parsing MQTT protocol",
"$ref": "#/$defs/stats_applayer_error"
},
"mysql": {
"description": "Errors encountered parsing MySQL protocol",
"$ref": "#/$defs/stats_applayer_error"
},
"nfs_tcp": {
"description": "Errors encountered parsing NFS/TCP protocol",
"$ref": "#/$defs/stats_applayer_error"
Expand Down Expand Up @@ -4838,6 +4871,10 @@
"description": "Number of flows for MQTT protocol",
"type": "integer"
},
"mysql": {
"description": "Number of flows for MySQL protocol",
"type": "integer"
},
"nfs_tcp": {
"description": "Number of flows for NFS/TCP protocol",
"type": "integer"
Expand Down Expand Up @@ -5008,6 +5045,10 @@
"description": "Number of transactions for MQTT protocol",
"type": "integer"
},
"mysql": {
"description": "Number of flows for MySQL protocol",
"type": "integer"
},
"nfs_tcp": {
"description": "Number of transactions for NFS/TCP protocol",
"type": "integer"
Expand Down
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub mod ffi;
pub mod feature;
pub mod sdp;
pub mod ldap;
pub mod mysql;

#[allow(unused_imports)]
pub use suricata_lua_sys;
166 changes: 166 additions & 0 deletions rust/src/mysql/detect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/* Copyright (C) 2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

// Author: QianKaiLin <[email protected]>

/// Detect
/// Get the mysql query
use super::mysql::{MysqlTransaction, ALPROTO_MYSQL};
use crate::detect::{
DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperGetData,
DetectHelperGetMultiData, DetectHelperKeywordRegister, DetectHelperMultiBufferMpmRegister,
DetectSignatureSetAppProto, SCSigTableElmt, SIGMATCH_NOOPT,
};
use std::os::raw::{c_int, c_void};

static mut G_MYSQL_COMMAND_BUFFER_ID: c_int = 0;
static mut G_MYSQL_ROWS_BUFFER_ID: c_int = 0;

#[no_mangle]
unsafe extern "C" fn SCMysqlCommandSetup(
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
) -> c_int {
if DetectSignatureSetAppProto(s, ALPROTO_MYSQL) != 0 {
return -1;
}
if DetectBufferSetActiveList(de, s, G_MYSQL_COMMAND_BUFFER_ID) < 0 {
return -1;
}
return 0;
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetCommand(
de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
tx: *const c_void, list_id: c_int,
) -> *mut c_void {
return DetectHelperGetData(
de,
transforms,
flow,
flow_flags,
tx,
list_id,
SCMysqlGetCommandData,
);
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetCommandData(
tx: *const c_void, _flags: u8, buf: *mut *const u8, len: *mut u32,
) -> bool {
let tx = cast_pointer!(tx, MysqlTransaction);
if let Some(command) = &tx.command {
if !command.is_empty() {
*buf = command.as_ptr();
*len = command.len() as u32;
return true;
}
}

false
}

#[no_mangle]
unsafe extern "C" fn SCMysqlRowsSetup(
de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
) -> c_int {
if DetectSignatureSetAppProto(s, ALPROTO_MYSQL) != 0 {
return -1;
}
if DetectBufferSetActiveList(de, s, G_MYSQL_ROWS_BUFFER_ID) < 0 {
return -1;
}
return 0;
}

#[no_mangle]
unsafe extern "C" fn SCMysqlGetRows(
de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
tx: *const c_void, list_id: c_int, local_id: u32,
) -> *mut c_void {
return DetectHelperGetMultiData(
de,
transforms,
flow,
flow_flags,
tx,
list_id,
local_id,
SCMysqlGetRowsData,
);
}

/// Get the mysql rows at index i
#[no_mangle]
pub unsafe extern "C" fn SCMysqlGetRowsData(
tx: *const c_void, _flow_flags: u8, local_id: u32, buf: *mut *const u8, len: *mut u32,
) -> bool {
let tx = cast_pointer!(tx, MysqlTransaction);
if let Some(rows) = &tx.rows {
if !rows.is_empty() {
let index = local_id as usize;
if let Some(row) = rows.get(index) {
*buf = row.as_ptr();
*len = row.len() as u32;
return true;
}
}
}

false
}

#[no_mangle]
pub unsafe extern "C" fn ScDetectMysqlRegister() {
let kw = SCSigTableElmt {
name: b"mysql.command\0".as_ptr() as *const libc::c_char,
desc: b"sticky buffer to match on the MySQL command\0".as_ptr() as *const libc::c_char,
url: b"/rules/mysql-keywords.html#mysql-command\0".as_ptr() as *const libc::c_char,
Setup: SCMysqlCommandSetup,
flags: SIGMATCH_NOOPT,
AppLayerTxMatch: None,
Free: None,
};
let _g_mysql_command_kw_id = DetectHelperKeywordRegister(&kw);
G_MYSQL_COMMAND_BUFFER_ID = DetectHelperBufferMpmRegister(
b"mysql.command\0".as_ptr() as *const libc::c_char,
b"mysql.command\0".as_ptr() as *const libc::c_char,
ALPROTO_MYSQL,
false,
true,
SCMysqlGetCommand,
);
let kw = SCSigTableElmt {
name: b"mysql.rows\0".as_ptr() as *const libc::c_char,
desc: b"sticky buffer to match on the MySQL Rows\0".as_ptr() as *const libc::c_char,
url: b"/rules/mysql-keywords.html#mysql-rows\0".as_ptr() as *const libc::c_char,
Setup: SCMysqlRowsSetup,
flags: SIGMATCH_NOOPT,
AppLayerTxMatch: None,
Free: None,
};
let _g_mysql_rows_kw_id = DetectHelperKeywordRegister(&kw);
G_MYSQL_ROWS_BUFFER_ID = DetectHelperMultiBufferMpmRegister(
b"mysql.rows\0".as_ptr() as *const libc::c_char,
b"mysql select statement resultset\0".as_ptr() as *const libc::c_char,
ALPROTO_MYSQL,
true,
false,
SCMysqlGetRows,
);
}
Loading