From 632f24354b354d23a81e03e13c0acdea6cc2df11 Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Sun, 28 Oct 2018 19:17:47 -0700 Subject: [PATCH] Tunnel rough work-in-progress; data sent (wrong length) and CSOTunClose handling needs work, but it's a start... --- hkexnet/consts.go | 11 +++++---- hkexnet/hkexnet.go | 59 +++++++++++++--------------------------------- hkexsh/hkexsh.go | 25 +++++++++++++++----- 3 files changed, 42 insertions(+), 53 deletions(-) diff --git a/hkexnet/consts.go b/hkexnet/consts.go index 34ea3a0..4248beb 100644 --- a/hkexnet/consts.go +++ b/hkexnet/consts.go @@ -33,8 +33,8 @@ type KEXAlg uint8 // Extended exit status codes - indicate comm/pty issues // rather than remote end normal UNIX exit codes const ( - CSENone = 1024 + iota - CSEBadAuth // Failed login password + CSENone = 1024 + iota + //CSEBadAuth // Failed login password CSETruncCSO // No CSOExitStatus in payload CSEStillOpen // Channel closed unexpectedly CSEExecFail // cmd.Start() (exec) failed @@ -50,8 +50,11 @@ const ( CSONone = iota // No error, normal packet CSOHmacInvalid // HMAC mismatch detected on remote end CSOTermSize // set term size (rows:cols) - CSOTunReq // client tunnel open request (dstport) - CSOTunAck // server tunnel open ack (tunport) + CSOTunReq // client tunnel open request (dstport) + CSOTunAck // server tunnel open ack (tunport) + CSOTunData // packet contains [rport:data] + CSOTunClose // request to close connection (tunnel stays open) + CSOTunRefused // tunnel has died or could not be established to rport CSOExitStatus // Remote cmd exit status CSOChaff // Dummy packet, do not pass beyond decryption ) diff --git a/hkexnet/hkexnet.go b/hkexnet/hkexnet.go index ffdd5f4..992df53 100644 --- a/hkexnet/hkexnet.go +++ b/hkexnet/hkexnet.go @@ -73,30 +73,6 @@ type ( szMax uint // max size in bytes } - // Tunnels - // -- - // 1. client is given (lport, remhost, rport) by local user - // 2. client sends [CSOTunReq:rport] to server - // client=> [CSOTunReq:rport] =>remhost - // t := TunEndpoint{dataPort: lport, peer: remhost} - // - // remhost allocates dynamic (Tport) - // t := TunEndpoint{dataPort: rport, peer: client, tunPort: Tport} - // - // remhost spawns goroutine forwarding data between (Tport,rport) - // client<= [CSOTunAck:Tport] <=remhost - // t.tunPort = Tport - // - // client spawns goroutine forwarding data between (lport,Tport) - // -- - - // TunEndpoint [securePort:peer:dataPort] - TunEndpoint struct { - TunPort uint16 - Peer string //net.Addr - DataPort uint16 - } - // Conn is a connection wrapping net.Conn with KEX & session state Conn struct { kex KEXAlg // KEX/KEM propsal (client -> server) @@ -109,8 +85,7 @@ type ( Cols uint16 chaff ChaffConfig - - tuns []TunEndpoint + tuns map[uint16]chan []byte closeStat *CSOType // close status (CSOExitStatus) r cipher.Stream //read cipherStream @@ -127,7 +102,7 @@ var ( // Return string (suitable as map key) for a tunnel endpoint func (t *TunEndpoint) String() string { - return fmt.Sprintf("[%d:%s:%d]", t.DataPort, t.Peer, t.TunPort) + return fmt.Sprintf("[%d:%s:%d]", t.Lport, t.Peer, t.Rport) } func _initLogging(d bool, c string, f logger.Priority) { @@ -261,12 +236,6 @@ func _new(kexAlg KEXAlg, conn *net.Conn) (hc *Conn, e error) { } func (hc *Conn) applyConnExtensions(extensions ...string) { - //fmt.Printf("CSENone:%d CSEBadAuth:%d CSETruncCSO:%d CSEStillOpen:%d CSEExecFail:%d CSEPtyExecFail:%d\n", - // CSENone, CSEBadAuth, CSETruncCSO, CSEStillOpen, CSEExecFail, CSEPtyExecFail) - - //fmt.Printf("CSONone:%d CSOHmacInvalid:%d CSOTermSize:%d CSOExitStatus:%d CSOChaff:%d\n", - // CSONone, CSOHmacInvalid, CSOTermSize, CSOExitStatus, CSOChaff) - for _, s := range extensions { switch s { case "C_AES_256": @@ -821,16 +790,20 @@ func (hc Conn) Read(b []byte) (n int, err error) { } hc.Close() } else if ctrlStatOp == CSOTunReq { - // This should ONLY be sent from client -> server! - // TODO: Hmm. should this package (hkexnet) take a 'server'/'client' context - // in order to know how to handle mis-uses? - addrs, _ := net.InterfaceAddrs() - t := TunEndpoint{Peer: addrs[0].String()} - t.TunPort = binary.BigEndian.Uint16(payloadBytes) - //fmt.Sscanf(string(payloadBytes), "%d", &t.tunPort) - Log.Notice(fmt.Sprintf("[TODO: Client Tunnel Open Request - traffic for server %s, port %d]\n", t.Peer, t.TunPort)) - } else if ctrlStatOp == CSOTunAck { - Log.Notice("[Server Tunnel Open Ack - TODO]\n") + // Client wants a tunnel set up - args [lport:rport] + lport := binary.BigEndian.Uint16(payloadBytes) + rport := binary.BigEndian.Uint16(payloadBytes[2:4]) + startServerTunnel(&hc, lport, rport) + } else if ctrlStatOp == CSOTunData { + lport := binary.BigEndian.Uint16(payloadBytes) + rport := binary.BigEndian.Uint16(payloadBytes[2:4]) + fmt.Printf("[Got CSOTunData: [lport %d:rport %d] data:%v\n", lport, rport, payloadBytes[4:]) + hc.tuns[rport] <- payloadBytes[4:] + } else if ctrlStatOp == CSOTunClose { + lport := binary.BigEndian.Uint16(payloadBytes) + rport := binary.BigEndian.Uint16(payloadBytes[2:4]) + fmt.Printf("[Got CSOTunClose: [lport %d:rport %d]\n", lport, rport) + hc.tuns[rport] = nil } else { hc.dBuf.Write(payloadBytes) //log.Printf("hc.dBuf: %s\n", hex.Dump(hc.dBuf.Bytes())) diff --git a/hkexsh/hkexsh.go b/hkexsh/hkexsh.go index e9d01ab..76a687b 100755 --- a/hkexsh/hkexsh.go +++ b/hkexsh/hkexsh.go @@ -337,12 +337,27 @@ func rejectUserMsg() string { return "Begone, " + spinsult.GetSentence() + "\r\n" } -func requestTunnel(c *hkexnet.Conn, dp uint16, p string /*net.Addr*/, tp uint16) (t hkexnet.TunEndpoint) { - t = hkexnet.TunEndpoint{DataPort: dp, Peer: p, TunPort: tp} +// Transmit request to server for it to set up the remote end of a tunnel +// +// Server responds with [CSOTunAck:rport] or [CSOTunRefused:rport] +func requestTunnel(hc *hkexnet.Conn, lp uint16, p string /*net.Addr*/, rp uint16) (t hkexnet.TunEndpoint) { var bTmp bytes.Buffer - binary.Write(&bTmp, binary.BigEndian, t.DataPort) - c.WritePacket(bTmp.Bytes(), hkexnet.CSOTunReq) + binary.Write(&bTmp, binary.BigEndian, lp) + binary.Write(&bTmp, binary.BigEndian, rp) + hc.WritePacket(bTmp.Bytes(), hkexnet.CSOTunReq) + // Server should reply immediately with success (lport:rport) or + // refusal (lport:0) + var lportReply, rportReply uint16 + errL := binary.Read(hc, binary.BigEndian, &lportReply) + errR := binary.Read(hc, binary.BigEndian, &rportReply) + if errL == nil && errR == nil { + fmt.Printf("Server established tunnel [%d:%d]\n", lportReply, rportReply) + hkexnet.StartClientTunnel(hc, lp, rp) + } else { + fmt.Println("FAILED reading remPort") + } + t = hkexnet.TunEndpoint{Lport: lportReply, Peer: p, Rport: rportReply} return } @@ -631,8 +646,6 @@ func main() { } if shellMode { - // TODO: tunnel setup would be here or within doShellMode() - // TESTING - tunnel remAddrs, _ := net.LookupHost(remoteHost) t := requestTunnel(&conn, 6001, remAddrs[0], 7001)