From 7eba7410fd3b6e77b23521dbdbb96b1ac0e8bb49 Mon Sep 17 00:00:00 2001 From: aus Date: Sat, 2 Feb 2019 22:06:16 -0600 Subject: [PATCH 1/4] add client-outbound SOCKS support --- client/client.go | 24 ++++++++++++++++++++++++ main.go | 10 +++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/client/client.go b/client/client.go index e278e99..9127092 100644 --- a/client/client.go +++ b/client/client.go @@ -4,13 +4,17 @@ import ( "context" "fmt" "io" + "io/ioutil" "net" + "log" "net/http" "net/url" + "os" "regexp" "strings" "time" + socks5 "github.com/armon/go-socks5" "github.com/gorilla/websocket" "github.com/jpillora/backoff" "github.com/jpillora/chisel/share" @@ -29,6 +33,7 @@ type Config struct { HTTPProxy string Remotes []string HostHeader string + Socks5 bool } //Client represents a client instance @@ -42,6 +47,7 @@ type Client struct { running bool runningc chan error connStats chshare.ConnStats + socksServer *socks5.Server } //NewClient creates a new client instance @@ -102,6 +108,20 @@ func NewClient(config *Config) (*Client, error) { Timeout: 30 * time.Second, } + if config.Socks5 { + socksConfig := &socks5.Config{} + if client.Debug { + socksConfig.Logger = log.New(os.Stdout, "[client socks]", log.Ldate|log.Ltime) + } else { + socksConfig.Logger = log.New(ioutil.Discard, "", 0) + } + client.socksServer, err = socks5.New(socksConfig) + if err != nil { + return nil, err + } + client.Infof("client-side SOCKS5 server enabled") + } + return client, nil } @@ -141,6 +161,10 @@ func (c *Client) Start(ctx context.Context) error { } } } + // start socks server + if c.socksServer != nil { + go c.socksServer.ListenAndServe("tcp", "127.0.0.1:1081") + } c.Infof("Connecting to %s%s\n", c.server, via) //optional keepalive loop if c.config.KeepAlive > 0 { diff --git a/main.go b/main.go index 570b508..b6c02fa 100644 --- a/main.go +++ b/main.go @@ -124,7 +124,7 @@ var serverHelp = ` chisel receives a normal HTTP request. Useful for hiding chisel in plain sight. - --socks5, Allow clients to access the internal SOCKS5 proxy. See + --socks5, Allow clients to access the server-outbound SOCKS5 proxy. See chisel client --help for more information. --reverse, Allow clients to specify reverse port forwarding remotes @@ -266,6 +266,12 @@ var clientHelp = ` --hostname, Optionally set the 'Host' header (defaults to the host found in the server url). + + --socks5, Start a client-outbound SOCKS5 server on 127.0.0.1:1081. + Combine this option with a reverse port forward remote to expose the + client network to the chisel server. For example, the following + remote R:127.0.0.1:5000:127.0.0.1:1081 would allow the sever to + access the client network via socks5://127.0.0.1:5000. ` + commonHelp func client(args []string) { @@ -280,6 +286,7 @@ func client(args []string) { proxy := flags.String("proxy", "", "") pid := flags.Bool("pid", false, "") hostname := flags.String("hostname", "", "") + socks5 := flags.Bool("socks5", false, "") verbose := flags.Bool("v", false, "") flags.Usage = func() { fmt.Print(clientHelp) @@ -304,6 +311,7 @@ func client(args []string) { Server: args[0], Remotes: args[1:], HostHeader: *hostname, + Socks5: *socks5, }) if err != nil { log.Fatal(err) From 8607fa10b41614e309e622bad5dedaaa705a47ea Mon Sep 17 00:00:00 2001 From: aus Date: Sat, 23 Feb 2019 15:09:25 -0600 Subject: [PATCH 2/4] rework to support remote R:socks syntax --- client/client.go | 30 +++++++++++++----------------- main.go | 10 +--------- server/handler.go | 16 +--------------- share/remote.go | 5 ----- share/ssh.go | 15 +++++++++++++++ 5 files changed, 30 insertions(+), 46 deletions(-) diff --git a/client/client.go b/client/client.go index 9127092..30f1211 100644 --- a/client/client.go +++ b/client/client.go @@ -4,17 +4,14 @@ import ( "context" "fmt" "io" - "io/ioutil" "net" - "log" "net/http" "net/url" - "os" "regexp" "strings" "time" - socks5 "github.com/armon/go-socks5" + "github.com/armon/go-socks5" "github.com/gorilla/websocket" "github.com/jpillora/backoff" "github.com/jpillora/chisel/share" @@ -33,7 +30,6 @@ type Config struct { HTTPProxy string Remotes []string HostHeader string - Socks5 bool } //Client represents a client instance @@ -74,11 +70,15 @@ func NewClient(config *Config) (*Client, error) { //swap to websockets scheme u.Scheme = strings.Replace(u.Scheme, "http", "ws", 1) shared := &chshare.Config{} + createSocksServer := false for _, s := range config.Remotes { r, err := chshare.DecodeRemote(s) if err != nil { return nil, fmt.Errorf("Failed to decode remote '%s': %s", s, err) } + if r.Socks && r.Reverse { + createSocksServer = true + } shared.Remotes = append(shared.Remotes, r) } config.shared = shared @@ -108,18 +108,12 @@ func NewClient(config *Config) (*Client, error) { Timeout: 30 * time.Second, } - if config.Socks5 { + if createSocksServer { socksConfig := &socks5.Config{} - if client.Debug { - socksConfig.Logger = log.New(os.Stdout, "[client socks]", log.Ldate|log.Ltime) - } else { - socksConfig.Logger = log.New(ioutil.Discard, "", 0) - } client.socksServer, err = socks5.New(socksConfig) if err != nil { return nil, err } - client.Infof("client-side SOCKS5 server enabled") } return client, nil @@ -161,10 +155,6 @@ func (c *Client) Start(ctx context.Context) error { } } } - // start socks server - if c.socksServer != nil { - go c.socksServer.ListenAndServe("tcp", "127.0.0.1:1081") - } c.Infof("Connecting to %s%s\n", c.server, via) //optional keepalive loop if c.config.KeepAlive > 0 { @@ -296,6 +286,7 @@ func (c *Client) Close() error { func (c *Client) connectStreams(chans <-chan ssh.NewChannel) { for ch := range chans { remote := string(ch.ExtraData()) + socks := remote == "socks" stream, reqs, err := ch.Accept() if err != nil { c.Debugf("Failed to accept stream: %s", err) @@ -303,6 +294,11 @@ func (c *Client) connectStreams(chans <-chan ssh.NewChannel) { } go ssh.DiscardRequests(reqs) l := c.Logger.Fork("conn#%d", c.connStats.New()) - go chshare.HandleTCPStream(l, &c.connStats, stream, remote) + if socks { + go chshare.HandleSocksStream(l, c.socksServer, &c.connStats, stream) + } else { + go chshare.HandleTCPStream(l, &c.connStats, stream, remote) + } + } } diff --git a/main.go b/main.go index b6c02fa..570b508 100644 --- a/main.go +++ b/main.go @@ -124,7 +124,7 @@ var serverHelp = ` chisel receives a normal HTTP request. Useful for hiding chisel in plain sight. - --socks5, Allow clients to access the server-outbound SOCKS5 proxy. See + --socks5, Allow clients to access the internal SOCKS5 proxy. See chisel client --help for more information. --reverse, Allow clients to specify reverse port forwarding remotes @@ -266,12 +266,6 @@ var clientHelp = ` --hostname, Optionally set the 'Host' header (defaults to the host found in the server url). - - --socks5, Start a client-outbound SOCKS5 server on 127.0.0.1:1081. - Combine this option with a reverse port forward remote to expose the - client network to the chisel server. For example, the following - remote R:127.0.0.1:5000:127.0.0.1:1081 would allow the sever to - access the client network via socks5://127.0.0.1:5000. ` + commonHelp func client(args []string) { @@ -286,7 +280,6 @@ func client(args []string) { proxy := flags.String("proxy", "", "") pid := flags.Bool("pid", false, "") hostname := flags.String("hostname", "", "") - socks5 := flags.Bool("socks5", false, "") verbose := flags.Bool("v", false, "") flags.Usage = func() { fmt.Print(clientHelp) @@ -311,7 +304,6 @@ func client(args []string) { Server: args[0], Remotes: args[1:], HostHeader: *hostname, - Socks5: *socks5, }) if err != nil { log.Fatal(err) diff --git a/server/handler.go b/server/handler.go index 290f5a6..b45af56 100644 --- a/server/handler.go +++ b/server/handler.go @@ -2,7 +2,6 @@ package chserver import ( "context" - "io" "net/http" "strings" "sync/atomic" @@ -179,22 +178,9 @@ func (s *Server) handleSSHChannels(clientLog *chshare.Logger, chans <-chan ssh.N //handle stream type connID := s.connStats.New() if socks { - go s.handleSocksStream(clientLog.Fork("socksconn#%d", connID), stream) + go chshare.HandleSocksStream(clientLog.Fork("socksconn#%d", connID), s.socksServer, &s.connStats, stream) } else { go chshare.HandleTCPStream(clientLog.Fork("conn#%d", connID), &s.connStats, stream, remote) } } } - -func (s *Server) handleSocksStream(l *chshare.Logger, src io.ReadWriteCloser) { - conn := chshare.NewRWCConn(src) - s.connStats.Open() - l.Debugf("%s Opening", s.connStats) - err := s.socksServer.ServeConn(conn) - s.connStats.Close() - if err != nil && !strings.HasSuffix(err.Error(), "EOF") { - l.Debugf("%s: Closed (error: %s)", s.connStats, err) - } else { - l.Debugf("%s: Closed", s.connStats) - } -} diff --git a/share/remote.go b/share/remote.go index 3aad30e..dce3a8e 100644 --- a/share/remote.go +++ b/share/remote.go @@ -43,11 +43,6 @@ func DecodeRemote(s string) (*Remote, error) { p := parts[i] //last part "socks"? if i == len(parts)-1 && p == "socks" { - if reverse { - // TODO allow reverse+socks by having client - // automatically start local SOCKS5 server - return nil, errors.New("'socks' incompatible with reverse port forwarding") - } r.Socks = true continue } diff --git a/share/ssh.go b/share/ssh.go index 610c59b..c31d0ce 100644 --- a/share/ssh.go +++ b/share/ssh.go @@ -12,6 +12,7 @@ import ( "net" "strings" + "github.com/armon/go-socks5" "github.com/jpillora/sizestr" "golang.org/x/crypto/ssh" ) @@ -56,3 +57,17 @@ func HandleTCPStream(l *Logger, connStats *ConnStats, src io.ReadWriteCloser, re connStats.Close() l.Debugf("%s: Close (sent %s received %s)", connStats, sizestr.ToString(s), sizestr.ToString(r)) } + +func HandleSocksStream(l *Logger, server *socks5.Server, connStats *ConnStats, src io.ReadWriteCloser) { + conn := NewRWCConn(src) + connStats.Open() + l.Debugf("%s Opening", connStats) + err := server.ServeConn(conn) + connStats.Close() + + if err != nil && !strings.HasSuffix(err.Error(), "EOF") { + l.Debugf("%s: Closed (error: %s)", connStats, err) + } else { + l.Debugf("%s: Closed", connStats) + } +} From cac0114b949a2798249c2cded17f4d5790b4a31f Mon Sep 17 00:00:00 2001 From: aus Date: Sat, 23 Feb 2019 22:52:32 -0600 Subject: [PATCH 3/4] update usage for R:socks info --- main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.go b/main.go index 570b508..990e34d 100644 --- a/main.go +++ b/main.go @@ -224,6 +224,8 @@ var clientHelp = ` socks 5000:socks R:2222:localhost:22 + R:socks + R:5000:socks When the chisel server has --socks5 enabled, remotes can specify "socks" in place of remote-host and remote-port. @@ -235,6 +237,8 @@ var clientHelp = ` be prefixed with R to denote that they are reversed. That is, the server will listen and accept connections, and they will be proxied through the client which specified the remote. + Reverse remotes specifying "R:socks" will terminate a connection + at the client's internal SOCKS5 proxy. Options: From 0791425d444e63f9f53442fd7938a263f22ac1f8 Mon Sep 17 00:00:00 2001 From: aus Date: Sun, 24 Feb 2019 17:47:24 -0600 Subject: [PATCH 4/4] add nil check for c.socksServer --- client/client.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/client.go b/client/client.go index 30f1211..34df064 100644 --- a/client/client.go +++ b/client/client.go @@ -287,6 +287,11 @@ func (c *Client) connectStreams(chans <-chan ssh.NewChannel) { for ch := range chans { remote := string(ch.ExtraData()) socks := remote == "socks" + if socks && c.socksServer == nil { + c.Debugf("Denied socks request, please enable client socks remote.") + ch.Reject(ssh.Prohibited, "SOCKS5 is not enabled on the client") + continue + } stream, reqs, err := ch.Accept() if err != nil { c.Debugf("Failed to accept stream: %s", err)