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

Python: Pycurl SSL Disabled #16812

Merged
merged 7 commits into from
Oct 25, 2024
Merged

Python: Pycurl SSL Disabled #16812

merged 7 commits into from
Oct 25, 2024

Conversation

porcupineyhairs
Copy link
Contributor

Pycurl is a library which provides curl binding in python. The original library is partially modelled in codeql. This PR adds support to test for SSL certificate validation when using pycurl.

@porcupineyhairs porcupineyhairs requested a review from a team as a code owner June 23, 2024 12:37
@ghsecuritylab ghsecuritylab marked this pull request as draft June 24, 2024 00:10
@ghsecuritylab
Copy link
Collaborator

Hello porcupineyhairs 👋
You have submitted this pull request as a bug bounty report in the github/securitylab repository and therefore this pull request has been put into draft state to give time for the GitHub Security Lab to assess the PR. When GitHub Security Lab has finished assessing your pull request, it will be marked automatically as Ready for review. Until then, please don't change the draft state.

In the meantime, feel free to make changes to the pull request. If you'd like to maximize payout for your this and future submissions, here are a few general guidelines, that we might take into consideration when reviewing a submission.

  • the submission models widely-used frameworks/libraries
  • the vulnerability modeled in the submission is impactful
  • the submission finds new true positive vulnerabilities
  • the submission finds very few false positives
  • code in the submission is easy to read and will be easy to maintain
  • documentation is written clearly, highlighting the impact of the issue it finds and is written without grammatical or other errors. The code samples clearly show the vulnerability
  • the submission includes tests, change note etc.

Please note that these are guidelines, not rules. Since we have a lot of different types of submissions, the guidelines might vary for each submission.

Happy hacking!

@ghsecuritylab ghsecuritylab marked this pull request as ready for review July 1, 2024 11:22
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

Nice to get this TODO addressed. I do think, though, that it ought to be done slightly differently.

exists(IntegerLiteral i | i.getN() = "0" and c.getArg(1).asExpr() = i)
|
disablingNode = c and argumentOrigin = c.getArg(1)
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This class is representing calls to setopt where the first argument is specifically "PyCurl.URL". For these calls, the current implementation of disablesCertificateValidation as none() is correct. What you have done is expressing that this call disables certificate validation if there is another call with arguments (PyCurl.SSL_VERIFYPEER, 0).

What you would need to do is likely to create a new class extending Http::Client::Request::Range representing calls to setopt where the first argument is specifically "PyCurl.SSL_VERIFYPEER" and then you can implement disablesCertificateValidation by comparing the second argument with 0 (and also with False, please).

Copy link
Contributor

Choose a reason for hiding this comment

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

Closer, but you are still looking at another call (c = setopt().getACall()). You have to look at the argument for the call represented by the class; something like c = this.getACall(). Also, the boolean has to come from the call; you are now just checking if they wrote false anywhere in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@yoff Changes done! PTAL.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, much better :-) I think the boolean would also have to go into arg(1)?

Comment on lines 43 to 47
/** Gets a reference to an instance of `pycurl.Curl.SSL_VERIFYPEER`. */
private API::Node sslverifypeer() {
result = API::moduleImport("pycurl").getMember("SSL_VERIFYPEER")
}
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 also come from an instance, see http://pycurl.io/docs/latest/curlobject.html#pycurl.Curl.setopt.

exists(API::CallNode c |
c = setopt().getACall() and
sslverifypeer().getAValueReachableFromSource() = c.getArg(0) and
exists(IntegerLiteral i | i.getN() = "0" and c.getArg(1).asExpr() = i)
Copy link
Contributor

Choose a reason for hiding this comment

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

It is possible to just compare the integer values

Suggested change
exists(IntegerLiteral i | i.getN() = "0" and c.getArg(1).asExpr() = i)
exists(IntegerLiteral i | i.getValue() = 0 and c.getArg(1).asExpr() = i)

@sylwia-budzynska
Copy link
Contributor

Hi @porcupineyhairs - we would like to close the last remaining bug bounty PRs soon. Do you think you will keep on working on this PR?

@porcupineyhairs
Copy link
Contributor Author

@sylwia-budzynska Sorry for the delay. Last couple of weeks have been busy. I will get this cleared soon.

@yoff I have made the changes now. PTAL. However, for some reason, Codeql is giving me duplicate results when I run the original query.

Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

Just a small semantic error now, I think :-)

exists(IntegerLiteral i | i.getN() = "0" and c.getArg(1).asExpr() = i)
|
disablingNode = c and argumentOrigin = c.getArg(1)
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, much better :-) I think the boolean would also have to go into arg(1)?

Comment on lines 95 to 97
exists(IntegerLiteral i | i.getValue() = 0 and this.getArg(1).asExpr() = i)
or
exists(BooleanLiteral b | b.booleanValue() = false and this.getArg(_).asExpr() = b)
Copy link
Contributor

@yoff yoff Oct 21, 2024

Choose a reason for hiding this comment

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

Suggested change
exists(IntegerLiteral i | i.getValue() = 0 and this.getArg(1).asExpr() = i)
or
exists(BooleanLiteral b | b.booleanValue() = false and this.getArg(_).asExpr() = b)
this.getArg(1).asExpr().(IntegerLiteral).getValue() = 0
or
this.getArg(1).asExpr().(BooleanLiteral).booleanValue() = false

I think it is easier to read without the exists, but the important change here is that for the boolean value, we also only look at arg(1).

Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

LGTM

@yoff yoff merged commit 7338eaf into github:main Oct 25, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants