Projects STRLCPY reverse_ssh Commits bd083646
🤬
  • Fix shell designation for linux

  • Loading...
  • NHAS committed 2 years ago
    bd083646
    1 parent 702610f4
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    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 + 
  • ■ ■ ■ ■ ■ ■
    internal/client/handlers/shell.go
    skipped 54 lines
    55 55   
    56 56  }
    57 57   
    58  -//This basically handles exactly like a SSH server would
    59  -func shell(user *internal.User, connection ssh.Channel, requests <-chan *ssh.Request, log logger.Logger) {
     58 +func runCommandWithPty(command string, args []string, user *internal.User, requests <-chan *ssh.Request, log logger.Logger, connection ssh.Channel) {
    60 59   
    61  - path := ""
    62  - if len(shells) != 0 {
    63  - path = shells[0]
     60 + if user.Pty == nil {
     61 + log.Error("Requested to run a command with a pty, but did not start a pty")
     62 + return
    64 63   }
    65 64   
    66 65   // Fire up a shell for this session
    67  - shell := exec.Command(path)
     66 + shell := exec.Command(command, args...)
    68 67   shell.Env = os.Environ()
    69 68   
    70 69   close := func() {
    skipped 13 lines
    84 83   var err error
    85 84   var shellIO io.ReadWriteCloser
    86 85   
    87  - if user.Pty != nil {
    88  - shell.Env = append(shell.Env, "TERM="+user.Pty.Term)
     86 + shell.Env = append(shell.Env, "TERM="+user.Pty.Term)
    89 87   
    90  - log.Info("Creating pty...")
    91  - shellIO, err = pty.StartWithSize(shell, &pty.Winsize{Cols: uint16(user.Pty.Columns), Rows: uint16(user.Pty.Rows)})
    92  - if err != nil {
    93  - log.Info("Could not start pty (%s)", err)
    94  - close()
    95  - return
    96  - }
    97  - } else {
    98  - 
    99  - stdinPipe, err := shell.StdinPipe()
    100  - stdoutPipe, err := shell.StdoutPipe()
    101  - shell.Stderr = shell.Stdout
    102  - 
    103  - shellIO = &ReaderWriteCloser{in: stdinPipe, out: stdoutPipe}
    104  - 
    105  - err = shell.Start()
    106  - if err != nil {
    107  - log.Info("Could not start shell (%s)", err)
    108  - close()
    109  - return
    110  - }
     88 + log.Info("Creating pty...")
     89 + shellIO, err = pty.StartWithSize(shell, &pty.Winsize{Cols: uint16(user.Pty.Columns), Rows: uint16(user.Pty.Rows)})
     90 + if err != nil {
     91 + log.Info("Could not start pty (%s)", err)
     92 + close()
     93 + return
    111 94   }
    112 95   
    113  - //pipe session to bash and visa-versa
     96 + // pipe session to bash and visa-versa
    114 97   var once sync.Once
    115 98   go func() {
    116 99   io.Copy(connection, shellIO)
    skipped 28 lines
    145 128   
    146 129   defer once.Do(close)
    147 130   shell.Wait()
    148  - 
    149 131  }
    150 132   
    151  -type ReaderWriteCloser struct {
    152  - in io.WriteCloser
    153  - out io.ReadCloser
    154  -}
     133 +// This basically handles exactly like a SSH server would
     134 +func shell(user *internal.User, connection ssh.Channel, requests <-chan *ssh.Request, log logger.Logger) {
    155 135   
    156  -func (c *ReaderWriteCloser) Read(b []byte) (n int, err error) {
    157  - return c.out.Read(b)
    158  -}
     136 + path := ""
     137 + if len(shells) != 0 {
     138 + path = shells[0]
     139 + }
    159 140   
    160  -func (c *ReaderWriteCloser) Write(b []byte) (n int, err error) {
    161  - return c.in.Write(b)
    162  -}
     141 + if user.Pty != nil {
     142 + runCommandWithPty(path, nil, user, requests, log, connection)
     143 + return
     144 + }
    163 145   
    164  -func (c *ReaderWriteCloser) Close() error {
    165  - c.in.Close()
    166  - 
    167  - err := c.out.Close()
     146 + runCommand(path, nil, connection)
    168 147   
    169  - return err
    170 148  }
    171 149   
  • ■ ■ ■ ■ ■ ■
    internal/client/handlers/shell_windows.go
    skipped 7 lines
    8 8   "io"
    9 9   "os"
    10 10   "os/exec"
     11 + "strings"
    11 12   "syscall"
    12 13   
    13 14   "github.com/ActiveState/termtest/conpty"
    skipped 12 lines
    26 27   return
    27 28   }
    28 29   
     30 + path, err := exec.LookPath("powershell.exe")
     31 + if err != nil {
     32 + path, err = exec.LookPath("cmd.exe")
     33 + if err != nil {
     34 + path = "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
     35 + }
     36 + }
     37 + 
     38 + runCommandWithPty(path, nil, user, requests, log, connection)
     39 + 
     40 + connection.Close()
     41 + 
     42 +}
     43 + 
     44 +func runCommandWithPty(command string, args []string, user *internal.User, requests <-chan *ssh.Request, log logger.Logger, connection ssh.Channel) {
     45 + 
     46 + fullCommand := command + strings.Join(args, " ")
    29 47   vsn := windows.RtlGetVersion()
    30 48   if vsn.MajorVersion < 10 || vsn.BuildNumber < 17763 {
    31 49   
    32 50   log.Info("Windows version too old for Conpty (%d, %d), using basic shell", vsn.MajorVersion, vsn.BuildNumber)
    33  - 
    34  - winpty, err := winpty.Open("powershell.exe", user.Pty.Columns, user.Pty.Rows)
    35  - if err != nil {
    36  - log.Info("Winpty failed. %s", err)
    37  - basicShell(connection, requests, log)
    38  - return
    39  - }
    40  - 
    41  - go func() {
    42  - io.Copy(connection, winpty)
    43  - connection.Close()
    44  - }()
     51 + runWithWinPty(fullCommand, connection, requests, log, *user.Pty)
    45 52   
    46  - io.Copy(winpty, connection)
    47  - winpty.Close()
    48 53   } else {
    49  - err := conptyShell(connection, requests, log, *user.Pty)
     54 + err := runWithConpty(fullCommand, connection, requests, log, *user.Pty)
    50 55   if err != nil {
    51  - log.Error("%v", err)
     56 + log.Error("unable to run with conpty, falling back to winpty: %v", err)
     57 + runWithWinPty(fullCommand, connection, requests, log, *user.Pty)
    52 58   }
    53 59   }
     60 +}
    54 61   
    55  - connection.Close()
     62 +func runWithWinPty(command string, connection ssh.Channel, reqs <-chan *ssh.Request, log logger.Logger, ptyReq internal.PtyReq) error {
     63 + winpty, err := winpty.Open(command, ptyReq.Columns, ptyReq.Rows)
     64 + if err != nil {
     65 + log.Info("Winpty failed. %s", err)
     66 + return err
     67 + }
     68 + 
     69 + go func() {
     70 + io.Copy(connection, winpty)
     71 + connection.Close()
     72 + }()
     73 + 
     74 + io.Copy(winpty, connection)
     75 + winpty.Close()
    56 76   
     77 + return nil
    57 78  }
    58 79   
    59  -func conptyShell(connection ssh.Channel, reqs <-chan *ssh.Request, log logger.Logger, ptyReq internal.PtyReq) error {
     80 +func runWithConpty(command string, connection ssh.Channel, reqs <-chan *ssh.Request, log logger.Logger, ptyReq internal.PtyReq) error {
    60 81   
    61 82   cpty, err := conpty.New(int16(ptyReq.Columns), int16(ptyReq.Rows))
    62 83   if err != nil {
    skipped 1 lines
    64 85   }
    65 86   defer cpty.Close()
    66 87   
    67  - path, err := exec.LookPath("powershell.exe")
     88 + path, err := exec.LookPath(command)
    68 89   if err != nil {
    69  - path, err = exec.LookPath("cmd.exe")
    70  - if err != nil {
    71  - path = "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
    72  - }
     90 + return err
    73 91   }
    74 92   
    75 93   // Spawn and catch new powershell process
    skipped 10 lines
    86 104   log.Info("New process with pid %d spawned", pid)
    87 105   process, err := os.FindProcess(pid)
    88 106   if err != nil {
    89  - log.Fatal("Failed to find process: %v", err)
     107 + return fmt.Errorf("Failed to find process: %v", err)
    90 108   }
    91 109   
    92 110   // Dynamically handle resizes of terminal window
    skipped 114 lines
  • ■ ■ ■ ■ ■ ■
    internal/global.go
    skipped 18 lines
    19 19   
    20 20  var Version string
    21 21   
     22 +type ShellStruct struct {
     23 + Shell string
     24 +}
     25 + 
    22 26  type RemoteForwardRequest struct {
    23 27   BindAddr string
    24 28   BindPort uint32
    skipped 126 lines
  • ■ ■ ■ ■ ■ ■
    internal/server/commands/connect.go
    skipped 27 lines
    28 28   return fmt.Errorf("connect can only be called from the terminal, if you want to connect to your clients without connecting to the terminal use jumphost syntax -J")
    29 29   }
    30 30   
    31  - if len(line.Arguments) != 1 {
     31 + if len(line.Arguments) < 1 {
    32 32   return fmt.Errorf(c.Help(false))
    33 33   }
    34 34   
    35  - foundClients, err := clients.Search(line.Arguments[0].Value())
     35 + shell, _ := line.GetArgString("shell")
     36 + 
     37 + client := line.Arguments[len(line.Arguments)-1].Value()
     38 + 
     39 + foundClients, err := clients.Search(client)
    36 40   if err != nil {
    37 41   return err
    38 42   }
    39 43   
    40 44   if len(foundClients) == 0 {
    41  - return fmt.Errorf("No clients matched '%s'", line.Arguments[0].Value())
     45 + return fmt.Errorf("No clients matched '%s'", client)
    42 46   }
    43 47   
    44 48   if len(foundClients) > 1 {
    45  - return fmt.Errorf("'%s' matches multiple clients please choose a more specific identifier", line.Arguments[0].Value())
     49 + return fmt.Errorf("'%s' matches multiple clients please choose a more specific identifier", client)
    46 50   }
    47 51   
    48 52   var target ssh.Conn
    skipped 10 lines
    59 63   
    60 64   //Attempt to connect to remote host and send inital pty request and screen size
    61 65   // If we cant, report and error to the clients terminal
    62  - newSession, err := createSession(target, *c.user.Pty)
     66 + newSession, err := createSession(target, *c.user.Pty, shell)
    63 67   if err != nil {
    64 68   
    65 69   c.log.Error("Creating session failed: %s", err)
    skipped 27 lines
    93 97   }
    94 98   
    95 99   return terminal.MakeHelpText(
    96  - "connect " + autocomplete.RemoteId,
     100 + "connect "+autocomplete.RemoteId,
     101 + "\t--shell\tSet the shell (or program) to start on connection",
    97 102   )
    98 103  }
    99 104   
    skipped 6 lines
    106 111   }
    107 112  }
    108 113   
    109  -func createSession(sshConn ssh.Conn, ptyReq internal.PtyReq) (sc ssh.Channel, err error) {
     114 +func createSession(sshConn ssh.Conn, ptyReq internal.PtyReq, shell string) (sc ssh.Channel, err error) {
    110 115   
    111 116   splice, newrequests, err := sshConn.OpenChannel("session", nil)
    112 117   if err != nil {
    skipped 6 lines
    119 124   return sc, fmt.Errorf("Unable to send PTY request: %s", err)
    120 125   }
    121 126   
    122  - _, err = splice.SendRequest("shell", true, nil)
     127 + _, err = splice.SendRequest("shell", true, ssh.Marshal(internal.ShellStruct{Shell: shell}))
    123 128   if err != nil {
    124 129   return sc, fmt.Errorf("Unable to start shell: %s", err)
    125 130   }
    skipped 56 lines
Please wait...
Page is in error, reload to recover