Skip to content

Commit 00d8bce

Browse files
author
Michael Cuomo
authored
Adding Additional Locations for compstatus.csv Logging (#214)
* Add TODO Comments Highlighting Possible SMB calls * feat: Add ComputerStatusEvent when ResolveHostToSid Succeeds, Add Event ot LdapPropertyProcessor and SPNProcessors * chore: Remove Todos, Add Logs, Clean up ComputerName with Helper Function * test: Add tests for sending computer status event * test: Fixed Unit Test for Build * test: Move ComputerStatus to ReadUserProperties TrustedToAuthTest
1 parent f05a995 commit 00d8bce

8 files changed

Lines changed: 130 additions & 0 deletions

File tree

src/CommonLib/LdapUtils.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ public bool GetDomain(out Domain domain) {
589589
if (await GetWorkstationInfo(strippedHost) is (true, var workstationInfo)) {
590590
var tempName = workstationInfo.ComputerName;
591591
var tempDomain = workstationInfo.LanGroup;
592+
_log.LogTrace("Get workstation info for {HostName} succeeded. Workstation {ComputerName} found.", host, tempName);
592593

593594
if (string.IsNullOrWhiteSpace(tempDomain)) {
594595
tempDomain = domain;
@@ -673,7 +674,10 @@ public bool GetDomain(out Domain domain) {
673674
/// <returns></returns>
674675
private async Task<(bool Success, NetAPIStructs.WorkstationInfo100 Info)> GetWorkstationInfo(string hostname) {
675676
if (!await _portScanner.CheckPort(hostname))
677+
{
678+
_log.LogTrace("CheckPort returned false for {HostName}.", hostname);
676679
return (false, default);
680+
}
677681

678682
var result = _nativeMethods.CallNetWkstaGetInfo(hostname);
679683
if (result.IsSuccess) return (true, result.Value);

src/CommonLib/Processors/ComputerSessionProcessor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,16 @@ await SendComputerStatus(new CSVComputerStatus {
137137
if (computerSessionName is "[::1]" or "127.0.0.1")
138138
resolvedComputerSID = computerSid;
139139
else if (await _utils.ResolveHostToSid(computerSessionName, computerDomain) is (true, var tempSid))
140+
{
140141
//Attempt to resolve the host name to a SID
141142
resolvedComputerSID = tempSid;
143+
await SendComputerStatus(new CSVComputerStatus {
144+
Status = CSVComputerStatus.StatusSuccess,
145+
Task = "NetSessionEnum",
146+
ComputerName = computerSessionName,
147+
});
148+
}
149+
142150

143151
//Throw out this data if we couldn't resolve it successfully.
144152
if (resolvedComputerSID == null || !resolvedComputerSID.StartsWith("S-1")) {
@@ -153,10 +161,17 @@ await SendComputerStatus(new CSVComputerStatus {
153161
else {
154162
var res = await _utils.ResolveAccountName(username, computerDomain);
155163
if (res.Success)
164+
{
165+
await SendComputerStatus(new CSVComputerStatus {
166+
Status = CSVComputerStatus.StatusSuccess,
167+
Task = "NetSessionEnum",
168+
ComputerName = computerSessionName,
169+
});
156170
results.Add(new Session {
157171
ComputerSID = resolvedComputerSID,
158172
UserSID = res.Principal.ObjectIdentifier
159173
});
174+
}
160175
}
161176
}
162177

src/CommonLib/Processors/LdapPropertyProcessor.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
namespace SharpHoundCommonLib.Processors {
1818
public class LdapPropertyProcessor {
1919
private static readonly HashSet<string> ReservedAttributes = new();
20+
public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
21+
public event ComputerStatusDelegate ComputerStatusEvent;
2022

2123
static LdapPropertyProcessor() {
2224
ReservedAttributes.UnionWith(CommonProperties.TypeResolutionProps);
@@ -249,10 +251,17 @@ public async Task<UserProperties> ReadUserProperties(IDirectoryObject entry, str
249251

250252
var resolvedHost = await _utils.ResolveHostToSid(d, domain);
251253
if (resolvedHost.Success && resolvedHost.SecurityIdentifier.Contains("S-1"))
254+
{
255+
await SendComputerStatus(new CSVComputerStatus {
256+
Status = CSVComputerStatus.StatusSuccess,
257+
Task = nameof(ReadUserProperties),
258+
ComputerName = Helpers.StripServicePrincipalName(d).ToUpper().TrimEnd('$'),
259+
});
252260
comps.Add(new TypedPrincipal {
253261
ObjectIdentifier = resolvedHost.SecurityIdentifier,
254262
ObjectType = Label.Computer
255263
});
264+
}
256265
}
257266
}
258267

@@ -369,10 +378,17 @@ public async Task<ComputerProperties> ReadComputerProperties(IDirectoryObject en
369378

370379
var resolvedHost = await _utils.ResolveHostToSid(d, domain);
371380
if (resolvedHost.Success && resolvedHost.SecurityIdentifier.Contains("S-1"))
381+
{
382+
await SendComputerStatus(new CSVComputerStatus {
383+
Status = CSVComputerStatus.StatusSuccess,
384+
Task = nameof(ReadComputerProperties),
385+
ComputerName = d,
386+
});
372387
comps.Add(new TypedPrincipal {
373388
ObjectIdentifier = resolvedHost.SecurityIdentifier,
374389
ObjectType = Label.Computer
375390
});
391+
}
376392
}
377393
}
378394

@@ -917,6 +933,10 @@ private enum IsTextUnicodeFlags {
917933
IS_TEXT_UNICODE_NOT_UNICODE_MASK = 0x0F00,
918934
IS_TEXT_UNICODE_NOT_ASCII_MASK = 0xF000
919935
}
936+
937+
private async Task SendComputerStatus(CSVComputerStatus status) {
938+
if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
939+
}
920940
}
921941

922942
public class ParsedCertificate {

src/CommonLib/Processors/PortScanner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public virtual async Task<bool> CheckPort(string hostname, int port = 445, int t
4747
PortScanCache.TryAdd(key, false);
4848
return false;
4949
}
50+
_log.LogTrace("CheckPort Succeeded for {HostName}:{Port}", hostname, port);
5051

5152
PortScanCache.TryAdd(key, true);
5253
return true;

src/CommonLib/Processors/SPNProcessors.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
3+
using System.Threading.Tasks;
34
using Microsoft.Extensions.Logging;
45
using SharpHoundCommonLib.Enums;
56
using SharpHoundCommonLib.OutputTypes;
@@ -9,6 +10,8 @@ public class SPNProcessors {
910
private const string MSSQLSPNString = "mssqlsvc";
1011
private readonly ILogger _log;
1112
private readonly ILdapUtils _utils;
13+
public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
14+
public event ComputerStatusDelegate ComputerStatusEvent;
1215

1316
public SPNProcessors(ILdapUtils utils, ILogger log = null) {
1417
_utils = utils;
@@ -52,6 +55,11 @@ public async IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(string[] servicePrinc
5255

5356
if (await _utils.ResolveHostToSid(spn, domainName) is (true, var host) && host.StartsWith("S-1")) {
5457
_log.LogTrace("Resolved {SPN} to {Hostname}", spn, host);
58+
await SendComputerStatus(new CSVComputerStatus {
59+
Status = CSVComputerStatus.StatusSuccess,
60+
Task = nameof(ReadSPNTargets),
61+
ComputerName = Helpers.StripServicePrincipalName(spn).ToUpper().TrimEnd('$'),
62+
});
5563
yield return new SPNPrivilege {
5664
ComputerSID = host,
5765
Port = port,
@@ -61,5 +69,9 @@ public async IAsyncEnumerable<SPNPrivilege> ReadSPNTargets(string[] servicePrinc
6169
}
6270
}
6371
}
72+
73+
private async Task SendComputerStatus(CSVComputerStatus status) {
74+
if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
75+
}
6476
}
6577
}

test/unit/ComputerSessionProcessorTest.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,37 @@ public async Task ComputerSessionProcessor_TestTimeoutPrivileged() {
257257
var status = receivedStatus[0];
258258
Assert.Equal("Timeout", status.Status);
259259
}
260+
261+
[Fact]
262+
public async Task ComputerSessionProcessor_ReadUserSessionSendsComputerStatus()
263+
{
264+
var nativeMethods = new Mock<NativeMethods>();
265+
nativeMethods.Setup(x => x.NetWkstaUserEnum(It.IsAny<string>())).Returns(() => {
266+
Task.Delay(1000).Wait();
267+
return Array.Empty<NetWkstaUserEnumResults>();
268+
});
269+
var receivedStatus = new List<CSVComputerStatus>();
270+
var mockNativeMethods = new Mock<NativeMethods>();
271+
var apiResult = new NetSessionEnumResults[] {
272+
new("admin", "\\\\127.0.0.1")
273+
};
274+
mockNativeMethods.Setup(x => x.NetSessionEnum(It.IsAny<string>())).Returns(apiResult);
275+
276+
var expected = new Session[] {
277+
new() {
278+
ComputerSID = _computerSid,
279+
UserSID = "S-1-5-21-3130019616-2776909439-2417379446-2116"
280+
}
281+
};
282+
283+
var processor = new ComputerSessionProcessor(new MockLdapUtils(), mockNativeMethods.Object,null, "dfm");
284+
processor.ComputerStatusEvent += async status => { receivedStatus.Add(status); };
285+
var result = await processor.ReadUserSessions("win10", _computerSid, _computerDomain);
286+
Assert.True(result.Collected);
287+
Assert.Equal(expected, result.Results);
288+
Assert.Single(receivedStatus);
289+
var status = receivedStatus[0];
290+
Assert.Equal("Success", status.Status);
291+
}
260292
}
261293
}

test/unit/LdapPropertyTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ public async Task LDAPPropertyProcessor_ReadUserProperties_TestTrustedToAuth()
204204
}, "S-1-5-21-3130019616-2776909439-2417379446-1101", "");
205205

206206
var processor = new LdapPropertyProcessor(new MockLdapUtils());
207+
var receivedStatus = new List<CSVComputerStatus>();
208+
processor.ComputerStatusEvent += async status => { receivedStatus.Add(status); };
207209
var test = await processor.ReadUserProperties(mock, "testlab.local");
208210
var props = test.Props;
209211
var keys = props.Keys;
@@ -230,6 +232,13 @@ public async Task LDAPPropertyProcessor_ReadUserProperties_TestTrustedToAuth()
230232
}
231233
};
232234
Assert.Equal(expected, atdr);
235+
236+
// Send Computer Status
237+
Assert.NotEmpty(receivedStatus);
238+
foreach (var status in receivedStatus)
239+
{
240+
Assert.Equal("Success", status.Status);
241+
}
233242
}
234243

235244
[Fact]
@@ -444,6 +453,8 @@ public async Task LDAPPropertyProcessor_ReadComputerProperties_HappyPath()
444453
}, "S-1-5-21-3130019616-2776909439-2417379446-1101","");
445454

446455
var processor = new LdapPropertyProcessor(new MockLdapUtils());
456+
var receivedStatus = new List<CSVComputerStatus>();
457+
processor.ComputerStatusEvent += async status => { receivedStatus.Add(status); };
447458
var test = await processor.ReadComputerProperties(mock, "testlab.local");
448459
var props = test.Props;
449460
var keys = props.Keys;
@@ -497,6 +508,13 @@ public async Task LDAPPropertyProcessor_ReadComputerProperties_HappyPath()
497508
ObjectIdentifier = "S-1-5-21-3130019616-2776909439-2417379446-1105",
498509
ObjectType = Label.User
499510
}, test.SidHistory);
511+
512+
// Send Computer Status
513+
Assert.NotEmpty(receivedStatus);
514+
foreach (var status in receivedStatus)
515+
{
516+
Assert.Equal("Success", status.Status);
517+
}
500518
}
501519

