use stubs libs && add heuristic sanitizers

This commit is contained in:
pupiles
2021-11-10 14:12:45 +08:00
parent 70a268dc6d
commit 4d9ce49816
10 changed files with 382 additions and 34 deletions

View File

@@ -1,22 +1,37 @@
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 |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:59:3:59:11 | untrusted |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:61:3:61:51 | ...+... |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:62:3:62:33 | slice literal |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:66:3:66:11 | untrusted |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:68:3:68:51 | ...+... |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:69:3:69:33 | slice literal |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:73:3:73:11 | untrusted |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:75:3:75:51 | ...+... |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:76:3:76:33 | slice literal |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:80:22:80:30 | untrusted |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:81:25:81:33 | untrusted |
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 |
| LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | semmle.label | call to UserAgent : string |
| LDAPInjectction.go:59:3:59:11 | untrusted | semmle.label | untrusted |
| LDAPInjectction.go:61:3:61:51 | ...+... | semmle.label | ...+... |
| LDAPInjectction.go:62:3:62:33 | slice literal | semmle.label | slice literal |
| LDAPInjectction.go:66:3:66:11 | untrusted | semmle.label | untrusted |
| LDAPInjectction.go:68:3:68:51 | ...+... | semmle.label | ...+... |
| LDAPInjectction.go:69:3:69:33 | slice literal | semmle.label | slice literal |
| LDAPInjectction.go:73:3:73:11 | untrusted | semmle.label | untrusted |
| LDAPInjectction.go:75:3:75:51 | ...+... | semmle.label | ...+... |
| LDAPInjectction.go:76:3:76:33 | slice literal | semmle.label | slice literal |
| LDAPInjectction.go:80:22:80:30 | untrusted | semmle.label | untrusted |
| LDAPInjectction.go:81:25:81:33 | untrusted | semmle.label | untrusted |
#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 |
| LDAPInjectction.go:59:3:59:11 | untrusted | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:59:3:59:11 | untrusted | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:61:3:61:51 | ...+... | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:61:3:61:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:62:3:62:33 | slice literal | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:62:3:62:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:66:3:66:11 | untrusted | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:66:3:66:11 | untrusted | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:68:3:68:51 | ...+... | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:68:3:68:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:69:3:69:33 | slice literal | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:69:3:69:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:73:3:73:11 | untrusted | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:73:3:73:11 | untrusted | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:75:3:75:51 | ...+... | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:75:3:75:51 | ...+... | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:76:3:76:33 | slice literal | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:76:3:76:33 | slice literal | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:80:22:80:30 | untrusted | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:80:22:80:30 | untrusted | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |
| LDAPInjectction.go:81:25:81:33 | untrusted | LDAPInjectction.go:57:15:57:29 | call to UserAgent : string | LDAPInjectction.go:81:25:81:33 | untrusted | LDAP query parameter is derived from $@. | LDAPInjectction.go:57:15:57:29 | call to UserAgent | a user-provided value |

View File

