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 protocol parser v1 #11800

Closed
wants to merge 4 commits into from

Conversation

Kotodian
Copy link

@Kotodian Kotodian commented Sep 19, 2024

Make sure these boxes are signed before submitting your Pull Request -- thank you.

Link to ticket: https://redmine.openinfosecfoundation.org/issues/3446

Describe changes:
This is a WIP patchset that implements an application layer for MySQL protocol, many features are not supported.

TBD:
Support Protocol:Handshake V9
Support Mysql Protocol 32
Support Protocol:AuthSwitchRequest
Support Protocol:AuthSwitchResponse
Support Protocol:AuthMoreData
Support Protocol:AuthNextFactor
Support Compression
Add documentation
Add SV tests
Fix CI

@victorjulien
Copy link
Member

@glongo I think you mentioned you had some WIP code as well. Care to have a look at this?

Copy link

NOTE: This PR may contain new authors.

Copy link

codecov bot commented Sep 19, 2024

Codecov Report

Attention: Patch coverage is 3.36927% with 717 lines in your changes missing coverage. Please review.

Please upload report for BASE (master@1420c83). Learn more about missing BASE report.
Report is 23 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##             master   #11800   +/-   ##
=========================================
  Coverage          ?   78.80%           
=========================================
  Files             ?      923           
  Lines             ?   251982           
  Branches          ?        0           
=========================================
  Hits              ?   198566           
  Misses            ?    53416           
  Partials          ?        0           
Flag Coverage Δ
fuzzcorpus 59.78% <0.53%> (?)
livemode 18.51% <3.36%> (?)
pcap 43.55% <3.09%> (?)
unittests 58.90% <0.53%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Copy link
Contributor

@jufajardini jufajardini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the TBD list ;)

  • update etc/schema.json
  • update commit messages to contain Ticket number in the commit body (Task #3446 )
  • update your git author to follow the format FirstName LastName

:P

nit: in the commit messages, you can remove the rust/ portion

@glongo
Copy link
Contributor

glongo commented Sep 19, 2024

@glongo I think you mentioned you had some WIP code as well. Care to have a look at this?

Yes sure.

@Kotodian Kotodian force-pushed the dev-3346-mysql-proto-v1 branch 2 times, most recently from 5083bc9 to 3fe510b Compare September 20, 2024 01:17
@Kotodian
Copy link
Author

For the TBD list ;)

* update etc/schema.json

* update commit messages to contain Ticket number in the commit body (`Task #3446` )

* update your git author to follow the format `FirstName LastName`

:P

nit: in the commit messages, you can remove the rust/ portion

I have fixed, please check again, thx.

return TM_ECODE_FAILED;
}

if (!rs_mysql_logger(txptr, thread->mysqllog_ctx->flags, jb)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not need this file now
rs_mysql_logger (which should rather be named SCMysqlLogger) cf EveJsonSimpleTxLogFunc in output.c

Copy link
Author

@Kotodian Kotodian Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.Why keep simple logger only?

@catenacyber
Copy link
Contributor

Are you planning on adding keywords as well ?


fn log_mysql(tx: &MysqlTransaction, _flags: u32, js: &mut JsonBuilder) -> Result<(), JsonError> {
js.open_object("mysql")?;
js.set_uint("tx_id", tx.tx_id)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it meaningful to log the tx id ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, all of fields are based on my project, if you have any idea in logger, please comment, thx.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.

js.set_bool("tls", false)?;
}

if tx.command.is_some() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if let Some(c) = tx.command ;-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.

Ok(())
}

