在域内遇到瓶颈时,不妨看看域内机器开放的共享,获取有些收获。
大多数内网渗透总结在提到使用 WinAPI枚举系统
只是讲述了利用 NetSessionEnum
来找寻登陆 sessions
, 利用 NetWkstaUserEnum
来枚举登陆的用户,利用 NetShareEnum
来找寻共享,但却未说明其原型理论。由于前篇文章 【域渗透】域内会话收集) 已经针对 NetSessionEnum
和 NetWkstaUserEnum
进行说明,所以本文是对 NetShareEnum
做一个概述及应用的补充。
以当前权限检索有关服务器上每个共享资源的信息。还可以使用 WNetEnumResource 函数来检索资源信息。但是,WNetEnumResource不会枚举隐藏的共享或连接到共享的用户。
该函数原型为:
它需要 7 个参数。
servername:指向一个字符串的指针,该字符串指定要在其上执行该功能的远程服务器的DNS或NetBIOS名称。如果此参数为NULL,则使用本地计算机
level:指定数据的信息级别。
| 值 | 含义 |
| ---- | ---------------------------------------- |
| 0 | 返回共享名称。所述bufptr参数指向的数组 SHARE_INFO_0结构。 |
| 1 | 返回有关共享资源的信息,包括资源的名称和类型以及与资源关联的注释。所述bufptr参数指向的数组 SHARE_INFO_1结构。 |
| 2 | 返回有关共享资源的信息,包括资源名称,类型和权限,密码以及连接数。所述bufptr参数指向的数组 SHARE_INFO_2结构。 |
| 502 | 返回有关共享资源的信息,包括资源名称,类型和权限,连接数以及其他相关信息。所述bufptr参数指向的数组 SHARE_INFO_502结构。不返回来自不同范围的共享。有关范围界定的更多信息,请参见NetServerTransportAddEx函数的文档的“备注”部分。 |
| 503 | 返回有关共享资源的信息,包括资源名称,类型和权限,连接数以及其他相关信息。所述bufptr参数指向的数组SHARE_INFO_503结构。返回所有范围的共享。如果此结构的shi503_servername成员为“ *”,则没有配置的服务器名称,并且NetShareEnum函数枚举所有未作用域名称的共享。Windows Server 2003和Windows XP: 不支持此信息级别。 |
bufptr:向接收数据的缓冲区的指针。该数据的格式取决于 level 参数的值。
prefmaxlen:指定返回数据的首选最大长度,以字节为单位。如果指定MAX_PREFERRED_LENGTH,则该函数分配数据所需的内存量。如果在此参数中指定另一个值,则它可以限制函数返回的字节数。如果缓冲区大小不足以容纳所有条目,则该函数返回ERROR_MORE_DATA。
totalentries:指向一个值的值,该值接收可能已经枚举的条目总数。
resume_handle:指向包含恢复句柄的值的指针,该恢复句柄用于继续现有的共享搜索。
而此 API 的调用示例为:
string server = "rcoil.me"; int ret = NetShareEnum(server, 1, ref bufPtr, MAX_PREFERRED_LENGTH, ref entriesread, ref totalentries, ref resume_handle);
它会返回如下内容:
shi1_netname - ADMIN$
shi1_remark - Remote management
shi1_netname - C$
shi1_remark - Default share
....
关键源码如下:
/// <summary> /// 返回指定计算机所开放的共享,并返回 SHARE_INFO_1[] 数组结构 /// https://www.pinvoke.net/default.aspx/netapi32/netshareenum.html /// </summary> /// <param name="Server"></param> /// <returns></returns> public static SHARE_INFO_1[] EnumNetShares(string Server) { List<SHARE_INFO_1> ShareInfos = new List<SHARE_INFO_1>(); int entriesread = 0; int totalentries = 0; int resume_handle = 0; int nStructSize = Marshal.SizeOf(typeof(SHARE_INFO_1)); IntPtr bufPtr = IntPtr.Zero; StringBuilder server = new StringBuilder(Server); int ret = NetShareEnum(server, 1, ref bufPtr, MAX_PREFERRED_LENGTH, ref entriesread, ref totalentries, ref resume_handle); if (ret == NERR_Success) { IntPtr currentPtr = bufPtr; for (int i = 0; i < entriesread; i++) { SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure(currentPtr, typeof(SHARE_INFO_1)); ShareInfos.Add(shi1); currentPtr += nStructSize; } NetApiBufferFree(bufPtr); return ShareInfos.ToArray(); } else { ShareInfos.Add(new SHARE_INFO_1("ERROR=" + ret.ToString(), 10, string.Empty)); return ShareInfos.ToArray(); } }
演示结果:
判断可读,是根据当前用户权限进行判断的。
string path = String.Format("\\\\{0}\\{1}", computer, share.shi1_netname); var files = System.IO.Directory.GetFiles(path);
直接根据以上方法进行访问测试即可。效果如下
接下来就是以当前权限,对可访问的共享进行遍历即可。获取文件名、文件大小,再进行下一步的筛选。
/// <summary> /// 对路径进行遍历 /// </summary> /// <param name="info">提供的根路径</param> public static void ListFiles(FileSystemInfo info) { if (!info.Exists) return; DirectoryInfo dir = info as DirectoryInfo; //不是目录 if (dir == null) return; try { FileSystemInfo[] files = dir.GetFileSystemInfos(); for (int i = 0; i < files.Length; i++) { FileInfo file = files[i] as FileInfo; //是文件 if (file != null) Console.WriteLine(file.FullName); //对于子目录,进行递归调用 else ListFiles(files[i]); } } catch { } }
到此,整个过程就可以结束了。