From dc219e2f7f54df996aa8007e2d23a71111aeb12f Mon Sep 17 00:00:00 2001 From: bagas Date: Wed, 5 Feb 2025 15:38:18 +0700 Subject: [PATCH] Initial commit --- .gitignore | 4 + go.mod | 13 +++ go.sum | 10 ++ main.go | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 333 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bb67d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +id_rsa* +*.pub +.idea +.env \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..23f0956 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module tunnel_pls + +go 1.23 + +require ( + github.com/kr/pty v1.1.8 + golang.org/x/crypto v0.32.0 +) + +require ( + github.com/creack/pty v1.1.7 // indirect + golang.org/x/sys v0.29.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ae3014c --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= diff --git a/main.go b/main.go new file mode 100644 index 0000000..90eb6da --- /dev/null +++ b/main.go @@ -0,0 +1,306 @@ +package main + +import ( + "bytes" + "encoding/binary" + "fmt" + "golang.org/x/crypto/ssh" + "io" + "log" + "net" + "os" + "strconv" +) + +func main() { + sshConfig := &ssh.ServerConfig{ + NoClientAuth: true, + PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + return nil, nil + }, + } + + privateBytes, err := os.ReadFile("id_rsa") + if err != nil { + log.Fatal("Failed to load private key (./id_rsa)") + } + + private, err := ssh.ParsePrivateKey(privateBytes) + if err != nil { + log.Fatal("Failed to parse private key") + } + + sshConfig.AddHostKey(private) + listen, err := net.Listen("tcp", ":2200") + if err != nil { + log.Fatal(err) + return + } + log.Println("Listening on port 2200") + + for { + tcpConn, err := listen.Accept() + if err != nil { + log.Fatal(err) + return + } + sshConn, connChan, globalConnChan, err := ssh.NewServerConn(tcpConn, sshConfig) + if err != nil { + log.Printf("Failed to handshake (%s)", err) + continue + } + log.Printf("New SSH connection from %s (%s) with User (%s)", sshConn.RemoteAddr(), sshConn.ClientVersion(), sshConn.User()) + + go handleRequests(globalConnChan, sshConn) + go handleChannels(connChan, sshConn) + } +} + +func handleChannels(chans <-chan ssh.NewChannel, sshConn *ssh.ServerConn) { + for newChannel := range chans { + go handleChannel(newChannel, sshConn) + } +} + +func handleRequests(reqs <-chan *ssh.Request, sshConn *ssh.ServerConn) { + for req := range reqs { + log.Printf("Received global request: %s", req.Type) + + if req.Type == "tcpip-forward" { + log.Println("Port forwarding request detected") + + reader := bytes.NewReader(req.Payload) + + addr, err := readSSHString(reader) + if err != nil { + log.Println("Failed to read address from payload:", err) + req.Reply(false, nil) + continue + } + + var portToBind uint32 + if err := binary.Read(reader, binary.BigEndian, &portToBind); err != nil { + log.Println("Failed to read port from payload:", err) + req.Reply(false, nil) + continue + } + + log.Printf("Requested forwarding on %s:%d", addr, portToBind) + + listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", portToBind)) + if err != nil { + log.Printf("Failed to bind to port %d: %v", portToBind, err) + req.Reply(false, nil) + continue + } + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Error accepting connection: %v", err) + continue + } + go handleForwardedConnection(conn, sshConn, portToBind) + } + }() + + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, uint32(portToBind)) + + log.Printf("Forwarding approved on port: %d", portToBind) + req.Reply(true, buf.Bytes()) + } else { + req.Reply(false, nil) + } + } +} + +func handleForwardedConnection(conn net.Conn, sshConn *ssh.ServerConn, port uint32) { + defer conn.Close() + log.Printf("Handling new forwarded connection from %s", conn.RemoteAddr()) + + payload := createForwardedTCPIPPayload(conn, port) + channel, reqs, err := sshConn.OpenChannel("forwarded-tcpip", payload) + if err != nil { + log.Printf("Failed to open forwarded-tcpip channel: %v", err) + return + } + defer channel.Close() + + go io.Copy(channel, conn) + io.Copy(conn, channel) + + go func() { + for req := range reqs { + req.Reply(false, nil) + } + }() +} + +func handleChannel(newChannel ssh.NewChannel, sshConn *ssh.ServerConn) { + switch newChannel.ChannelType() { + case "session": + handleSessionChannel(newChannel) + + case "forwarded-tcpip": + //handleForwardedTCPIP(newChannel) + default: + newChannel.Reject(ssh.UnknownChannelType, "unsupported channel type") + } +} + +func handleSessionChannel(newChannel ssh.NewChannel) { + connection, requests, err := newChannel.Accept() + if err != nil { + log.Printf("Could not accept channel: %s", err) + return + } + var bandwidth uint32 + go func() { + var commandBuffer bytes.Buffer + buf := make([]byte, 1) + for { + n, err := connection.Read(buf) + bandwidth += uint32(n) + fmt.Println("using ", bandwidth) + if n > 0 { + char := buf[0] + connection.Write(buf[:n]) + if char == 8 || char == 127 { + if commandBuffer.Len() > 0 { + commandBuffer.Truncate(commandBuffer.Len() - 1) + connection.Write([]byte("\b \b")) + } + continue + } + + if char == '/' { + commandBuffer.Reset() + commandBuffer.WriteByte(char) + continue + } + + if commandBuffer.Len() > 0 { + if char == 13 { + command := commandBuffer.String() + fmt.Println("User entered command:", command) + + if command == "/bye" { + fmt.Println("Closing connection...") + connection.Close() + return + } else if command == "/help" { + connection.Write([]byte("Available commands: /bye, /help")) + } else { + connection.Write([]byte("Unknown command")) + } + + commandBuffer.Reset() + continue + } + + commandBuffer.WriteByte(char) + continue + } + } + + if err != nil { + if err != io.EOF { + log.Printf("Error reading from client: %s", err) + } + break + } + } + }() + + go func() { + connection.Write([]byte("hello world")) + for req := range requests { + switch req.Type { + case "shell", "pty-req": + req.Reply(true, nil) + default: + fmt.Println("Unknown request type") + req.Reply(false, nil) + } + } + }() +} + +//func handleForwardedTCPIP(newChannel ssh.NewChannel) { +// reader := bytes.NewReader(newChannel.ExtraData()) +// +// destAddr, err := readSSHString(reader) +// if err != nil { +// log.Println("Failed to read destination address:", err) +// newChannel.Reject(ssh.ConnectionFailed, "invalid destination") +// return +// } +// +// var destPort uint32 +// if err := binary.Read(reader, binary.BigEndian, &destPort); err != nil { +// log.Println("Failed to read destination port:", err) +// newChannel.Reject(ssh.ConnectionFailed, "invalid port") +// return +// } +// +// log.Printf("Forwarding connection to %s:%d", destAddr, destPort) +// +// targetConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", destAddr, destPort)) +// fmt.Println("connected to", destAddr) +// if err != nil { +// log.Printf("Failed to connect to %s:%d: %v", destAddr, destPort, err) +// newChannel.Reject(ssh.ConnectionFailed, "could not connect to target") +// return +// } +// +// channel, _, err := newChannel.Accept() +// if err != nil { +// log.Printf("Could not accept forwarded channel: %v", err) +// targetConn.Close() +// return +// } +// +// go io.Copy(channel, targetConn) +// go io.Copy(targetConn, channel) +//} + +func writeSSHString(buffer *bytes.Buffer, str string) { + binary.Write(buffer, binary.BigEndian, uint32(len(str))) + buffer.WriteString(str) +} + +func parseAddr(addr string) (string, int) { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + log.Println("Failed to parse origin address:", err) + return "0.0.0.0", 0 + } + port, _ := strconv.Atoi(portStr) + return host, port +} + +func createForwardedTCPIPPayload(conn net.Conn, port uint32) []byte { + var buf bytes.Buffer + host, originPort := parseAddr(conn.RemoteAddr().String()) + + writeSSHString(&buf, "localhost") + binary.Write(&buf, binary.BigEndian, uint32(port)) + writeSSHString(&buf, host) + binary.Write(&buf, binary.BigEndian, uint32(originPort)) + + return buf.Bytes() +} + +func readSSHString(reader *bytes.Reader) (string, error) { + var length uint32 + if err := binary.Read(reader, binary.BigEndian, &length); err != nil { + return "", err + } + strBytes := make([]byte, length) + if _, err := reader.Read(strBytes); err != nil { + return "", err + } + return string(strBytes), nil +}