fn log_mysql_alert(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is different with log_mysql ?

Copy link
Author

@Kotodian Kotodian Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my project's demand.I will remove it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.


client_flags: u32,
version: Option<String>,
tls: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you handle protocol change ? (like STARTTLS does in SMTP)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/// Probe for a valid mysql message
pub fn probe(input: &[u8]) -> bool {
if parse_packet_header(input).is_ok() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can not it say "not enough data to know" ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like parse_packet_header accepts pretty much everything

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// Probe for a valid mysql message
pub fn probe(i: &[u8]) -> IResult<&[u8], ()> {
    let (i, _) = parse_packet_header(i)?;
    Ok((i, ()))
}

Did you mean this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I mean that the probing parser is likely not strict enough

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have another probe function rs_mysql_probing_tc and rs_mysql_probing_ts for real probing


/// Get the mysql query
#[no_mangle]
pub unsafe extern "C" fn SCMysqlTxGetCommandName(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused now...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.

get_state_data: rs_mysql_get_state_data,
apply_tx_config: None,
flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS,
get_frame_id_by_name: None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you planning on adding frames support ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No

Copy link
Contributor

@catenacyber catenacyber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this work already.

I left some comments and questions to get deeper

}
}

pub fn parse_request(&mut self, flow: *const Flow, i: &[u8]) -> AppLayerResult {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a corner case to handle here: if a payload length is set to ff ff ff, it means that it is fragmented and the payload should be reassembled before of being parsed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, i will fix.

Copy link
Author

@Kotodian Kotodian Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When i implement this, i found some problems in rust lifetime that i can't reassemble, do you have any advice?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have implemented a low performance version, please check.

) -> IResult<&[u8], MysqlRequest> {
let (i, header) = parse_packet_header(i)?;
let (i, command_code) = be_u8(i)?;
match command_code {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be rewritten as:

let (i, req) = match command_code {
    0x01 => (i, MysqlRequest {
                command_code,
                command: MysqlCommand::Quit,
    })
    ...
}
Ok((i, req))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, please check again.

let (i, _tag) = verify(be_u8, |&x| x == 0xfe)(i)?;
let (i, warnings) = le_u16(i)?;
let (i, status_flags) = le_u16(i)?;

Copy link
Contributor

@glongo glongo Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warnings and status_flags can be parsed only if the following condition is true:

if capabilities & CLIENT_PROTOCOL_41 != 0

The flags parsed during the handshake phase should be passed here as a parameter to check this condition.

Reference: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_eof_packet.html

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When i parse handshake response, i have checked this.

))
}

fn parse_response_ok(i: &[u8], length: u32) -> IResult<&[u8], MysqlResponse> {
Copy link
Contributor

@glongo glongo Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, some fields depend on flags, and certain conditions should be checked before parsing them.
Reference: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html

Copy link
Author

@Kotodian Kotodian Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When i parse ok response, i just take the fields what I need.
As mentioned before, when i parse handshake response, i have checked capabilities should contain CLIENT_PROTOCOL_41.

let (i, error_code) = le_u16(i)?;
let (i, _) = take(6_u32)(i)?;
// sql state maker & sql state
let (i, _) = take(6_u32)(i)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sql state marker and state are available only if capabilities & CLIENT_PROTOCOL_41 is true.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When i parse handshake response, i have checked this.

@Kotodian
Copy link
Author

Are you planning on adding keywords as well ?

No, all of fields are based on my project, if you have any idea in logger, please comment, thx.

@jufajardini
Copy link
Contributor

Are you planning on adding keywords as well ?

No, all of fields are based on my project, if you have any idea in logger, please comment, thx.

The keywords are for making detection easier and more efficient, not for the logging part.
This can, however, be a separate step of the work.

Thanks for incorporating feedback :)

I'll mark this PR as a draft, as there are still a lot of moving parts. Once you consider you've reached something more stable, do submit another PR version, with SV tests, as those work well not only for our CI checks testing but also for showcasing the EVE output.

@jufajardini jufajardini marked this pull request as draft September 24, 2024 19:22
@Kotodian
Copy link
Author

Are you planning on adding keywords as well ?

No, all of fields are based on my project, if you have any idea in logger, please comment, thx.

The keywords are for making detection easier and more efficient, not for the logging part. This can, however, be a separate step of the work.

Thanks for incorporating feedback :)

I'll mark this PR as a draft, as there are still a lot of moving parts. Once you consider you've reached something more stable, do submit another PR version, with SV tests, as those work well not only for our CI checks testing but also for showcasing the EVE output.

Ok, I will move my project's detection keywords here, and do more tests.

@catenacyber
Copy link
Contributor

Continued in #11842 if I am not mistaken

@catenacyber catenacyber closed this Oct 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

6 participants