From f5804b2428abf45dc9c90e32de45048de576a3c8 Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Mon, 21 Sep 2020 11:46:56 +1000 Subject: [PATCH] Fix possible recursion bug. (#30) Resolving ATTRIBUTE_LIST recursively could get into a recursive loop. Fixes: #29 --- parser/attribute.go | 23 +++++++++++----------- parser/debug.go | 2 +- parser/mft.go | 47 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/parser/attribute.go b/parser/attribute.go index f2116b9..b76eb45 100644 --- a/parser/attribute.go +++ b/parser/attribute.go @@ -681,16 +681,18 @@ func (self *ATTRIBUTE_LIST_ENTRY) Attributes( attr_list_entry := self.Profile.ATTRIBUTE_LIST_ENTRY( self.Reader, self.Offset+offset) - Printf("ATTRIBUTE_LIST_ENTRY %v\n", - attr_list_entry.DebugString()) + Printf("%v ATTRIBUTE_LIST_ENTRY %v\n", mft_entry.Record_number(), + DebugString(attr_list_entry, "")) // The attribute_list_entry points to a different MFT // entry than the one we are working on now. We need // to fetch it from there. + mft_ref := attr_list_entry.MftReference() if ntfs.RootMFT != nil && - attr_list_entry.MftReference() != uint64(mft_entry.Record_number()) { + mft_ref != uint64(mft_entry.Record_number()) { - Printf("Fetching from MFT Entry %v\n", attr_list_entry.MftReference()) + Printf("While working on %v - Fetching from MFT Entry %v\n", + mft_entry.Record_number(), mft_ref) attr, err := attr_list_entry.GetAttribute(ntfs) if err != nil { Printf("Error %v\n", err) @@ -723,14 +725,13 @@ func (self *ATTRIBUTE_LIST_ENTRY) GetAttribute( if err != nil { return nil, err } - for _, attr := range mft.EnumerateAttributes(ntfs) { - if attr.Type().Value == uint64(mytype) && - attr.Attribute_id() == uint16(myid) { - return attr, nil - } + res, err := mft.GetDirectAttribute(ntfs, mytype, uint16(myid)) + if err != nil { + Printf("MFT %v not found in target\n", mft.Record_number()) + } else { + Printf("Found %v\n", DebugString(res, " ")) } - - return nil, errors.New("No attribute found.") + return res, err } // The STANDARD_INDEX_HEADER has a second layer of fixups. diff --git a/parser/debug.go b/parser/debug.go index 20e674a..1bd3aaa 100644 --- a/parser/debug.go +++ b/parser/debug.go @@ -25,7 +25,7 @@ type Debugger interface { func DebugString(arg interface{}, indent string) string { debugger, ok := arg.(Debugger) - if ok { + if debug && ok { lines := strings.Split(debugger.DebugString(), "\n") for idx, line := range lines { lines[idx] = indent + line diff --git a/parser/mft.go b/parser/mft.go index 00e5c50..90a05b1 100644 --- a/parser/mft.go +++ b/parser/mft.go @@ -50,6 +50,53 @@ func (self *MFT_ENTRY) EnumerateAttributes(ntfs *NTFSContext) []*NTFS_ATTRIBUTE return result } +// See https://github.com/CCXLabs/CCXDigger/issues/13 + +// It is possible that an attribute list is pointing to an mft entry +// which also contains an attribute list. The second attribute list +// may also point to another entry inside the first MFT entry. This +// causes an infinite loop. + +// Previous versions of the code erroneously called +// EnumerateAttributes to resolve a foreign attribute reference but +// this is not strictly correct because a foreign reference is never +// indirect and so never should traverse ATTRIBUTE_LISTs recursively +// anyway. + +// The GetDirectAttribute() function looks for an exact attribute and +// type inside an MFT entry without following any attribute +// lists. This breaks the recursion and is a more correct approach. + +// Search the MFT entry for a contained attribute - does not expand +// ATTRIBUTE_LISTs. This version is suitable to be called from within +// an ATTRIBUTE_LIST expansion. +func (self *MFT_ENTRY) GetDirectAttribute( + ntfs *NTFSContext, attr_type uint64, attr_id uint16) (*NTFS_ATTRIBUTE, error) { + offset := int64(self.Attribute_offset()) + + for { + // Instantiate the attribute over the fixed up address space. + attribute := self.Profile.NTFS_ATTRIBUTE(self.Reader, offset) + + // Reached the end of the MFT entry. + mft_size := int64(self.Mft_entry_size()) + attribute_size := int64(attribute.Length()) + if attribute_size == 0 || + attribute_size+offset > mft_size { + break + } + + if attribute.Type().Value == attr_type && + attribute.Attribute_id() == attr_id { + return attribute, nil + } + + // Go to the next attribute. + offset += int64(attribute.Length()) + } + return nil, errors.New("No attribute found.") +} + // Open the MFT entry specified by a path name. Walks all directory // indexes in the path to find the right MFT entry. func (self *MFT_ENTRY) Open(ntfs *NTFSContext, filename string) (*MFT_ENTRY, error) {