github.com/google/osv-scalibr@v0.4.1/detector/weakcredentials/winlocal/samreg/userv.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package samreg
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"errors"
    21  
    22  	"golang.org/x/text/encoding/unicode"
    23  )
    24  
    25  var (
    26  	errReadOutOfBounds = errors.New("failed to read: out of bounds")
    27  	errNoHashInfoFound = errors.New("no hash information found")
    28  )
    29  
    30  // userV is the V structure containing the user's information in the SAM hive.
    31  // See https://web.archive.org/web/20190717124313/http://www.beginningtoseethelight.org/ntsecurity/index.htm#D3BC3F5643A17823
    32  // for more information about the V structure.
    33  type userV struct {
    34  	rid    string
    35  	header *userVHeader
    36  	data   []byte
    37  }
    38  
    39  // newUserV parses the V structure from the provided buffer.
    40  func newUserV(data []byte, rid string) (*userV, error) {
    41  	entry := userVHeader{}
    42  	if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &entry); err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	return &userV{
    47  		rid:    rid,
    48  		data:   data,
    49  		header: &entry,
    50  	}, nil
    51  }
    52  
    53  // read finds and reads data in the V structure from the provided offset and size.
    54  func (s *userV) read(offset, size uint32) ([]byte, error) {
    55  	offset += 0xCC
    56  	limit := int(offset) + int(size)
    57  	if len(s.data) < limit {
    58  		return nil, errReadOutOfBounds
    59  	}
    60  
    61  	return s.data[offset:limit], nil
    62  }
    63  
    64  // Username returns the username of the user.
    65  func (s *userV) Username() (string, error) {
    66  	data, err := s.read(s.header.NameOffset, s.header.NameLength)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  
    71  	decoded, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().Bytes(data)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  
    76  	return string(decoded), nil
    77  }
    78  
    79  // EncryptedHashes returns the encrypted LM and NT hashes of the user.
    80  // Note that at this point, the hashes are still encrypted with RC4 or AES.
    81  func (s *userV) EncryptedHashes() ([]byte, []byte, error) {
    82  	if s.header.NtHashLength == 0 {
    83  		return nil, nil, errNoHashInfoFound
    84  	}
    85  
    86  	ntHash, err := s.read(s.header.NtHashOffset, s.header.NtHashLength)
    87  	if err != nil {
    88  		return nil, nil, err
    89  	}
    90  
    91  	lmHash, err := s.read(s.header.LmHashOffset, s.header.LmHashLength)
    92  	if err != nil {
    93  		return nil, nil, err
    94  	}
    95  
    96  	return lmHash, ntHash, nil
    97  }
    98  
    99  type userVHeader struct {
   100  	Reserved0            [12]byte
   101  	NameOffset           uint32
   102  	NameLength           uint32
   103  	Reserved1            uint32
   104  	FullNameOffset       uint32
   105  	FullNameLength       uint32
   106  	Reserved2            uint32
   107  	CommentOffset        uint32
   108  	CommentLength        uint32
   109  	Reserved3            uint32
   110  	UserCommentOffset    uint32
   111  	UserCommentLength    uint32
   112  	Reserved4            uint32
   113  	Reserved5            [12]byte
   114  	HomeDirOffset        uint32
   115  	HomeDirLength        uint32
   116  	Reserved6            uint32
   117  	HomeDirConnectOffset uint32
   118  	HomeDirConnectLength uint32
   119  	Reserved7            uint32
   120  	ScriptPathOffset     uint32
   121  	ScriptPathLength     uint32
   122  	Reserved8            uint32
   123  	ProfilePathOffset    uint32
   124  	ProfilePathLength    uint32
   125  	Reserved9            uint32
   126  	WorkstationsOffset   uint32
   127  	WorkstationsLength   uint32
   128  	Reserved10           uint32
   129  	HoursAllowedOffset   uint32
   130  	HoursAllowedLength   uint32
   131  	Reserved11           uint32
   132  	Reserved12           [12]byte
   133  	LmHashOffset         uint32
   134  	LmHashLength         uint32
   135  	Reserved13           uint32
   136  	NtHashOffset         uint32
   137  	NtHashLength         uint32
   138  	Reserved14           uint32
   139  	Reserved15           [24]byte
   140  }