在域内遇到瓶颈时,不妨看看域内机器开放的共享,获取有些收获。

0x00 前言

大多数内网渗透总结在提到使用 WinAPI枚举系统 只是讲述了利用 NetSessionEnum 来找寻登陆 sessions, 利用 NetWkstaUserEnum 来枚举登陆的用户,利用 NetShareEnum 来找寻共享,但却未说明其原型理论。由于前篇文章 【域渗透】域内会话收集) 已经针对 NetSessionEnumNetWkstaUserEnum 进行说明,所以本文是对 NetShareEnum 做一个概述及应用的补充。

0x01 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

  • entriesread:指向一个值的指针,该值接收实际枚举的元素数。
  • 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();
    }
}

演示结果:

0X02 进一步拓展

2.1、判断是否可读

判断可读,是根据当前用户权限进行判断的。

string path = String.Format("\\\\{0}\\{1}", computer, share.shi1_netname);
var files = System.IO.Directory.GetFiles(path);

直接根据以上方法进行访问测试即可。效果如下

接下来就是以当前权限,对可访问的共享进行遍历即可。获取文件名、文件大小,再进行下一步的筛选。

2.2、遍历

/// <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 { }
}

到此,整个过程就可以结束了。

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