会话搜集
在cmd下调用query session
命令可以获得当前环境下的windows会话
NetSessionEnum
这个函数不允许直接查询是谁登陆,但是它允许查询是谁在访问此工作站的网络资源时所创建的网络会话,从而知道来自何处,此函数不需要高权限即可查询
NET_API_STATUS NET_API_FUNCTION NetSessionEnum( [in] LMSTR servername, [in] LMSTR UncClientName, [in] LMSTR username, [in] DWORD level, [out] LPBYTE *bufptr, [in] DWORD prefmaxlen, [out] LPDWORD entriesread, [out] LPDWORD totalentries, [in, out] LPDWORD resume_handle );
第一个参数是servername
,我们可以通过servername
指定一个远程的主机,然后这个 API 会去调用远程主机的 RPC,然后返回其他用户在访问这台远程主机的网络资源时所创建的网络会话,从而可以看到这个用户来自何处
该 API 并不能查询到是谁登陆了这台远程主机,但是可以看到访问这台远程主机的网络资源时所创建的网络会话。从这个网络会话中可以看到哪个域用户来自哪个 IP,并且该 API 不需要在远程主机上有管理员权限
返回值有点特殊,NERR_Success
和ERROR_MORE_DATA
都是证明函数使用成功
level
的数值需要设置为10,是唯一以未经身份验证的方式就可以获取所需数据的级别
通过wireshark抓包可以得到NetSessionEnum
分为6步操作
- 与远程主机建立 SMB 连接(Kerberos 身份验证)
- 连接到
IPC$
共享 - 打开
srvsvc
命名管道 srvsvc
使用其 UUID绑定到接口4b324fc8-1670-01d3-1278-5a47bf6ee188
- 查询
NetSessionEnum
- 关闭并注销
实现代码
// FindADPC.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include #ifndef UNICODE #define UNICODE #endif #pragma comment(lib, "Netapi32.lib") #pragma warning(disable:4996) #include #include #include #include #include int session_enum(LPTSTR pszServerName) { NET_API_STATUS nStatus; LPSESSION_INFO_10 pBuf = NULL; LPSESSION_INFO_10 pTmpBuf; DWORD dwLevel = 10; DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwResumeHandle = 0; DWORD i; DWORD dwTotalCount = 0; do { nStatus = NetSessionEnum(pszServerName, NULL, NULL, dwLevel, (LPBYTE*)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle); if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA)) { if ((pTmpBuf = pBuf) != NULL) { for (i = 0; (i < dwEntriesRead); i++) { assert(pTmpBuf != NULL); if (pTmpBuf == NULL) { fprintf(stderr, "An access violation has occurred"); break; } SYSTEMTIME sys; GetLocalTime(&sys); char current_time[64] = { NULL }; sprintf(current_time, "%4d-%02d-%02d %02d:%02d:%02d ", sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond); printf("[%s] [%ws] [%ws] [%ws]", current_time, pszServerName, pTmpBuf->sesi10_cname, pTmpBuf->sesi10_username); pTmpBuf++; dwTotalCount++; } } } else fprintf(stderr, "A system error has occurred: %d", nStatus); if (pBuf != NULL) { NetApiBufferFree(pBuf); pBuf = NULL; } }while (nStatus == ERROR_MORE_DATA); if (pBuf != NULL) NetApiBufferFree(pBuf); return 0; } int wmain(int argc, wchar_t* argv[]) { if (argc == 1) { printf("Using:\t FindADPC.exe \\\\dc1 "); return 0; } while (true) { for (size_t i = 0; i < argc; i++) { if (i == 0) { continue; } session_enum(argv[i]); } Sleep(5000); } return 0; }
实现效果
查询域控4624登录成功日志
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; namespace EventLog4624 { class Program { static void Main(string[] args) { EventLog_4624(); } public static string MidStrEx(string sourse, string startstr, string endstr) { string result = string.Empty; int startindex, endindex; startindex = sourse.IndexOf(startstr); if (startindex == -1) return result; string tmpstr = sourse.Substring(startindex + startstr.Length); endindex = tmpstr.IndexOf(endstr); if (endindex == -1) return result; result = tmpstr.Remove(endindex); return result; } public static void EventLog_4624() { EventLog log = new EventLog("Security"); var entries = log.Entries.Cast().Where(x => x.InstanceId == 4624); entries.Select(x => new { x.MachineName, x.Site, x.Source, x.Message, x.TimeGenerated }).ToList(); foreach (EventLogEntry log1 in entries) { string text = log1.Message; string ipaddress = MidStrEx(text, " 源网络地址: ", " 源端口:"); string username = MidStrEx(text, "新登录:", "进程信息:"); username = MidStrEx(username, " 帐户名: ", " 帐户域: "); DateTime Time = log1.TimeGenerated; if (ipaddress.Length >= 7) { Console.WriteLine("\r-----------------------------------"); Console.WriteLine("Time: " + Time); Console.WriteLine("Username: " + username.Replace("", "").Replace(" ", "").Replace("\t", "").Replace("\r", "")); Console.WriteLine("Remote IP: " + ipaddress.Replace("", "").Replace(" ", "").Replace("\t", "").Replace("\r", "")); } } } } }
gpo下发query user写log到sysvol
GPO(Group Policy Object)是 Windows 中的一种管理技术,用于管理域中用户和计算机的设置。通过 GPO,管理员可以下发策略来配置用户和计算机的系统设置,以实现统一的管理和控制。
在 GPO 管理环境下,管理员可以使用 query user 命令来查询当前连接到计算机的用户的信息。query user 命令可以查询用户的登录时间、登录状态、连接状态等信息。
管理员可以将 query user 命令的输出写入到 sysvol 目录中,以便对用户登录情况进行记录和统计。sysvol 目录是 Windows 中一个共享目录,用于存储域控制器上的组策略文件。管理员可以将 query user 命令的输出信息写入到 sysvol 目录中,以便通过域控制器来管理和访问这些信息。
创建组策略
Import-Module GroupPolicy;new-gpo -name QueryDomainUser01
连接到域
powershell Import-Module GroupPolicy;new-gplink -name QueryDomainUser01 -Target "dc=god,dc=org"
修改sysvol
的权限
icacls c:\windows\sysvol\ /grant Everyone:(OI)(CI)(F) /T
下发执行
SharpGPOAbuse.exe --AddComputerTask --TaskName "QueryDomainUser001" --Author owa\\administrator --Command "cmd.exe" --Arguments "cmd /c query user > \\owa\sysvol\%COMPUTERNAME%.txt" --GPOName "QueryDomainUser01"
强制执行
gpupdate /force