-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Ruby: Add Improper LDAP Authentication query (CWE-287) #13313
Ruby: Add Improper LDAP Authentication query (CWE-287) #13313
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @maikypedia, I've given this an initial review - mostly stylistic suggestions. There's a minor merge conflict in NetLdapConnection
, I think relating to its charpred. Would you mind merging in main and fixing this conflict?
ruby/ql/lib/codeql/ruby/Concepts.qll
Outdated
/** Holds if the binding process is anonymous. */ | ||
predicate isEmptyPassword() { | ||
( | ||
this.getPassword().getConstantValue().isStringlikeValue("") | ||
or | ||
this.getPassword().(DataFlow::ExprNode).getExprNode().getConstantValue().getValueType() = | ||
"nil" | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This predicate looks to be unused - can we remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be more convenient to declare nil
and ""
as sources?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this at all? I can't find anywhere that this predicate is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to finish it, it was to consider the empty password and nil as vulnerable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maikypedia Okay, that makes sense to me. Would you mind either adding this as a source? I think the predicate itself probably belongs in ImproperLdapAuthCustomizations.qll
rather than Concepts.qll
.
As a tiny nitpick, we could replace getValueType() = "nil"
with just isNil()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexrford Done! Although the output is a bit ugly, every step of the source node is considered a new source 🥲
ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb
Outdated
Show resolved
Hide resolved
ruby/ql/test/query-tests/experimental/ImproperLdapAuth/ImproperLdapAuth.rb
Outdated
Show resolved
Hide resolved
Co-authored-by: Alex Ford <[email protected]>
Co-authored-by: Alex Ford <[email protected]>
Co-authored-by: Alex Ford <[email protected]>
Co-authored-by: Alex Ford <[email protected]>
QHelp previews: ruby/ql/src/experimental/ldap-improper-auth/ImproperLdapAuth.qhelpImproper LDAP AuthenticationIf an LDAP connection uses user-supplied data as password, anonymous bind could be caused using an empty password to result in a successful authentication. RecommendationDon't use user-supplied data as password while establishing an LDAP connection. ExampleIn the following Rails example, an In the first example, the code builds a LDAP query whose authentication depends on user supplied data. class FooController < ActionController::Base
def some_request_handler
pass = params[:pass]
ldap = Net::LDAP.new(
host: 'ldap.example.com',
port: 636,
encryption: :simple_tls,
auth: {
method: :simple,
username: 'uid=admin,dc=example,dc=com',
password: pass
}
)
ldap.bind
end
end In the second example, the authentication is established using a default password. class FooController < ActionController::Base
def some_request_handler
pass = params[:pass]
ldap = Net::LDAP.new(
host: 'ldap.example.com',
port: 636,
encryption: :simple_tls,
auth: {
method: :simple,
username: 'uid=admin,dc=example,dc=com',
password: '$uper$password123'
}
)
ldap.bind
end
end References
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny note, and I left another comment on isEmptyPassword()
ruby/ql/src/change-notes/2023-29-05-improper-ldap-auth-query.md
Outdated
Show resolved
Hide resolved
@maikypedia sorry, just one last comment! I think we can probably just revert the last commit to remove pass = get_a_secure_password
pass += "" # this taints `pass` with an "empty password"
ldap = Net::LDAP.new(
host: 'ldap.example.com',
port: 636,
encryption: :simple_tls,
auth: {
method: :simple,
username: 'uid=admin,dc=example,dc=com',
password: pass # False positive:
}
)
ldap.bind We can technically fix this by either using value only tracking for this case, but this feels like a potentially significant piece of work and I'm keen to get this merged so we can then iterate on this query. Would you mind reverting the last commit, then this should be fine to merge? |
This reverts commit 664c1eb.
Sure, reverted 😄 |
Thanks for your patience on the review 🙇, just ran the formatter and waiting on checks to complete before merging. |
This pull request adds a query for Improper LDAP Authentication to prevent attackers use an empty password. This branch is created from
maikypedia/ldap-injection
branch (PR here) so maybe it is more convenient to review this one once the other is resolved.Looking forward to your suggestions.