隧道构建:端口转发的原理和实现

时间:2022-07-25
本文章向大家介绍隧道构建:端口转发的原理和实现,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

众所周知,黑客入侵到内网环境,必做的两件事就是:构架隧道和敏感信息翻查。通过翻查到的敏感信息再结合已构建的隧道就可以顺利攻下内网敏感系统。

近期正好在研究构架隧道的方法:端口转发,今天科普一下场景和具体实现逻辑,共同探讨下

端口转发概述

所谓端口转发,网上的定义多种多样,不过按照作者的理解,其实端口转发就是将内网服务的端口通过正向或反向连接的方式公布出去,让攻击着可以方便快捷的直接访问罢了。不过也许这个定义不好理解,下面咱们从使用场景和原理详细看下,相信就可以明白了。

端口转发使用场景和原理

1、反向连接:

纯反向连接:

黑客已拿下一台终端,需要以此终端为跳板对其他终端进行攻击,扩大战果,获取更多敏感数据。这种情况下,一般是利用被控终端木马,进行端口转发,以被控终端为跳板,将其他终端的服务端口转发出去,攻击者通过反向的方式,从被控终端B的随机端口访问其他终端的开放端口。

数据流向:

  • 被控终端B(随机端口1)---> 攻击者(开放端口)
  • 被控终端B(随机端口2)---> 其他终端(开放端口)
  • 被控终端B(随机端口1)<--->被控终端B(随机端口2)

最终实现:

  • 攻击者(开放端口)<--->(随机端口1)被控终端B(随机端口2)<--->其他终端(开放端口)

反向连接+正向连接:

黑客又拿下了一台开放端口的被控终端A,这样黑客就可以再次利用端口转发,通过正向连接,访问其他终端的开放端口。

数据流向:

  • 被控终端B(随机端口1)---> 被控终端A(开放端口1)
  • 被控终端B(随机端口2)---> 其他终端(开放端口)
  • 被控终端B(随机端口1)<--->被控终端B(随机端口2)
  • 被控终端A(开放端口1)<--->被控终端A(开放端口2)
  • 攻击者(随机端口) --->被控终端A(开放端口2)

最终实现:

  • 攻击者(随机端口)<--->(开放端口2)被控终端A(开放端口1)<--->(随机端口1)被控终端B(随机端口2)<--->其他终端(开放端口)

示意图如下:

2、正向连接:

纯正向连接:

代理类的业务暴漏在黑客面前(如代理),黑客可直接利用。这种情况下,攻击者利用已开放的代理类服务为跳板,访问其他终端。

数据流向:

  • 攻击者(随机端口)---> 代理类服务(开放端口)
  • 代理类服务(随机端口)---> 其他终端(开放端口)
  • 代理类服务(开放端口)<--->代理类服务(随机端口)

最终实现:

  • 攻击者(随机端口)<--->(开放端口)代理类服务(随机端口)<--->其他终端(开放端口)

也可实现:

  • 攻击者(随机端口)<--->(开放端口)代理类服务(随机端口)<--->代理类服务本身127.0.0.1(开放端口)

正向连接+反向连接:

如代理类服务器在内网,可通过拿下一台被控终端B的方式,结合反向连接实现访问。

数据流向:

  • 被控终端B(随机端口1)---> 攻击者(开放端口)
  • 被控终端B(随机端口2)---> 代理类服务器(开放端口)
  • 被控终端B(随机端口1)<--->被控终端B(随机端口2)
  • 代理类服务(随机端口)---> 其他终端(开放端口)
  • 代理类服务(开放端口)<--->代理类服务(随机端口)

最终实现:

  • 攻击者(开放端口)<--->(随机端口1)被控终端B(随机端口2)<--->(开放端口)代理类服务(随机端口)<--->其他终端(开放端口)

示意图如下:

综上可见,无论是正向连接还是反向连接,实现端口转发核心原理是解决一台终端内多个端口间通信,也就是说将不同的socket通信流串到一起,如:

  • 情况一:(随机端口1)被控终端B(随机端口2)
  • 情况二:(开放端口2)被控终端A(开放端口1)
  • 情况三:(开放端口)代理类服务(随机端口)

