Skip to content

Commit

Permalink
BED-5018 NTLM Ingestion Updates (#1176)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvlipka authored Feb 27, 2025
1 parent 551de18 commit 6ac03b6
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 8 deletions.
2 changes: 1 addition & 1 deletion cmd/api/src/daemons/datapipe/convertors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

func convertComputerData(computer ein.Computer, converted *ConvertedData) {
baseNodeProp := ein.ConvertObjectToNode(computer.IngestBase, ad.Computer)
baseNodeProp := ein.ConvertComputerToNode(computer, ad.Computer)
converted.RelProps = append(converted.RelProps, ein.ParseACEData(baseNodeProp, computer.Aces, computer.ObjectIdentifier, ad.Computer)...)
if primaryGroupRel := ein.ParsePrimaryGroup(computer.IngestBase, ad.Computer, computer.PrimaryGroupSID); primaryGroupRel.IsValid() {
converted.RelProps = append(converted.RelProps, primaryGroupRel)
Expand Down
16 changes: 16 additions & 0 deletions cmd/ui/src/queryClient.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
// Copyright 2025 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

import { QueryClient } from 'react-query';

export const queryClient = new QueryClient();
44 changes: 41 additions & 3 deletions packages/go/ein/ad.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,44 @@ func ConvertObjectToNode(item IngestBase, itemType graph.Kind) IngestibleNode {
}
}

func ConvertComputerToNode(item Computer, itemType graph.Kind) IngestibleNode {
itemProps := item.Properties
if itemProps == nil {
itemProps = make(map[string]any)
}

convertOwnsEdgeToProperty(item.IngestBase, itemProps)

if item.IsWebClientRunning.Collected {
itemProps[ad.WebClientRunning.String()] = item.IsWebClientRunning.Result
}

if item.SmbInfo.Collected {
itemProps[ad.SMBSigning.String()] = item.SmbInfo.SigningEnabled
}

if item.RegistryData.Collected {
/*
RestrictSendingNtlmTraffic is sent to us as an uint
The possible values are
0: Allow All
1: Audit All
2: Deny All
*/
if item.RegistryData.RestrictSendingNtlmTraffic == 0 {
itemProps[ad.RestrictOutboundNTLM.String()] = false
} else {
itemProps[ad.RestrictOutboundNTLM.String()] = true
}
}

return IngestibleNode{
ObjectID: item.ObjectIdentifier,
PropertyMap: itemProps,
Label: itemType,
}
}

// This function is to support our new method of doing Owns edges and makes older data sets backwards compatible
func convertOwnsEdgeToProperty(item IngestBase, itemProps map[string]any) {
for _, ace := range item.Aces {
Expand Down Expand Up @@ -91,7 +129,7 @@ func stringToBool(itemProps map[string]any, keyName string) {
itemProps[keyName] = final
}
case bool:
//pass
// pass
default:
slog.Debug(fmt.Sprintf("Removing %s with type %T", converted, converted))
delete(itemProps, keyName)
Expand All @@ -109,7 +147,7 @@ func stringToInt(itemProps map[string]any, keyName string) {
itemProps[keyName] = final
}
case int:
//pass
// pass
default:
slog.Debug(fmt.Sprintf("Removing %s with type %T", keyName, converted))
delete(itemProps, keyName)
Expand Down Expand Up @@ -288,7 +326,7 @@ func ParseACEData(targetNode IngestibleNode, aces []ACE, targetID string, target
}
}

//TODO: When inheritance hashes are added, add them to these aces
// TODO: When inheritance hashes are added, add them to these aces

// Process abusable permissions granted to the OWNER RIGHTS SID if any were found
if len(ownerLimitedPrivs) > 0 {
Expand Down
34 changes: 34 additions & 0 deletions packages/go/ein/ad_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,37 @@ func TestParseDomainTrusts_TrustAttributesFix(t *testing.T) {
assert.Contains(t, rel.RelProps, "trustattributes")
assert.Equal(t, rel.RelProps["trustattributes"], 12345)
}

func TestConvertComputerToNode(t *testing.T) {
computer := ein.Computer{
IngestBase: ein.IngestBase{
Properties: map[string]any{
"isdc": true,
},
},
RegistryData: ein.RegistryDataAPIResult{
APIResult: ein.APIResult{
Collected: true,
},
RestrictSendingNtlmTraffic: 1,
},
IsWebClientRunning: ein.BoolAPIResult{
APIResult: ein.APIResult{
Collected: true,
},
Result: true,
},
SmbInfo: ein.SMBSigningAPIResult{
APIResult: ein.APIResult{
Collected: true,
},
SigningEnabled: true,
},
}

result := ein.ConvertComputerToNode(computer, ad.Computer)
assert.Equal(t, true, result.PropertyMap[ad.IsDC.String()])
assert.Equal(t, true, result.PropertyMap[ad.WebClientRunning.String()])
assert.Equal(t, true, result.PropertyMap[ad.RestrictOutboundNTLM.String()])
assert.Equal(t, true, result.PropertyMap[ad.SMBSigning.String()])
}
21 changes: 21 additions & 0 deletions packages/go/ein/incoming_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,24 @@ type UserRightsAssignmentAPIResult struct {
Privilege string
}

type BoolAPIResult struct {
APIResult
Result bool
}

type SMBSigningAPIResult struct {
APIResult
SigningEnabled bool
OSVersion string
OSBuild string
DnsComputerName string
}

type RegistryDataAPIResult struct {
APIResult
RestrictSendingNtlmTraffic uint
}

type Computer struct {
IngestBase
PrimaryGroupSID string
Expand All @@ -287,6 +305,9 @@ type Computer struct {
IsDC bool
DomainSID string
UnconstrainedDelegation bool
SmbInfo SMBSigningAPIResult
IsWebClientRunning BoolAPIResult
RegistryData RegistryDataAPIResult
}

type OU struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@
//
// SPDX-License-Identifier: Apache-2.0

import Composition from './Composition';
import General from './General';
import LinuxAbuse from './LinuxAbuse';
import Opsec from './Opsec';
import References from './References';
import WindowsAbuse from './WindowsAbuse';
import Composition from "./Composition";

const CoerceAndRelayNTLMToADCS = {
general: General,
windowsabuse: WindowsAbuse,
linuxabuse: LinuxAbuse,
opsec: Opsec,
references: References,
composition: Composition
composition: Composition,
};

export default CoerceAndRelayNTLMToADCS;
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@
//
// SPDX-License-Identifier: Apache-2.0

import Composition from './Composition';
import General from './General';
import LinuxAbuse from './LinuxAbuse';
import Opsec from './Opsec';
import References from './References';
import WindowsAbuse from './WindowsAbuse';
import Composition from "./Composition";

const CoerceAndRelayNTLMToSMB = {
general: General,
windowsabuse: WindowsAbuse,
linuxabuse: LinuxAbuse,
opsec: Opsec,
references: References,
composition: Composition
composition: Composition,
};

export default CoerceAndRelayNTLMToSMB;

0 comments on commit 6ac03b6

Please sign in to comment.