Skip to content

Commit

Permalink
[+] #28 Support for dynamic port forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
WangYihang committed Jul 15, 2021
1 parent 9b4101d commit cc2709a
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 16 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,10 @@ First use `Jump` to select a client, then type `PTY`, then type `Interact` to dr
- [ ] Upgrade to Metepreter session
- [ ] Electron frontend
- [ ] [#53 Reload config file](https://github.com/WangYihang/Platypus/issues/53)
- [ ] [#28 Suport remote port forwarding](https://github.com/WangYihang/Platypus/issues/28))
- [ ] [#28 Suport dynamic port forwarding](https://github.com/WangYihang/Platypus/issues/28))
- [ ] [#28 Suport enable internet on the internal machine](https://github.com/WangYihang/Platypus/issues/28))
- [ ] Add version checking in Termite
- [x] [#28 Suport dynamic port forwarding](https://github.com/WangYihang/Platypus/issues/28))
- [x] [#28 Suport remote port forwarding](https://github.com/WangYihang/Platypus/issues/28))
- [x] [#28 Suport local port forwarding](https://github.com/WangYihang/Platypus/issues/28))
- [x] Design Private Protocol
- [x] Check exit state in WebSocket
Expand Down Expand Up @@ -254,7 +255,6 @@ First use `Jump` to select a client, then type `PTY`, then type `Interact` to dr
- [x] Upgrade common reverse shell session into full interactive session
- [x] Docker support (Added by [@yeya24](https://github.com/yeya24))


## Contributors

This project exists thanks to all the people who contribute.
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.14

require (
github.com/WangYihang/readline v0.0.0-20200229084751-518dcf4f57b3
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/creack/pty v1.1.11
Expand All @@ -24,6 +25,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 // indirect
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
github.com/rhysd/go-github-selfupdate v1.2.3
github.com/sevlyar/go-daemon v0.1.5
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ github.com/WangYihang/readline v0.0.0-20200229084751-518dcf4f57b3 h1:vnLf9dGMiEb
github.com/WangYihang/readline v0.0.0-20200229084751-518dcf4f57b3/go.mod h1:S7Ulsa01ssaZKuWV9IRCrAdCtCdI4dQIt39FYOzAEa4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
Expand Down Expand Up @@ -244,6 +246,8 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
3 changes: 1 addition & 2 deletions lib/cli/dispatcher/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ func (dispatcher Dispatcher) Tunnel(args []string) {
log.Info("Mapping local (%s) to remote (%s)", local_address, remote_address)
context.AddPushTunnelConfig(context.Ctx.CurrentTermite, local_address, remote_address)
case "dynamic":
log.Error("TBD")
// context.AddDynamicTunnelConfig(context.Ctx.CurrentTermite, local_address, remote_address)
context.Ctx.CurrentTermite.StartSocks5Server()
case "internet":
log.Error("TBD")
// context.AddInternetTunnelConfig(context.Ctx.CurrentTermite, local_address, remote_address)
Expand Down
5 changes: 4 additions & 1 deletion lib/context/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ func CreateTCPClient(conn net.Conn, server *TCPServer) *TCPClient {

func (c *TCPClient) Close() {
c.conn.Close()
if Ctx.Current == c {
Ctx.Current = nil
}
}

func (c *TCPClient) GetConnString() string {
Expand Down Expand Up @@ -1001,7 +1004,7 @@ func (c *TCPClient) Upload(src string, dst string, broadcast bool) bool {
bar.IncrBy(segmentSize)
bar.DecoratorEwmaUpdate(time.Since(start))

if broadcast && i%64 == 0 {
if broadcast && i%0x10 == 0 {
c.NotifyWebSocketUploadingTermite(bytesSent, totalBytes)
}
}
Expand Down
25 changes: 25 additions & 0 deletions lib/context/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/WangYihang/Platypus/lib/util/str"
humanize "github.com/dustin/go-humanize"
"github.com/jedib0t/go-pretty/table"
"github.com/phayes/freeport"
)

type WebSocketMessage struct {
Expand Down Expand Up @@ -471,10 +472,12 @@ func (s *TCPServer) AddTermiteClient(client *TermiteClient) {
log.Error("Duplicated income connection detected!")

// Respond to termite client that the client is duplicated
client.EncoderLock.Lock()
err := client.Encoder.Encode(message.Message{
Type: message.DUPLICATED_CLIENT,
Body: message.BodyDuplicateClient{},
})
client.EncoderLock.Unlock()
if err != nil {
// TODO: handle network error
log.Error("Network error: %s", err)
Expand Down Expand Up @@ -607,50 +610,58 @@ func TermiteMessageDispatcher(client *TermiteClient) {
conn, err := net.Dial("tcp", tc.Address)
if err != nil {
log.Error("Connecting to %s failed: %s", tc.Address, err.Error())
tc.Termite.EncoderLock.Lock()
tc.Termite.Encoder.Encode(message.Message{
Type: message.PUSH_TUNNEL_CONNECT_FAILED,
Body: message.BodyPushTunnelConnectFailed{
Token: token,
Reason: err.Error(),
},
})
tc.Termite.EncoderLock.Unlock()
} else {
log.Success("Connecting to %s succeed", tc.Address)
Ctx.PushTunnelInstance[token] = PushTunnelInstance{
Termite: tc.Termite,
Conn: &conn,
}
tc.Termite.EncoderLock.Lock()
tc.Termite.Encoder.Encode(message.Message{
Type: message.PUSH_TUNNEL_CONNECTED,
Body: message.BodyPushTunnelConnected{
Token: token,
},
})
tc.Termite.EncoderLock.Unlock()
go func() {
for {
buffer := make([]byte, 0x400)
n, err := conn.Read(buffer)
if err != nil {
log.Debug("Reading from %s failed: %s", tc.Address, err.Error())
tc.Termite.EncoderLock.Lock()
tc.Termite.Encoder.Encode(message.Message{
Type: message.PUSH_TUNNEL_DISCONNECTED,
Body: message.BodyPushTunnelDisonnected{
Token: token,
Reason: err.Error(),
},
})
tc.Termite.EncoderLock.Unlock()
conn.Close()
delete(Ctx.PushTunnelInstance, token)
break
} else {
log.Debug("%d bytes read from %s", n, tc.Address)
tc.Termite.EncoderLock.Lock()
tc.Termite.Encoder.Encode(message.Message{
Type: message.PUSH_TUNNEL_DATA,
Body: message.BodyPushTunnelData{
Token: token,
Data: buffer[0:n],
},
})
tc.Termite.EncoderLock.Unlock()
}
}
}()
Expand Down Expand Up @@ -703,19 +714,33 @@ func TermiteMessageDispatcher(client *TermiteClient) {
if ti, exists := Ctx.PushTunnelInstance[token]; exists {
_, err := (*ti.Conn).Write(data)
if err != nil {
ti.Termite.EncoderLock.Lock()
ti.Termite.Encoder.Encode(message.Message{
Type: message.PUSH_TUNNEL_CONNECT_FAILED,
Body: message.BodyPushTunnelConnectFailed{
Token: token,
Reason: err.Error(),
},
})
ti.Termite.EncoderLock.Unlock()
(*ti.Conn).Close()
delete(Ctx.PushTunnelInstance, token)
}
} else {
log.Debug("No such tunnel: %s", token)
}
case message.DYNAMIC_TUNNEL_CREATED:
port := msg.Body.(*message.BodyDynamicTunnelCreated).Port
local_address := fmt.Sprintf("127.0.0.1:%d", freeport.GetPort())
remote_address := fmt.Sprintf("127.0.0.1:%d", port)
log.Success("Mapping remote socks server (%s) into local address (%s)", remote_address, local_address)
AddPullTunnelConfig(
Ctx.CurrentTermite,
local_address,
remote_address,
)
case message.DYNAMIC_TUNNEL_CREATE_FAILED:
log.Error(msg.Body.(*message.BodyDynamicTunnelCreateFailed).Reason)
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions lib/context/termite.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ func (c *TermiteClient) GetHashFormat() string {
return c.server.hashFormat
}

func (c *TermiteClient) StartSocks5Server() {
c.EncoderLock.Lock()
c.Encoder.Encode(message.Message{
Type: message.DYNAMIC_TUNNEL_CREATE,
Body: message.BodyDynamicTunnelCreate{},
})
c.EncoderLock.Unlock()
}

func (c *TermiteClient) GatherClientInfo(hashFormat string) bool {
log.Info("Gathering information from termite client...")

Expand Down Expand Up @@ -299,6 +308,9 @@ func (c *TermiteClient) Close() {
}
}
c.conn.Close()
if Ctx.CurrentTermite == c {
Ctx.CurrentTermite = nil
}
}

func (c *TermiteClient) AsTable() {
Expand Down
26 changes: 26 additions & 0 deletions lib/util/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (
PUSH_TUNNEL_CONNECT_FAILED
PUSH_TUNNEL_DISCONNECTED
PUSH_TUNNEL_DISCONNECT_FAILED
DYNAMIC_TUNNEL_CREATE
DYNAMIC_TUNNEL_DESTROY

// Termite -> Platypus
PROCESS_STARTED
Expand All @@ -40,6 +42,10 @@ const (
PUSH_TUNNEL_CREATE_FAILED
PUSH_TUNNEL_DELETED
PUSH_TUNNEL_DELETE_FAILED
DYNAMIC_TUNNEL_CREATED
DYNAMIC_TUNNEL_CREATE_FAILED
DYNAMIC_TUNNEL_DESTROIED
DYNAMIC_TUNNEL_DESTROY_FAILED
)

type Message struct {
Expand Down Expand Up @@ -175,6 +181,19 @@ type BodyPushTunnelDisonnectFailed struct {
Token string
}

type BodyDynamicTunnelCreate struct{}
type BodyDynamicTunnelCreated struct {
Port int
}
type BodyDynamicTunnelCreateFailed struct {
Reason string
}
type BodyDynamicTunnelDestroy struct{}
type BodyDynamicTunnelDestroied struct{}
type BodyDynamicTunnelDestroyFailed struct {
Reason string
}

func RegisterGob() {
// Client Management
gob.Register(&BodyClientInfo{})
Expand Down Expand Up @@ -208,4 +227,11 @@ func RegisterGob() {
gob.Register(&BodyPushTunnelDisonnect{})
gob.Register(&BodyPushTunnelDisonnected{})
gob.Register(&BodyPushTunnelDisonnectFailed{})
// Dynamic port forwarding
gob.Register(&BodyDynamicTunnelCreate{})
gob.Register(&BodyDynamicTunnelCreated{})
gob.Register(&BodyDynamicTunnelCreateFailed{})
gob.Register(&BodyDynamicTunnelDestroy{})
gob.Register(&BodyDynamicTunnelDestroied{})
gob.Register(&BodyDynamicTunnelDestroyFailed{})
}
Loading

0 comments on commit cc2709a

Please sign in to comment.