其中,情况三是代理类程序自身提供的功能,我们仅仅是利用了这种功能罢了,但原理其实就是将两个socket串到了一起。其实实现端口转发的工具实在是太多了,咱们就不一一分析了,网上资料很多,自行搜索。下面仅从代码实现角度简单分析下。

端口转发逻辑实现

以下是完整python代码(作者不擅长代码,谅解),为实现方便,作者利用自己的终端作为测试,稍微修改了逻辑,但原理相同,如下:

具体原理见代码注释(查照上图理解代码逻辑)

#解释:定义编码,导入需要库

# -*- coding: cp936 -*-
import socket
import thread
import time

#解释:定义其他终端的ip和开放端口,还有代理类服务地址和开放端口

conHost = '其他终端ip'
conPort = '80、8080等其他终端开放的端口'
#注意:使用rdp(windows远程桌面)只能是1024 否则会提示数据加密出错!!!
dataSize = 1024
proxyaddr = '代理类服务ip'

#解释:定义第一个socket,与代理类服务的开放端口80进行连接

#解释:实现被控制终端B(随机端口)---> 代理类服务(开放端口80)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((proxyaddr, 80))

#解释:利用代理类服务与其他终端的开放端口通信,实现:代理类服务(随机端口)---> 其他终端(开放端口)

s.send('CONNECT '+ conHost +':'+conPort+' HTTP/1.1rn Host: '+ conHost +':'+conPort+' rnConnection: closernrn')
print '隧道建立成功,详情如下:'
print s.recv(1024)

#解释:连接建立后,代理服务服务程序已自动完成 (开放端口)代理类服务(随机端口),我们无需关注。

#解释:定义第二个socket,实现攻击者(随机端口)-->被控制端B(开放端口,这里仅对本机127.0.0.1开放52345进行测试,也防止他人访问)

s2 = socket.socket() 
host = socket.gethostname()
print host
port = 52345
print port
s2.bind(('127.0.0.1', port))
s2.listen(1)
d2,Myaddress = s2.accept()
print '接受完毕'
print d2.getpeername()
print d2.getsockname()
whilecount = 0
SendData = ''

#解释:定义本地socket发送和接收函数,并利用多线程将实现同步发送接收

#发送函数
def SendData(threadName):
  SendData= d2.recv(dataSize) #从攻击者的随机端口接收数据
  print '接收数据 '
  print SendData
  #开始发送数据
  s.sendall(SendData) 

#解释:将从攻击者的随机端口接收数据发送到已建立的socket1,这样就把第一个socket和第二个socket串起来了,实现 实现攻击者(随机端口)-->被控制端B(开放端口)<--->被控制端B(随机端口)--->其他终端(开放端口)

  print 'send------------------------------------ rn'+ SendData
  print threadName + time.ctime(time.time()) + ' donern'
#接收数据,原理同上
def RecvData(threadName):
  returndate = s.recv(dataSize)
  print 'from:------------------------------------rn'+conHost + ' '+returndate
  d2.sendall(returndate)
  print threadName + time.ctime(time.time()) + ' donern'
while 1:
  try:
     thread.start_new_thread(SendData, ("SendData Thread-1", ) )
     thread.start_new_thread(RecvData, ("RecvData Thread-2", ) )
   except:
     print time.ctime(time.time()) + "Error: unable to start threadrn"
    #rdp只可以大于1否则无法接受数据,报错
   time.sleep(1)

端口转发的检测思路

知道了原理,那么检测思路其实就清晰了。

想实现端口转发,必须在被控终端存在一个进程,这个进程既可以与左边通信又可以与右边通信(参考原理图),并且左右通信量相同。但具体检测逻辑还要考虑很多可变因素,例如终端存在多个进程并且进程间通信、黑客使用压缩算法造成左右通信量不同等等。

欢迎各位大神共同探讨,谢谢!

【技术创作101训练营】