跨平台LDAP图形化工具pyLDAPGui的开发之旅

本文详细介绍了作者开发pyLDAPGui这一基于Python的跨平台LDAP图形浏览工具的缘起、技术选型、设计思路、开发挑战以及其核心功能。工具旨在为安全测试和系统管理提供一个类似AD Explorer的体验,并支持与BloodHound的集成。

pyLDAPGui - 诞生记

pyLDAPGui是我近几个月一直在开发的一款应用程序,直到最近我才决定以概念验证(PoC)的形式发布它,供大家试用。这个想法的诞生源于我在开发“无恶意软件对抗模拟”(MAE)课程期间,当时我正在寻找有效的、可跨操作系统“就地取材”(Living off the Land)的工具。这就像往常一样,让我一头扎进了寻找便携式方案的兔子洞。

如果你曾经进行过任何形式的渗透测试、红队活动或专注于Active Directory环境的审计,你很可能用过Active Directory Explorer(AD Explorer)。它是一个非常棒的实用工具,可以在Windows主机上连接到LDAP服务器(通常是域控制器),为你提供一个清晰的AD环境视图。如果你没用过,下图就是连接到AD时的视图。作为系统管理工具,它很棒,但也有很多局限:速度可能很慢;它导出的快照是.dat格式,虽然可以用ADExplorerSnapshot.py转换成BloodHound数据;而且它仅限于Windows主机,有时处理起来有点麻烦。

对我而言,AD Explorer最大的限制在于它只在Windows上可用。这在大多数环境中没问题,但如果你想在*nix虚拟机或Mac上操作,就没法用它了(我知道有Mono和Wine存在,技术上可以让它运行,但不是原生支持)。

寻找替代方案

在寻找带有UI的替代LDAP浏览器的过程中,我发现了一些有用的系统管理工具,功能类似。GoDAP是一个终端UI,而非图形界面,在终端中操作也不错,但我想要一个GUI。同样,ldap_shell有一个功能丰富的终端UI。还有Softerra LDAP浏览器,它让我想起了以前用于Xbox和游戏修改的C#应用程序,界面很熟悉,但同样依赖于Windows。LDAP Wiki也有一个很好的浏览器索引列表,但选择仍然有限。这促使我开始研究基于GUI的创作。

Python与GUI

在创建GUI应用方面,我写过一些程序,但没什么特别激动人心的:一些Flask Web应用、常见的VBS/C# GUI,以及用Python和Go写的几个简单的“Hello World”应用。因此,我从语言多功能性的角度来考虑,最终选择了Python。是的,我本可以用Go、Rust、C等,但Python对我来说是最简单的选择。接下来的挑战/选择是确定在Python中使用哪个库来创建GUI。有几个选项,在阅读了每个库的文档后,我选择了PyQt,因为它易于使用、可扩展性好且支持跨操作系统(技术上其他几个也支持):

  • Tkinter
  • PyQt
  • Kivy

pyLDAPGui

在对GUI、可用选项和待办事项进行了一番深思熟虑后,下一步是确定我到底希望这个工具做什么。最初在考虑语言和GUI时,我尝试过Go和Rust,但后来意识到学习曲线和拼凑代码以实现轻松构建和支持的目标并不容易达成。Go的GUI方面,虽然Rust在GUI外观上提供了更多,感觉类似Proxmox的UI,但不幸的是,我想要的功能集不够完善,或者更准确地说,不够容易配置,这最终让我选择了Python路线。

设计

确定了库和语言后,我开始规划工具的功能。正如这类事情通常的发展过程,我从一个简单的、想要实现的功能骨架开始,随着我编写函数,其他想法也随之而来。

设计需要简单易用,但又要具备足够的功能,使其易于使用和访问。其核心,我希望拥有类似于AD Explorer的功能:

  • 连接到LDAP并显示树状视图
  • 同时支持LDAP和LDAPS
  • 能够导出为合理的格式,主要是CSV和JSON(为了兼容BloodHound)
  • 拥有一个快速、简便的搜索功能,以及运行特定LDAP查询的能力

从这个简单的基线开始,我很快做出了一个可用的PoC,主要参考了Stack Overflow上的一个帖子作为学习基础。

标志与GPT的烦恼

所有酷工具都有ASCII艺术标志,我想与众不同,所以我决定基于原始的BloodHound标志设计一个带有Python风格的标志。为了节省时间,我把标志扔给了ChatGPT,要求它生成一个变体版本,使用了以下提示。这花了一些功夫,但我们做到了:

“为我生成一张图片,使用一条蟒蛇缠绕这个图像,让蟒蛇从另一个方向过来,从猎犬的头部钻出。”

尝试1: 看起来还行,但我想要更好看些,把蟒蛇变成绿色,猎犬变成彩虹色。 尝试2: 看起来还行,但我仍然希望它更好,于是让它把蟒蛇变成猎犬的尾巴。 尝试3: 现在加点颜色,瞧!最终的图像诞生了。

挑战

编写这个工具时遇到了一些挑战,主要是在Python中设计GUI并不像我最初想的那么直接,另外还要确定用于导出的某些文件的结构。此外,让SOCKs正常工作也不像我最初想的那么容易。经过大量的搜索和试错,我终于让它运行起来了。需要说明的是,这个工具的许多功能都是在一个用Ludus生成的小型实验室中测试的,因此其性能尚未在更广泛的环境中进行测试。

