Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BED-5018 NTLM Ingestion Updates #1176

Merged
merged 9 commits into from
Feb 27, 2025
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;
Loading