From b762ce8e0c7189223c36f673660103a4dfa5ae3d Mon Sep 17 00:00:00 2001 From: Nicolas Chatelain Date: Mon, 25 Dec 2023 22:55:10 +0100 Subject: [PATCH] WIP: project structure --- cmd/agent/main.go | 336 +---------------------- cmd/proxy/app/app.go | 24 +- cmd/proxy/main.go | 6 +- pkg/agent/handler.go | 342 ++++++++++++++++++++++++ pkg/{proxy => controller}/agent.go | 2 +- pkg/{proxy => controller}/controller.go | 2 +- 6 files changed, 361 insertions(+), 351 deletions(-) create mode 100644 pkg/agent/handler.go rename pkg/{proxy => controller}/agent.go (98%) rename pkg/{proxy => controller}/controller.go (99%) diff --git a/cmd/agent/main.go b/cmd/agent/main.go index fa63fdc..96abf57 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -1,30 +1,16 @@ package main import ( - "context" "crypto/tls" - "errors" "flag" - "fmt" "github.com/hashicorp/yamux" - "github.com/nicocha30/ligolo-ng/pkg/agent/neterror" - "github.com/nicocha30/ligolo-ng/pkg/agent/smartping" - "github.com/nicocha30/ligolo-ng/pkg/protocol" - "github.com/nicocha30/ligolo-ng/pkg/relay" + "github.com/nicocha30/ligolo-ng/pkg/agent" "github.com/sirupsen/logrus" goproxy "golang.org/x/net/proxy" "net" - "os" - "os/user" - "syscall" "time" ) -var listenerConntrack map[int32]net.Conn -var listenerMap map[int32]interface{} -var connTrackID int32 -var listenerID int32 - func main() { var tlsConfig tls.Config var ignoreCertificate = flag.Bool("ignore-cert", false, "ignore TLS certificate validation (dangerous), only for debug purposes") @@ -58,9 +44,6 @@ func main() { var conn net.Conn - listenerConntrack = make(map[int32]net.Conn) - listenerMap = make(map[int32]interface{}) - for { var err error if *socksProxy != "" { @@ -110,321 +93,6 @@ func connect(conn net.Conn, config *tls.Config) error { if err != nil { return err } - go handleConn(conn) - } -} - -// Listener is the base class implementing listener sockets for Ligolo -type Listener struct { - net.Listener -} - -// NewListener register a new listener -func NewListener(network string, addr string) (Listener, error) { - lis, err := net.Listen(network, addr) - if err != nil { - return Listener{}, err - } - return Listener{lis}, nil -} - -// ListenAndServe fill new listener connections to a channel -func (s *Listener) ListenAndServe(connTrackChan chan int32) error { - for { - conn, err := s.Accept() - if err != nil { - return err - } - connTrackID++ - connTrackChan <- connTrackID - listenerConntrack[connTrackID] = conn - } -} - -// Close request the main listener to exit -func (s *Listener) Close() error { - return s.Listener.Close() -} - -// UDPListener is the base class implementing UDP listeners for Ligolo -type UDPListener struct { - *net.UDPConn -} - -// NewUDPListener register a new UDP listener -func NewUDPListener(network string, addr string) (UDPListener, error) { - udpaddr, err := net.ResolveUDPAddr(network, addr) - if err != nil { - return UDPListener{}, nil - } - - udplis, err := net.ListenUDP(network, udpaddr) - if err != nil { - return UDPListener{}, err - } - return UDPListener{udplis}, err -} - -func handleConn(conn net.Conn) { - decoder := protocol.NewDecoder(conn) - if err := decoder.Decode(); err != nil { - panic(err) - } - - e := decoder.Envelope.Payload - switch decoder.Envelope.Type { - - case protocol.MessageConnectRequest: - connRequest := e.(protocol.ConnectRequestPacket) - encoder := protocol.NewEncoder(conn) - - logrus.Debugf("Got connect request to %s:%d", connRequest.Address, connRequest.Port) - var network string - if connRequest.Transport == protocol.TransportTCP { - network = "tcp" - } else { - network = "udp" - } - if connRequest.Net == protocol.Networkv4 { - network += "4" - } else { - network += "6" - } - - var d net.Dialer - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - targetConn, err := d.DialContext(ctx, network, fmt.Sprintf("%s:%d", connRequest.Address, connRequest.Port)) - defer cancel() - - var connectPacket protocol.ConnectResponsePacket - if err != nil { - - var serr syscall.Errno - if errors.As(err, &serr) { - // Magic trick ! If the error syscall indicate that the system responded, send back a RST packet! - if neterror.HostResponded(serr) { - connectPacket.Reset = true - } - } - - connectPacket.Established = false - } else { - connectPacket.Established = true - } - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageConnectResponse, - Payload: connectPacket, - }); err != nil { - logrus.Fatal(err) - } - if connectPacket.Established { - relay.StartRelay(targetConn, conn) - } - case protocol.MessageHostPingRequest: - pingRequest := e.(protocol.HostPingRequestPacket) - encoder := protocol.NewEncoder(conn) - - pingResponse := protocol.HostPingResponsePacket{Alive: smartping.TryResolve(pingRequest.Address)} - - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageHostPingResponse, - Payload: pingResponse, - }); err != nil { - logrus.Fatal(err) - } - case protocol.MessageInfoRequest: - var username string - encoder := protocol.NewEncoder(conn) - hostname, err := os.Hostname() - if err != nil { - hostname = "UNKNOWN" - } - - userinfo, err := user.Current() - if err != nil { - username = "Unknown" - } else { - username = userinfo.Username - } - - netifaces, err := net.Interfaces() - if err != nil { - logrus.Error("could not get network interfaces") - return - } - infoResponse := protocol.InfoReplyPacket{ - Name: fmt.Sprintf("%s@%s", username, hostname), - Interfaces: protocol.NewNetInterfaces(netifaces), - } - - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageInfoReply, - Payload: infoResponse, - }); err != nil { - logrus.Fatal(err) - } - case protocol.MessageListenerCloseRequest: - // Request to close a listener - closeRequest := e.(protocol.ListenerCloseRequestPacket) - encoder := protocol.NewEncoder(conn) - - var err error - if lis, ok := listenerMap[closeRequest.ListenerID]; ok { - if l, ok := lis.(net.Listener); ok { - l.Close() - } - if l, ok := lis.(*net.UDPConn); ok { - l.Close() - } - } else { - err = errors.New("invalid listener id") - } - - listenerResponse := protocol.ListenerCloseResponsePacket{ - Err: err != nil, - } - if err != nil { - listenerResponse.ErrString = err.Error() - } - - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerCloseResponse, - Payload: listenerResponse, - }); err != nil { - logrus.Error(err) - } - - case protocol.MessageListenerRequest: - listenRequest := e.(protocol.ListenerRequestPacket) - encoder := protocol.NewEncoder(conn) - connTrackChan := make(chan int32) - stopChan := make(chan error) - - if listenRequest.Network == "tcp" { - listener, err := NewListener(listenRequest.Network, listenRequest.Address) - if err != nil { - listenerResponse := protocol.ListenerResponsePacket{ - ListenerID: 0, - Err: true, - ErrString: err.Error(), - } - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerResponse, - Payload: listenerResponse, - }); err != nil { - logrus.Error(err) - } - return - } - listenerMap[listenerID] = listener.Listener - listenerResponse := protocol.ListenerResponsePacket{ - ListenerID: listenerID, - Err: false, - ErrString: "", - } - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerResponse, - Payload: listenerResponse, - }); err != nil { - logrus.Error(err) - } - go func() { - if err := listener.ListenAndServe(connTrackChan); err != nil { - stopChan <- err - } - }() - defer listener.Close() - - } else if listenRequest.Network == "udp" { - udplistener, err := NewUDPListener(listenRequest.Network, listenRequest.Address) - if err != nil { - listenerResponse := protocol.ListenerResponsePacket{ - ListenerID: 0, - Err: true, - ErrString: err.Error(), - } - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerResponse, - Payload: listenerResponse, - }); err != nil { - logrus.Error(err) - } - return - } - listenerMap[listenerID] = udplistener.UDPConn - listenerResponse := protocol.ListenerResponsePacket{ - ListenerID: listenerID, - Err: false, - ErrString: "", - } - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerResponse, - Payload: listenerResponse, - }); err != nil { - logrus.Error(err) - } - go relay.StartRelay(conn, udplistener) - } - - listenerID++ - if listenRequest.Network == "tcp" { - for { - var bindResponse protocol.ListenerBindReponse - select { - case err := <-stopChan: - logrus.Error(err) - bindResponse = protocol.ListenerBindReponse{ - SockID: 0, - Err: true, - ErrString: err.Error(), - } - case connTrackID := <-connTrackChan: - bindResponse = protocol.ListenerBindReponse{ - SockID: connTrackID, - Err: false, - } - } - - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerBindResponse, - Payload: bindResponse, - }); err != nil { - logrus.Error(err) - } - - if bindResponse.Err { - break - } - - } - } - case protocol.MessageListenerSockRequest: - sockRequest := e.(protocol.ListenerSockRequestPacket) - encoder := protocol.NewEncoder(conn) - - var sockResponse protocol.ListenerSockResponsePacket - if _, ok := listenerConntrack[sockRequest.SockID]; !ok { - // Handle error - sockResponse.ErrString = "invalid or unexistant SockID" - sockResponse.Err = true - } - - if err := encoder.Encode(protocol.Envelope{ - Type: protocol.MessageListenerSockResponse, - Payload: sockResponse, - }); err != nil { - logrus.Fatal(err) - } - - if sockResponse.Err { - return - } - - netConn := listenerConntrack[sockRequest.SockID] - relay.StartRelay(netConn, conn) - - case protocol.MessageClose: - os.Exit(0) - + go agent.HandleConn(conn) } } diff --git a/cmd/proxy/app/app.go b/cmd/proxy/app/app.go index 05591e3..9525904 100644 --- a/cmd/proxy/app/app.go +++ b/cmd/proxy/app/app.go @@ -6,8 +6,8 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/desertbit/grumble" "github.com/jedib0t/go-pretty/v6/table" + "github.com/nicocha30/ligolo-ng/pkg/controller" "github.com/nicocha30/ligolo-ng/pkg/protocol" - "github.com/nicocha30/ligolo-ng/pkg/proxy" "github.com/nicocha30/ligolo-ng/pkg/proxy/netstack" "github.com/nicocha30/ligolo-ng/pkg/relay" "github.com/sirupsen/logrus" @@ -19,9 +19,9 @@ import ( "time" ) -var AgentList map[int]proxy.LigoloAgent +var AgentList map[int]controller.LigoloAgent var AgentListMutex sync.Mutex -var ListenerList map[int]proxy.Listener +var ListenerList map[int]controller.Listener var ListenerListMutex sync.Mutex var ( @@ -34,7 +34,7 @@ const ( MaxConnectionHandler = 4096 ) -func RegisterAgent(agent proxy.LigoloAgent) error { +func RegisterAgent(agent controller.LigoloAgent) error { AgentListMutex.Lock() AgentList[agent.Id] = agent AgentListMutex.Unlock() @@ -43,13 +43,13 @@ func RegisterAgent(agent proxy.LigoloAgent) error { func Run(stackSettings netstack.StackSettings) { // CurrentAgent points to the selected agent in the UI (when running session) - var CurrentAgent proxy.LigoloAgent + var CurrentAgent controller.LigoloAgent // ListeningAgent points to the currently running agent (forwarding packets) - var ListeningAgent proxy.LigoloAgent + var ListeningAgent controller.LigoloAgent // AgentList contains all the connected agents - AgentList = make(map[int]proxy.LigoloAgent) + AgentList = make(map[int]controller.LigoloAgent) // ListenerList contains all listener relays - ListenerList = make(map[int]proxy.Listener) + ListenerList = make(map[int]controller.Listener) // Create a new stack, but without connPool. // The connPool will be created when using the *start* command @@ -369,7 +369,7 @@ func Run(stackSettings netstack.StackSettings) { logrus.Info("Listener created on remote agent!") // Register the listener in the UI - listener := proxy.Listener{ + listener := controller.Listener{ Agent: CurrentAgent, Network: netProto, ListenerAddr: c.Flags.String("addr"), @@ -378,10 +378,10 @@ func Run(stackSettings netstack.StackSettings) { ListenerID: response.ListenerID, } ListenerListMutex.Lock() - ListenerList[proxy.ListenerCounter] = listener + ListenerList[controller.ListenerCounter] = listener ListenerListMutex.Unlock() - currentListener := proxy.ListenerCounter - proxy.ListenerCounter++ + currentListener := controller.ListenerCounter + controller.ListenerCounter++ if netProto == "udp" { diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 68d94cc..2c4bfca 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -5,7 +5,7 @@ import ( "github.com/desertbit/grumble" "github.com/hashicorp/yamux" "github.com/nicocha30/ligolo-ng/cmd/proxy/app" - "github.com/nicocha30/ligolo-ng/pkg/proxy" + "github.com/nicocha30/ligolo-ng/pkg/controller" "github.com/nicocha30/ligolo-ng/pkg/proxy/netstack" "github.com/sirupsen/logrus" "os" @@ -42,7 +42,7 @@ func main() { MaxInflight: *maxInflight, }) - proxyController := proxy.New(proxy.ControllerConfig{ + proxyController := controller.New(controller.ControllerConfig{ EnableAutocert: *enableAutocert, EnableSelfcert: *enableSelfcert, Address: *listenInterface, @@ -66,7 +66,7 @@ func main() { panic(err) } - agent, err := proxy.NewAgent(yamuxConn) + agent, err := controller.NewAgent(yamuxConn) if err != nil { logrus.Errorf("could not register agent, error: %v", err) continue diff --git a/pkg/agent/handler.go b/pkg/agent/handler.go new file mode 100644 index 0000000..db1fd0c --- /dev/null +++ b/pkg/agent/handler.go @@ -0,0 +1,342 @@ +package agent + +import ( + "context" + "errors" + "fmt" + "github.com/nicocha30/ligolo-ng/pkg/agent/neterror" + "github.com/nicocha30/ligolo-ng/pkg/agent/smartping" + "github.com/nicocha30/ligolo-ng/pkg/protocol" + "github.com/nicocha30/ligolo-ng/pkg/relay" + "github.com/sirupsen/logrus" + "net" + "os" + "os/user" + "syscall" + "time" +) + +var listenerConntrack map[int32]net.Conn +var listenerMap map[int32]interface{} +var connTrackID int32 +var listenerID int32 + +func init() { + listenerConntrack = make(map[int32]net.Conn) + listenerMap = make(map[int32]interface{}) +} + +// Listener is the base class implementing listener sockets for Ligolo +type Listener struct { + net.Listener +} + +// NewListener register a new listener +func NewListener(network string, addr string) (Listener, error) { + lis, err := net.Listen(network, addr) + if err != nil { + return Listener{}, err + } + return Listener{lis}, nil +} + +// ListenAndServe fill new listener connections to a channel +func (s *Listener) ListenAndServe(connTrackChan chan int32) error { + for { + conn, err := s.Accept() + if err != nil { + return err + } + connTrackID++ + connTrackChan <- connTrackID + listenerConntrack[connTrackID] = conn + } +} + +// Close request the main listener to exit +func (s *Listener) Close() error { + return s.Listener.Close() +} + +// UDPListener is the base class implementing UDP listeners for Ligolo +type UDPListener struct { + *net.UDPConn +} + +// NewUDPListener register a new UDP listener +func NewUDPListener(network string, addr string) (UDPListener, error) { + udpaddr, err := net.ResolveUDPAddr(network, addr) + if err != nil { + return UDPListener{}, nil + } + + udplis, err := net.ListenUDP(network, udpaddr) + if err != nil { + return UDPListener{}, err + } + return UDPListener{udplis}, err +} + +func HandleConn(conn net.Conn) { + decoder := protocol.NewDecoder(conn) + if err := decoder.Decode(); err != nil { + panic(err) + } + + e := decoder.Envelope.Payload + switch decoder.Envelope.Type { + + case protocol.MessageConnectRequest: + connRequest := e.(protocol.ConnectRequestPacket) + encoder := protocol.NewEncoder(conn) + + logrus.Debugf("Got connect request to %s:%d", connRequest.Address, connRequest.Port) + var network string + if connRequest.Transport == protocol.TransportTCP { + network = "tcp" + } else { + network = "udp" + } + if connRequest.Net == protocol.Networkv4 { + network += "4" + } else { + network += "6" + } + + var d net.Dialer + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + targetConn, err := d.DialContext(ctx, network, fmt.Sprintf("%s:%d", connRequest.Address, connRequest.Port)) + defer cancel() + + var connectPacket protocol.ConnectResponsePacket + if err != nil { + + var serr syscall.Errno + if errors.As(err, &serr) { + // Magic trick ! If the error syscall indicate that the system responded, send back a RST packet! + if neterror.HostResponded(serr) { + connectPacket.Reset = true + } + } + + connectPacket.Established = false + } else { + connectPacket.Established = true + } + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageConnectResponse, + Payload: connectPacket, + }); err != nil { + logrus.Fatal(err) + } + if connectPacket.Established { + relay.StartRelay(targetConn, conn) + } + case protocol.MessageHostPingRequest: + pingRequest := e.(protocol.HostPingRequestPacket) + encoder := protocol.NewEncoder(conn) + + pingResponse := protocol.HostPingResponsePacket{Alive: smartping.TryResolve(pingRequest.Address)} + + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageHostPingResponse, + Payload: pingResponse, + }); err != nil { + logrus.Fatal(err) + } + case protocol.MessageInfoRequest: + var username string + encoder := protocol.NewEncoder(conn) + hostname, err := os.Hostname() + if err != nil { + hostname = "UNKNOWN" + } + + userinfo, err := user.Current() + if err != nil { + username = "Unknown" + } else { + username = userinfo.Username + } + + netifaces, err := net.Interfaces() + if err != nil { + logrus.Error("could not get network interfaces") + return + } + infoResponse := protocol.InfoReplyPacket{ + Name: fmt.Sprintf("%s@%s", username, hostname), + Interfaces: protocol.NewNetInterfaces(netifaces), + } + + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageInfoReply, + Payload: infoResponse, + }); err != nil { + logrus.Fatal(err) + } + case protocol.MessageListenerCloseRequest: + // Request to close a listener + closeRequest := e.(protocol.ListenerCloseRequestPacket) + encoder := protocol.NewEncoder(conn) + + var err error + if lis, ok := listenerMap[closeRequest.ListenerID]; ok { + if l, ok := lis.(net.Listener); ok { + l.Close() + } + if l, ok := lis.(*net.UDPConn); ok { + l.Close() + } + } else { + err = errors.New("invalid listener id") + } + + listenerResponse := protocol.ListenerCloseResponsePacket{ + Err: err != nil, + } + if err != nil { + listenerResponse.ErrString = err.Error() + } + + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerCloseResponse, + Payload: listenerResponse, + }); err != nil { + logrus.Error(err) + } + + case protocol.MessageListenerRequest: + listenRequest := e.(protocol.ListenerRequestPacket) + encoder := protocol.NewEncoder(conn) + connTrackChan := make(chan int32) + stopChan := make(chan error) + + if listenRequest.Network == "tcp" { + listener, err := NewListener(listenRequest.Network, listenRequest.Address) + if err != nil { + listenerResponse := protocol.ListenerResponsePacket{ + ListenerID: 0, + Err: true, + ErrString: err.Error(), + } + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerResponse, + Payload: listenerResponse, + }); err != nil { + logrus.Error(err) + } + return + } + listenerMap[listenerID] = listener.Listener + listenerResponse := protocol.ListenerResponsePacket{ + ListenerID: listenerID, + Err: false, + ErrString: "", + } + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerResponse, + Payload: listenerResponse, + }); err != nil { + logrus.Error(err) + } + go func() { + if err := listener.ListenAndServe(connTrackChan); err != nil { + stopChan <- err + } + }() + defer listener.Close() + + } else if listenRequest.Network == "udp" { + udplistener, err := NewUDPListener(listenRequest.Network, listenRequest.Address) + if err != nil { + listenerResponse := protocol.ListenerResponsePacket{ + ListenerID: 0, + Err: true, + ErrString: err.Error(), + } + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerResponse, + Payload: listenerResponse, + }); err != nil { + logrus.Error(err) + } + return + } + listenerMap[listenerID] = udplistener.UDPConn + listenerResponse := protocol.ListenerResponsePacket{ + ListenerID: listenerID, + Err: false, + ErrString: "", + } + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerResponse, + Payload: listenerResponse, + }); err != nil { + logrus.Error(err) + } + go relay.StartRelay(conn, udplistener) + } + + listenerID++ + if listenRequest.Network == "tcp" { + for { + var bindResponse protocol.ListenerBindReponse + select { + case err := <-stopChan: + logrus.Error(err) + bindResponse = protocol.ListenerBindReponse{ + SockID: 0, + Err: true, + ErrString: err.Error(), + } + case connTrackID := <-connTrackChan: + bindResponse = protocol.ListenerBindReponse{ + SockID: connTrackID, + Err: false, + } + } + + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerBindResponse, + Payload: bindResponse, + }); err != nil { + logrus.Error(err) + } + + if bindResponse.Err { + break + } + + } + } + case protocol.MessageListenerSockRequest: + sockRequest := e.(protocol.ListenerSockRequestPacket) + encoder := protocol.NewEncoder(conn) + + var sockResponse protocol.ListenerSockResponsePacket + if _, ok := listenerConntrack[sockRequest.SockID]; !ok { + // Handle error + sockResponse.ErrString = "invalid or unexistant SockID" + sockResponse.Err = true + } + + if err := encoder.Encode(protocol.Envelope{ + Type: protocol.MessageListenerSockResponse, + Payload: sockResponse, + }); err != nil { + logrus.Fatal(err) + } + + if sockResponse.Err { + return + } + + netConn := listenerConntrack[sockRequest.SockID] + relay.StartRelay(netConn, conn) + + case protocol.MessageClose: + os.Exit(0) + + } +} diff --git a/pkg/proxy/agent.go b/pkg/controller/agent.go similarity index 98% rename from pkg/proxy/agent.go rename to pkg/controller/agent.go index 7b67780..f735dd6 100644 --- a/pkg/proxy/agent.go +++ b/pkg/controller/agent.go @@ -1,4 +1,4 @@ -package proxy +package controller import ( "fmt" diff --git a/pkg/proxy/controller.go b/pkg/controller/controller.go similarity index 99% rename from pkg/proxy/controller.go rename to pkg/controller/controller.go index aecd06e..0149316 100644 --- a/pkg/proxy/controller.go +++ b/pkg/controller/controller.go @@ -1,4 +1,4 @@ -package proxy +package controller import ( "crypto/ecdsa"