内网穿透工具--Stowaway流量特征检测分析

本文介绍了Stowaway,一个由Go语言编写的渗透测试工具,它支持多级代理和内网访问突破。文章详细讲解了环境配置、测试流程以及Admin和Agent的逻辑分析,特别关注了Normal_ACTIVE和NormalPassive模式的工作原理。
摘要由CSDN通过智能技术生成

工具介绍

项目地址
https://github.com/ph4ntonn/Stowaway
Stowaway是一个利用go语言编写、专为渗透测试工作者制作的多级代理工具
用户可使用此程序将外部流量通过多个节点代理至内网,突破内网访问限制,构造树状节点网络,并轻松实现管理功能
结构特征:
感觉类似termite

流量&逻辑分析

截止到目前为止,官网最新版本为2.1版本

环境&测试准备

环境配置
外部服务器Ubantu 192.168.2.134 stowaway admin端
内部服务器WIn10 192.168.2.1 stowaway agent端
内部资源Win7 http://192.168.2.130:8888 只允许192.168.2.1访问

测试流程

  1. Ubantu运行下面命令对3000端口进行监听处理

./linux_x64_admin -l 3000

image.png

  1. Win10连接Ubantu监听的3000端口

.\windows_x64_agent.exe -c 192.168.2.134:3000image.png

  1. 在Ubantu上可以执行各种功能

image.png

检测特征

其整体架构和相关的页面个人感觉和Termite非常类似,其中admin和agent的入口函数分别在agent目录下的agent.go和admin目录下的admin.go
image.png
admin和agent中都会调用initial.ParseOptions方法对输入的参数做解析,确定运行的模式的一些其他功能的开启与否。

Agent逻辑分析

下面这些逻辑和分析基于未使用-s参数,即未加密情况进行,加密情况另行研究
主入口函数为agent#agent.go#19#main.php,主要方法如下:
image.png
参数解析需要注意一下,关于up和down的参数,默认只有http和raw两种类型,直接跟踪process.NewAgent方法
image.png
其中UUID参数值在protocol#protocol.go中定义
image.png
childrenMessChan用于生成ChildrenMess传输信道,其由两部分组成,Header+message,Header同样在protocol.go中定义
image.png
之后针对不同的分支做不同的处理,其实就是进入method.go的不同分支,针对admin和agent都是一样的操作。

NORMAL_ACTIVE类型(-c参数)

image.png
函数入口在agent#initial#method.go#46-110,主要代码如下:

func NormalActive(userOptions *Options, proxy share.Proxy) (net.Conn, string) {
    //实例化发送消息和接收消息
    var sMessage, rMessage protocol.Message
    // 生成HIMEss,相关数据结构在protocol.go中定义
    hiMess := &protocol.HIMess{
        GreetingLen: uint16(len("Shhh...")),
        Greeting:    "Shhh...",
        UUIDLen:     uint16(len(protocol.TEMP_UUID)), //IAMNEWHERE
        UUID:        protocol.TEMP_UUID,	//IAMNEWHERE
        IsAdmin:     0,
        IsReconnect: 0,
    }
    header := &protocol.Header{
        Sender:      protocol.TEMP_UUID, //IAMNEWHERE
        Accepter:    protocol.ADMIN_UUID, //IAMADMINXD
        MessageType: protocol.HI, //0 uint16
        RouteLen:    uint32(len([]byte(protocol.TEMP_ROUTE))), //THEREISNOROUTE uint32
        Route:       protocol.TEMP_ROUTE,
    }
    for {
        ...
    	//授权认证,取serect值的后16位(如果没设置就是0的md5)发送到服务器,并获取服务器返回判断是否相同
        if err := share.ActivePreAuth(conn, userOptions.Secret); err != nil {
            ...
        }
        //依据不同的协议生成raw或者http类型数据,http相比于raw来说就是增加了Header部分,后面的raw数据在body中,如果设置-s参数,后续的加密会使用AES进行加密(会对key进行填充或切割处理,32位)
        sMessage = protocol.PrepareAndDecideWhichSProtoToUpper(conn, userOptions.Secret, protocol.TEMP_UUID)
    	//生成Message,这一步中会依次调用ConstructData(生成头部数据和body数据)、ConstructHeader(生成HTTP头部)、ConstructSuffix(暂未实现,暂时不用关注)
        protocol.ConstructMessage(sMessage, header, hiMess, false)
        //发送数据
        sMessage.SendMessage()
        //判断上游协议
        rMessage = protocol.PrepareAndDecideWhichRProtoFromUpper(conn, userOptions.Secret, protocol.TEMP_UUID)
        //解析数据,Contstruct的逆向操作
        fHeader, fMessage, err := protocol.DestructMessage(rMessage)
        ...
        if fHeader.MessageType == protocol.HI {
            mmess := fMessage.(*protocol.HIMess)
            if mmess.Greeting == "Keep slient" && mmess.IsAdmin == 1 {
                //接收UUID处理
                uuid := achieveUUID(conn, userOptions.Secret)
                return conn, uuid
            }
        }
    }
}

其中HEAER接口如下
image.png
HTTPMESSAGE中用于生成HTTP HEADER的ConstructHeader如下
image.png
重点关注一下raw类型数据中的ConstructData方法和DeconstructData方法,关键部分代码如下:

