diff --git a/internal/goad/optldap/crud.go b/internal/goad/optldap/crud.go index 1be123c..6dff412 100644 --- a/internal/goad/optldap/crud.go +++ b/internal/goad/optldap/crud.go @@ -42,8 +42,8 @@ func (o *Options) read(target string) { case ObjectSid: data = append(data, DecodeSID(UnpackToString(m[a]))) case ManagedPassword: - d := UnpackToString(m[ManagedPassword]) - blob := mstypes.NewMSDSManagedPasswordBlob([]byte(d)) + var blob mstypes.MSDSManagedPasswordBlob + _ = mstypes.UnmarshalBinary(&blob, []byte(UnpackToString(m[ManagedPassword]))) data = append(data, mstypes.HashDataNTLM(blob.CurrentPassword)) default: data = append(data, UnpackToString(m[a])) diff --git a/pkg/mstypes/common.go b/pkg/mstypes/common.go new file mode 100644 index 0000000..f3224a1 --- /dev/null +++ b/pkg/mstypes/common.go @@ -0,0 +1,92 @@ +package mstypes + +import ( + "encoding/binary" + "fmt" + "reflect" +) + +func next(b []byte, pattern []byte) int { + for i := len(pattern); i < len(b); i++ { + var ok bool = false + for j := 0; j < len(pattern); j++ { + if b[i-len(pattern)+j] == pattern[j] { + if j == 0 { + ok = true + } + ok = ok && true + } else { + ok = false + } + } + if ok { + return i - len(pattern) + } + } + return 0 +} + +func UnmarshalBinary(s any, d []byte) error { + t := reflect.TypeOf(s) + v := reflect.ValueOf(s) + + defer func() { + if r := recover(); r != nil { + fmt.Println("==> panic recovered: ", r) + } + }() + + if t.Kind() == reflect.Ptr { + t = t.Elem() + v = v.Elem() + } + + var offset int = 0 + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + ft := t.Field(i) + + switch ft.Type.Kind() { + case reflect.Uint16: + f.Set(reflect.ValueOf(binary.LittleEndian.Uint16(d[offset : offset+2]))) + offset += 2 + case reflect.Uint32: + f.Set(reflect.ValueOf(binary.LittleEndian.Uint32(d[offset : offset+4]))) + offset += 4 + case reflect.Uint64: + f.Set(reflect.ValueOf(binary.LittleEndian.Uint64(d[offset : offset+8]))) + offset += 8 + case reflect.Slice: + delim, ok := ft.Tag.Lookup("delimiter") + if ok { + switch delim { + case "16bitnull": + end := next(d[offset:], []byte{0, 0}) + f.Set(reflect.ValueOf(d[offset : offset+end])) + offset = offset + end + default: + } + } + padding, ok := ft.Tag.Lookup("padding") + if ok { + switch padding { + case "null": + var stop bool = false + var end int + for !stop { + tmp := next(d[offset+end:], []byte{0}) + if tmp != 0 && tmp == end+1 { + end = tmp + } else { + stop = true + } + } + f.Set(reflect.ValueOf(d[offset : offset+end])) + offset = offset + end + default: + } + } + } + } + return nil +} diff --git a/pkg/mstypes/msds.go b/pkg/mstypes/msds.go index 6409b8d..c01b9d7 100644 --- a/pkg/mstypes/msds.go +++ b/pkg/mstypes/msds.go @@ -1,65 +1,16 @@ package mstypes -import ( - "encoding/binary" -) - type MSDSManagedPasswordBlob struct { Version uint16 + Reserved uint16 Lenght uint32 CurrentPasswordOffset uint16 PreviousPasswordOffset uint16 QueryPasswordIntervalOffset uint16 UnchangedPasswordIntervalOffset uint16 - CurrentPassword []byte - PreviousPassword []byte + CurrentPassword []byte `delimiter:"16bitnull"` + PreviousPassword []byte `delimiter:"16bitnull"` + AlignmentPadding []byte `padding:"null"` QueryPasswordInterval uint64 UnchangedPasswordInterval uint64 } - -func nextNul(b []byte) uint16 { - var stop bool = false - for i, n := range b { - if n == 0 { - if stop { - return uint16(i - 1) - } else { - stop = true - } - } else { - stop = false - } - } - return 0 -} - -func NewMSDSManagedPasswordBlob(data []byte) *MSDSManagedPasswordBlob { - var blob MSDSManagedPasswordBlob - - blob.Version = binary.LittleEndian.Uint16(data[0:2]) - blob.Lenght = binary.LittleEndian.Uint32(data[4:8]) - blob.CurrentPasswordOffset = binary.LittleEndian.Uint16(data[8:10]) - blob.PreviousPasswordOffset = binary.LittleEndian.Uint16(data[10:12]) - blob.QueryPasswordIntervalOffset = binary.LittleEndian.Uint16(data[12:14]) - blob.UnchangedPasswordIntervalOffset = binary.LittleEndian.Uint16(data[14:16]) - - endOfCurrentPassword := 16 + nextNul(data[16:]) - blob.CurrentPassword = data[16:endOfCurrentPassword] - endOfPreviousPassword := endOfCurrentPassword + nextNul(data[endOfCurrentPassword:]) - blob.PreviousPassword = data[endOfCurrentPassword:endOfPreviousPassword] - - endOfPadding := endOfPreviousPassword - stop := false - for !stop { - tmp := endOfPadding - endOfPadding = nextNul(data[endOfPadding:]) - if endOfPadding == 0 { - endOfPadding = tmp - stop = true - } - } - - blob.QueryPasswordInterval = binary.LittleEndian.Uint64(data[endOfPadding : endOfPadding+8]) - blob.UnchangedPasswordInterval = binary.LittleEndian.Uint64(data[endOfPadding+8 : endOfPadding+16]) - return &blob -}