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

parser: fix possible attack vector #4297

Merged
merged 1 commit into from
Oct 29, 2018
Merged

Conversation

sougou
Copy link
Contributor

@sougou sougou commented Oct 21, 2018

The force_eof rule used in the parser allowed for arbitrary
statements to be appended to a DDL after a ';', which gets
passed through to mysql without any validation.

This basically means that someone could append a statement
that bypasses the ACL checks in vttablets. This is not a big
deal because people that are allowed to execute DDLs generally
have all privileges. But it's better to play this safe.

Also, the skip to end was looking for ';' characters instead
of an actual ';' token, which could also lead to exploitation.

I had to refactor the code a bit because we also ignore all
errors on partial DDL parse. We have to make sure there are
no new tokens past the ';' before accepting partial ddls.

In the process, I've renamed 'force eof' -> 'skip to end'.

Signed-off-by: Sugu Sougoumarane ssougou@gmail.com

The force_eof rule used in the parser allowed for arbitrary
statements to be appended to a DDL after a ';', which gets
passed through to mysql without any validation.

This basically means that someone could append a statement
that bypasses the ACL checks in vttablets. This is not a big
deal because people that are allowed to execute DDLs generally
have all privileges. But it's better to play this safe.

Also, the skip to end was looking for ';' characters instead
of an actual ';' token, which could also lead to exploitation.

I had to refactor the code a bit because we also ignore all
errors on partial DDL parse. We have to make sure there are
no new tokens past the ';' before accepting partial ddls.

In the process, I've renamed 'force eof' -> 'skip to end'.

Signed-off-by: Sugu Sougoumarane <ssougou@gmail.com>
@sougou sougou requested review from demmer and rafael October 21, 2018 18:08
Copy link
Member

@demmer demmer left a comment

Choose a reason for hiding this comment

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

Overall I think this looks fine but I have a couple detailed questions about the changes that would love clarification on.

// we should not accept partially parsed DDLs. They
// should instead result in parser errors. See the
// Parse function to see how this is handled.
tkn.partialDDL = nil
Copy link
Member

Choose a reason for hiding this comment

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

What is this particular case trying to protect against? It's not immediately obvious to me why this is necessary since the check in Parse() seems like it would be sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this reset, a statement like this would get quietly accepted by vitess: incomplete ddl; evil query.
This is the case where a ; tirggers a parser error. That will cause the Parse function to detect that it was a partial DDL. So, it will accept the entire string, including evil query. It's the second test case in TestSkipToEnd.

Copy link
Member

Choose a reason for hiding this comment

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

I guess I'm confused about the specific sequence we expect to happen in that evil example above.
From my read of the code, it flows like this:

  1. The incomplete DDL sets SkipToEnd
  2. The next time through the lexer it should scan ahead to the ; character using skipStatement, but no further
  3. It then returns to Parse with the partialDDL flag set.
  4. Parse then calls Scan again expecting only a 0 but finds more tokens from evil query and fails then.

Presumably I'm missing something but can you clarify where my logic is wrong?

Copy link
Member

Choose a reason for hiding this comment

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

After some DM discussion in Slack I now understand.

if tkn.lastChar != ';' {
tkn.skipStatement()
}
tkn.skipStatement()
Copy link
Member

Choose a reason for hiding this comment

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

Why did you remove the if tkn.lastChar != ';' guard here?

Would some input likeselect invalid; select ok from table; end up skipping the 2nd statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is because skipStatement has been refactored to take care of that same check.

@demmer demmer merged commit a785874 into vitessio:master Oct 29, 2018
@sougou sougou deleted the ss-safe-ddl branch October 30, 2018 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants