I think there is a problem with this coding pattern :
var netWkstaTask = Task.Run(() => NetWkstaGetInfo(hostname, 100, out wkstaData));
if (await Task.WhenAny(Task.Delay(5000), netWkstaTask) != netWkstaTask)
return (false, new WorkstationInfo100());
If I understand what it is doing, it waits 5 secords or when the native API completes, whichever comes first. If the timeout came first, it exits early from the function. The question is what happened to the task making the native API call? and in particular the out param wkstaData. It would seem the timeout case always leaks the memory because nothing calls NetApiBufferFree on it. I am not sure there is a safe way to call these kind of APIs where the caller is expected to do the memory management.
private static async Task<(bool success, WorkstationInfo100 info)> CallNetWkstaGetInfo(string hostname)
{
if (!Helpers.CheckPort(hostname, 445))
return (false, new WorkstationInfo100());
var wkstaData = IntPtr.Zero;
var netWkstaTask = Task.Run(() => NetWkstaGetInfo(hostname, 100, out wkstaData));
if (await Task.WhenAny(Task.Delay(5000), netWkstaTask) != netWkstaTask)
return (false, new WorkstationInfo100());
! Does this leak wkstaData?
if (netWkstaTask.Result != 0)
return (false, new WorkstationInfo100());
try
{
var wkstaInfo = Marshal.PtrToStructure<WorkstationInfo100>(wkstaData);
return (true, wkstaInfo);
}
finally
{
if (wkstaData != IntPtr.Zero)
NetApiBufferFree(wkstaData);
}
}
|
return (false, new WorkstationInfo100()); |
Issue 2
Here is another case. If the timeout happens, it returns early. The finally block runs and it calls NetApiBufferFree(ptrInfo) but is there any guarantee that the NetSessionEnum task has completed when this finally block runs?
private static async Task<List<Session>> GetNetSessions(Computer computer)
{
var resumeHandle = IntPtr.Zero;
var sessionInfoType = typeof(SESSION_INFO_10);
var entriesRead = 0;
var ptrInfo = IntPtr.Zero;
var sessionList = new List<Session>();
try
{
var task = Task.Run(() => NetSessionEnum(computer.APIName, null, null, 10,
out ptrInfo, -1, out entriesRead, out _, ref resumeHandle));
//10 second timeout
if (await Task.WhenAny(task, Task.Delay(10000)) != task)
{
if (Options.Instance.DumpComputerStatus)
OutputTasks.AddComputerStatus(new ComputerStatus
{
ComputerName = computer.DisplayName,
Status = "Timeout",
Task = "NetSessionEnum"
});
return sessionList;
! Early return triggers finally block
}
...
return sessionList;
}
finally
{
if (ptrInfo != IntPtr.Zero)
NetApiBufferFree(ptrInfo);
! Is it ensured that the NetSessionEnum task completes before this check?
! Otherwise the task could complete later and nothing will free the memory.
}
}
|
if (await Task.WhenAny(task, Task.Delay(10000)) != task) |
I am not an expert in these async tasks constructs when calling unmanaged code but I am not sure how this is safe.
I think there is a problem with this coding pattern :
If I understand what it is doing, it waits 5 secords or when the native API completes, whichever comes first. If the timeout came first, it exits early from the function. The question is what happened to the task making the native API call? and in particular the
outparamwkstaData. It would seem the timeout case always leaks the memory because nothing callsNetApiBufferFreeon it. I am not sure there is a safe way to call these kind of APIs where the caller is expected to do the memory management.private static async Task<(bool success, WorkstationInfo100 info)> CallNetWkstaGetInfo(string hostname) { if (!Helpers.CheckPort(hostname, 445)) return (false, new WorkstationInfo100()); var wkstaData = IntPtr.Zero; var netWkstaTask = Task.Run(() => NetWkstaGetInfo(hostname, 100, out wkstaData)); if (await Task.WhenAny(Task.Delay(5000), netWkstaTask) != netWkstaTask) return (false, new WorkstationInfo100()); ! Does this leak wkstaData? if (netWkstaTask.Result != 0) return (false, new WorkstationInfo100()); try { var wkstaInfo = Marshal.PtrToStructure<WorkstationInfo100>(wkstaData); return (true, wkstaInfo); } finally { if (wkstaData != IntPtr.Zero) NetApiBufferFree(wkstaData); } }SharpHound3/SharpHound3/ResolutionHelpers.cs
Line 542 in 1ba6ff2
Issue 2
Here is another case. If the timeout happens, it returns early. The
finallyblock runs and it callsNetApiBufferFree(ptrInfo)but is there any guarantee that theNetSessionEnumtask has completed when this finally block runs?private static async Task<List<Session>> GetNetSessions(Computer computer) { var resumeHandle = IntPtr.Zero; var sessionInfoType = typeof(SESSION_INFO_10); var entriesRead = 0; var ptrInfo = IntPtr.Zero; var sessionList = new List<Session>(); try { var task = Task.Run(() => NetSessionEnum(computer.APIName, null, null, 10, out ptrInfo, -1, out entriesRead, out _, ref resumeHandle)); //10 second timeout if (await Task.WhenAny(task, Task.Delay(10000)) != task) { if (Options.Instance.DumpComputerStatus) OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = "NetSessionEnum" }); return sessionList; ! Early return triggers finally block } ... return sessionList; } finally { if (ptrInfo != IntPtr.Zero) NetApiBufferFree(ptrInfo); ! Is it ensured that the NetSessionEnum task completes before this check? ! Otherwise the task could complete later and nothing will free the memory. } }SharpHound3/SharpHound3/Tasks/NetSessionTasks.cs
Line 59 in f0cfadf
I am not an expert in these async tasks constructs when calling unmanaged code but I am not sure how this is safe.