Skip to content

Commit 5e27b23

Browse files
authored
Merge pull request #2158 from BishopFox/fix/shell
Fix `shell` pty resizing
2 parents 6011d23 + 86f41e9 commit 5e27b23

21 files changed

Lines changed: 1748 additions & 928 deletions

client/command/exec/execute-shellcode.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,25 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r
157157

158158
tunnel := core.GetTunnels().Start(rpcTunnel.GetTunnelID(), rpcTunnel.GetSessionID())
159159

160+
var rows uint32
161+
var cols uint32
162+
if !noPty {
163+
colsInt, rowsInt, err := term.GetSize(int(os.Stdout.Fd()))
164+
if err != nil || rowsInt <= 0 || colsInt <= 0 {
165+
colsInt, rowsInt, err = term.GetSize(int(os.Stdin.Fd()))
166+
}
167+
if err == nil && rowsInt > 0 && colsInt > 0 {
168+
rows = uint32(rowsInt)
169+
cols = uint32(colsInt)
170+
}
171+
}
172+
160173
shell, err := con.Rpc.Shell(context.Background(), &sliverpb.ShellReq{
161174
Request: con.ActiveTarget.Request(cmd),
162175
Path: hostProc,
163176
EnablePTY: !noPty,
177+
Rows: rows,
178+
Cols: cols,
164179
TunnelID: tunnel.ID,
165180
})
166181
if err != nil {
@@ -200,6 +215,12 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r
200215
}
201216
}
202217

