├── .circleci └── config.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── go.mod ├── go.sum ├── main.go ├── rclient.go ├── rdns.go ├── rserver.go ├── tlshelp.go ├── tools └── genrelease.sh └── version.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | # specify the version 9 | - image: cimg/go:1.19 10 | 11 | # Specify service dependencies here if necessary 12 | # CircleCI maintains a library of pre-built images 13 | # documented at https://circleci.com/docs/2.0/circleci-images/ 14 | # - image: circleci/postgres:9.4 15 | 16 | #### TEMPLATE_NOTE: go expects specific checkout path representing url 17 | #### expecting it in the form of 18 | #### /go/src/github.com/circleci/go-tool 19 | #### /go/src/bitbucket.org/circleci/go-tool 20 | # working_directory: /go/src/github.com/{{ORG_NAME}}/{{REPO_NAME}} 21 | steps: 22 | - checkout 23 | 24 | # specify any bash command here prefixed with `run: ` 25 | - run: make tools 26 | - run: make 27 | - run: go test -v -race -covermode atomic -timeout 30s ./... 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4.x 5 | - 1.5.x 6 | - 1.6.x 7 | - 1.7.x 8 | - 1.8.x 9 | - 1.9.x 10 | - tip 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2023 Vlatko Kosturjak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=2.8 2 | GIT_COMMIT = `git rev-parse HEAD | cut -c1-7` 3 | BUILD_OPTIONS = -ldflags "-X main.Version=$(VERSION) -X main.CommitID=$(GIT_COMMIT)" 4 | STATIC_OPTIONS = -ldflags "-extldflags='-static' -X main.Version=$(VERSION) -X main.CommitID=$(GIT_COMMIT)" 5 | 6 | revsocks: dep 7 | go build ${BUILD_OPTIONS} 8 | 9 | dep: 10 | #go get -u ./... 11 | go get 12 | 13 | tools: 14 | go install github.com/mitchellh/gox@latest 15 | go install github.com/tcnksm/ghr@latest 16 | 17 | ver: 18 | echo version $(VERSION) 19 | 20 | gittag: 21 | git tag v$(VERSION) 22 | git push --tags origin master 23 | 24 | clean: 25 | rm -rf dist 26 | 27 | dist: 28 | mkdir -p dist 29 | 30 | gox: 31 | CGO_ENABLED=0 gox -osarch="!darwin/386" -ldflags="-s -w -X main.Version=$(VERSION) -X main.CommitID=$(GIT_COMMIT)" -output="dist/{{.Dir}}_{{.OS}}_{{.Arch}}" 32 | 33 | goxwin: 34 | CGO_ENABLED=0 gox -osarch="windows/amd64 windows/386" -ldflags="-s -w -X main.Version=$(VERSION) -X main.CommitID=$(GIT_COMMIT)" -output="dist/{{.Dir}}_{{.OS}}_{{.Arch}}" 35 | 36 | dokbuild: 37 | docker run -it --rm -v $(PWD):/app golang:alpine /bin/sh -c 'apk add make file git && git config --global --add safe.directory /app && cd /app && make -B tools && make gox && make goxwin' 38 | 39 | draft: 40 | ghr -draft v$(VERSION) dist/ 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/kost/revsocks.svg?style=svg)](https://circleci.com/gh/kost/revsocks) 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3c687bcd445e4a828c914e4e2384196e)](https://www.codacy.com/manual/kost/revsocks?utm_source=github.com&utm_medium=referral&utm_content=kost/revsocks&utm_campaign=Badge_Grade) 3 | 4 | # revsocks 5 | 6 | Reverse socks5 tunneler with SSL/TLS and proxy support (without proxy authentication and with basic/NTLM proxy authentication) 7 | Based on and 8 | 9 | # Features 10 | 11 | - Single executable (thanks to Go!) 12 | - Linux/Windows/Mac/BSD support 13 | - Encrypted communication with TLS 14 | - DNS tunneling support (SOCKS5 over DNS) 15 | - Support for proxies (without authentication or with basic/NTLM proxy authentication) 16 | - Automatic SSL/TLS certificate generation if not specified 17 | 18 | # Architecture 19 | 20 | - server = locally listening socks server 21 | - client = client which connects back to server 22 | 23 | ## Usage 24 | 25 | ### reverse TCP 26 | 27 | Usage: 28 | 1) Start on VPS: revsocks -listen :8443 -socks 127.0.0.1:1080 -pass SuperSecretPassword 29 | 2) Start on client: revsocks -connect clientIP:8443 -pass SuperSecretPassword 30 | 3) Connect to 127.0.0.1:1080 on the VPS with any socks5 client. 31 | 4) Enjoy. :] 32 | 33 | ### reverse TCP with TLS encryption 34 | 35 | Usage: 36 | 1) Start on VPS: revsocks -listen :8443 -socks 127.0.0.1:1080 -pass SuperSecretPassword -tls 37 | 2) Start on client: revsocks -connect clientIP:8443 -pass SuperSecretPassword -tls 38 | 3) Connect to 127.0.0.1:1080 on the VPS with any socks5 client. 39 | 4) Enjoy. :] 40 | 41 | ### reverse websocket with TLS encryption 42 | 43 | Usage: 44 | 1) Start on VPS: `revsocks -listen :8443 -socks 127.0.0.1:1080 -pass SuperSecretPassword -tls -ws` 45 | 2) Start on client: `revsocks -connect https://clientIP:8443 -pass SuperSecretPassword -ws` 46 | 3) Connect to 127.0.0.1:1080 on the VPS with any socks5 client. 47 | 48 | ### DNS tunnel 49 | 50 | ```sh 51 | 0) setup your domain records 52 | 1) Start on the DNS server: revsocks -dns example.com -dnslisten :53 -socks 127.0.0.1:1080 -pass 52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c64 53 | 2) Start on the target: revsocks -dns example.com -pass 52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c64 54 | 3) Connect to 127.0.0.1:1080 on the DNS server with any socks5 client. 55 | ``` 56 | 57 | ## Useful parameters 58 | 59 | Add params: 60 | -proxy 1.2.3.4:3128 - connect via proxy 61 | -proxyauth Domain/username:password - proxy creds 62 | -proxytimeout 2000 - server and clients will wait for 2000 msec for proxy connections... (Sometime it should be up to 4000...) 63 | -useragent "Internet Explorer 9.99" - User-Agent used in proxy connection (sometimes it is usefull) 64 | -pass Password12345 - challenge password between client and server (if not match - server reply 301 redirect) 65 | -recn - reconnect times number. Default is 3. If 0 - infinite reconnection 66 | -rect - time delay in secs between reconnection attempts. Default is 30 67 | 68 | ## Options 69 | 70 | Complete list of command line options 71 | 72 | ``` 73 | -agent string 74 | User agent to use (default "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko") 75 | -cert string 76 | certificate file 77 | -connect string 78 | connect address:port (or https://address:port for ws) 79 | -debug 80 | display debug info 81 | -dns string 82 | DNS domain to use for DNS tunneling 83 | -dnsdelay string 84 | Delay/sleep time between requests (200ms by default) 85 | -dnslisten string 86 | Where should DNS server listen 87 | -listen string 88 | listen port for receiver address:port 89 | -pass string 90 | Connect password 91 | -proxy string 92 | use proxy address:port for connecting (or http://address:port for ws) 93 | -proxyauth string 94 | proxy auth Domain/user:Password 95 | -proxytimeout string 96 | proxy response timeout (ms) 97 | -q Be quiet 98 | -recn int 99 | reconnection limit (default 3) 100 | -rect int 101 | reconnection delay (default 30) 102 | -socks string 103 | socks address:port (default "127.0.0.1:1080") 104 | -tls 105 | use TLS for connection 106 | -verify 107 | verify TLS connection 108 | -version 109 | version information 110 | -ws 111 | use websocket for connection 112 | ``` 113 | 114 | # Requirements 115 | 116 | - Go 1.4 or higher 117 | - Few external Go modules (yamux, go-socks5 and go-ntlmssp) 118 | 119 | # Compile and Installation 120 | 121 | Linux VPS 122 | 123 | - install Golang: apt install golang make 124 | 125 | ```sh 126 | make 127 | ``` 128 | 129 | launch: 130 | 131 | ```sh 132 | ./revsocks -listen :8443 -socks 127.0.0.1:1080 -pass Password1234 133 | ``` 134 | 135 | Windows client: 136 | 137 | - download and install golang 138 | 139 | ```sh 140 | go get 141 | go build 142 | ``` 143 | 144 | ## Windows optional 145 | 146 | optional: to build as Windows GUI: 147 | 148 | ```sh 149 | go build -ldflags -H=windowsgui 150 | ``` 151 | 152 | You can also compress exe - just use any exe packer, ex: UPX 153 | 154 | ```sh 155 | upx revsocks 156 | ``` 157 | 158 | ## Usage examples 159 | 160 | ```sh 161 | revsocks -connect clientIP:8443 -pass Password1234 162 | ``` 163 | 164 | or with proxy and user agent: 165 | 166 | ```sh 167 | revsocks -connect clientIP:8443 -pass Password1234 -proxy proxy.domain.local:3128 -proxyauth Domain/userpame:userpass -useragent "Mozilla 5.0/IE Windows 10" 168 | ``` 169 | 170 | Client connects to server and send agentpassword to authorize on server. If server does not receive agentpassword or reveive wrong pass from client (for example if spider or client browser connects to server ) then it send HTTP 301 redirect code to www.microsoft.com 171 | 172 | ## Custom certificate 173 | 174 | Generate self-signed certificate with openssl: 175 | 176 | ```sh 177 | openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes 178 | ``` 179 | 180 | ## Debug 181 | 182 | For debugging (especially DNS part): 183 | ```sh 184 | go build -tags debug 185 | ``` 186 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 2.x | :white_check_mark: | 8 | | < 2.0 | :x: | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | Feel free to report vulnerability through github. 13 | 14 | Critical vulnerabilities (if confirmed!) will be fixed in a week time. Consider it is open source and non-paid project. 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kost/revsocks 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 7 | github.com/hashicorp/yamux v0.1.1 8 | github.com/kost/dnstun v0.0.0-20230511164951-6e7f5656a900 9 | github.com/kost/go-ntlmssp v0.0.0-20190601005913-a22bdd33b2a4 10 | golang.org/x/crypto v0.14.0 11 | nhooyr.io/websocket v1.8.10 12 | ) 13 | 14 | require ( 15 | github.com/Jeffail/tunny v0.1.4 // indirect 16 | github.com/acomagu/bufpipe v1.0.4 // indirect 17 | github.com/golang/protobuf v1.5.3 // indirect 18 | github.com/kost/chashell v0.0.0-20230409212000-cf0fbd106275 // indirect 19 | github.com/miekg/dns v1.1.54 // indirect 20 | github.com/rs/xid v1.5.0 // indirect 21 | golang.org/x/mod v0.10.0 // indirect 22 | golang.org/x/net v0.17.0 // indirect 23 | golang.org/x/sys v0.13.0 // indirect 24 | golang.org/x/text v0.13.0 // indirect 25 | golang.org/x/tools v0.9.1 // indirect 26 | google.golang.org/protobuf v1.30.0 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Jeffail/tunny v0.1.4 h1:chtpdz+nUtaYQeCKlNBg6GycFF/kGVHOr6A3cmzTJXs= 2 | github.com/Jeffail/tunny v0.1.4/go.mod h1:P8xAx4XQl0xsuhjX1DtfaMDCSuavzdb2rwbd0lk+fvo= 3 | github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= 4 | github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 5 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 6 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 7 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 8 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 9 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 10 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 11 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 12 | github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= 13 | github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= 14 | github.com/kost/chashell v0.0.0-20230409212000-cf0fbd106275 h1:OTUMnRSyi/iJ4bK99YKUrwta8a+daLTJfWQWi7NSybI= 15 | github.com/kost/chashell v0.0.0-20230409212000-cf0fbd106275/go.mod h1:x4/v1llkZM8wm+spL3wVA5jXQ7kKsYwCoivIxa9MP2A= 16 | github.com/kost/dnstun v0.0.0-20230511164951-6e7f5656a900 h1:BU0+tomIHRGeqMjuZvHsgGlzAIsUrebUVDtxraBbgGQ= 17 | github.com/kost/dnstun v0.0.0-20230511164951-6e7f5656a900/go.mod h1:ygRgqm2qygdLqcl5si7KUy/JKRof/xOumODY0BOPPKQ= 18 | github.com/kost/go-ntlmssp v0.0.0-20190601005913-a22bdd33b2a4 h1:4VVc/TqlDSrhuWN34tsXQZBwzSB7oNfkoc7lry3GZDI= 19 | github.com/kost/go-ntlmssp v0.0.0-20190601005913-a22bdd33b2a4/go.mod h1:GF8vL2Lfura79VXDTAYnqz7pC60m6fqFJEUemjzaRC8= 20 | github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= 21 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 22 | github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= 23 | github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 24 | github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= 25 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 26 | golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= 27 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 28 | golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= 29 | golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 30 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 31 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 32 | golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 33 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 34 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 36 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 37 | golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 38 | golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 39 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 40 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 41 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 42 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 43 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 44 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 45 | nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= 46 | nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | 10 | "time" 11 | 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | var agentpassword string 17 | 18 | type AppOptions struct { 19 | usetls bool 20 | verify bool 21 | usewebsocket bool 22 | useragent string 23 | envproxy bool 24 | debug bool 25 | autocert string 26 | recn int 27 | rect int 28 | optquiet bool 29 | proxyauthstring string 30 | optproxytimeout string 31 | optdnslisten string 32 | optdnsdelay string 33 | optdnsdomain string 34 | listen string 35 | certificate string 36 | socks string 37 | connect string 38 | proxy string 39 | agentpassword string 40 | } 41 | 42 | var CurOptions AppOptions 43 | 44 | func main() { 45 | flag.StringVar(&CurOptions.listen, "listen", "", "listen port for receiver address:port") 46 | flag.StringVar(&CurOptions.certificate, "cert", "", "certificate file") 47 | flag.StringVar(&CurOptions.socks, "socks", "127.0.0.1:1080", "socks address:port") 48 | flag.StringVar(&CurOptions.connect, "connect", "", "connect address:port (or https://address:port for ws)") 49 | flag.StringVar(&CurOptions.proxy, "proxy", "", "use proxy address:port for connecting (or http://address:port for ws)") 50 | flag.StringVar(&CurOptions.optdnslisten, "dnslisten", "", "Where should DNS server listen") 51 | flag.StringVar(&CurOptions.optdnsdelay, "dnsdelay", "", "Delay/sleep time between requests (200ms by default)") 52 | flag.StringVar(&CurOptions.optdnsdomain, "dns", "", "DNS domain to use for DNS tunneling") 53 | flag.StringVar(&CurOptions.optproxytimeout, "proxytimeout", "", "proxy response timeout (ms)") 54 | flag.StringVar(&CurOptions.proxyauthstring, "proxyauth", "", "proxy auth Domain/user:Password") 55 | flag.StringVar(&CurOptions.autocert, "autocert", "", "use domain.tld and automatically obtain TLS certificate") 56 | flag.StringVar(&CurOptions.useragent, "agent", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko", "User agent to use") 57 | flag.StringVar(&CurOptions.agentpassword, "pass", "", "Connect password") 58 | flag.BoolVar(&CurOptions.optquiet, "q", false, "Be quiet - do not display output") 59 | flag.IntVar(&CurOptions.recn, "recn", 3, "reconnection limit") 60 | flag.IntVar(&CurOptions.rect, "rect", 30, "reconnection delay") 61 | flag.BoolVar(&CurOptions.debug, "debug", false, "display debug info") 62 | flag.BoolVar(&CurOptions.usetls, "tls", false, "use TLS for connection") 63 | flag.BoolVar(&CurOptions.usewebsocket, "ws", false, "use websocket for connection") 64 | flag.BoolVar(&CurOptions.verify, "verify", false, "verify TLS connection") 65 | version := flag.Bool("version", false, "version information") 66 | 67 | flag.Usage = func() { 68 | 69 | fmt.Printf("revsocks - reverse socks5 server/client by kost %s (%s)\n", Version, CommitID) 70 | fmt.Println("") 71 | flag.PrintDefaults() 72 | fmt.Println("") 73 | fmt.Println("Usage (standard tcp):") 74 | fmt.Println("1) Start on the client: revsocks -listen :8080 -socks 127.0.0.1:1080 -pass test -tls") 75 | fmt.Println("2) Start on the server: revsocks -connect client:8080 -pass test -tls") 76 | fmt.Println("3) Connect to 127.0.0.1:1080 on the client with any socks5 client.") 77 | fmt.Println("Usage (dns):") 78 | fmt.Println("1) Start on the DNS server: revsocks -dns example.com -dnslisten :53 -socks 127.0.0.1:1080") 79 | fmt.Println("2) Start on the target: revsocks -dns example.com -pass ") 80 | fmt.Println("3) Connect to 127.0.0.1:1080 on the DNS server with any socks5 client.") 81 | } 82 | 83 | flag.Parse() 84 | 85 | if CurOptions.optquiet { 86 | log.SetOutput(ioutil.Discard) 87 | } 88 | 89 | if *version { 90 | fmt.Printf("revsocks - reverse socks5 server/client %s (%s)\n", Version, CommitID) 91 | os.Exit(0) 92 | } 93 | 94 | if CurOptions.agentpassword == "" { 95 | agentpassword = RandString(64) 96 | log.Printf("No password specified. Generated password is %s", agentpassword) 97 | } 98 | 99 | if CurOptions.listen != "" { 100 | log.Println("Starting to listen for clients") 101 | if CurOptions.optproxytimeout != "" { 102 | opttimeout, _ := strconv.Atoi(CurOptions.optproxytimeout) 103 | proxytout = time.Millisecond * time.Duration(opttimeout) 104 | } else { 105 | proxytout = time.Millisecond * 1000 106 | } 107 | 108 | //listenForSocks(*listen, *certificate) 109 | if CurOptions.usewebsocket { 110 | log.Fatal(listenForWebsocketAgents(CurOptions.usetls, CurOptions.listen, CurOptions.socks, CurOptions.certificate, CurOptions.autocert)) 111 | } else { 112 | log.Fatal(listenForAgents(CurOptions.usetls, CurOptions.listen, CurOptions.socks, CurOptions.certificate, CurOptions.autocert)) 113 | } 114 | } 115 | 116 | if CurOptions.connect != "" { 117 | 118 | if CurOptions.optproxytimeout != "" { 119 | opttimeout, _ := strconv.Atoi(CurOptions.optproxytimeout) 120 | proxytimeout = time.Millisecond * time.Duration(opttimeout) 121 | } else { 122 | proxytimeout = time.Millisecond * 1000 123 | } 124 | 125 | if CurOptions.proxyauthstring != "" { 126 | if strings.Contains(CurOptions.proxyauthstring, "/") { 127 | domain = strings.Split(CurOptions.proxyauthstring, "/")[0] 128 | username = strings.Split(strings.Split(CurOptions.proxyauthstring, "/")[1], ":")[0] 129 | password = strings.Split(strings.Split(CurOptions.proxyauthstring, "/")[1], ":")[1] 130 | } else { 131 | username = strings.Split(CurOptions.proxyauthstring, ":")[0] 132 | password = strings.Split(CurOptions.proxyauthstring, ":")[1] 133 | } 134 | log.Printf("Using domain %s with %s:%s", domain, username, password) 135 | } else { 136 | domain = "" 137 | username = "" 138 | password = "" 139 | } 140 | 141 | //log.Fatal(connectForSocks(*connect,*proxy)) 142 | if CurOptions.recn > 0 { 143 | for i := 1; i <= CurOptions.recn; i++ { 144 | log.Printf("Connecting to the far end. Try %d of %d", i, CurOptions.recn) 145 | if CurOptions.usewebsocket { 146 | WSconnectForSocks(CurOptions.verify, CurOptions.connect, CurOptions.proxy) 147 | } else { 148 | error1 := connectForSocks(CurOptions.usetls, CurOptions.verify, CurOptions.connect, CurOptions.proxy) 149 | log.Print(error1) 150 | } 151 | log.Printf("Sleeping for %d sec...", CurOptions.rect) 152 | tsleep := time.Second * time.Duration(CurOptions.rect) 153 | time.Sleep(tsleep) 154 | } 155 | 156 | } else { 157 | for { 158 | log.Printf("Reconnecting to the far end... ") 159 | if CurOptions.usewebsocket { 160 | WSconnectForSocks(CurOptions.verify, CurOptions.connect, CurOptions.proxy) 161 | } else { 162 | error1 := connectForSocks(CurOptions.usetls, CurOptions.verify, CurOptions.connect, CurOptions.proxy) 163 | log.Print(error1) 164 | } 165 | log.Printf("Sleeping for %d sec...", CurOptions.rect) 166 | tsleep := time.Second * time.Duration(CurOptions.rect) 167 | time.Sleep(tsleep) 168 | } 169 | } 170 | 171 | log.Fatal("Ending...") 172 | } 173 | 174 | if CurOptions.optdnsdomain != "" { 175 | dnskey := CurOptions.agentpassword 176 | if CurOptions.agentpassword == "" { 177 | dnskey = GenerateKey() 178 | log.Printf("No password specified, generated following (recheck if same on both sides): %s", dnskey) 179 | } 180 | if len(dnskey) != 64 { 181 | fmt.Fprintf(os.Stderr, "Specified key of incorrect size for DNS (should be 64 in hex)\n") 182 | os.Exit(1) 183 | } 184 | if CurOptions.optdnslisten != "" { 185 | ServeDNS(CurOptions.optdnslisten, CurOptions.optdnsdomain, CurOptions.socks, dnskey, CurOptions.optdnsdelay) 186 | } else { 187 | DnsConnectSocks(CurOptions.optdnsdomain, dnskey, CurOptions.optdnsdelay) 188 | } 189 | log.Fatal("Ending...") 190 | } 191 | 192 | flag.Usage() 193 | fmt.Fprintf(os.Stderr, "You must specify a listen port or a connect address\n") 194 | os.Exit(1) 195 | } 196 | -------------------------------------------------------------------------------- /rclient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "log" 7 | "net" 8 | 9 | "errors" 10 | 11 | "context" 12 | "net/url" 13 | 14 | "bufio" 15 | "bytes" 16 | "encoding/base64" 17 | socks5 "github.com/armon/go-socks5" 18 | "github.com/hashicorp/yamux" 19 | "io/ioutil" 20 | "net/http" 21 | "strings" 22 | "time" 23 | 24 | ntlmssp "github.com/kost/go-ntlmssp" 25 | 26 | "nhooyr.io/websocket" 27 | ) 28 | 29 | var encBase64 = base64.StdEncoding.EncodeToString 30 | var decBase64 = base64.StdEncoding.DecodeString 31 | var username string 32 | var domain string 33 | var password string 34 | var connectproxystring string 35 | var useragent string 36 | var proxytimeout = time.Millisecond * 1000 //timeout for proxyserver response 37 | 38 | func GetSystemProxy(method string, urlstr string) (*url.URL, error) { 39 | req, err := http.NewRequest(method, urlstr, nil) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | // Get the proxy URL from the environment 45 | proxyURL, err := http.ProxyFromEnvironment(req) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return proxyURL, nil 50 | } 51 | 52 | func WSconnectForSocks(verify bool, address string, proxy string) error { 53 | // Define the proxy URL and WebSocket endpoint URL 54 | proxyURL := proxy // Change this to your proxy URL 55 | wsURL := address // Change this to your WebSocket endpoint 56 | 57 | server, err := socks5.New(&socks5.Config{}) 58 | if err != nil { 59 | log.Printf("Error setting up socks server: %v", err) 60 | return err 61 | } 62 | httpClient := &http.Client{ 63 | Transport: &http.Transport{ 64 | TLSClientConfig: &tls.Config{InsecureSkipVerify: !verify}, 65 | }, 66 | } 67 | 68 | if proxyURL != "" { 69 | ntlmssp.NewNegotiateMessage(domain, "") 70 | 71 | // Create an HTTP client that authenticates via NTLMSSP 72 | negmsg, err := ntlmssp.NewNegotiateMessage(domain, "") 73 | if err != nil { 74 | log.Printf("Error getting domain negotiate message: %v", err) 75 | return err 76 | } 77 | tsproxy := http.ProxyURL(mustParseURL(proxyURL)) 78 | if proxyURL == "." { 79 | tsproxy = http.ProxyFromEnvironment 80 | } 81 | httpClient = &http.Client{ 82 | Transport: &http.Transport{ 83 | TLSClientConfig: &tls.Config{InsecureSkipVerify: !verify}, 84 | Proxy: tsproxy, 85 | ProxyConnectHeader: http.Header{ 86 | "Proxy-Authorization": []string{string(negmsg)}, 87 | }, 88 | }, 89 | } 90 | 91 | // resp, err := http.Get(wsURL) 92 | // resp, err := httpClient.Get(wsURL) 93 | req, err := http.NewRequest("GET", wsURL, nil) 94 | if err != nil { 95 | log.Printf("error creating http request to %s: %s\n", wsURL, err) 96 | return err 97 | } 98 | 99 | req.Header.Set("User-Agent", useragent) 100 | 101 | resp, err := httpClient.Do(req) 102 | if err != nil { 103 | log.Printf("error making http request to %s: %s\n", wsURL, err) 104 | return err 105 | } 106 | 107 | if resp.StatusCode == 200 { 108 | log.Printf("No proxy auth required. Will make standard request") 109 | } else if resp.StatusCode == 407 { 110 | ntlmchall := resp.Header.Get("Proxy-Authenticate") 111 | log.Printf("Got following challenge: %s", ntlmchall) 112 | if strings.Contains(ntlmchall, "NTLM") { 113 | ntlmchall = ntlmchall[5:] 114 | challengeMessage, errb := base64.StdEncoding.DecodeString(ntlmchall) 115 | if errb != nil { 116 | log.Printf("Error getting base64 decode of challengde: %v", errb) 117 | return errb 118 | } 119 | authenticateMessage, erra := ntlmssp.ProcessChallenge(challengeMessage, username, password) 120 | if erra != nil { 121 | log.Printf("Error getting auth message for challenge: %v", erra) 122 | } 123 | authMessage := fmt.Sprintf("NTLM %s", base64.StdEncoding.EncodeToString(authenticateMessage)) 124 | httpClient = &http.Client{ 125 | Transport: &http.Transport{ 126 | Proxy: tsproxy, 127 | ProxyConnectHeader: http.Header{ 128 | "Proxy-Authorization": []string{string(authMessage)}, 129 | }, 130 | }, 131 | } 132 | } else if strings.Contains(ntlmchall, "Basic") { 133 | authCombo := fmt.Sprintf("%s:%s", username, password) 134 | authMessage := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(authCombo))) 135 | httpClient = &http.Client{ 136 | Transport: &http.Transport{ 137 | Proxy: tsproxy, 138 | ProxyConnectHeader: http.Header{ 139 | "Proxy-Authorization": []string{authMessage}, 140 | }, 141 | }, 142 | } 143 | } else { 144 | log.Printf("Unknown proxy challenge: %s", ntlmchall) 145 | return errors.New("Unknown proxy challenge") 146 | } 147 | } else { 148 | log.Printf("Unknown http response code: %d", resp.StatusCode) 149 | } 150 | 151 | } 152 | 153 | // Connect to the WebSocket endpoint via the proxy 154 | wconn, _, err := websocket.Dial(context.Background(), wsURL, &websocket.DialOptions{ 155 | HTTPClient: httpClient, 156 | HTTPHeader: http.Header{"User-Agent": []string{useragent}, "Accept-Language": []string{agentpassword}, "Sec-WebSocket-Protocol": []string{"chat"}}, 157 | Subprotocols: []string{"chat"}, 158 | }) 159 | if err != nil { 160 | fmt.Println("Error connecting to the WebSocket:", err) 161 | return err 162 | } 163 | defer wconn.Close(websocket.StatusInternalError, "Connection closed") 164 | 165 | nc_over_ws := websocket.NetConn(context.Background(), wconn, websocket.MessageBinary) 166 | 167 | session, erry := yamux.Server(nc_over_ws, nil) 168 | if erry != nil { 169 | fmt.Println("Error creating yamux server:", err) 170 | return erry 171 | } 172 | 173 | for { 174 | stream, err := session.Accept() 175 | if err != nil { 176 | fmt.Println("Error accepting stream:", err) 177 | continue 178 | } 179 | log.Println("Accepted stream") 180 | go func() { 181 | err = server.ServeConn(stream) 182 | if err != nil { 183 | log.Printf("Error serving: %v", err) 184 | } 185 | }() 186 | } 187 | return nil 188 | } 189 | 190 | func mustParseURL(u string) *url.URL { 191 | parsedURL, err := url.Parse(u) 192 | if err != nil { 193 | panic(err) 194 | } 195 | return parsedURL 196 | } 197 | 198 | func connectviaproxy(tlsenable bool, proxyaddress string, connectaddr string) net.Conn { 199 | var proxyurl *url.URL 200 | socksdebug := CurOptions.debug 201 | connectproxystring = "" 202 | 203 | proxyurl = nil 204 | proxyaddr := proxyaddress 205 | 206 | if proxyaddress == "." { 207 | prefixstr := "https://" // always https since CONNECT is needed 208 | sysproxy, err := GetSystemProxy("POST", prefixstr+connectaddr) 209 | if err != nil { 210 | log.Printf("Error getting system proxy for %s: %v", prefixstr+connectaddr, err) 211 | return nil 212 | } 213 | proxyurl = sysproxy 214 | proxyaddr = sysproxy.Host 215 | } 216 | if (username != "") && (password != "") && (domain != "") { 217 | negotiateMessage, errn := ntlmssp.NewNegotiateMessage(domain, "") 218 | if errn != nil { 219 | log.Println("NEG error") 220 | log.Println(errn) 221 | // return nil 222 | } 223 | log.Print(negotiateMessage) 224 | negheader := fmt.Sprintf("NTLM %s", base64.StdEncoding.EncodeToString(negotiateMessage)) 225 | 226 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 227 | "\r\nUser-Agent: " + useragent + 228 | "\r\nProxy-Authorization: " + negheader + 229 | "\r\nProxy-Connection: Keep-Alive" + 230 | "\r\n\r\n" 231 | 232 | } else { 233 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 234 | "\r\nUser-Agent: " + useragent + 235 | "\r\nProxy-Connection: Keep-Alive" + 236 | "\r\n\r\n" 237 | } 238 | 239 | if socksdebug { 240 | log.Print(connectproxystring) 241 | } 242 | 243 | conn, err := net.Dial("tcp", proxyaddr) 244 | if err != nil { 245 | // handle error 246 | log.Printf("Error connect to %s: %v", proxyaddr, err) 247 | } 248 | conn.Write([]byte(connectproxystring)) 249 | 250 | time.Sleep(proxytimeout) //Because socket does not close - we need to sleep for full response from proxy 251 | 252 | resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"}) 253 | status := resp.Status 254 | 255 | if socksdebug { 256 | log.Print(status) 257 | log.Print(resp) 258 | } 259 | 260 | if (resp.StatusCode == 200) || (strings.Contains(status, "HTTP/1.1 200 ")) || 261 | (strings.Contains(status, "HTTP/1.0 200 ")) { 262 | log.Print("Connected via proxy. No auth required") 263 | return conn 264 | } 265 | 266 | if socksdebug { 267 | log.Print("Checking proxy auth") 268 | } 269 | if resp.StatusCode == 407 { 270 | log.Print("Got Proxy status code (407)") 271 | ntlmchall := resp.Header.Get("Proxy-Authenticate") 272 | log.Print(ntlmchall) 273 | if strings.Contains(ntlmchall, "NTLM") { 274 | if socksdebug { 275 | log.Print("Got NTLM challenge:") 276 | log.Print(ntlmchall) 277 | } 278 | 279 | /* 280 | negstring:= fmt.Sprintf("NTLM %s", base64.StdEncoding.EncodeToString(negotiateMessage)) 281 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 282 | "\r\nUser-Agent: "+useragent+ 283 | "\r\nProxy-Authorization: " + negstring + 284 | "\r\nProxy-Connection: Keep-Alive" + 285 | "\r\n\r\n" 286 | */ 287 | 288 | ntlmchall = ntlmchall[5:] 289 | if socksdebug { 290 | log.Print("NTLM challenge:") 291 | log.Print(ntlmchall) 292 | } 293 | challengeMessage, errb := decBase64(ntlmchall) 294 | if errb != nil { 295 | log.Println("BASE64 Decode error") 296 | log.Println(errb) 297 | return nil 298 | } 299 | user := "" 300 | pass := "" 301 | if proxyaddress == "." { 302 | user = proxyurl.User.Username() 303 | p, pset := proxyurl.User.Password() 304 | if pset { 305 | pass = p 306 | } 307 | } 308 | if (username != "") && (password != "") { 309 | user = username 310 | pass = password 311 | } 312 | authenticateMessage, erra := ntlmssp.ProcessChallenge(challengeMessage, user, pass) 313 | if erra != nil { 314 | log.Println("Process challenge error") 315 | log.Println(erra) 316 | return nil 317 | } 318 | 319 | authMessage := fmt.Sprintf("NTLM %s", base64.StdEncoding.EncodeToString(authenticateMessage)) 320 | 321 | //log.Print(authenticate) 322 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 323 | "\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko" + 324 | "\r\nProxy-Authorization: " + authMessage + 325 | "\r\nProxy-Connection: Keep-Alive" + 326 | "\r\n\r\n" 327 | } else if strings.Contains(ntlmchall, "Basic") { 328 | if socksdebug { 329 | log.Print("Got Basic challenge:") 330 | } 331 | var authbuffer bytes.Buffer 332 | if (username != "") && (password != "") { 333 | authbuffer.WriteString(username) 334 | authbuffer.WriteString(":") 335 | authbuffer.WriteString(password) 336 | } else if proxyaddress == "." { 337 | authbuffer.WriteString(proxyurl.User.String()) 338 | } 339 | 340 | basicauth := encBase64(authbuffer.Bytes()) 341 | 342 | //log.Print(authenticate) 343 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 344 | "\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko" + 345 | "\r\nProxy-Authorization: Basic " + basicauth + 346 | "\r\nProxy-Connection: Keep-Alive" + 347 | "\r\n\r\n" 348 | } else { 349 | log.Print("Unknown authentication") 350 | return nil 351 | } 352 | log.Print("Connecting to proxy") 353 | log.Print(connectproxystring) 354 | conn.Write([]byte(connectproxystring)) 355 | 356 | //read response 357 | bufReader := bufio.NewReader(conn) 358 | conn.SetReadDeadline(time.Now().Add(proxytimeout)) 359 | statusb, _ := ioutil.ReadAll(bufReader) 360 | 361 | status = string(statusb) 362 | 363 | //disable socket read timeouts 364 | conn.SetReadDeadline(time.Now().Add(100 * time.Hour)) 365 | 366 | if resp.StatusCode == 200 || strings.Contains(status, "HTTP/1.1 200 ") || 367 | strings.Contains(status, "HTTP/1.0 200 ") { 368 | log.Print("Connected via proxy") 369 | return conn 370 | } 371 | log.Printf("Not Connected via proxy. Status:%v", status) 372 | return nil 373 | 374 | } 375 | log.Print("Not connected via proxy") 376 | conn.Close() 377 | return nil 378 | } 379 | 380 | func connectForSocks(tlsenable bool, verify bool, address string, proxy string) error { 381 | var session *yamux.Session 382 | server, err := socks5.New(&socks5.Config{}) 383 | if err != nil { 384 | return err 385 | } 386 | 387 | conf := &tls.Config{ 388 | InsecureSkipVerify: !verify, 389 | } 390 | 391 | var conn net.Conn 392 | var connp net.Conn 393 | var newconn net.Conn 394 | //var conntls tls.Conn 395 | //var conn tls.Conn 396 | if proxy == "" { 397 | log.Println("Connecting to far end") 398 | if tlsenable { 399 | conn, err = tls.Dial("tcp", address, conf) 400 | } else { 401 | conn, err = net.Dial("tcp", address) 402 | } 403 | if err != nil { 404 | return err 405 | } 406 | } else { 407 | log.Println("Connecting to proxy ...") 408 | connp = connectviaproxy(tlsenable, proxy, address) 409 | if connp != nil { 410 | log.Println("Proxy successfull. Connecting to far end") 411 | if tlsenable { 412 | conntls := tls.Client(connp, conf) 413 | err := conntls.Handshake() 414 | if err != nil { 415 | log.Printf("Error connect: %v", err) 416 | return err 417 | } 418 | newconn = net.Conn(conntls) 419 | } else { 420 | newconn = connp 421 | } 422 | } else { 423 | log.Println("Proxy NOT successfull. Exiting") 424 | return nil 425 | } 426 | } 427 | 428 | log.Println("Starting client") 429 | if proxy == "" { 430 | conn.Write([]byte(agentpassword)) 431 | //time.Sleep(time.Second * 1) 432 | session, err = yamux.Server(conn, nil) 433 | } else { 434 | 435 | //log.Print(conntls) 436 | newconn.Write([]byte(agentpassword)) 437 | time.Sleep(time.Second * 1) 438 | session, err = yamux.Server(newconn, nil) 439 | } 440 | if err != nil { 441 | return err 442 | } 443 | 444 | for { 445 | stream, err := session.Accept() 446 | log.Println("Accepting stream") 447 | if err != nil { 448 | return err 449 | } 450 | log.Println("Passing off to socks5") 451 | go func() { 452 | err = server.ServeConn(stream) 453 | if err != nil { 454 | log.Println(err) 455 | } 456 | }() 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /rdns.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/kost/dnstun" 7 | 8 | socks5 "github.com/armon/go-socks5" 9 | ) 10 | 11 | func GenerateKey() string { 12 | return dnstun.GenerateKey() 13 | } 14 | 15 | func DnsConnectSocks(targetDomain string, encryptionKey string, dnsdelay string) error { 16 | server, err := socks5.New(&socks5.Config{}) 17 | if err != nil { 18 | log.Printf("Error socks5.new: %v", err) 19 | return err 20 | } 21 | dt := dnstun.NewDnsTunnel(targetDomain, encryptionKey) 22 | if dnsdelay != "" { 23 | err = dt.SetDnsDelay(dnsdelay) 24 | if err != nil { 25 | log.Printf("Error setting delay: %v", err) 26 | return err 27 | } 28 | } 29 | for { 30 | session, err := dt.DnsClient() 31 | if err != nil { 32 | log.Printf("Error yamux transport: %v", err) 33 | return err 34 | } 35 | for { 36 | stream, err := session.Accept() 37 | log.Println("Accepting stream") 38 | if err != nil { 39 | log.Printf("Error accepting stream: %v", err) 40 | break 41 | } 42 | log.Println("Passing off to socks5") 43 | go func() { 44 | err = server.ServeConn(stream) 45 | if err != nil { 46 | log.Println(err) 47 | } 48 | }() 49 | } 50 | } 51 | } 52 | 53 | func ServeDNS(dnslisten string, DnsDomain string, clients string, enckey string, dnsdelay string) error { 54 | dt := dnstun.NewDnsTunnel(DnsDomain, enckey) 55 | if dnsdelay != "" { 56 | err := dt.SetDnsDelay(dnsdelay) 57 | if err != nil { 58 | log.Printf("Error parsing DNS delay/sleep duration %s: %v", dnsdelay, err) 59 | return err 60 | } 61 | } 62 | dt.DnsServer(dnslisten, clients) 63 | err := dt.DnsServerStart() 64 | if err != nil { 65 | log.Printf("Error starting DNS server %s: %v", DnsDomain, err) 66 | return err 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /rserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "os" 10 | 11 | "bufio" 12 | "github.com/hashicorp/yamux" 13 | "strconv" 14 | "strings" 15 | "time" 16 | 17 | "context" 18 | "net/http" 19 | "nhooyr.io/websocket" 20 | "sync" 21 | 22 | "golang.org/x/crypto/acme/autocert" 23 | "path/filepath" 24 | ) 25 | 26 | var proxytout = time.Millisecond * 1000 //timeout for wait magicbytes 27 | 28 | type agentHandler struct { 29 | mu sync.Mutex 30 | listenstr string // listen string for clients 31 | portnext int // next port for listen 32 | timeout time.Duration 33 | sessions []*yamux.Session // all sessions 34 | // agentstr string // connecting agent combo (IP:port) 35 | } 36 | 37 | func (h *agentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 38 | var session *yamux.Session 39 | var erry error 40 | 41 | agentstr := r.RemoteAddr 42 | log.Printf("[%s] Got HTTP request (%s): %s", agentstr, r.Method, r.URL.String()) 43 | 44 | if r.Header.Get("Upgrade") != "websocket" { 45 | w.Header().Set("Location", "https://www.microsoft.com/") 46 | w.WriteHeader(http.StatusFound) // Use 302 status code for redirect 47 | // fmt.Fprintf(w, "OK") 48 | return 49 | } 50 | 51 | if r.Header.Get("Accept-Language") != agentpassword { 52 | w.Header().Set("Location", "https://www.microsoft.com/") 53 | w.WriteHeader(http.StatusFound) // Use 302 status code for redirect 54 | // fmt.Fprintf(w, "OK") 55 | return 56 | } 57 | 58 | c, err := websocket.Accept(w, r, nil) 59 | if err != nil { 60 | log.Printf("[%s] Error upgrading to socket (%s): %v", agentstr, r.RemoteAddr, err) 61 | http.Error(w, "Bad request - Go away!", 500) 62 | return 63 | } 64 | defer c.CloseNow() 65 | 66 | if h.timeout > 0 { 67 | _, cancel := context.WithTimeout(r.Context(), time.Second*60) 68 | defer cancel() 69 | } 70 | 71 | nc_over_ws := websocket.NetConn(context.Background(), c, websocket.MessageBinary) 72 | 73 | //Add connection to yamux 74 | session, erry = yamux.Client(nc_over_ws, nil) 75 | if erry != nil { 76 | log.Printf("[%s] Error creating client in yamux for (%s): %v", agentstr, r.RemoteAddr, erry) 77 | http.Error(w, "Bad request - Go away!", 500) 78 | return 79 | } 80 | h.sessions = append(h.sessions, session) 81 | h.mu.Lock() 82 | listenport := h.portnext 83 | h.portnext = h.portnext + 1 84 | h.mu.Unlock() 85 | listenForClients(agentstr, h.listenstr, listenport, session) 86 | 87 | c.Close(websocket.StatusNormalClosure, "") 88 | } 89 | 90 | func listenForWebsocketAgents(tlslisten bool, address string, clients string, certificate string, autocertdomain string) error { 91 | var cer tls.Certificate 92 | var err error 93 | log.Printf("Will start listening for clients on %s", clients) 94 | var listenstr = strings.Split(clients, ":") 95 | portnum, errc := strconv.Atoi(listenstr[1]) 96 | if errc != nil { 97 | log.Printf("Error converting listen str %s: %v", clients, errc) 98 | } 99 | 100 | aHandler := &agentHandler{ 101 | portnext: portnum, 102 | listenstr: listenstr[0], 103 | } 104 | server := &http.Server{ 105 | Addr: address, // e.g. ":8443" 106 | Handler: aHandler, 107 | } 108 | if tlslisten { 109 | if autocertdomain != "" { 110 | log.Printf("Getting TLS certificate for %s", autocertdomain) 111 | dirname, err := os.UserHomeDir() 112 | if err != nil { 113 | log.Printf("Error getting TLS certificate for %s: %v", autocertdomain, err) 114 | } 115 | cachepath := filepath.Join(dirname, ".revsocks-autocert") 116 | m := &autocert.Manager{ 117 | Cache: autocert.DirCache(cachepath), 118 | Prompt: autocert.AcceptTOS, 119 | // Email: "example@example.org", 120 | HostPolicy: autocert.HostWhitelist(autocertdomain), 121 | } 122 | server.TLSConfig = m.TLSConfig() 123 | } else { 124 | if certificate == "" { 125 | cer, err = getRandomTLS(2048) 126 | log.Println("No TLS certificate. Generated random one.") 127 | } else { 128 | cer, err = tls.LoadX509KeyPair(certificate+".crt", certificate+".key") 129 | } 130 | if err != nil { 131 | log.Printf("Error creating/loading certificate file %s: %v", certificate, err) 132 | return err 133 | } 134 | // config := &tls.Config{Certificates: []tls.Certificate{cer}} 135 | server.TLSConfig = &tls.Config{ 136 | Certificates: []tls.Certificate{cer}, 137 | } 138 | } 139 | } 140 | 141 | log.Printf("Listening for websocket agents on %s (TLS: %t)", address, tlslisten) 142 | if tlslisten { 143 | err = server.ListenAndServeTLS("", "") 144 | } else { 145 | err = server.ListenAndServe() 146 | } 147 | 148 | return nil 149 | } 150 | 151 | // listen for agents 152 | func listenForAgents(tlslisten bool, address string, clients string, certificate string, autocertdomain string) error { 153 | var err, erry error 154 | var cer tls.Certificate 155 | var session *yamux.Session 156 | var sessions []*yamux.Session 157 | var ln net.Listener 158 | 159 | log.Printf("Will start listening for clients on %s and agents on %s (TLS: %t)", clients, address, tlslisten) 160 | if tlslisten { 161 | if autocertdomain != "" { 162 | log.Printf("Getting TLS certificate for %s", autocertdomain) 163 | dirname, err := os.UserHomeDir() 164 | if err != nil { 165 | log.Printf("Error getting TLS certificate for %s: %v", autocertdomain, err) 166 | } 167 | cachepath := filepath.Join(dirname, ".revsocks-autocert") 168 | m := &autocert.Manager{ 169 | Cache: autocert.DirCache(cachepath), 170 | Prompt: autocert.AcceptTOS, 171 | // Email: "example@example.org", 172 | HostPolicy: autocert.HostWhitelist(autocertdomain), 173 | } 174 | ln, err = tls.Listen("tcp", address, m.TLSConfig()) 175 | } else { 176 | if certificate == "" { 177 | cer, err = getRandomTLS(2048) 178 | log.Println("No TLS certificate. Generated random one.") 179 | } else { 180 | cer, err = tls.LoadX509KeyPair(certificate+".crt", certificate+".key") 181 | } 182 | if err != nil { 183 | log.Println(err) 184 | return err 185 | } 186 | config := &tls.Config{Certificates: []tls.Certificate{cer}} 187 | ln, err = tls.Listen("tcp", address, config) 188 | } 189 | } else { 190 | ln, err = net.Listen("tcp", address) 191 | } 192 | if err != nil { 193 | log.Printf("Error listening on %s: %v", address, err) 194 | return err 195 | } 196 | var listenstr = strings.Split(clients, ":") 197 | portnum, errc := strconv.Atoi(listenstr[1]) 198 | if errc != nil { 199 | log.Printf("Error converting listen str %s: %v", clients, errc) 200 | } 201 | portinc := 0 202 | for { 203 | conn, err := ln.Accept() 204 | conn.RemoteAddr() 205 | agentstr := conn.RemoteAddr().String() 206 | log.Printf("[%s] Got a connection from %v: ", agentstr, conn.RemoteAddr()) 207 | if err != nil { 208 | fmt.Fprintf(os.Stderr, "Errors accepting!") 209 | } 210 | 211 | reader := bufio.NewReader(conn) 212 | 213 | //read only 64 bytes with timeout=1-3 sec. So we haven't delay with browsers 214 | conn.SetReadDeadline(time.Now().Add(proxytout)) 215 | statusb := make([]byte, 64) 216 | _, _ = io.ReadFull(reader, statusb) 217 | 218 | //Alternatively - read all bytes with timeout=1-3 sec. So we have delay with browsers, but get all GET request 219 | //conn.SetReadDeadline(time.Now().Add(proxytout)) 220 | //statusb,_ := ioutil.ReadAll(magicBuf) 221 | 222 | //log.Printf("magic bytes: %v",statusb[:6]) 223 | //if hex.EncodeToString(statusb) != magicbytes { 224 | if string(statusb)[:len(agentpassword)] != agentpassword { 225 | //do HTTP checks 226 | log.Printf("Received request: %v", string(statusb[:64])) 227 | status := string(statusb) 228 | if strings.Contains(status, " HTTP/1.1") { 229 | httpresonse := "HTTP/1.1 301 Moved Permanently" + 230 | "\r\nContent-Type: text/html; charset=UTF-8" + 231 | "\r\nLocation: https://www.microsoft.com/" + 232 | "\r\nServer: Apache" + 233 | "\r\nContent-Length: 0" + 234 | "\r\nConnection: close" + 235 | "\r\n\r\n" 236 | 237 | conn.Write([]byte(httpresonse)) 238 | conn.Close() 239 | } else { 240 | conn.Close() 241 | } 242 | 243 | } else { 244 | //magic bytes received. 245 | //disable socket read timeouts 246 | log.Printf("[%s] Got Client from %s", agentstr, conn.RemoteAddr()) 247 | conn.SetReadDeadline(time.Now().Add(100 * time.Hour)) 248 | //Add connection to yamux 249 | session, erry = yamux.Client(conn, nil) 250 | if erry != nil { 251 | log.Printf("[%s] Error creating client in yamux for %s: %v", agentstr, conn.RemoteAddr(), erry) 252 | continue 253 | } 254 | sessions = append(sessions, session) 255 | go listenForClients(agentstr, listenstr[0], portnum+portinc, session) 256 | portinc = portinc + 1 257 | } 258 | } 259 | return nil 260 | } 261 | 262 | // Catches local clients and connects to yamux 263 | func listenForClients(agentstr string, listen string, port int, session *yamux.Session) error { 264 | var ln net.Listener 265 | var address string 266 | var err error 267 | portinc := port 268 | for { 269 | address = fmt.Sprintf("%s:%d", listen, portinc) 270 | log.Printf("[%s] Handshake recognized. Waiting for clients on %s", agentstr, address) 271 | ln, err = net.Listen("tcp", address) 272 | if err != nil { 273 | log.Printf("[%s] Error listening on %s: %v", agentstr, address, err) 274 | portinc = portinc + 1 275 | } else { 276 | break 277 | } 278 | } 279 | for { 280 | conn, err := ln.Accept() 281 | if err != nil { 282 | log.Printf("[%s] Error accepting on %s: %v", agentstr, address, err) 283 | return err 284 | } 285 | if session == nil { 286 | log.Printf("[%s] Session on %s is nil", agentstr, address) 287 | conn.Close() 288 | continue 289 | } 290 | log.Printf("[%s] Got client. Opening stream for %s", agentstr, conn.RemoteAddr()) 291 | 292 | stream, err := session.Open() 293 | if err != nil { 294 | log.Printf("[%s] Error opening stream for %s: %v", agentstr, conn.RemoteAddr(), err) 295 | return err 296 | } 297 | 298 | // connect both of conn and stream 299 | 300 | go func() { 301 | log.Printf("[%s] Starting to copy conn to stream for %s", agentstr, conn.RemoteAddr()) 302 | io.Copy(conn, stream) 303 | conn.Close() 304 | log.Printf("[%s] Done copying conn to stream for %s", agentstr, conn.RemoteAddr()) 305 | }() 306 | go func() { 307 | log.Printf("[%s] Starting to copy stream to conn for %s", agentstr, conn.RemoteAddr()) 308 | io.Copy(stream, conn) 309 | stream.Close() 310 | log.Printf("[%s] Done copying stream to conn for %s", agentstr, conn.RemoteAddr()) 311 | }() 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /tlshelp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "crypto/x509/pkix" 8 | "encoding/pem" 9 | "log" 10 | "math/big" 11 | "time" 12 | ) 13 | import "crypto/tls" 14 | 15 | const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 16 | 17 | // RandString generates random string of n size 18 | // It returns the generated random string.and any write error encountered. 19 | func RandString(n int) string { 20 | r := make([]byte, n) 21 | _, err := rand.Read(r) 22 | if err != nil { 23 | return "" 24 | } 25 | 26 | b := make([]byte, n) 27 | l := len(letters) 28 | for i := range b { 29 | b[i] = letters[int(r[i])%l] 30 | } 31 | return string(b) 32 | } 33 | 34 | // RandBytes generates random bytes of n size 35 | // It returns the generated random bytes 36 | func RandBytes(n int) []byte { 37 | r := make([]byte, n) 38 | _, err := rand.Read(r) 39 | if err != nil { 40 | } 41 | 42 | return r 43 | } 44 | 45 | // RandBigInt generates random big integer with max number 46 | // It returns the generated random big integer 47 | func RandBigInt(max *big.Int) *big.Int { 48 | r, _ := rand.Int(rand.Reader, max) 49 | return r 50 | } 51 | 52 | func genPair(keysize int) (cacert []byte, cakey []byte, cert []byte, certkey []byte) { 53 | 54 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 55 | 56 | ca := &x509.Certificate{ 57 | SerialNumber: RandBigInt(serialNumberLimit), 58 | Subject: pkix.Name{ 59 | Country: []string{RandString(16)}, 60 | Organization: []string{RandString(16)}, 61 | OrganizationalUnit: []string{RandString(16)}, 62 | }, 63 | NotBefore: time.Now(), 64 | NotAfter: time.Now().AddDate(10, 0, 0), 65 | SubjectKeyId: RandBytes(5), 66 | BasicConstraintsValid: true, 67 | IsCA: true, 68 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 69 | KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 70 | } 71 | 72 | priv, _ := rsa.GenerateKey(rand.Reader, keysize) 73 | pub := &priv.PublicKey 74 | caBin, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) 75 | if err != nil { 76 | log.Println("create ca failed", err) 77 | return 78 | } 79 | 80 | cert2 := &x509.Certificate{ 81 | SerialNumber: RandBigInt(serialNumberLimit), 82 | Subject: pkix.Name{ 83 | Country: []string{RandString(16)}, 84 | Organization: []string{RandString(16)}, 85 | OrganizationalUnit: []string{RandString(16)}, 86 | }, 87 | NotBefore: time.Now(), 88 | NotAfter: time.Now().AddDate(10, 0, 0), 89 | SubjectKeyId: RandBytes(6), 90 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, 91 | KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 92 | } 93 | priv2, _ := rsa.GenerateKey(rand.Reader, keysize) 94 | pub2 := &priv2.PublicKey 95 | cert2Bin, err2 := x509.CreateCertificate(rand.Reader, cert2, ca, pub2, priv) 96 | if err2 != nil { 97 | log.Println("create cert2 failed", err2) 98 | return 99 | } 100 | 101 | privBin := x509.MarshalPKCS1PrivateKey(priv) 102 | priv2Bin := x509.MarshalPKCS1PrivateKey(priv2) 103 | 104 | return caBin, privBin, cert2Bin, priv2Bin 105 | 106 | } 107 | 108 | func verifyCert(cacert []byte, cert []byte) bool { 109 | caBin, _ := x509.ParseCertificate(cacert) 110 | cert2Bin, _ := x509.ParseCertificate(cert) 111 | err3 := cert2Bin.CheckSignatureFrom(caBin) 112 | if err3 != nil { 113 | return false 114 | } 115 | return true 116 | } 117 | 118 | func getPEMs(cert []byte, key []byte) (pemcert []byte, pemkey []byte) { 119 | certPem := pem.EncodeToMemory(&pem.Block{ 120 | Type: "CERTIFICATE", 121 | Bytes: cert, 122 | }) 123 | 124 | keyPem := pem.EncodeToMemory(&pem.Block{ 125 | Type: "RSA PRIVATE KEY", 126 | Bytes: key, 127 | }) 128 | 129 | return certPem, keyPem 130 | } 131 | 132 | func getTLSPair(certPem []byte, keyPem []byte) (tls.Certificate, error) { 133 | tlspair, errt := tls.X509KeyPair(certPem, keyPem) 134 | if errt != nil { 135 | return tlspair, errt 136 | } 137 | return tlspair, nil 138 | } 139 | 140 | func getRandomTLS(keysize int) (tls.Certificate, error) { 141 | _, _, cert, certkey := genPair(keysize) 142 | certPem, keyPem := getPEMs(cert, certkey) 143 | tlspair, err := getTLSPair(certPem, keyPem) 144 | return tlspair, err 145 | } 146 | -------------------------------------------------------------------------------- /tools/genrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # apk add go-cross 3 | 4 | mkdir release/ 5 | export GOX=$HOME/go/bin/gox 6 | export CGO_ENABLED=0 7 | export GOX_LINUX_AMD64_LDFLAGS="-extldflags -static -s -w" 8 | export GOX_LINUX_386_LDFLAGS="-extldflags -static -s -w" 9 | 10 | $GOX -ldflags="-s -w" -output="release/{{.Dir}}_{{.OS}}_{{.Arch}}" 2>release/error.log 11 | 12 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var Version = "unknown_version" 4 | var CommitID = "unknown_commit" 5 | --------------------------------------------------------------------------------