diff --git a/go.mod b/go.mod index 5d77091a50c..20136d29bd7 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,9 @@ require ( ) require ( + github.com/go-ldap/ldap v3.0.3+incompatible golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/ldap.v2 v2.5.1 ) diff --git a/go.sum b/go.sum index 57b82477ee7..a6a85546fa3 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk= +github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -26,3 +28,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= +gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= diff --git a/ql/test/experimental/CWE-090/LDAPInjectction.expected b/ql/test/experimental/CWE-090/LDAPInjectction.expected new file mode 100644 index 00000000000..76f46835b2c --- /dev/null +++ b/ql/test/experimental/CWE-090/LDAPInjectction.expected @@ -0,0 +1,22 @@ +edges +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:21:3:21:51 | ...+... | +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:22:3:22:33 | slice literal | +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:28:3:28:51 | ...+... | +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:29:3:29:33 | slice literal | +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:35:3:35:51 | ...+... | +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:36:3:36:33 | slice literal | +nodes +| LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| LDAPInjectction.go:21:3:21:51 | ...+... | semmle.label | ...+... | +| LDAPInjectction.go:22:3:22:33 | slice literal | semmle.label | slice literal | +| LDAPInjectction.go:28:3:28:51 | ...+... | semmle.label | ...+... | +| LDAPInjectction.go:29:3:29:33 | slice literal | semmle.label | slice literal | +| LDAPInjectction.go:35:3:35:51 | ...+... | semmle.label | ...+... | +| LDAPInjectction.go:36:3:36:33 | slice literal | semmle.label | slice literal | +#select +| LDAPInjectction.go:21:3:21:51 | ...+... | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:21:3:21:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | +| LDAPInjectction.go:22:3:22:33 | slice literal | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:22:3:22:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | +| LDAPInjectction.go:28:3:28:51 | ...+... | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:28:3:28:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | +| LDAPInjectction.go:29:3:29:33 | slice literal | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:29:3:29:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | +| LDAPInjectction.go:35:3:35:51 | ...+... | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:35:3:35:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | +| LDAPInjectction.go:36:3:36:33 | slice literal | LDAPInjectction.go:17:15:17:29 | call to UserAgent : string | LDAPInjectction.go:36:3:36:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:17:15:17:29 | call to UserAgent | a user-provided value | diff --git a/ql/test/experimental/CWE-090/LDAPInjectction.go b/ql/test/experimental/CWE-090/LDAPInjectction.go new file mode 100644 index 00000000000..cc03b348d45 --- /dev/null +++ b/ql/test/experimental/CWE-090/LDAPInjectction.go @@ -0,0 +1,75 @@ +package main + +import ( + "net/http" + + goldap "github.com/go-ldap/ldap" + goldapv3 "github.com/go-ldap/ldap/v3" + ldapclient "github.com/jtblin/go-ldap-client" + gopkgldapv2 "gopkg.in/ldap.v2" +) + +func main() {} + +// bad is an example of a bad implementation +func bad(req *http.Request) { + // ... + untrusted := req.UserAgent() + goldap.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+untrusted, // BAD: untrusted filter + []string{"dn", "cn", untrusted}, // BAD: untrusted attribute + nil, + ) + goldapv3.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+untrusted, // BAD: untrusted filter + []string{"dn", "cn", untrusted}, // BAD: untrusted attribute + nil, + ) + gopkgldapv2.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+untrusted, // BAD: untrusted filter + []string{"dn", "cn", untrusted}, // BAD: untrusted attribute + nil, + ) + ldapclient.Authenticate(untrusted, "123456") // BAD: untrusted filter + ldapclient.GetGroupsOfUser(untrusted) // BAD: untrusted filter + // ... +} + +// good is an example of a good implementation +func good(req *http.Request) { + // ... + untrusted := req.UserAgent() + escapegoldap := goldap.EscapedFilter(untrusted) + goldap.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+escapegoldap, // GOOD: sanitized filter + []string{"dn", "cn", escapegoldap}, // GOOD: sanitized attribute + nil, + ) + escapegoldapv3 := goldapv3.EscapedFilter(untrusted) + goldapv3.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+escapegoldapv3, // GOOD: sanitized filter + []string{"dn", "cn", escapegoldapv3}, // GOOD: sanitized attribute + nil, + ) + escapegopkgv2 := gopkgldapv2.EscapedFilter(untrusted) + gopkgldapv2.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+escapegopkgv2, // GOOD: sanitized filter + []string{"dn", "cn", escapegopkgv2}, // GOOD: sanitized attribute + nil, + ) + ldapclient.Authenticate(escapegoldapv3, "123456") // GOOD: sanitized filter + ldapclient.GetGroupsOfUser(escapegoldapv3) // GOOD: sanitized filter + // ... +} diff --git a/ql/test/experimental/CWE-090/LDAPInjectction.qlref b/ql/test/experimental/CWE-090/LDAPInjectction.qlref new file mode 100644 index 00000000000..da1e5f588a9 --- /dev/null +++ b/ql/test/experimental/CWE-090/LDAPInjectction.qlref @@ -0,0 +1 @@ +experimental/CWE-090/LDAPInjection.ql \ No newline at end of file