操作安全(Opsec)考量

在开发中期,我也考虑到了限速/操作安全的问题,因此在一些查询中加入了一些初步的操作安全设计,如下所示。我翻转并随机化了工具发出的一些查询。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
self.attribute_variations = {
    'samaccountname': ['sAMAccountName', 'samAccountName', 'SAMACCOUNTNAME', 'sAmAcCoUnTnAmE'],
    'objectclass': ['objectClass', 'OBJECTCLASS', 'ObjectClass', 'oBjEcTcLaSs'],
    'memberof': ['memberOf', 'MEMBEROF', 'MemberOf', 'mEmBeRoF'],
    'serviceprincipalname': ['servicePrincipalName', 'SERVICEPRINCIPALNAME', 'ServicePrincipalName'],
    'useraccountcontrol': ['userAccountControl', 'USERACCOUNTCONTROL', 'UserAccountControl'],
    'objectcategory': ['objectCategory', 'OBJECTCATEGORY', 'ObjectCategory'],
    'cn': ['CN', 'cn', 'Cn', 'cN'],
    'ou': ['OU', 'ou', 'Ou', 'oU'],
    'dc': ['DC', 'dc', 'Dc', 'dC'],
    'distinguishedname': ['distinguishedName', 'DISTINGUISHEDNAME', 'DistinguishedName'],
    'name': ['name', 'NAME', 'Name', 'nAmE'],
    'description': ['description', 'DESCRIPTION', 'Description', 'dEsCrIpTiOn'],
    'mail': ['mail', 'MAIL', 'Mail', 'mAiL'],
    'givenname': ['givenName', 'GIVENNAME', 'GivenName', 'gIvEnNaMe'],
    'sn': ['sn', 'SN', 'Sn', 'sN'],
    'displayname': ['displayName', 'DISPLAYNAME', 'DisplayName', 'dIsPlAyNaMe'],
    'pwdlastset': ['pwdLastSet', 'PWDLASTSET', 'PwdLastSet', 'pWdLaStSeT'],
    'lastlogon': ['lastLogon', 'LASTLOGON', 'LastLogon', 'lAsTlOgOn'],
    'admincount': ['adminCount', 'ADMINCOUNT', 'AdminCount', 'aDmInCoUnT'],
    'primarygroupid': ['primaryGroupID', 'PRIMARYGROUPID', 'PrimaryGroupID'],
}

我还希望输出尽可能精简,用最少的查询来维持BloodHound所需的输出。该工具会进行以下几种类型的LDAP查询:

  • 树状导航 :在展开树节点时触发。
  • 条目详情 :在选择项目时触发。
  • 批量导出 :为BloodHound进行的6个大型查询。
  • 常规搜索 :用于CSV导出和浏览。

除了减少查询次数,我还在查询之间添加了限速,试图避免请求淹没服务器,同时也试图保持一定程度的隐蔽性。因此,查询之间基本上有随机延迟(0.5-2秒),并且每次运行查询时都会打乱顺序,以尝试规避标准特征检测。

还可以对代码进行更多修改以尝试保持更低的可见性,我还没有实现它们,但主要是在main.py第699行左右建立连接的地方更改一些设置:

1
2
3
ldap_conn.set_opsec_mode(True, 10.0, 30.0)  # 10-30秒随机延迟
ldap_conn.set_throttle_settings(True, 2, 1)  # 每分钟仅2次查询
ldap_conn.set_cache_settings(500, 86400)     # 24小时缓存

GitHub Actions

我遇到的另一个挑战是GitHub Actions。我喜欢CI/CD,并尽可能在我所有的工具创作中使用它,主要是因为它消除了为每个系统维护标准开发环境的麻烦,并允许进行临时构建以便于部署。话虽如此,GitHub Actions的代码需要一些试错才能正确!

注意: 如果你想通过GitHub Actions构建发布版本,需要设置以下两个权限,否则你会花很多时间(像我一样)抓狂,不知道为什么能构建但无法推送到任何地方!

1
2
3
permissions:
  contents: write
  packages: write

不过,我确实学到了如何编写GitHub Actions来清理提交历史和工作流提交,这带来了更多乐趣。我尝试了26次以上才让它顺利工作,但正如你在提交历史中看到的,多亏了Git能够轻松分离分支和重建,历史记录很干净!

成果

当所有部分整合在一起后,这个GUI实际上看起来还不错,有一些按钮(有些按钮目前还没什么用,但至少有计划)。目前可用的核心功能包括:

  • 连接到LDAP/LDAPS服务器并以树状视图列出
  • 导出为CSV
  • 导出为BloodHound格式
  • 直接注入到Neo4j(只要您能从运行此工具的主机访问它,您可以通过网络进行,工具会接收用户/密码/Neo4j数据库信息,并愉快地将数据注入到BloodHound数据库中)。

我还有一些额外的计划来进一步改进代码并添加更多功能,例如前面提到的使用OpenGraph为BloodHound CE进行数据注入,以及导出特定详细信息并从数据中解析更多信息的能力——我希望“ADCS分析”按钮最终能实现这些功能!

目前,感谢您阅读我过去几个月的趣事,希望您能从我的PoC中获得一些用处。如果您对更多“就地取材”的乐趣感兴趣,请注册我的课程预购,以便在发布时获得通知。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计