diff --git a/ql/src/experimental/CWE-090/LDAPinjection.qhelp b/ql/src/experimental/CWE-090/LDAPinjection.qhelp index 053f0ea3828..66725304d7e 100644 --- a/ql/src/experimental/CWE-090/LDAPinjection.qhelp +++ b/ql/src/experimental/CWE-090/LDAPinjection.qhelp @@ -4,35 +4,33 @@

If an LDAP query or DN is built using string concatenation or string formatting, and the -components of the concatenation include user input without any proper sanitization, a user +components of the concatenation include user input without any proper sanitization, a user is likely to be able to run malicious LDAP queries.

If user input must be included in an LDAP query or DN, it should be escaped to avoid a malicious user providing special characters that change the meaning -of the query. In Go, user input should be escaped with EscapeFilter. -A good practice is to escape filter characters +of the query. In Go, user input should be escaped with EscapeFilter. +A good practice is to escape filter characters that could change the meaning of the query (https://tools.ietf.org/search/rfc4515#section-3).

-

In the following examples, the code accepts both filter and attr from the user, +

In the following examples, the code accepts both filter and attr from the user, which it then uses to build a LDAP query and DN.

-

The first and the second example uses the unsanitized user input directly +

The following example uses the unsanitized user input directly in the search filter and DN for the LDAP query. A malicious user could provide special characters to change the meaning of these components, and search for a completely different set of values.

- - + -

In the third and four example, the input provided by the user is sanitized before it is included in the search filter or DN. +

In the following example, the input provided by the user is sanitized before it is included in the search filter or DN. This ensures the meaning of the query cannot be changed by a malicious user.

- - +
diff --git a/ql/src/experimental/CWE-090/example/example_bad.go b/ql/src/experimental/CWE-090/example/example_bad.go new file mode 100644 index 00000000000..1212b4d4887 --- /dev/null +++ b/ql/src/experimental/CWE-090/example/example_bad.go @@ -0,0 +1,13 @@ +package main + +func LDAPInjectionVulnerable(untrusted string) { + // ... + searchRequest := ldap.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson)) + untrusted", // BAD: untrusted filter + []string{"dn", "cn", untrusted}, // BAD: untrusted attribute + nil, + ) + // ... +} diff --git a/ql/src/experimental/CWE-090/example/example_bad1.go b/ql/src/experimental/CWE-090/example/example_bad1.go deleted file mode 100644 index 264944783d5..00000000000 --- a/ql/src/experimental/CWE-090/example/example_bad1.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/gin-gonic/gin" - "github.com/go-ldap/ldap" -) - -// ExampleConnSearch ldap demo -func ExampleConnSearchBad1(c *gin.Context) { - filter := c.Query("name") - l, err := ldap.DialURL("ldap://127.0.0.1:389") - if err != nil { - log.Fatal(err) - } - defer l.Close() - - searchRequest := ldap.NewSearchRequest( - "dc=example,dc=com", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=organizationalPerson))", // The filter to apply - []string{"dn", "cn", filter}, // A list attributes to retrieve - nil, - ) - - sr, err := l.Search(searchRequest) - if err != nil { - log.Fatal(err) - } - - for _, entry := range sr.Entries { - fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn")) - } -} diff --git a/ql/src/experimental/CWE-090/example/example_bad2.go b/ql/src/experimental/CWE-090/example/example_bad2.go deleted file mode 100644 index 70dfa36c0f8..00000000000 --- a/ql/src/experimental/CWE-090/example/example_bad2.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/gin-gonic/gin" - "github.com/go-ldap/ldap" -) - -// ExampleConnSearch ldap demo -func ExampleConnSearchBad2(c *gin.Context) { - filter := c.Query("name") - l, err := ldap.DialURL("ldap://127.0.0.1:389") - if err != nil { - log.Fatal(err) - } - defer l.Close() - - searchRequest := ldap.NewSearchRequest( - "dc=example,dc=com", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=organizationalPerson))"+filter, // The filter to apply - []string{"dn", "cn", filter}, // A list attributes to retrieve - nil, - ) - - sr, err := l.Search(searchRequest) - if err != nil { - log.Fatal(err) - } - - for _, entry := range sr.Entries { - fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn")) - } -} diff --git a/ql/src/experimental/CWE-090/example/example_good.go b/ql/src/experimental/CWE-090/example/example_good.go new file mode 100644 index 00000000000..e4125292388 --- /dev/null +++ b/ql/src/experimental/CWE-090/example/example_good.go @@ -0,0 +1,15 @@ +package main + +func LDAPInjectionVulnerable(untrusted string) { + // ... + safe := ldap.EscapeFilter(untrusted) + + searchRequest := ldap.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))"+safe, // GOOD: sanitized filter + []string{"dn", "cn", safe}, // GOOD: sanitized attribute + nil, + ) + // ... +} diff --git a/ql/src/experimental/CWE-090/example/example_good1.go b/ql/src/experimental/CWE-090/example/example_good1.go deleted file mode 100644 index 40eafaf0e17..00000000000 --- a/ql/src/experimental/CWE-090/example/example_good1.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/gin-gonic/gin" - "github.com/go-ldap/ldap" -) - -// ExampleConnSearch ldap demo -func ExampleConnSearchGood1(c *gin.Context) { - filter := c.Query("name") - l, err := ldap.DialURL("ldap://127.0.0.1:389") - if err != nil { - log.Fatal(err) - } - defer l.Close() - - searchRequest := ldap.NewSearchRequest( - "dc=example,dc=com", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=organizationalPerson))", // The filter to apply - []string{"dn", "cn", ldap.EscapeFilter(filter)}, // A list attributes to retrieve - nil, - ) - - sr, err := l.Search(searchRequest) - if err != nil { - log.Fatal(err) - } - - for _, entry := range sr.Entries { - fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn")) - } -} diff --git a/ql/src/experimental/CWE-090/example/example_good2.go b/ql/src/experimental/CWE-090/example/example_good2.go deleted file mode 100644 index e8602cc4635..00000000000 --- a/ql/src/experimental/CWE-090/example/example_good2.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/gin-gonic/gin" - "github.com/go-ldap/ldap" -) - -// ExampleConnSearch ldap demo -func ExampleConnSearchGood2(c *gin.Context) { - filter := c.Query("name") - l, err := ldap.DialURL("ldap://127.0.0.1:389") - if err != nil { - log.Fatal(err) - } - defer l.Close() - - searchRequest := ldap.NewSearchRequest( - "dc=example,dc=com", // The base dn to search - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=organizationalPerson))"+ldap.EscapeFilter(filter), // The filter to apply - []string{"dn", "cn"}, // A list attributes to retrieve - nil, - ) - - sr, err := l.Search(searchRequest) - if err != nil { - log.Fatal(err) - } - - for _, entry := range sr.Entries { - fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn")) - } -}