再探BeaconEye

渗透技巧 2年前 (2022) admin
733 0 0

一、写在前面

1、 BeaconEye从发布到现在已经有半年多时间,本文仅是对现有的绕过以及检测方法的改进。
2、特别感谢cheery师傅的指导。
3、Beacon扫描代码地址: https://github.com/j0urney1/BeaconEye_C
4、绕过内存扫描代码地址: https://github.com/j0urney1/TitanLdr_for_memory

二、目前的绕过方法

目前已知绕过BeaconEye的方法如下
1、修改beacon.dll中memset的值。
2、shellcode运行完之后将PEB中NumberOfHeaps的值修改为0。

三、修改NumberOfHeaps的值

1、先查看BeaconEye代码,扫描进程时通过读取进程PEB中NumberOfHeaps的值来确定堆个数。

再探BeaconEye

avater


如果在加载完shellcode之后将进程NumberOfHeaps修改成0,就可以绕过BeaconEye的扫描,这种方法的检测也很简单,即使将NumberOfHeaps改成了0,ProcessHeaps的值任然不会变,且是以null结束,如下图,所以在扫描内存的时候直接改为死循环即可。

再探BeaconEye

avater


四、修改beacon.dll

1、beaconconfig数据类型如下,每个成员以一个指针大小对齐。

 1typedef struct _data
2{

3    USHORT dataType;
4    union 
5    {
6        USHORT shortdata;//1
7        DWORD  dworddata;//2
8        PVOID  pvoiddata;//3
9    }datastruct;
10}data, * pdata;
11typedef struct _config
12{

13    PVOID unuse[2];
14    data data[ANYSIZE_ARRAY];
15}config,*Pconfig;

2、修改了memset设置内存的值之后,BeaconConfig的内存如下。

再探BeaconEye

avater


这种内存似乎不能用rule规则来扫描,因为不可预测memset之后内存的值,但是可以通过比对内存来进行扫描,因为memset之后,没有修改过的内存的值是一样的,可能不是很好描述,具体代码如下。
64位进程扫描


 1BOOL CheckX64(HANDLE hProcess,ULONGLONG heapAddress, DWORD dwHezpSize,PWSTR ProcessName,DWORD PID)
2{
3    BOOL re = FALSE;
4    PBYTE buffer = NULL;
5    BYTE temp[0x10];
6    DWORD i = 0, offset;
7    ULONGLONG CheckValue = 0;
8
9    if (dwHezpSize >= 0x810)
10    {
11        if (buffer = LocalAlloc(LPTR, dwHezpSize))
12        {
13            if (NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess, heapAddress, buffer, dwHezpSize, NULL)))
14            {
15                for (i = 0x10; i dwHezpSize - 106i++)
16                {
17                    memset(tempbuffer[i], sizeof(temp));
18                    CheckValue = *(PULONGLONG)temp & ~0xFFFF;
19                    if (RtlEqualMemory(buffer + itemp0x10))
20                    {
21                        offset = i + 0x10;
22                        if (*(USHORT*)(buffer + offset) == 0x1 && ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue))
23                        {
24                            offset += 8;
25                            if (*(USHORT*)(buffer + offset) == 0x00 || *(USHORT*)(buffer + offset) == 0x01 || *(USHORT*)(buffer + offset) == 0x02 || *(USHORT*)(buffer + offset) == 0x04 || *(USHORT*)(buffer + offset) == 0x08 || *(USHORT*)(buffer + offset) == 0x10)
26                            {
27                                if ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue)
28                                {
29                                    offset += 8;
30                                    if (*(USHORT*)(buffer + offset) == 0x1 && ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue))
31                                    {
32                                        offset += 10;
33                                        if ((*(PULONGLONG)(buffer + offset) & 0xFFFFFFFFFFFF) == (*(PULONGLONG)temp & 0xFFFFFFFFFFFF))
34                                        {
35                                            offset += 6;
36                                            if (*(USHORT*)(buffer + offset) == 0x2 && ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue))
37                                            {
38                                                offset += 12;
39                                                if (*(PDWORD)(buffer + offset) == *(PDWORD)temp)
40                                                {
41                                                    offset += 4;
42                                                    if (*(USHORT*)(buffer + offset) == 0x2 && ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue))
43                                                    {
44                                                        offset += 12;
45                                                        if (*(PDWORD)(buffer + offset) == *(PDWORD)temp)
46                                                        {
47                                                            offset += 4;
48                                                            if (*(USHORT*)(buffer + offset) == 0x1 && ((*(PULONGLONG)(buffer + offset) & ~0xFFFF) == CheckValue))
49                                                            {
50                                                                offset += 10;
51                                                                if (RtlEqualMemory(buffer + offsettemp0x10))
52                                                                {
53                                                                    wprintf(L"Process: %ws Pid: %d Arch x64ntFind Data at %I64Xn", ProcessNamePIDheapAddress + i);
54                                                                    DisplayX64(hProcessheapAddress + i);
55                                                                    re = TRUE;
56                                                                    break;
57                                                                }
58                                                            }
59                                                        }
60                                                    }
61                                                }
62                                            }
63                                        }
64                                    }
65                                }
66                            }
67                        }
68                    }
69                }
70            }
71            LocalFree(buffer);
72        }
73    }
74    return re;
75
76}
77

