/
interact.go
159 lines (142 loc) · 4.38 KB
/
interact.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package dispatcher
import (
"bufio"
"fmt"
"os"
"strings"
"time"
"github.com/WangYihang/Platypus/lib/context"
"github.com/WangYihang/Platypus/lib/util/log"
"golang.org/x/term"
)
func (dispatcher Dispatcher) Interact(args []string) {
if context.Ctx.Current == nil && context.Ctx.CurrentTermite == nil {
log.Error("Interactive session is not set, please use `Jump` command to set the interactive Interact")
return
}
if context.Ctx.Current != nil {
current := context.Ctx.Current
log.Info("Interacting with %s", current.FullDesc())
// Set to interactive
current.GetInteractingLock().Lock()
defer func() { current.GetInteractingLock().Unlock() }()
current.SetInteractive(true)
defer func() { current.SetInteractive(false) }()
context.Ctx.Interacting.Lock()
defer func() { context.Ctx.Interacting.Unlock() }()
if current.GetPtyEstablished() {
// Step 4: Enable Raw mode of attacker pty
log.Info("Setting attacker terminal to raw mode")
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
panic(err)
}
// Restore tty properties
defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }()
var cont bool = true
// Client output -> Platypus stdout
go func() {
for current.GetInteractive() && current.GetPtyEstablished() && cont {
current.GetConn().SetReadDeadline(time.Time{})
m := make([]byte, 1)
n, err := current.ReadConnLock(m)
if err == nil {
os.Stdout.Write(m[0:n])
}
}
}()
magic := "platyquit"
firstLine := true
inputQueueIndex := 0
inputQueueLength := len(magic) + 1 // + 1 for clrf
inputQueue := make([]byte, inputQueueLength)
firstHint := true
// Client input <- Platypus stdin
for current.GetInteractive() && current.GetPtyEstablished() && cont {
// Magic exit mantra: 'exit' is typed
// Check whether user want to exit pty mode
// BUG: Only works in shell prompt,
// failed in foreground process trying to read from stdin (eg: vim / htop)
// failed in nested shell (eg: bash -> ... -> bash)
var pattern string
if firstLine {
pattern = magic
} else {
pattern = "\r" + magic
}
matched := false
if firstHint && strings.Contains(string(inputQueue)+string(inputQueue), "exit") {
log.Info("You can type `%s` to return to Platypus", magic)
firstHint = false
}
if strings.Contains(string(inputQueue)+string(inputQueue), pattern) {
// Exit Pty
matched = true
}
m := make([]byte, 1)
n, err := os.Stdin.Read(m)
if err == nil {
for i := 0; i < n; i++ {
// Backspace
if m[i] == 8 {
// inputQueueIndex = int(math.Max(float64(0), float64(inputQueueIndex-1)))
inputQueueIndex--
if inputQueueIndex < 0 {
inputQueueIndex = inputQueueLength + inputQueueIndex
}
} else {
// user typed: `\rexit` + `\r`
if m[i] == 13 && matched {
firstLine = false
cont = false
current.SetPtyEstablished(false)
term.Restore(int(os.Stdin.Fd()), oldState)
break
}
inputQueue[inputQueueIndex] = m[i]
inputQueueIndex += n
inputQueueIndex %= inputQueueLength
}
}
current.Write(m[0:n])
}
}
} else {
log.Error("PTY is not established, drop into normal reverse shell mode. You can use `PTY` command to enable PTY mode.")
// Client output -> Platypus stdout
go func() {
for current.GetInteractive() && !current.GetPtyEstablished() {
current.GetConn().SetReadDeadline(time.Time{})
m := make([]byte, 0x100)
n, err := current.ReadConnLock(m)
if err == nil {
os.Stdout.Write(m[0:n])
}
}
}()
// Client input <- Platypus stdin
for current.GetInteractive() && !current.GetPtyEstablished() {
inputReader := bufio.NewReader(os.Stdin)
command, _ := inputReader.ReadString('\n')
command = strings.TrimSpace(command)
if command == "exit" {
break
}
current.Write([]byte(command + "\n"))
}
}
return
}
if context.Ctx.CurrentTermite != nil {
current := context.Ctx.CurrentTermite
current.StartShell()
}
}
func (dispatcher Dispatcher) InteractHelp(args []string) {
fmt.Println("Usage of Interact")
fmt.Println("\tInteract")
}
func (dispatcher Dispatcher) InteractDesc(args []string) {
fmt.Println("Interact")
fmt.Println("\tPop up a interactive session, you can communicate with it via stdin/stdout")
}