func (message *RawMessage) ConstructData(header *Header, mess interface{}, isPass bool) {
    ...
    //默认为0
    binary.BigEndian.PutUint16(messageTypeBuf, header.MessageType)
    //默认值为0000000e
	binary.BigEndian.PutUint32(routeLenBuf, header.RouteLen)
    //写入Header数据,默认为IAMNEWHEREIAMADMINXD\x00\x00\x00\x00\x00\x0eTHEREISNOROUTE
    headerBuffer.Write([]byte(header.Sender))
	headerBuffer.Write([]byte(header.Accepter))
	headerBuffer.Write(messageTypeBuf)
	headerBuffer.Write(routeLenBuf)
	headerBuffer.Write([]byte(header.Route))
    ...
    //dataBuffer为hiMess处理之后内容,默认为0007536868682e2e2e000a49414d4e45574845524500000000
    //默认会进行此操作,isPass默认为False
    if !isPass {
        //Gzip压缩,默认数据为1f8b08000000000000ff62600fcec8c8d0d3d363e0f274f4f5730df7700d726560606000040000ffff7cebbead19000000,注意这里关于go中的gzip压缩和其余的压缩数据稍有不同,具体原因可自行百度
		message.DataBuffer = crypto.GzipCompress(message.DataBuffer)
        //进行AES加密,如果未设置key的话,这一步不会进行
		message.DataBuffer = crypto.AESEncrypt(message.DataBuffer, message.CryptoSecret)
	}
    
}

DeconstructData方法用于解析收到的数据,解析为header+body的形式,为ConstructData的逆向处理过程,其中会存在针对MessageType的判断行为,判断是哪种数据类型从而成功对应的新的请求数据:
image.png
在上面不同的选择处理分支都处理完成之后都会进入到agent.Run()代码
image.png

Admin逻辑分析

入口在admin.go中image.png
其中topo是保存整个链路网络的全部信息和节点通信之间的调度处理,非常重要。
按照对应Agent端的程序逻辑,其中NormalPassive在agent代码分析中已经分析完了,只剩下NormalPassive类型

NormalPassive(-l)

主要代码如下:

func NormalPassive(userOptions *Options, topo *topology.Topology) net.Conn {
    ...
    //监听端口
	listener, err := net.Listen("tcp", listenAddr)
	...
	var sMessage, rMessage protocol.Message
    //admin端的默认hiMess
	hiMess := &protocol.HIMess{
		GreetingLen: uint16(len("Keep slient")), //0b
		Greeting:    "Keep slient",
		UUIDLen:     uint16(len(protocol.ADMIN_UUID)), //0a
		UUID:        protocol.ADMIN_UUID, //IAMADMINXD
		IsAdmin:     1, //int16
		IsReconnect: 0,	//int16
	}
	//Header初始化
	header := &protocol.Header{
		Sender:      protocol.ADMIN_UUID,	//IAMADMINXD
		Accepter:    protocol.TEMP_UUID,	//IAMNEWHERE
		MessageType: protocol.HI,	//0 int16
		RouteLen:    uint32(len([]byte(protocol.TEMP_ROUTE))),	//0e
		Route:       protocol.TEMP_ROUTE,	//THEREISNOROUTE
	}
	for {
        //监听
		conn, err := listener.Accept()
        //和客户端相同的密码验证
		if err := share.PassivePreAuth(conn, userOptions.Secret); err != nil {
        	...
		}
    	//选择协议
		rMessage = protocol.PrepareAndDecideWhichRProtoFromLower(conn, userOptions.Secret, protocol.ADMIN_UUID)
        //解析Cleint发送的hiMess
		fHeader, fMessage, err := protocol.DestructMessage(rMessage)
		if fHeader.MessageType == protocol.HI {
			...
				...
                //构造服务端的HiMessage并发送,默认的HeaderBuff为IAMADMINXDIAMNEWHERE00000000000eTHEREISNOROUTE0000000000000005,默认的DataBuffer为1f8b08000000000000ff62e0f64e4d2d5028cec94ccd2b61e0f274f47574f1f5f48b706160646000040000ffffcb28c8cb1d000000
				protocol.ConstructMessage(sMessage, header, hiMess, false)
				sMessage.SendMessage()
				if mmess.IsReconnect == 0 {
                    //disapatchUUID用于和agent确认uuid,admin中生成通过Message形式发送给agent,其中调用了GenerateUUID,生成了10位[0-9a-z]数据,默认Header为IAMADMINXDIAMNEWHERE00010000000eTHEREISNOROUTE0000000000000024,默认Data为1f8b0b000000000000ff62e0b230494d354a32b24c4a03040000ffff92a1c4f80c000000
					node := topology.NewNode(dispatchUUID(conn, userOptions.Secret), conn.RemoteAddr().String())
					task := &topology.TopoTask{
						Mode:       topology.ADDNODE,
						Target:     node,
						ParentUUID: protocol.TEMP_UUID,
						IsFirst:    true,
					}
                    //发送到task信道处理
					topo.TaskChan <- task

					<-topo.ResultChan

					printer.Success("[*] Connection from node %s is set up successfully! Node id is 0\r\n", conn.RemoteAddr().String())
				} else {
                    //针对设置了重连选项的节点,直接使用当前的UUID
					node := topology.NewNode(mmess.UUID, conn.RemoteAddr().String())
            	...
	}
}
  • 27
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值