工具介绍
项目地址:
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访问
测试流程
- Ubantu运行下面命令对3000端口进行监听处理
./linux_x64_admin -l 3000
- Win10连接Ubantu监听的3000端口
.\windows_x64_agent.exe -c 192.168.2.134:3000
- 在Ubantu上可以执行各种功能
检测特征
其整体架构和相关的页面个人感觉和Termite非常类似,其中admin和agent的入口函数分别在agent目录下的agent.go和admin目录下的admin.go
admin和agent中都会调用initial.ParseOptions方法对输入的参数做解析,确定运行的模式的一些其他功能的开启与否。
Agent逻辑分析
下面这些逻辑和分析基于未使用-s参数,即未加密情况进行,加密情况另行研究
主入口函数为agent#agent.go#19#main.php,主要方法如下:
参数解析需要注意一下,关于up和down的参数,默认只有http和raw两种类型,直接跟踪process.NewAgent方法
其中UUID参数值在protocol#protocol.go中定义
childrenMessChan用于生成ChildrenMess传输信道,其由两部分组成,Header+message,Header同样在protocol.go中定义
之后针对不同的分支做不同的处理,其实就是进入method.go的不同分支,针对admin和agent都是一样的操作。
NORMAL_ACTIVE类型(-c参数)
函数入口在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接口如下
HTTPMESSAGE中用于生成HTTP HEADER的ConstructHeader如下
重点关注一下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的判断行为,判断是哪种数据类型从而成功对应的新的请求数据:
在上面不同的选择处理分支都处理完成之后都会进入到agent.Run()代码
Admin逻辑分析
入口在admin.go中
其中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())
...
}
}
检测规则
主要检测思路,检测数据的Header部分,body涉及到加密问题,但是Header部分不涉及到加密