端口复用:隐藏 嗅探与攻击
在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用戶下都能成功進行截聽,剩余的就是大家根據自己的需要,進行一些特殊剪裁的問題了:如是隱藏,嗅探數據,高權限用戶欺騙等。
C
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <strings.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> //遠程服務器端口和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()函數來達到目的。
Python
import sockettcp1=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了這方面的文章才知道,解決這個問題理論上有三種辦 法。
-
- 1.SOCK_REUSEADDR:曾經在研究內網穿透的時候跟這個東西打過交道。如果一個監聽的套接字被設置為允許地址復用,那么套接字綁定到 的地址不會被獨占,所以必然不會存在Address already in use的問題。而且如果有兩個套接字綁定到同一個端口,也只允許一個套接字進行監聽,后一個套接字在調用listen的時候會報錯。因此這個方案顯然是最 佳方案。對于完全不知道我在說什么的童鞋,你們只要這么做
- 2.不過據說這樣的做法容易產生安全性問題,某些操作系統(沒有指明Linux或是BSD或是Windows)允許分別屬于兩個進程的兩個套接字 綁定到兩個地址的同一個端口。大多數程序把套接字綁定到0.0.0.0這個地址上進行監聽(也即監聽所有網絡設備),這種情形下另外一個進程可以綁定到 127.0.0.1或者是其他網絡設備的IP地址的同一個端口,并進行監聽,就可能把外部的連接給攔截下來(因為127.0.0.1這樣的IP是屬于特定 設備的,比0.0.0.0這虛擬設備更specific)。
而解決這個安全性的問題的方法其實也不難(其實換個沒問題的操作系統就可以了不是?),只要把套接字綁定綁定到具體的網絡設備的IP地址(比如綁定到 127.0.0.1,或者a.b.c.d)即可,大不了為每個網絡設備建一個套接字。如果實施起來有難度,只能考慮后面的兩種方法。 - 3.讓客戶端先關閉連接。如果所有的連接關閉事件都在客戶端首先發生,那么也不會存在這個問題。不過這種做法可能需要修改協議,而且貌似很容易惡意連接攻擊。修改系統TCP超時時間,這種方法很不推薦。
參考
?
http://www.2cto.com/Article/201104/87002.html
http://www.2cto.com/kf/201208/150347.html
http://www.xfocus.net/projects/Xcon/2002/Xcon2002_noble_shi.pdf
http://www.2cto.com/Article/201110/107860.html
http://fuzzexp.org/python-port-multiplexing-and-thread-operations.html
http://www.pudn.com/downloads159/sourcecode/others/detail709718.html
http://fuzzexp.org/php-port-complex-with-the-use-of.html
http://www.2cto.com/kf/200804/25668.html
https://www.corelan.be/index.php/2011/07/27/metasploit-bounty-the-good-the-bad-and-the-ugly/
總結
以上是生活随笔為你收集整理的端口复用:隐藏 嗅探与攻击的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 防止入侵者嗅探web密码
- 下一篇: 内网突破SSL嗅探的探究