218+
stopPtyResize := func() {}
219+
if !noPty {
220+
stopPtyResize = startPtyResizeWatcher(con, cmd, tunnel.ID)
221+
}
222+
defer stopPtyResize()
223+
203224
log.Printf("Starting stdin/stdout shell ...")
204225
go func() {
205226
n, err := io.Copy(os.Stdout, tunnel)

client/command/exec/resize_unix.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//go:build !windows
2+
3+
package exec
4+
5+
import (
6+
"context"
7+
"os"
8+
"os/signal"
9+
"sync"
10+
"syscall"
11+
"time"
12+
13+
"github.com/bishopfox/sliver/client/console"
14+
"github.com/bishopfox/sliver/protobuf/sliverpb"
15+
"github.com/spf13/cobra"
16+
"golang.org/x/term"
17+
"google.golang.org/grpc/codes"
18+
"google.golang.org/grpc/status"
19+
)
20+
21+
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
22+
if con == nil || con.Rpc == nil {
23+
return func() {}
24+
}
25+
26+
stdoutFd := int(os.Stdout.Fd())
27+
stdinFd := int(os.Stdin.Fd())
28+
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
29+
return func() {}
30+
}
31+
32+
sigCh := make(chan os.Signal, 1)
33+
signal.Notify(sigCh, syscall.SIGWINCH)
34+
35+
stopCh := make(chan struct{})
36+
doneCh := make(chan struct{})
37+
38+
go func() {
39+
defer close(doneCh)
40+
41+
lastCols, lastRows := -1, -1
42+
send := func() bool {
43+
cols, rows, err := term.GetSize(stdoutFd)
44+
if err != nil || cols <= 0 || rows <= 0 {
45+
cols, rows, err = term.GetSize(stdinFd)
46+
}
47+
if err != nil || cols <= 0 || rows <= 0 {
48+
return true
49+
}
50+
if cols == lastCols && rows == lastRows {
51+
return true
52+
}
53+
lastCols, lastRows = cols, rows
54+
55+
req := con.ActiveTarget.Request(cmd)
56+
if req == nil {
57+
return false
58+
}
59+
req.Timeout = int64(2 * time.Second)
60+
61+
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
62+
Request: req,
63+
TunnelID: tunnelID,
64+
Rows: uint32(rows),
65+
Cols: uint32(cols),
66+
})
67+
if status.Code(err) == codes.Unimplemented {
68+
return false
69+
}
70+
return true
71+
}
72+
73+
if !send() {
74+
return
75+
}
76+
77+
for {
78+
select {
79+
case <-stopCh:
80+
return
81+
case <-sigCh:
82+
if !send() {
83+
return
84+
}
85+
}
86+
}
87+
}()
88+
89+
var once sync.Once
90+
return func() {
91+
once.Do(func() {
92+
signal.Stop(sigCh)
93+
close(stopCh)
94+
<-doneCh
95+
})
96+
}
97+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//go:build windows
2+
3+
package exec
4+
5+
import (
6+
"context"
7+
"github.com/bishopfox/sliver/client/console"
8+
"github.com/bishopfox/sliver/protobuf/sliverpb"
9+
"github.com/spf13/cobra"
10+
"golang.org/x/term"
11+
"google.golang.org/grpc/codes"
12+
"google.golang.org/grpc/status"
13+
"os"
14+
"sync"
15+
"time"
16+
)
17+
18+
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
19+
if con == nil || con.Rpc == nil {
20+
return func() {}
21+
}
22+
23+
stdoutFd := int(os.Stdout.Fd())
24+
stdinFd := int(os.Stdin.Fd())
25+
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
26+
return func() {}
27+
}
28+
29+
stopCh := make(chan struct{})
30+
doneCh := make(chan struct{})
31+
32+
go func() {
33+
defer close(doneCh)
34+
35+
ticker := time.NewTicker(250 * time.Millisecond)
36+
defer ticker.Stop()
37+
38+
lastCols, lastRows := -1, -1
39+
send := func() bool {
40+
cols, rows, err := term.GetSize(stdoutFd)
41+
if err != nil || cols <= 0 || rows <= 0 {
42+
cols, rows, err = term.GetSize(stdinFd)
43+
}
44+
if err != nil || cols <= 0 || rows <= 0 {
45+
return true
46+
}
47+
if cols == lastCols && rows == lastRows {
48+
return true
49+
}
50+
lastCols, lastRows = cols, rows
51+
52+
req := con.ActiveTarget.Request(cmd)
53+
if req == nil {
54+
return false
55+
}
56+
req.Timeout = int64(2 * time.Second)
57+
58+
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
59+
Request: req,
60+
TunnelID: tunnelID,
61+
Rows: uint32(rows),
62+
Cols: uint32(cols),
63+
})
64+
if status.Code(err) == codes.Unimplemented {
65+
return false
66+
}
67+
return true
68+
}
69+
70+
if !send() {
71+
return
72+
}
73+
74+
for {
75+
select {
76+
case <-stopCh:
77+
return
78+
case <-ticker.C:
79+
if !send() {
80+
return
81+
}
82+
}
83+
}
84+
}()
85+
86+
var once sync.Once
87+
return func() {
88+
once.Do(func() {
89+
close(stopCh)
90+
<-doneCh
91+
})
92+
}
93+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//go:build !windows
2+
3+
package shell
4+
5+
import (
6+
"context"
7+
"os"
8+
"os/signal"
9+
"sync"
10+
"syscall"
11+
"time"
12+
13+
"github.com/bishopfox/sliver/client/console"
14+
"github.com/bishopfox/sliver/protobuf/sliverpb"
15+
"github.com/spf13/cobra"
16+
"golang.org/x/term"
17+
"google.golang.org/grpc/codes"
18+
"google.golang.org/grpc/status"
19+
)
20+
21+
func startPtyResizeWatcher(con *console.SliverClient, cmd *cobra.Command, tunnelID uint64) func() {
22+
if con == nil || con.Rpc == nil {
23+
return func() {}
24+
}
25+
26+
stdoutFd := int(os.Stdout.Fd())
27+
stdinFd := int(os.Stdin.Fd())
28+
if !term.IsTerminal(stdoutFd) && !term.IsTerminal(stdinFd) {
29+
return func() {}
30+
}
31+
32+
sigCh := make(chan os.Signal, 1)
33+
signal.Notify(sigCh, syscall.SIGWINCH)
34+
35+
stopCh := make(chan struct{})
36+
doneCh := make(chan struct{})
37+
38+
go func() {
39+
defer close(doneCh)
40+
41+
lastCols, lastRows := -1, -1
42+
send := func() bool {
43+
cols, rows, err := term.GetSize(stdoutFd)
44+
if err != nil || cols <= 0 || rows <= 0 {
45+
cols, rows, err = term.GetSize(stdinFd)
46+
}
47+
if err != nil || cols <= 0 || rows <= 0 {
48+
return true
49+
}
50+
if cols == lastCols && rows == lastRows {
51+
return true
52+
}
53+
lastCols, lastRows = cols, rows
54+
55+
req := con.ActiveTarget.Request(cmd)
56+
if req == nil {
57+
return false
58+
}
59+
req.Timeout = int64(2 * time.Second)
60+
61+
_, err = con.Rpc.ShellResize(context.Background(), &sliverpb.ShellResizeReq{
62+
Request: req,
63+
TunnelID: tunnelID,
64+
Rows: uint32(rows),
65+
Cols: uint32(cols),
66+
})
67+
if status.Code(err) == codes.Unimplemented {
68+
return false
69+
}
70+
return true
71+
}
72+
73+
if !send() {
74+
return
75+
}
76+
77+
for {
78+
select {
79+
case <-stopCh:
80+
return
81+
case <-sigCh:
82+
if !send() {
83+
return
84+
}
85+
}
86+
}
87+
}()
88+
89+
var once sync.Once
90+
return func() {
91+
once.Do(func() {
92+
signal.Stop(sigCh)
93+
close(stopCh)
94+
<-doneCh
95+
})
96+
}
97+
}

0 commit comments

Comments
 (0)