502520
[Fact]

test/unit/SPNProcessorsTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34
using CommonLibTest.Facades;
45
using SharpHoundCommonLib;
@@ -103,5 +104,32 @@ public async void ReadSPNTargets_SPNWithAddressSign_NotRead()
103104
await foreach (var spn in processor.ReadSPNTargets(servicePrincipalNames, distinguishedName))
104105
Assert.Null(spn);
105106
}
107+
108+
[Fact]
109+
public async void ReadSPNTargets_SendComputerStatus()
110+
{
111+
var processor = new SPNProcessors(new MockLdapUtils());
112+
string[] servicePrincipalNames = {"MSSQLSvc/PRIMARY.TESTLAB.LOCAL:2345"};
113+
const string distinguishedName = "cn=policies,cn=system,DC=testlab,DC=local";
114+
var receivedStatus = new List<CSVComputerStatus>();
115+
processor.ComputerStatusEvent += async status => { receivedStatus.Add(status); };
116+
117+
var expected = new SPNPrivilege
118+
{
119+
ComputerSID = "S-1-5-21-3130019616-2776909439-2417379446-1001", Port = 2345,
120+
Service = EdgeNames.SQLAdmin
121+
};
122+
123+
var count = 0;
124+
await foreach (var actual in processor.ReadSPNTargets(servicePrincipalNames, distinguishedName))
125+
{
126+
Assert.Equal(expected.ComputerSID, actual.ComputerSID);
127+
Assert.Equal(expected.Port, actual.Port);
128+
Assert.Equal(expected.Service, actual.Service);
129+
count += 1;
130+
}
131+
Assert.NotEmpty(receivedStatus);
132+
Assert.Equal(count, receivedStatus.Count);
133+
}
106134
}
107135
}

0 commit comments

Comments
 (0)