@@ -2,6 +2,7 @@ package main
22
33import (
44 "bytes"
5+ "context"
56 "crypto/sha256"
67 "crypto/tls"
78 "crypto/x509"
@@ -14,7 +15,11 @@ import (
1415 "github.com/sirupsen/logrus"
1516 goproxy "golang.org/x/net/proxy"
1617 "net"
18+ "net/http"
19+ "net/url"
20+ "nhooyr.io/websocket"
1721 "os"
22+ "strings"
1823 "time"
1924)
2025
@@ -30,11 +35,12 @@ func main() {
3035 var acceptFingerprint = flag .String ("accept-fingerprint" , "" , "accept certificates matching the following SHA256 fingerprint (hex format)" )
3136 var verbose = flag .Bool ("v" , false , "enable verbose mode" )
3237 var retry = flag .Bool ("retry" , false , "auto-retry on error" )
33- var socksProxy = flag .String ("socks" , "" , "socks5 proxy address (ip:port)" )
34- var socksUser = flag .String ("socks-user" , "" , "socks5 username" )
35- var socksPass = flag .String ("socks-pass" , "" , "socks5 password" )
38+ var socksProxy = flag .String ("proxy" , "" , "proxy URL address (http://admin:secret@127.0.0.1:8080)" +
39+ " or socks://admin:secret@127.0.0.1:8080" )
3640 var serverAddr = flag .String ("connect" , "" , "connect to proxy (domain:port)" )
3741 var bindAddr = flag .String ("bind" , "" , "bind to ip:port" )
42+ var userAgent = flag .String ("ua" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
43+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" , "HTTP User-Agent" )
3844 var versionFlag = flag .Bool ("version" , false , "show the current version" )
3945
4046 flag .Usage = func () {
@@ -91,11 +97,24 @@ func main() {
9197 if * serverAddr == "" {
9298 logrus .Fatal ("please, specify the target host user -connect host:port" )
9399 }
94- host , _ , err := net .SplitHostPort (* serverAddr )
95- if err != nil {
96- logrus .Fatal ("invalid connect address, please use host:port" )
100+
101+ if strings .Contains (* serverAddr , "https://" ) {
102+ //websocket https connection
103+ host , _ , err := net .SplitHostPort (strings .Replace (* serverAddr , "https://" , "" , 1 ))
104+ if err != nil {
105+ logrus .Info ("There is no port in address string, assuming that port is 443" )
106+ host = strings .Replace (* serverAddr , "https://" , "" , 1 )
107+ }
108+ tlsConfig .ServerName = host
109+ } else {
110+ //direct connection
111+ host , _ , err := net .SplitHostPort (* serverAddr )
112+ if err != nil {
113+ logrus .Fatal ("Invalid connect address, please use host:port" )
114+ }
115+ tlsConfig .ServerName = host
97116 }
98- tlsConfig . ServerName = host
117+
99118 if * ignoreCertificate {
100119 logrus .Warn ("warning, certificate validation disabled" )
101120 tlsConfig .InsecureSkipVerify = true
@@ -105,33 +124,53 @@ func main() {
105124
106125 for {
107126 var err error
108- if * socksProxy != "" {
109- if _ , _ , err := net . SplitHostPort ( * socksProxy ); err != nil {
110- logrus . Fatal ( "invalid socks5 address, please use host:port" )
111- }
112- conn , err = sockDial ( * serverAddr , * socksProxy , * socksUser , * socksPass )
127+ if strings . Contains ( * serverAddr , "https://" ) ||
128+ strings . Contains ( * serverAddr , "wss://" ) {
129+ * serverAddr = strings . Replace ( * serverAddr , "https://" , "wss://" , 1 )
130+ //websocket
131+ err = wsconnect ( & tlsConfig , * serverAddr , * socksProxy , * userAgent )
113132 } else {
114- conn , err = net .Dial ("tcp" , * serverAddr )
115- }
116- if err == nil {
117- if * acceptFingerprint != "" {
118- tlsConfig .InsecureSkipVerify = true
119- tlsConfig .VerifyPeerCertificate = func (rawCerts [][]byte , verifiedChains [][]* x509.Certificate ) error {
120- crtFingerprint := sha256 .Sum256 (rawCerts [0 ])
121- crtMatch , err := hex .DecodeString (* acceptFingerprint )
133+ if * socksProxy != "" {
134+ if strings .Contains (* socksProxy , "http://" ) {
135+ //TODO: http proxy CONNECT with direct ligolo protocol
136+ } else {
137+ //suppose that scheme is socks:// or socks5://
138+ var proxyUrl * url.URL
139+ proxyUrl , err = url .Parse (* socksProxy )
122140 if err != nil {
123- return fmt . Errorf ("invalid cert fingerprint: %v \n " , err )
141+ logrus . Fatal ("invalid socks5 address, please use host:port" )
124142 }
125- if bytes . Compare ( crtMatch , crtFingerprint [:]) != 0 {
126- return fmt . Errorf ( "certificate does not match fingerprint: %X != %X" , crtFingerprint , crtMatch )
143+ if _ , _ , err = net . SplitHostPort ( proxyUrl . Host ); err != nil {
144+ logrus . Fatal ( "invalid socks5 address, please use socks://host:port" )
127145 }
128- return nil
146+ pass , _ := proxyUrl .User .Password ()
147+ conn , err = sockDial (* serverAddr , proxyUrl .Host , proxyUrl .User .Username (), pass )
129148 }
149+
150+ } else {
151+ conn , err = net .Dial ("tcp" , * serverAddr )
130152 }
131- tlsConn := tls .Client (conn , & tlsConfig )
153+ if err == nil {
154+ if * acceptFingerprint != "" {
155+ tlsConfig .InsecureSkipVerify = true
156+ tlsConfig .VerifyPeerCertificate = func (rawCerts [][]byte , verifiedChains [][]* x509.Certificate ) error {
157+ crtFingerprint := sha256 .Sum256 (rawCerts [0 ])
158+ crtMatch , err := hex .DecodeString (* acceptFingerprint )
159+ if err != nil {
160+ return fmt .Errorf ("invalid cert fingerprint: %v\n " , err )
161+ }
162+ if bytes .Compare (crtMatch , crtFingerprint [:]) != 0 {
163+ return fmt .Errorf ("certificate does not match fingerprint: %X != %X" , crtFingerprint , crtMatch )
164+ }
165+ return nil
166+ }
167+ }
168+ tlsConn := tls .Client (conn , & tlsConfig )
132169
133- err = connect (tlsConn )
170+ err = connect (tlsConn )
171+ }
134172 }
173+
135174 logrus .Errorf ("Connection error: %v" , err )
136175 if * retry {
137176 logrus .Info ("Retrying in 5 seconds." )
@@ -169,3 +208,57 @@ func connect(conn net.Conn) error {
169208 go agent .HandleConn (conn )
170209 }
171210}
211+
212+ func wsconnect (config * tls.Config , wsaddr string , proxystr string , useragent string ) error {
213+
214+ //timeout for websocket library connection - 20 seconds
215+ //TODO: add timeout as cmd parameter
216+ ctx , cancel := context .WithTimeout (context .Background (), time .Second * 20 )
217+ defer cancel ()
218+
219+ //in case of websocket proxy can be http with login:pass
220+ //Ex: proxystr = "http://admin:secret@127.0.0.1:8080"
221+ proxyUrl , err := url .Parse (proxystr )
222+ if err != nil || proxystr == "" {
223+ proxyUrl = nil
224+ }
225+
226+ httpTransport := & http.Transport {}
227+ config .MinVersion = tls .VersionTLS10
228+
229+ httpTransport = & http.Transport {
230+ MaxIdleConns : http .DefaultMaxIdleConnsPerHost ,
231+ TLSClientConfig : config ,
232+ Proxy : http .ProxyURL (proxyUrl ),
233+ }
234+
235+ httpClient := & http.Client {Transport : httpTransport }
236+ httpheader := & http.Header {}
237+ httpheader .Add ("User-Agent" , useragent )
238+ //Add your additional headers here
239+ //httpheader.Add("X-Blablabla", "Blublublu")
240+ //TODO: set -H cmd param (as ffuf, wfuzz)
241+
242+ wsConn , _ , err := websocket .Dial (ctx , wsaddr , & websocket.DialOptions {HTTPClient : httpClient , HTTPHeader : * httpheader })
243+ if err != nil {
244+ return err
245+ }
246+
247+ //timeout for netconn derived from websocket connection - it must be very big
248+ netctx , cancel := context .WithTimeout (context .Background (), time .Hour * 999999 )
249+ netConn := websocket .NetConn (netctx , wsConn , websocket .MessageBinary )
250+ defer cancel ()
251+ yamuxConn , err := yamux .Server (netConn , yamux .DefaultConfig ())
252+ if err != nil {
253+ return err
254+ }
255+
256+ logrus .Info ("Websocket connection established" )
257+ for {
258+ conn , err := yamuxConn .Accept ()
259+ if err != nil {
260+ return err
261+ }
262+ go agent .HandleConn (conn )
263+ }
264+ }
0 commit comments