@@ -1,7 +1,13 @@
package main
//go:generate depstubber -vendor github.com/go-ldap/ldap "" ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter
//go:generate depstubber -vendor github.com/go-ldap/ldap/v3 "" ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter
//go:generate depstubber -vendor github.com/jtblin/go-ldap-client LDAPClient
//go:generate depstubber -vendor gopkg.in/ldap.v2 "" ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter
import (
"net/http"
"strings"
goldap "github.com/go-ldap/ldap"
goldapv3 "github.com/go-ldap/ldap/v3"
@@ -9,67 +15,121 @@ import (
gopkgldapv2 "gopkg.in/ldap.v2"
)
type Ldap struct{}
func (*Ldap) sanitizedUserQuery(username string) (string, bool) {
badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) {
return "", false
}
return username, true
}
func (*Ldap) sanitizedUserDN(username string) (string, bool) {
badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) {
return "", false
}
return username, true
}
func (*Ldap) sanitizedGroupFilter(username string) (string, bool) {
badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) {
return "", false
}
return username, true
}
func (*Ldap) sanitizedGroupDN(username string) (string, bool) {
badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) {
return "", false
}
return username, true
}
func main() {}
// bad is an example of a bad implementation
func bad(req *http.Request) {
func (ld *Ldap) bad(req *http.Request) {
// ...
untrusted := req.UserAgent()
goldap.NewSearchRequest(
"dc=example,dc=com", // The base dn to search
untrusted, // BAD: untrusted dn
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
untrusted, // BAD: untrusted dn
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
untrusted, // BAD: untrusted dn
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
client := &ldapclient.LDAPClient{}
client.Authenticate(untrusted, "123456") // BAD: untrusted filter
client.GetGroupsOfUser(untrusted) // BAD: untrusted filter
// ...
}
// good is an example of a good implementation
func good(req *http.Request) {
func (ld *Ldap) good(req *http.Request) {
// ...
untrusted := req.UserAgent()
escapegoldap := goldap.EscapedFilter(untrusted)
escapegoldap := goldap.EscapeFilter(untrusted)
goldap.NewSearchRequest(
"dc=example,dc=com", // The base dn to search
escapegoldap, // GOOD: sanitized dn
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)
escapegoldapv3 := goldapv3.EscapeFilter(untrusted)
goldapv3.NewSearchRequest(
"dc=example,dc=com", // The base dn to search
escapegoldapv3, // GOOD: sanitized dn
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)
escapegopkgv2 := gopkgldapv2.EscapeFilter(untrusted)
gopkgldapv2.NewSearchRequest(
"dc=example,dc=com", // The base dn to search
escapegopkgv2, // GOOD: sanitized dn
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
escapedusercustom, _ := ld.sanitizedUserQuery(untrusted) // GOOD: custom sanitized filter
escapedgroupcustom, _ := ld.sanitizedGroupFilter(untrusted) // GOOD: custom sanitized filter
escapeduserdncustom, _ := ld.sanitizedUserDN(untrusted) // GOOD: custom sanitized filter
escapedgroupdncustom, _ := ld.sanitizedGroupDN(untrusted) // GOOD: custom sanitized filter
client := &ldapclient.LDAPClient{}
client.Authenticate(escapedusercustom, "123456") // GOOD: sanitized filter
client.GetGroupsOfUser(escapedgroupcustom) // GOOD: sanitized filter
gopkgldapv2.NewSearchRequest(
escapeduserdncustom, // GOOD: sanitized dn
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=organizationalPerson))"+"(uid=1)",
[]string{"dn", "cn"},
nil,
)
gopkgldapv2.NewSearchRequest(
escapedgroupdncustom, // GOOD: sanitized dn
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=organizationalPerson))"+"(uid=1)",
[]string{"dn", "cn"},
nil,
)
// ...
}

View File

@@ -0,0 +1,11 @@
module example.com/ldap
go 1.16
require (
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-ldap/ldap/v3 v3.4.1
github.com/jtblin/go-ldap-client v0.0.0-20170223121919-b73f66626b33
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/ldap.v2 v2.5.1
)

View File

@@ -0,0 +1,38 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/go-ldap/ldap, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/go-ldap/ldap (exports: ; functions: ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter)
// Package ldap is a stub of github.com/go-ldap/ldap, generated by depstubber.
package ldap
type Control interface {
Encode() interface{}
GetControlType() string
String() string
}
func EscapeFilter(_ string) string {
return ""
}
var NeverDerefAliases int = 0
func NewSearchRequest(_ string, _ int, _ int, _ int, _ int, _ bool, _ string, _ []string, _ []Control) *SearchRequest {
return nil
}
var ScopeWholeSubtree int = 0
type SearchRequest struct {
BaseDN string
Scope int
DerefAliases int
SizeLimit int
TimeLimit int
TypesOnly bool
Filter string
Attributes []string
Controls []Control
}

View File

@@ -0,0 +1,38 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/go-ldap/ldap/v3, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/go-ldap/ldap/v3 (exports: ; functions: ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter)
// Package ldap is a stub of github.com/go-ldap/ldap/v3, generated by depstubber.
package ldap
type Control interface {
Encode() interface{}
GetControlType() string
String() string
}
func EscapeFilter(_ string) string {
return ""
}
var NeverDerefAliases int = 0
func NewSearchRequest(_ string, _ int, _ int, _ int, _ int, _ bool, _ string, _ []string, _ []Control) *SearchRequest {
return nil
}
var ScopeWholeSubtree int = 0
type SearchRequest struct {
BaseDN string
Scope int
DerefAliases int
SizeLimit int
TimeLimit int
TypesOnly bool
Filter string
Attributes []string
Controls []Control
}

View File

