@@ -2,6 +2,7 @@ package main
2
2
3
3
import (
4
4
"bytes"
5
+ "context"
5
6
"crypto/sha256"
6
7
"crypto/tls"
7
8
"crypto/x509"
@@ -14,7 +15,11 @@ import (
14
15
"github.com/sirupsen/logrus"
15
16
goproxy "golang.org/x/net/proxy"
16
17
"net"
18
+ "net/http"
19
+ "net/url"
20
+ "nhooyr.io/websocket"
17
21
"os"
22
+ "strings"
18
23
"time"
19
24
)
20
25
@@ -30,11 +35,12 @@ func main() {
30
35
var acceptFingerprint = flag .String ("accept-fingerprint" , "" , "accept certificates matching the following SHA256 fingerprint (hex format)" )
31
36
var verbose = flag .Bool ("v" , false , "enable verbose mode" )
32
37
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:[email protected] :8080)" +
39
+ " or socks://admin:[email protected] :8080" )
36
40
var serverAddr = flag .String ("connect" , "" , "connect to proxy (domain:port)" )
37
41
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" )
38
44
var versionFlag = flag .Bool ("version" , false , "show the current version" )
39
45
40
46
flag .Usage = func () {
@@ -91,11 +97,24 @@ func main() {
91
97
if * serverAddr == "" {
92
98
logrus .Fatal ("please, specify the target host user -connect host:port" )
93
99
}
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
97
116
}
98
- tlsConfig . ServerName = host
117
+
99
118
if * ignoreCertificate {
100
119
logrus .Warn ("warning, certificate validation disabled" )
101
120
tlsConfig .InsecureSkipVerify = true
@@ -105,33 +124,53 @@ func main() {
105
124
106
125
for {
107
126
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 )
113
132
} 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 )
122
140
if err != nil {
123
- return fmt . Errorf ("invalid cert fingerprint: %v \n " , err )
141
+ logrus . Fatal ("invalid socks5 address, please use host:port" )
124
142
}
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" )
127
145
}
128
- return nil
146
+ pass , _ := proxyUrl .User .Password ()
147
+ conn , err = sockDial (* serverAddr , proxyUrl .Host , proxyUrl .User .Username (), pass )
129
148
}
149
+
150
+ } else {
151
+ conn , err = net .Dial ("tcp" , * serverAddr )
130
152
}
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 )
132
169
133
- err = connect (tlsConn )
170
+ err = connect (tlsConn )
171
+ }
134
172
}
173
+
135
174
logrus .Errorf ("Connection error: %v" , err )
136
175
if * retry {
137
176
logrus .Info ("Retrying in 5 seconds." )
@@ -169,3 +208,57 @@ func connect(conn net.Conn) error {
169
208
go agent .HandleConn (conn )
170
209
}
171
210
}
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:[email protected] :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