Skip to content

C++: Tests for cpp/toctou-race-condition #6150

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

Merged
merged 12 commits into from
Jul 19, 2021
Merged

Conversation

geoffw0
Copy link
Contributor

@geoffw0 geoffw0 commented Jun 23, 2021

Add test cases to the cpp/toctou-race-condition query, some of which are inspired by real-world code. Clarify some parts of the query (without changing behaviour).

I have some ideas for minor improvements to the QL in this query, but the big question is which pairs of (check, use) should be flagged in the first place. At present the 'check' can be any operation understood by the query as long as it returns something, whereas the 'use' must be an operation that might change the file (except currently fopen(..., "r") is included even though it couldn't modify the file). Some pointers for discussion:

  • I've added a case of two fopens, which I'm fairly sure is a false positive (i.e. safe). But what if one or both fopen was for writing, is that dangerous?
  • are there any other (check, use) pairs we would not want to flag?
  • should we look more closely at what file name is being accessed and try to determine whether it's somehow likely to be sensitive / accessible to attackers?
  • should we care about the condition connecting them, i.e. whether the use occurs if the check succeeds, or if the check fails?

@geoffw0 geoffw0 added the C++ label Jun 23, 2021
@geoffw0 geoffw0 requested a review from a team as a code owner June 23, 2021 16:58
@jbj jbj requested a review from rdmarsh2 June 23, 2021 18:02
{
if (!rename(path1, path2))
{
remove(path1); // BAD
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we actually want a result here? My understanding is that this pattern is a race, but not one that's exploitable for a privilege escalation - to put something at path1, it would either be freshly created or moved. In the latter case, the attacker would need to have permission to delete it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I can't think of a way this could be exploited either, so it should probably not be within the scope of the query...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if the only cases we're [strongly] interested in are where the first operation is stat or access (or one of the variants of those). i.e. not fopen, rename, chmod or any of the modifying operations we look for as the second operation.

It appears this approach is sufficient to catch all of the SAMATE cases at any rate. It would mean we don't get the original test cases for this query in test.cpp though (which are also rename -> remove variants), and the fopen ->chmod cases in test2.cpp.

// ...
}

remove(path1); // GOOD (does not depend on the rename)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is meaningfully different in terms of security than the previous two tests.

Copy link
Contributor Author

@geoffw0 geoffw0 Jun 28, 2021

Choose a reason for hiding this comment

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

I'm inclined to agree, apart from my reservations (see the chmod case) about whether we're looking at two operations that are intended to be associated or logically independent. I'm not sure how much merit my argument there really has.

fclose(f);
}

chmod(path, 0); // GOOD (doesn't depend on the fopen)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't technically fall under CWE-367, since there isn't a control flow dependency on the call to fopen, but it is still a race condition with similar consequences. Do we have an alternate query that should handle these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the case above I can imagine two different scenarios.

(1) the chmod is closely associated with the fopen, so as you say there's a race condition if another process accesses / modifies the file between the fopen and chmod. In addition there's likely a second bug in that if the fopen fails calling chmod on whatever happens to be there may be wrong.

(2) the fopen and chmod are in fact independent operations that just happen to be done sequentially in this function. The fact that chmod happens regardless of the result of fopen is (weak) evidence that the two are intended to be independent.

I'm not aware of an alternate query, and I would imagine it would end up with a high FP rate.

@geoffw0 geoffw0 added the no-change-note-required This PR does not need a change note label Jun 28, 2021
@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 2, 2021

A comment from Slack that I should address: "The pattern that I have seen in the wild is access followed by open. I see that you already have a test for access followed by fopen. Can you add the Linux file descriptor version too?"

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 2, 2021

I've added a few more test cases and cleaned things up a bit. The ones marked BAD??? I'm still unsure about.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 15, 2021

I talked to @kevinbackhouse a few weeks ago, for the benefit of this thread he said:

  • [regarding which pairs of operations are dangerous] "any time the same path gets used twice, there's a potential for mischief"
  • "the problem is that it's probably only a genuine vulnerability if the process is running as root, but messing with files that are also accessible to an unprivileged user. That context is difficult to include in a CodeQL query.".

If anyone has any ideas what we might do with that second point, please speak up. I do think that even an unprivileged process should ideally not be laced with race conditions on file names, but that issue is much less severe.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 15, 2021

To move things forward, I've also now updated the test case comments to reflect my best understanding now. There are now three classifications - GOOD, BAD and DUBIOUS, where the latter are (I think) more likely to be race conditions rather than exploitable vulnerabilities.

I'm looking to get these changes merged soon so that I can get to work on the query itself.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 15, 2021

[the comment this was replying to has been deleted] Hi @bad-sarbot, would you like to try and explain your comment a bit better (or should I conclude that you're not human?)

@rdmarsh2
Copy link
Contributor

It also looks like there's an autoformat issue. I'm happy to merge once that's addressed.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jul 19, 2021

Fixes done.

Copy link
Contributor

@MathiasVP MathiasVP left a comment

Choose a reason for hiding this comment

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

LGTM!

@MathiasVP MathiasVP merged commit 7bc18ab into github:main Jul 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants