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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go: Add Improper LDAP Authentication query (CWE-287) #13366

Merged
merged 18 commits into from Nov 13, 2023

Conversation

maikypedia
Copy link
Contributor

This pull request adds a query for Improper LDAP Authentication to prevent attackers use an empty password. I am not very familiar with CodeQL Go, and I have been struggling to generate correct expected files 馃檭.

Looking forward to your suggestions.

@mbg
Copy link
Member

mbg commented Jun 21, 2023

Hi @maikypedia, thank you for opening this pull request!

Looking at the documentation for Bind, it seems that an empty password is not accepted by that method. Would it be correct in saying that this vulnerability cannot be exploited if that is the case?

@maikypedia
Copy link
Contributor Author

Hi @maikypedia, thank you for opening this pull request!

Looking at the documentation for Bind, it seems that an empty password is not accepted by that method. Would it be correct in saying that this vulnerability cannot be exploited if that is the case?

Hi @mbg , I didn't realize that in v3 empty password option with Bind is disabled, sorry for the inconvenience 馃槄

@maikypedia maikypedia closed this Jul 5, 2023
@maikypedia maikypedia reopened this Jul 5, 2023
@maikypedia
Copy link
Contributor Author

Would it be worth adding Bind as sink for v2 and UnauthenticatedBind for v3? As a warning query.

@maikypedia
Copy link
Contributor Author

ping @mbg

@mbg
Copy link
Member

mbg commented Aug 25, 2023

Hi @maikypedia, sorry, I must have missed your question. Thank you for pinging me to bring this to my attention.

For Bind in v2, the query does make sense so it could be worth adding and I would be happy to review that.

For UnauthenticatedBind, I am not sure it makes sense to add a query to detect its use. The name of the method already notes that it is unauthenticated so using it would have to be deliberate and intentional.

@ghsecuritylab ghsecuritylab marked this pull request as draft August 27, 2023 00:07
@ghsecuritylab ghsecuritylab marked this pull request as ready for review October 6, 2023 14:27
Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

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

Your test isn't giving any results - it should be finding the bad cases. You may need to make sure that the test file builds correctly (try running go build in the directory that it is in to see what the errors are).

Co-authored-by: Owen Mansel-Chan <62447351+owen-mc@users.noreply.github.com>
@github-actions
Copy link
Contributor

github-actions bot commented Oct 17, 2023

QHelp previews:

go/ql/src/experimental/CWE-287/ImproperLdapAuth.qhelp

Improper LDAP Authentication

If an LDAP connection uses user-supplied data as password, anonymous bind could be caused using an empty password to result in a successful authentication.

Recommendation

Don't use user-supplied data as password while establishing an LDAP connection.

Example

In the following examples, the code accepts a bind password via a HTTP request in variable bindPassword. The code builds a LDAP query whose authentication depends on user supplied data.

package main

import (
	"fmt"
	"log"
)

func bad() interface{} {
	bindPassword := req.URL.Query()["password"][0]

	// Connect to the LDAP server
	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
	if err != nil {
		log.Fatalf("Failed to connect to LDAP server: %v", err)
	}
	defer l.Close()

	err = l.Bind("cn=admin,dc=example,dc=com", bindPassword)
	if err != nil {
		log.Fatalf("LDAP bind failed: %v", err)
	}
}

In the following examples, the code accepts a bind password via a HTTP request in variable bindPassword. The function ensures that the password provided is not empty before binding.

package main

import (
	"fmt"
	"log"
)

func good() interface{} {
	bindPassword := req.URL.Query()["password"][0]

	// Connect to the LDAP server
	l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
	if err != nil {
		log.Fatalf("Failed to connect to LDAP server: %v", err)
	}
	defer l.Close()

	if bindPassword != "" {
		err = l.Bind("cn=admin,dc=example,dc=com", bindPassword)
		if err != nil {
			log.Fatalf("LDAP bind failed: %v", err)
		}
	}
}

References

@owen-mc
Copy link
Contributor

owen-mc commented Oct 17, 2023

The following files need to be reformatted using gofmt or have compilation errors:
Error: ./ql/src/experimental/CWE-287/examples/LdapAuthGood.go:1:1: expected 'package', found 'func'
Error: make: *** [Makefile:35: check-formatting] Error 1
Error: ./ql/src/experimental/CWE-287/examples/LdapAuthBad.go:1:1: expected 'package', found 'func'

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

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

Well done for generating stubs. You've actually put them in two places by mistake - you can remove all changes to go/vendor and keep the ones in the test folder. Your tests now generate the right results now, but they will also generate some build errors because of missing return statements. Please address that and the rest of my previous review comments.

@maikypedia
Copy link
Contributor Author

I don't know if I've forgotten something, I think that's all for now. 馃槜

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

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

Please don't make any changes to go/vendor/modules.txt. Please add a package declaration and imports to make LdapAuthBad.go and LdapAuthGood.go valid go files.

@maikypedia
Copy link
Contributor Author

Hi @owen-mc ! Sorry for the delay, I had some exams these weeks 馃槄 In the example files of other experimental queries they do not include the imports of libraries that are not builtin, should I leave it like that?

@owen-mc
Copy link
Contributor

owen-mc commented Nov 9, 2023

@maikypedia The reason for making these valid go files is so that gofmt can parse them, because it can highlight some syntax errors. It seems to have passed. I see now that imports aren't actually needed, just the package directive. I would leave it as it is.

Your test is failing. It couldn't build the go file. I think there is a missing go.mod file.

@maikypedia
Copy link
Contributor Author

@maikypedia The reason for making these valid go files is so that gofmt can parse them, because it can highlight some syntax errors. It seems to have passed. I see now that imports aren't actually needed, just the package directive. I would leave it as it is.

Your test is failing. It couldn't build the go file. I think there is a missing go.mod file.

Done :), thanks

go/ql/src/experimental/CWE-287/ImproperLdapAuth.qll Outdated Show resolved Hide resolved
Comment on lines 5 to 8
/**
* A LDAP connection node.
*/
abstract class LdapConn extends DataFlow::CallNode { }
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 ever used. If that is the case then please delete it.

go/ql/src/experimental/CWE-287/ImproperLdapAuth.qll Outdated Show resolved Hide resolved
go/ql/src/experimental/CWE-287/ImproperLdapAuth.qll Outdated Show resolved Hide resolved
@maikypedia
Copy link
Contributor Author

Done 馃槂 馃憤

@owen-mc owen-mc merged commit d931ade into github:main Nov 13, 2023
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.

None yet

3 participants