@@ -0,0 +1,43 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for github.com/jtblin/go-ldap-client, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: github.com/jtblin/go-ldap-client (exports: LDAPClient; functions: )
// Package go_pkg is a stub of github.com/jtblin/go-ldap-client, generated by depstubber.
package go_pkg
import (
tls "crypto/tls"
)
type LDAPClient struct {
Attributes []string
Base string
BindDN string
BindPassword string
GroupFilter string
Host string
ServerName string
UserFilter string
Conn interface{}
Port int
InsecureSkipVerify bool
UseSSL bool
SkipTLS bool
ClientCertificates []tls.Certificate
}
func (_ *LDAPClient) Authenticate(_ string, _ string) (bool, map[string]string, error) {
return false, nil, nil
}
func (_ *LDAPClient) Close() {}
func (_ *LDAPClient) Connect() error {
return nil
}
func (_ *LDAPClient) GetGroupsOfUser(_ string) ([]string, error) {
return nil, nil
}

View File

@@ -0,0 +1,11 @@
module example.com/ldap
go 1.16
require (
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-ldap/ldap/v3 v3.4.1
github.com/jtblin/go-ldap-client v0.0.0-20170223121919-b73f66626b33
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/ldap.v2 v2.5.1
)

View File

@@ -0,0 +1,79 @@
package main
import (
"encoding/gob"
"flag"
"fmt"
"os"
"reflect"
"github.com/github/depstubber/model"
pkg_ "gopkg.in/ldap.v2"
)
var output = flag.String("output", "", "The output file name, or empty to use stdout.")
func main() {
flag.Parse()
types := []struct{
sym string
typ reflect.Type
}{
}
values := []struct{
sym string
val reflect.Value
}{
{ "Authenticate", reflect.ValueOf(pkg_.Authenticate) },
{ "GetGroupsOfUser", reflect.ValueOf(pkg_.GetGroupsOfUser) },
}
// NOTE: This behaves contrary to documented behaviour if the
// package name is not the final component of the import path.
// The reflect package doesn't expose the package name, though.
pkg := model.NewPackage("gopkg.in/ldap.v2", false)
for _, t := range types {
err := pkg.AddType(t.sym, t.typ)
if err != nil {
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
os.Exit(1)
}
}
for _, v := range values {
err := pkg.AddValue(v.sym, v.val)
if err != nil {
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
os.Exit(1)
}
}
outfile := os.Stdout
if len(*output) != 0 {
var err error
outfile, err = os.Create(*output)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
}
defer func() {
if err := outfile.Close(); err != nil {
fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
os.Exit(1)
}
}()
}
if err := gob.NewEncoder(outfile).Encode(model.PackPkg(pkg)); err != nil {
fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
os.Exit(1)
}
}

View File

@@ -0,0 +1,38 @@
// Code generated by depstubber. DO NOT EDIT.
// This is a simple stub for gopkg.in/ldap.v2, strictly for use in testing.
// See the LICENSE file for information about the licensing of the original library.
// Source: gopkg.in/ldap.v2 (exports: ; functions: ScopeWholeSubtree,NeverDerefAliases,NewSearchRequest,EscapeFilter)
// Package ldap is a stub of gopkg.in/ldap.v2, generated by depstubber.
package ldap
type Control interface {
Encode() interface{}
GetControlType() string
String() string
}
func EscapeFilter(_ string) string {
return ""
}
var NeverDerefAliases int = 0
func NewSearchRequest(_ string, _ int, _ int, _ int, _ int, _ bool, _ string, _ []string, _ []Control) *SearchRequest {
return nil
}
var ScopeWholeSubtree int = 0
type SearchRequest struct {
BaseDN string
Scope int
DerefAliases int
SizeLimit int
TimeLimit int
TypesOnly bool
Filter string
Attributes []string
Controls []Control
}

View File

@@ -0,0 +1,15 @@
# github.com/go-ldap/ldap v3.0.3+incompatible
## explicit
github.com/go-ldap/ldap
# github.com/go-ldap/ldap/v3 v3.4.1
## explicit
github.com/go-ldap/ldap/v3
# github.com/jtblin/go-ldap-client v0.0.0-20170223121919-b73f66626b33
## explicit
github.com/jtblin/go-ldap-client
# gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d
## explicit
gopkg.in/asn1-ber.v1
# gopkg.in/ldap.v2 v2.5.1
## explicit
gopkg.in/ldap.v2