32位进程扫描

 1BOOL CheckX86(HANDLE hProcess, PVOID heapAddress, DWORD dwHezpSize, PWSTR ProcessName, DWORD PID)
2{
3    BOOL re = FALSE;
4    PBYTE buffer = NULL;
5    BYTE temp[0x8];
6    DWORD i = 0, offset, CheckValue = 0;
7
8    if (dwHezpSize >= 0x408)
9    {
10        if (buffer = LocalAlloc(LPTR, dwHezpSize))
11        {
12            NtReadVirtualMemory(hProcess, heapAddress, buffer, dwHezpSize, NULL);
13            for (i = 8; i dwHezpSize - 54i++)
14            {
15                memset(tempbuffer[i], sizeof(temp));
16                CheckValue = *(PDWORD)temp & ~0xFFFF;
17                if (RtlEqualMemory(buffer + itemp8))
18                {
19                    offset = i + 8;
20                    if ((*(USHORT*)(buffer + offset) == 0x1) && ((*(PDWORD)(buffer + offset) & ~0xFFFF) == CheckValue))
21                    {
22                        offset += 4;
23                        if (*(USHORT*)(buffer + offset) == 0x00 || *(USHORT*)(buffer + offset) == 0x01 || *(USHORT*)(buffer + offset) == 0x02 || *(USHORT*)(buffer + offset) == 0x04 || *(USHORT*)(buffer + offset) == 0x08 || *(USHORT*)(buffer + offset) == 0x10)
24                        {
25                            offset += 2;
26                            if (*(USHORT*)(buffer+offset) == *(USHORT*)temp)
27                            {
28                                offset += 2;
29                                if ((*(USHORT*)(buffer + offset) == 0x1) && ((*(PDWORD)(buffer + offset) & ~0xFFFF) == CheckValue))
30                                {
31                                    offset += 6;
32                                    if (*(USHORT*)(buffer + offset) == *(USHORT*)temp)
33                                    {
34                                        offset += 2;
35                                        if (*(USHORT*)(buffer + offset) == 0x2 && ((*(PDWORD)(buffer + offset) & ~0xFFFF) == CheckValue))
36                                        {
37                                            offset += 8;
38                                            if (*(USHORT*)(buffer + offset) == 0x2 && ((*(PDWORD)(buffer + offset) & ~0xFFFF) == CheckValue))
39                                            {
40                                                offset += 8;
41                                                if (*(USHORT*)(buffer + offset) == 0x1 && ((*(PDWORD)(buffer + offset) & ~0xFFFF) == CheckValue))
42                                                {
43                                                    offset += 6;
44                                                    if (RtlEqualMemory(buffer + offsettemp8))
45                                                    {
46                                                        wprintf(L"Process: %ws Pid: %d Arch x86ntFind Data at %pn", ProcessNamePID, (PBYTE)heapAddress + i);
47                                                        DisplayX86(hProcess, (PBYTE)heapAddress + i);
48                                                        re = TRUE;
49                                                        break;
50                                                    }
51                                                }
52                                            }
53                                        }
54                                    }
55                                }
56                            }
57                        }
58                    }
59                }
60            }
61            LocalFree(buffer);
62        }
63    }
64    return re;
65}
66

具体结果如下

再探BeaconEye

avater


再探BeaconEye

avater


再探BeaconEye

avater


五、绕过

1、修改后的代码应该是能无视修改NumberOfHeaps和beacon.dll的绕过方法。
2、在我的认知里目前能绕过的就是hook掉Sleep,在进入休眠加密BeaconConfig,这种方法对于自己写加载器似乎很好实现,但是对于注入到其他进程似乎不太好实现。
3、CS在4.4版本推出了自定义反射dll加载功能,该功能可以让用户自己实现反射dll的加载过程。具体步骤如下

11、添加异常处理,当sleep结束后触发,解密映射到内存的dll,以及beaconconfig。
22、找到反射dll映射到内存的地址,以及beaconconfig的地址,每次进入Sleep之后加密beaconconfig和dll在内存中的映射。
33、这样加密之后不仅能绕过BeaconEye的内存扫描,也可以绕过卡巴斯基的内存扫描

六、参考

1、https://www.anquanke.com/post/id/253039
2、https://www.cobaltstrike.com/blog/cobalt-strike-4-4-the-one-with-the-reconnect-button/
3、https://xz.aliyun.com/t/9399
4、https://github.com/SecIdiot/TitanLdr


原文始发于微信公众号(我真不是红队啊):再探BeaconEye

版权声明:admin 发表于 2022年4月15日 下午5:57。
转载请注明:再探BeaconEye | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...