stowaway改造计划1

    工具 lz520520 2年前 (2022-03-20) 1018次浏览

    前言

    前段时间一直有个想法,做个内网多级代理的工具,更方便突破网络限制,然后就开始物色各种代理工具,如frp、nps等等,frp的稳定性很出色,他的代码结构和实现细节也很值得借鉴,但缺点也很明显,他并不是为了渗透而生的,所以在功能上有许多不太符合之处,比如不支持正向代理,代理转发等配置不支持热启动,不支持级联等等。然后有朋友推荐了stowaway,看介绍是venom的改进版,我测试了下功能,确实蛮符合渗透要求的,多级代理、上传下载文件、热启动正反向端口转发及socks代理,很灵活,当然要投入实战的话,还有不少需要改进的地方,所以就有了后面的改造计划。

    代码分析

    在做改造之前,先简单分析下他的代码,方便后续的改造。

    项目地址:https://github.com/ph4ntonn/Stowaway

    工具分为agent和admin,admin是一个console交互式程序,用于管理agent。

    agent比frp小很多,也是一个好的点。具体功能可以参考项目readme,写的蛮详细的。

    代码目录如下,admin和agent分为单独的目录实现功能

    admin

    目录

    stowaway改造计划1

    • cli: console交互函数
    • handler: 消息处理函数,包括发送消息和接收消息处理(可看作任务)
    • initial: 参数解析以及连接初始化
    • manager:管理handler包的处理函数,用于协程间通信以及任务分发
    • printer: log打印
    • process: 主控程序,启动各个消息处理函数以及console函数,接收下游节点信息。

    连接分成两个阶段,初始化和监听阶段,都在main.go

    初始化阶段:

    根据当前模式,是主动连接还是被动监听,发起密钥交互(我后面多加了一个websocket头部交互和tls封装),然后返回conn。

    初始化函数放在initial包里。

    stowaway改造计划1

    initial包里有参数解析和认证。

    stowaway改造计划1

    监听阶段:

    然后是最下面的admin.Run(),启动各种处理函数

    stowaway改造计划1

    admin/process/process.go

    stowaway改造计划1

    其中go admin.handleMessFromDownstream(console)主要用于下游agent消息接收,然后把接收信息通过channel传递给各个以Dispatch开头的消息处理函数。

    这些消息处理函数主要发送消息给下游。

    console.Run()也会用于消息发送,是一个交互式shell用于操作。

    所以看到这个结构。

    admin.handleMessFromDownstream用于下游消息接收

    Dispatch消息处理函数和console.Run()用于发送消息给下游,处理函数统一放在handler包里。

    所以websocket的心跳包也在这里设计,添加一个DispatchKeepMess处理函数,用于定期发送数据给下游,保持会话。

    而Dispatch处理函数又由manager包进行管理,通过该包进行协程间通信以及任务处理

    agent

    目录结构

    stowaway改造计划1

    其实和admin的目录结构差不多

    • handler: 消息处理函数,在节点间发送信息
    • initial: 参数解析以及连接初始化
    • manager:管理handler包的处理函数,用于协程间通信以及任务分发
    • process: 主控程序,运行各个消息处理函数以及接收节点信息。

    和admin逻辑差不多。

    stowaway改造计划1

    启动管理端以及各个消息处理函数,最后运行handleDataFromUpstream处理上游数据

    stowaway改造计划1

    短连接接收数据错误

    在增加功能前,我在测试功能时发现个问题,socks、正向端口转发、反向端口转发,在遇到http、redis爆破等各种短连接时,会出现用户端数据接收不完整的问题,比如web访问有的页面文件加载不出来,爆破无效果等,而如果是rdp等长连接却没有这个问题。

    这个问题很影响代理,所以必须优先解决。

    测试环境PC(192.168.111.112)、admin(192.168.111.1)、agent(192.168.100.18)、web(192.168.100.1)

    socks代理,F12调试的时候发现无法加载的文件,提示都是ERR_CONTENT_LENGTH_MISMATCH,啥意思,就是响应包里的长度和body不一致。

    stowaway改造计划1

    点开一个查看,这里响应头是完整的,还有body长度

    stowaway改造计划1

    但body部分确实空的,这是怎么回事

    stowaway改造计划1

    抓包查看本地和socks之间的请求,响应确实只有header

    stowaway改造计划1

    客户端请求正常发送,但最后确实由服务端主动发送FIN请求,从而断开连接

    stowaway改造计划1

    然后看了其他正常接收的数据包,同样也是服务端发送的FIN请求,所以一个资源加载时灵时不灵,可能就和这个有关了。

    我们来看下不过socks代理,正常的请求包里,FIN是由客户端主动发起,从而断开连接,所以问题很可能就在于因为连接是服务端主动断开的,而不是客户端控制的,导致数据未接收完整。

    stowaway改造计划1

    接着我测试了frp,frp是稳定正常的,并且和不挂socks一样的过程,FIN是由客户端发起断开的,那么这里其实很明显了,stowaway的连接机制有问题。

    stowaway改造计划1

    那么具体问题,我们跟踪下代码看看

    admin/handler/socks.go#handleSocks

    conn是和客户端的链接

    读取客户端数据,这里conn.Close()是后来注释掉的

    stowaway改造计划1

    写入数据发送给客户端,这里调试的时候发现,agent完整传回数据给admin了,但这里写入居然报错,发现再写入最后数据时,conn已关闭。

    stowaway改造计划1

    然后找admin上关闭conn的位置,handlesocks里有几处close的地方,我注释掉了,但仍然还是有问题,就进一步跟踪conn。

    在启用handleSocks前,conn存储在SocksTask结构体里,并传输给mgr.SocksManager.TaskChan

    stowaway改造计划1

    最后定位到admin/manager/socks.go#closeTCP

    这里会关闭conn,closeTCP由谁调用呢

    stowaway改造计划1

    run()里,如果接收到agent发送的SocksTCPFin信号,那么就会强制关闭conn,那么后续就无法写入数据给客户端了。

    stowaway改造计划1

    stowaway改造计划1

    梳理下通信过程,大致如下,

    PC----socks---->admin----tcp---->agent----http---->web

    agent(192.168.100.18)和web(192.168.100.1)之间,可以看到是正常的客户端发起FIN,但因为这里是最早结束请求断开连接的,那么agent会发送TCP FIN信号给admin,让admin也断开连接,这时admin接收到的web数据可能还没来得及返回给PC,就因为TCP FIN信号断开和PC的链接,导致数据接收不完整。

    stowaway改造计划1

    所以我将这段代码注释掉,由PC主动和admin断开连接,而不是agent通知。

    stowaway改造计划1

    而handleSocks里的conn.Close改成defer,编译测试,一切都正常了。

    stowaway改造计划1

    然后抓了PC到admin之间的socks流量,可以看到这里TCP FIN就正常了,由PC 192.168.111.112主动发起,而不像原来是由admin发起的。

    stowaway改造计划1

    总结下来就是,agent和目标先一步交互完数据并断开连接,这时目标返回的数据会通过agent发送给admin,同时agent还会发送一个TCP FIN信号给admin,此时就可能出现admin先一步处理了TCP FIN信号,断开了和PC之间的链接,导致数据无法返回给PC。

    那么forward和backward应该也有一样的。

    admin/handler/forward.go#DispatchForwardMess

    stowaway改造计划1

    stowaway改造计划1

    修改后

    stowaway改造计划1

    backward是从agent端主动发起的请求,所以这里应该改的是agent端。

    PS: 从上面的可以看出TCP FIN是双向都会发送的,调整都是根据请求方向,在请求侧做优化。

    agent/handler/backward.go#DispatchBackwardMess

    stowaway改造计划1

    这里也是一样的现象,只不过是变成agent发起而已。

    stowaway改造计划1

    修改后

    stowaway改造计划1

    短连接接收数据错误-续

    我将该问题、解决方案和作者沟通了下,作者给出了另一种方案,我觉得更合适点。

    上面的操作是通过注释发送端的FIN信号,让请求者自己断开,但直接注释会导致不调用closeTCP,这样里面的channel不会关闭,导致无法释放

    stowaway改造计划1

    所以还是需要FIN信号,但如上在closeTCP里不调用conn.Close()

    closeSocks里也注释掉

    stowaway改造计划1

    那么再哪里关闭呢,既然是因为接收后写给客户端不完整导致的,那么在如下位置关闭即可。

    tcpDataChan在上面关闭了,但由于是一个非阻塞channel,那么如果还有数据会继续接收,直到为空后才会为false,接着就关闭conn了。这个思路更巧妙一点。

    stowaway改造计划1

    其他模块如上修改即可。

    流量全加密

    bug修复后,就可以开始改造了,作者在readme里说到该工具数据传输是通过AES加密的,所以就抓包看了下流量。

    实际上该工具的流量只加密了payload部分,而header部分是明文的,比如THREREISNOROUTE等等

    stowaway改造计划1

    这个问题其实好解决,只需要在原来的Conn外封装一层tls即可,这样其实payload都无需加密了。

    这个参考frp修改即可,frp本身也有一个tls_enable的选项,他就是这个思路。

    找到node之间连接的代码,搜索net.Dail或Accept(),如下在原来建立成功的conn对象后面,判断是否启动tls,然后调用WrapTLSClientConn封装即可。(为啥还要搞个选项,为了调试方式,不然全是密文,流量侧不好调试)

    这里tlsConfig暂时没传递证书,可以改成自定义证书,防止tls指纹,最后一个options.Connect是sni。

    stowaway改造计划1

    WrapTLSClientConn内部只是调用官方库的tls.Client来封装原来的Conn对象

    上面代码只是针对client主动发起连接,如果是listen的方式,代码有些许不同,在Accept()监听到连接后,需调用tls.Server来封装。

    stowaway改造计划1

    测试代码

    有多处需要封装的,如下是所有位置

    stowaway改造计划1

    修改后编译测试一波,可以看到与上面相比,流量全加密了

    stowaway改造计划1

    stowaway改造计划1

    当然调用了tls加密,付出的代价就是文件比原来大了1兆多。这个是没法避免的,因为不止是为了加密,后面做过cdn也是需要tls,所以这个步骤是必须的。

    stowaway改造计划1

    admin如何置于后台

    这里做个小tips,因为基本admin会被放在vps上,而vps大多是选择linux,所以就涉及一个问题,admin是console交互式,ssh连接退出就会影响admin运行,所以需要用到screen。

    PS: screen不会直接把程序放到后台,而是先进入交互,手动置于后台。

    1. 输入screen,会直接进入一个新的bash交互

    2. 执行admin,进入admin交互界面

      或者跳过第一步,直接再screen后台跟命令也行

    3. 切换到后台

    4. 查看screen托管的隐藏进程

    5. 从screen中切换到某进程的前台

      screen 进程树

    stowaway改造计划1

    编码处理

    stowaway还有一个功能,shell执行系统命令,但就如上面的图显示存在乱码,这是因为go里面,默认是utf8,而windows是gbk。

    方案一:

    在admin上修改

    shell在此处转换编码即可,或mgr.ShellManager.ShellMessChan发送处

    stowaway改造计划1

    在这个只是处理了在admin上显示的问题,如果admin输入带中文,agent上把UTF-8当成GBK执行就会乱码,无法操作中文路径等等。

    方案二:

    在执行的agent上修改,这样就能控制输入转换成gbk,而发送给admin的从GBK转换成UTF-8,admin上显示既不会乱码,agent执行的时候也能正常解析中文路径。

    agent模块

    parser.go增加字符集参数,除了自动识别,也可以手动指定。

    stowaway改造计划1

    如果没通过参数指定或者输入是错误字符集,则根据OS自动获取。

    stowaway改造计划1

    agent/process/process.go

    然后在分发函数这,将选项传入处理函数,这里其实就参考第一个处理函数才决定使用options操作,所以可以在做一些改动前,看看之前是怎么实现的,这样保证代码设计一致性。

    stowaway改造计划1

    agent/handler/shell.go

    admin传入命令转换成设定编码

    stowaway改造计划1

    执行结果发送给admin前,将指定编码转换成UTF-8

    注意count即接收字节大小也需要改动,否则会出现丢字符串的情况。

    stowaway改造计划1

    效果如下

    stowaway改造计划1

    这个操作其实没那么重要处理,因为命令执行在代理工具里不应该有,会增加特征导致被杀,命令执行就交给更专业的C2来实现。

    这里只是实验性质的,用于后续其他处理函数需要做编码转换来做准备。

    PS: 编码转换包后面换成了官方提供的golang.org/x/text/encoding/simplifiedchinese,这个打包出来会比gcharset小很多。

    数据压缩

    这个其实是想到frp也有这么一个功能,并且压缩数据对于传输来说很有意义,提高传输速度,尤其是一些大文件的传输。

    这个修改其实很简单。

    因为原来不是有一个数据加密吗,用AES对data进行加解密,而有了tls加密,这里的aes就无关紧要了,那么我们只需要替换这个加解密的位置,把数据从加解密变成解压缩就成了。

    定位到加密位置

    protocol/raw.go#ConstructData

    替换成gzip压缩

    stowaway改造计划1

    解密位置

    protocol/raw.go#DeconstructData

    替换成gzip解压

    stowaway改造计划1

    至于gzip的实现,很简单,调用内置库gzip即可。

    stowaway改造计划1

    然后测试下压缩率

    ipconfig /all: 6410->1136 17.7%

    stowaway改造计划1

    dir c:\windows\system32: 252599->51928 20.6%

    stowaway改造计划1

    fscan.exe(16M): 16539136->5855251 35.4%

    stowaway改造计划1

    测试压缩率还不错,总比没压缩的强上许多。

    PS: 这个压缩是不包含header字段的,当然这个字段撑死也就是几十字节,1K都不到,不影响的。

    总结

    stowaway作为一个专门为渗透设计的代理工具,有很多方便的功能,本次改造通过代码分析、短连接bug修复、流量全加密、数据压缩等各方面进行讲解,也进一步熟悉了这款工具的实现逻辑,也为后续重构打下基础。

    后续还会增加CDN穿透、多startnode功能、内联命令等等。

    说个题外话,这次挑选了一款代理工具做改造,其实市面上有各种各样的代理工具,要寻找到一款趁手的工具,其实不太容易,所以就很需要自己做一些二开改造,有人可能觉得重复造轮子浪费时间,这个嘛,主要看工作需要,看你的方向,如果仅仅需要一个能用就行的代理工具,那其实有很多,而需要一个好用的工具,那么就需要根据自身需求做一些开发,一方面对工具的实现更透彻,另一方面不也是为了提升工作效率嘛,没有最好的,只要更适合自己的,这不仅仅局限于代理工具。在渗透整个攻击链上,其实各个环节,总有一个环节需要你去打造自己的工具的,关注漏洞利用,你自己编写一个脚本或者利用工具,会对漏洞成因更理解,并且能更深入优化漏洞的利用链,否则拿一个现成脚本跑一下,可能明明有漏洞而你却利用不成功,就像shiro被大家玩出花来了,而weblogic流传的一些工具其实也是有蛮多问题,诸如此类的太多了;又或者专门做漏洞挖掘,那你肯定会需要打造一个属于自己的审计工具,市面上的,不管是商用的还是开源的,总有不如人意的地方,总会需要编写自己的"codeql脚本"(这里具象举例而已),合入自己经验上的sink、source。一个合格的安全研究者应该也要是一个合格的开发。


    Security , 版权所有丨如未注明 , 均为原创丨
    转载请注明原文链接:stowaway改造计划1
    喜欢 (2)