2026-04-06 00:20:51 -05:00

1313 lines
37 KiB
Go

package axscript
import (
"AdaptixServer/core/utils/logs"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
"time"
"unicode/utf16"
"github.com/dop251/goja"
)
func registerAxBridge(engine *ScriptEngine) {
rt := engine.runtime
axObj := rt.NewObject()
axObj.Set("create_command", func(call goja.FunctionCall) goja.Value {
name := ""
description := ""
example := ""
message := ""
if len(call.Arguments) > 0 {
name = call.Argument(0).String()
}
if len(call.Arguments) > 1 {
description = call.Argument(1).String()
}
if len(call.Arguments) > 2 {
example = call.Argument(2).String()
}
if len(call.Arguments) > 3 {
message = call.Argument(3).String()
}
builder := newJsCommandBuilder(engine, name, description, example, message)
obj := rt.NewObject()
obj.Set("addArgBool", builder.AddArgBool)
obj.Set("addArgInt", builder.AddArgInt)
obj.Set("addArgFlagInt", builder.AddArgFlagInt)
obj.Set("addArgString", builder.AddArgString)
obj.Set("addArgFlagString", builder.AddArgFlagString)
obj.Set("addArgFile", builder.AddArgFile)
obj.Set("addArgFlagFile", builder.AddArgFlagFile)
obj.Set("addSubCommands", builder.AddSubCommands)
obj.Set("setPreHook", builder.SetPreHook)
obj.Set("__builder", builder)
return obj
})
axObj.Set("create_commands_group", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) >= 2 {
groupName := call.Argument(0).String()
arrayVal := call.Argument(1)
group := newJsCommandGroupBuilder(engine)
group.SetParamsFromValue(groupName, arrayVal)
obj := rt.NewObject()
obj.Set("setParams", group.SetParams)
obj.Set("add", group.Add)
obj.Set("__group", group)
return obj
}
group := newJsCommandGroupBuilder(engine)
obj := rt.NewObject()
obj.Set("setParams", group.SetParams)
obj.Set("add", group.Add)
obj.Set("__group", group)
return obj
})
axObj.Set("execute_command", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
panic(rt.NewTypeError("execute_command: script manager not available"))
}
if len(call.Arguments) < 2 {
panic(rt.NewTypeError("execute_command requires at least 2 arguments: (agentId, cmdline)"))
}
agentId := call.Argument(0).String()
cmdline := call.Argument(1).String()
var postHookFn goja.Callable
var handlerFn goja.Callable
if len(call.Arguments) > 2 && !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
fn, ok := goja.AssertFunction(call.Argument(2))
if ok {
postHookFn = fn
}
}
if len(call.Arguments) > 3 && !goja.IsUndefined(call.Argument(3)) && !goja.IsNull(call.Argument(3)) {
fn, ok := goja.AssertFunction(call.Argument(3))
if ok {
handlerFn = fn
}
}
err := engine.manager.ExecuteCommand(engine, agentId, cmdline, postHookFn, handlerFn)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("execute_alias", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
panic(rt.NewTypeError("execute_alias: script manager not available"))
}
if len(call.Arguments) < 3 {
panic(rt.NewTypeError("execute_alias requires at least 3 arguments: (agentId, originalCmdline, aliasCmdline)"))
}
agentId := call.Argument(0).String()
displayCmdline := call.Argument(1).String()
aliasCmdline := call.Argument(2).String()
message := ""
if len(call.Arguments) > 3 && !goja.IsUndefined(call.Argument(3)) && !goja.IsNull(call.Argument(3)) {
message = call.Argument(3).String()
}
// arg4 = hook (optional)
// arg5 = handler (optional)
var postHookFn goja.Callable
var handlerFn goja.Callable
if len(call.Arguments) > 4 && !goja.IsUndefined(call.Argument(4)) && !goja.IsNull(call.Argument(4)) {
fn, ok := goja.AssertFunction(call.Argument(4))
if ok {
postHookFn = fn
}
}
if len(call.Arguments) > 5 && !goja.IsUndefined(call.Argument(5)) && !goja.IsNull(call.Argument(5)) {
fn, ok := goja.AssertFunction(call.Argument(5))
if ok {
handlerFn = fn
}
}
err := engine.manager.ExecuteAliasWithHooks(engine, agentId, displayCmdline, aliasCmdline, message, postHookFn, handlerFn)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("execute_alias_hook", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
panic(rt.NewTypeError("execute_alias_hook: script manager not available"))
}
if len(call.Arguments) < 5 {
panic(rt.NewTypeError("execute_alias_hook requires 5 arguments"))
}
agentId := call.Argument(0).String()
displayCmdline := call.Argument(1).String()
aliasCmdline := call.Argument(2).String()
message := ""
if len(call.Arguments) > 3 && !goja.IsUndefined(call.Argument(3)) && !goja.IsNull(call.Argument(3)) {
message = call.Argument(3).String()
}
var hookFn goja.Callable
if fn, ok := goja.AssertFunction(call.Argument(4)); ok {
hookFn = fn
}
err := engine.manager.ExecuteAliasWithHooks(engine, agentId, displayCmdline, aliasCmdline, message, hookFn, nil)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("execute_alias_handler", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
panic(rt.NewTypeError("execute_alias_handler: script manager not available"))
}
if len(call.Arguments) < 5 {
panic(rt.NewTypeError("execute_alias_handler requires 5 arguments"))
}
agentId := call.Argument(0).String()
displayCmdline := call.Argument(1).String()
aliasCmdline := call.Argument(2).String()
message := ""
if len(call.Arguments) > 3 && !goja.IsUndefined(call.Argument(3)) && !goja.IsNull(call.Argument(3)) {
message = call.Argument(3).String()
}
var handlerFn goja.Callable
if fn, ok := goja.AssertFunction(call.Argument(4)); ok {
handlerFn = fn
}
err := engine.manager.ExecuteAliasWithHooks(engine, agentId, displayCmdline, aliasCmdline, message, nil, handlerFn)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("agents", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue(map[string]interface{}{})
}
agents := engine.manager.GetAgents()
return rt.ToValue(agents)
})
axObj.Set("agent_info", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
property := call.Argument(1).String()
info := engine.manager.GetAgentInfo(agentId, property)
if info == nil {
return goja.Undefined()
}
return rt.ToValue(info)
})
axObj.Set("ids", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]string{})
}
return rt.ToValue(engine.manager.GetAgentIds())
})
axObj.Set("credentials", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(engine.manager.GetCredentials())
})
axObj.Set("targets", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(engine.manager.GetTargets())
})
axObj.Set("console_message", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 3 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
msgType := int(call.Argument(1).ToInteger())
message := call.Argument(2).String()
clearText := ""
if len(call.Arguments) > 3 {
clearText = call.Argument(3).String()
}
engine.manager.ConsoleMessage(agentId, msgType, message, clearText)
return goja.Undefined()
})
axObj.Set("log", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) > 0 {
logs.Info("", "[%s] %s", engine.name, call.Argument(0).String())
}
return goja.Undefined()
})
axObj.Set("log_error", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) > 0 {
logs.Error("", "[%s] %s", engine.name, call.Argument(0).String())
}
return goja.Undefined()
})
axObj.Set("execute_command_hook", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 3 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
cmdline := call.Argument(1).String()
var hookFn goja.Callable
if fn, ok := goja.AssertFunction(call.Argument(2)); ok {
hookFn = fn
}
err := engine.manager.ExecuteCommand(engine, agentId, cmdline, hookFn, nil)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("execute_command_handler", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 3 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
cmdline := call.Argument(1).String()
var handlerFn goja.Callable
if fn, ok := goja.AssertFunction(call.Argument(2)); ok {
handlerFn = fn
}
err := engine.manager.ExecuteCommand(engine, agentId, cmdline, nil, handlerFn)
if err != nil {
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("register_commands_group", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
groupVal := call.Argument(0)
agentsVal := call.Argument(1)
var groupBuilder *jsCommandGroupBuilder
if exported := groupVal.Export(); exported != nil {
if gb, ok := exported.(*jsCommandGroupBuilder); ok {
groupBuilder = gb
} else if m, ok := exported.(map[string]interface{}); ok {
if gv, exists := m["__group"]; exists {
if gb2, ok2 := gv.(*jsCommandGroupBuilder); ok2 {
groupBuilder = gb2
}
}
}
}
if groupBuilder == nil {
groupObj := groupVal.ToObject(rt)
gv := groupObj.Get("__group")
if gv != nil && !goja.IsUndefined(gv) && !goja.IsNull(gv) {
if gb, ok := gv.Export().(*jsCommandGroupBuilder); ok {
groupBuilder = gb
}
}
}
if groupBuilder == nil {
logs.Warn("", "register_commands_group: invalid group object")
return goja.Undefined()
}
metaVal := rt.Get("metadata")
if metaVal != nil && !goja.IsUndefined(metaVal) && !goja.IsNull(metaVal) {
metaObj := metaVal.ToObject(rt)
if groupBuilder.name == "" {
nameVal := metaObj.Get("name")
if nameVal != nil && !goja.IsUndefined(nameVal) && !goja.IsNull(nameVal) {
groupBuilder.name = nameVal.String()
}
}
if groupBuilder.description == "" {
descVal := metaObj.Get("description")
if descVal != nil && !goja.IsUndefined(descVal) && !goja.IsNull(descVal) {
groupBuilder.description = descVal.String()
}
}
}
agentNames := exportStringArray(rt, agentsVal)
var osList []int
if len(call.Arguments) > 2 {
osStrings := exportStringArray(rt, call.Argument(2))
for _, s := range osStrings {
if v := OsFromString(s); v != 0 {
osList = append(osList, v)
}
}
}
if len(osList) == 0 {
osList = []int{OsWindows, OsLinux, OsMac}
}
var listenerTypes []string
if len(call.Arguments) > 3 {
listenerTypes = exportStringArray(rt, call.Argument(3))
}
if len(listenerTypes) == 0 {
listenerTypes = []string{""}
}
var sourceType SourceType
if strings.HasPrefix(engine.name, "user:") {
sourceType = SourceUser
} else {
sourceType = SourceProfile
}
scriptName := engine.GetMetadataName()
if scriptName == "" {
scriptName = filepath.Base(engine.scriptPath)
}
for _, agentName := range agentNames {
group := groupBuilder.ToCommandGroup(scriptName)
for _, listener := range listenerTypes {
for _, osType := range osList {
engine.manager.CommandStore.RegisterGroups(sourceType, agentName, listener, osType, []CommandGroup{group}, engine)
}
}
}
return goja.Undefined()
})
axObj.Set("execute_browser", func(call goja.FunctionCall) goja.Value {
return goja.Undefined()
})
// --- Script management ---
axObj.Set("script_dir", func(call goja.FunctionCall) goja.Value {
return rt.ToValue(engine.scriptDir)
})
axObj.Set("script_load", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
path := call.Argument(0).String()
err := engine.manager.LoadAxScriptChild(engine, path)
if err != nil {
logs.Error("", "script_load error: %v", err)
panic(rt.NewGoError(err))
}
return goja.Undefined()
})
axObj.Set("script_import", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
path := call.Argument(0).String()
absPath, err := engine.manager.ImportAxScript(engine, path)
if err != nil {
logs.Error("", "script_import error: %v", err)
panic(rt.NewGoError(err))
}
engine.AddImportedFile(absPath)
return goja.Undefined()
})
axObj.Set("script_unload", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
name := call.Argument(0).String()
err := engine.manager.UnloadAxScript(name)
if err != nil {
logs.Warn("", "script_unload error: %v", err)
}
return goja.Undefined()
})
// --- BOF support ---
axObj.Set("bof_pack", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
panic(rt.NewTypeError("bof_pack requires 2 arguments: (types, args)"))
}
types := call.Argument(0).String()
argsVal := call.Argument(1)
argsObj := argsVal.ToObject(rt)
lengthVal := argsObj.Get("length")
if lengthVal == nil || goja.IsUndefined(lengthVal) {
panic(rt.NewTypeError("bof_pack: args must be an array"))
}
arrLen := int(lengthVal.ToInteger())
items := strings.Split(types, ",")
for i := range items {
items[i] = strings.TrimSpace(items[i])
}
if len(items) != arrLen {
panic(rt.NewTypeError("bof_pack: types count must match args count"))
}
var data []byte
for i := 0; i < arrLen; i++ {
val := argsObj.Get(fmt.Sprintf("%d", i))
switch items[i] {
case "cstr":
s := val.String()
b := []byte(s)
b = append(b, 0)
l := uint32(len(b))
lb := make([]byte, 4)
binary.LittleEndian.PutUint32(lb, l)
data = append(data, lb...)
data = append(data, b...)
case "wstr":
s := val.String()
runes := utf16.Encode([]rune(s))
runes = append(runes, 0)
byteLen := uint32(len(runes) * 2)
lb := make([]byte, 4)
binary.LittleEndian.PutUint32(lb, byteLen)
data = append(data, lb...)
for _, r := range runes {
rb := make([]byte, 2)
binary.LittleEndian.PutUint16(rb, r)
data = append(data, rb...)
}
case "bytes":
s := val.String()
decoded, err := base64.StdEncoding.DecodeString(s)
if err != nil {
decoded = []byte{}
}
l := uint32(len(decoded))
lb := make([]byte, 4)
binary.LittleEndian.PutUint32(lb, l)
data = append(data, lb...)
data = append(data, decoded...)
case "int":
n := int32(val.ToInteger())
nb := make([]byte, 4)
binary.LittleEndian.PutUint32(nb, uint32(n))
data = append(data, nb...)
case "short":
n := int16(val.ToInteger())
nb := make([]byte, 2)
binary.LittleEndian.PutUint16(nb, uint16(n))
data = append(data, nb...)
default:
panic(rt.NewTypeError(fmt.Sprintf("bof_pack: unknown type '%s'", items[i])))
}
}
// Prepend total length
totalLen := uint32(len(data))
header := make([]byte, 4)
binary.LittleEndian.PutUint32(header, totalLen)
result := append(header, data...)
return rt.ToValue(base64.StdEncoding.EncodeToString(result))
})
axObj.Set("arch", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue("x86")
}
agentId := call.Argument(0).String()
info := engine.manager.GetAgentInfo(agentId, "arch")
if info == nil {
return rt.ToValue("x86")
}
if s, ok := info.(string); ok {
return rt.ToValue(s)
}
return rt.ToValue("x86")
})
axObj.Set("is64", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue(false)
}
agentId := call.Argument(0).String()
info := engine.manager.GetAgentInfo(agentId, "arch")
if s, ok := info.(string); ok {
return rt.ToValue(s == "x64")
}
return rt.ToValue(false)
})
axObj.Set("isactive", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue(false)
}
agentId := call.Argument(0).String()
info := engine.manager.GetAgentInfo(agentId, "id")
if info == nil {
return rt.ToValue(false)
}
mark := engine.manager.GetAgentMark(agentId)
if mark == "Terminated" || mark == "Disconnect" || mark == "Inactive" || mark == "Unlink" {
return rt.ToValue(false)
}
return rt.ToValue(true)
})
axObj.Set("isadmin", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue(false)
}
agentId := call.Argument(0).String()
info := engine.manager.GetAgentInfo(agentId, "elevated")
if b, ok := info.(bool); ok {
return rt.ToValue(b)
}
return rt.ToValue(false)
})
// --- Credentials & Targets ---
axObj.Set("credentials_add", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil {
return goja.Undefined()
}
cred := make(map[string]interface{})
if len(call.Arguments) > 0 {
cred["username"] = call.Argument(0).String()
}
if len(call.Arguments) > 1 {
cred["password"] = call.Argument(1).String()
}
if len(call.Arguments) > 2 {
cred["realm"] = call.Argument(2).String()
}
if len(call.Arguments) > 3 {
cred["type"] = call.Argument(3).String()
}
if len(call.Arguments) > 4 {
cred["tag"] = call.Argument(4).String()
}
if len(call.Arguments) > 5 {
cred["storage"] = call.Argument(5).String()
}
if len(call.Arguments) > 6 {
cred["host"] = call.Argument(6).String()
}
_ = engine.manager.teamserver.AxCredentialsAdd([]map[string]interface{}{cred})
return goja.Undefined()
})
axObj.Set("credentials_add_list", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
exported := call.Argument(0).Export()
arr, ok := exported.([]interface{})
if !ok {
return goja.Undefined()
}
var creds []map[string]interface{}
for _, item := range arr {
if m, ok := item.(map[string]interface{}); ok {
creds = append(creds, m)
}
}
if len(creds) > 0 {
_ = engine.manager.teamserver.AxCredentialsAdd(creds)
}
return goja.Undefined()
})
axObj.Set("targets_add", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil {
return goja.Undefined()
}
target := make(map[string]interface{})
if len(call.Arguments) > 0 {
target["computer"] = call.Argument(0).String()
}
if len(call.Arguments) > 1 {
target["domain"] = call.Argument(1).String()
}
if len(call.Arguments) > 2 {
target["address"] = call.Argument(2).String()
}
if len(call.Arguments) > 3 {
target["os"] = call.Argument(3).String()
}
if len(call.Arguments) > 4 {
target["os_desc"] = call.Argument(4).String()
}
if len(call.Arguments) > 5 {
target["tag"] = call.Argument(5).String()
}
if len(call.Arguments) > 6 {
target["info"] = call.Argument(6).String()
}
if len(call.Arguments) > 7 {
target["alive"] = call.Argument(7).ToBoolean()
}
_ = engine.manager.teamserver.AxTargetsAdd([]map[string]interface{}{target})
return goja.Undefined()
})
axObj.Set("targets_add_list", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
exported := call.Argument(0).Export()
arr, ok := exported.([]interface{})
if !ok {
return goja.Undefined()
}
var targets []map[string]interface{}
for _, item := range arr {
if m, ok := item.(map[string]interface{}); ok {
targets = append(targets, m)
}
}
if len(targets) > 0 {
_ = engine.manager.teamserver.AxTargetsAdd(targets)
}
return goja.Undefined()
})
// --- File operations (sandboxed) ---
axObj.Set("file_basename", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
return rt.ToValue(fileBasename(call.Argument(0).String()))
})
axObj.Set("file_dirname", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
return rt.ToValue(filepath.Dir(call.Argument(0).String()))
})
axObj.Set("file_extension", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
ext := filepath.Ext(call.Argument(0).String())
if len(ext) > 0 {
ext = ext[1:] // remove leading dot
}
return rt.ToValue(ext)
})
axObj.Set("file_exists", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue(false)
}
path := call.Argument(0).String()
_, err := engine.ValidatePath(path)
if err != nil {
return rt.ToValue(false)
}
_, err = os.Stat(path)
return rt.ToValue(err == nil)
})
axObj.Set("file_read", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue("")
}
path := call.Argument(0).String()
data, err := engine.manager.ReadFileSandboxed(engine, path)
if err != nil {
return rt.ToValue("")
}
return rt.ToValue(base64.StdEncoding.EncodeToString(data))
})
axObj.Set("file_size", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue(0)
}
path := call.Argument(0).String()
validated, err := engine.ValidatePath(path)
if err != nil {
return rt.ToValue(0)
}
info, err := os.Stat(validated)
if err != nil {
return rt.ToValue(0)
}
return rt.ToValue(info.Size())
})
axObj.Set("file_write", func(call goja.FunctionCall) goja.Value {
logs.Warn("", "file_write is disabled on server")
return rt.ToValue(false)
})
axObj.Set("file_write_text", func(call goja.FunctionCall) goja.Value {
logs.Warn("", "file_write_text is disabled on server")
return rt.ToValue(false)
// if engine.manager == nil || len(call.Arguments) < 2 {
// return rt.ToValue(false)
// }
// path := call.Argument(0).String()
// content := call.Argument(1).String()
// appendMode := false
// if len(call.Arguments) > 2 {
// appendMode = call.Argument(2).ToBoolean()
// }
// err := engine.manager.WriteFileSandboxed(engine, path, []byte(content), appendMode)
// if err != nil {
// logs.Warn("", "file_write_text error: %v", err)
// return rt.ToValue(false)
// }
// return rt.ToValue(true)
})
axObj.Set("file_write_binary", func(call goja.FunctionCall) goja.Value {
logs.Warn("", "file_write_binary is disabled on server")
return rt.ToValue(false)
// if engine.manager == nil || len(call.Arguments) < 2 {
// return rt.ToValue(false)
// }
// path := call.Argument(0).String()
// b64Content := call.Argument(1).String()
// data, err := base64.StdEncoding.DecodeString(b64Content)
// if err != nil {
// logs.Warn("", "file_write_binary: invalid base64: %v", err)
// return rt.ToValue(false)
// }
// err = engine.manager.WriteFileSandboxed(engine, path, data, false)
// if err != nil {
// logs.Warn("", "file_write_binary error: %v", err)
// return rt.ToValue(false)
// }
// return rt.ToValue(true)
})
// --- Agent management ---
axObj.Set("agent_hide", func(call goja.FunctionCall) goja.Value {
return goja.Undefined() // client-only UI operation
})
axObj.Set("agent_remove", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) == 0 {
return goja.Undefined()
}
ids := exportStringArray(rt, call.Argument(0))
if len(ids) > 0 {
_ = engine.manager.teamserver.AxAgentRemove(ids)
}
return goja.Undefined()
})
axObj.Set("agent_set_color", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
ids := exportStringArray(rt, call.Argument(0))
bg := call.Argument(1).String()
fg := ""
if len(call.Arguments) > 2 {
fg = call.Argument(2).String()
}
reset := false
if len(call.Arguments) > 3 {
reset = call.Argument(3).ToBoolean()
}
_ = engine.manager.teamserver.AxAgentSetColor(ids, bg, fg, reset)
return goja.Undefined()
})
axObj.Set("agent_set_impersonate", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
impersonate := call.Argument(1).String()
elevated := false
if len(call.Arguments) > 2 {
elevated = call.Argument(2).ToBoolean()
}
updateData := map[string]interface{}{}
if elevated {
updateData["impersonated"] = impersonate + " *"
} else {
updateData["impersonated"] = impersonate
}
_ = engine.manager.teamserver.AxAgentUpdateData(agentId, updateData)
return goja.Undefined()
})
axObj.Set("agent_set_mark", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
ids := exportStringArray(rt, call.Argument(0))
mark := call.Argument(1).String()
_ = engine.manager.teamserver.AxAgentSetMark(ids, mark)
return goja.Undefined()
})
axObj.Set("agent_set_tag", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
ids := exportStringArray(rt, call.Argument(0))
tag := call.Argument(1).String()
_ = engine.manager.teamserver.AxAgentSetTag(ids, tag)
return goja.Undefined()
})
axObj.Set("agent_update_data", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || engine.manager.teamserver == nil || len(call.Arguments) < 2 {
return goja.Undefined()
}
agentId := call.Argument(0).String()
data := call.Argument(1).Export()
if m, ok := data.(map[string]interface{}); ok {
_ = engine.manager.teamserver.AxAgentUpdateData(agentId, m)
}
return goja.Undefined()
})
// --- Misc ---
axObj.Set("ticks", func(call goja.FunctionCall) goja.Value {
return rt.ToValue(time.Now().Unix())
})
axObj.Set("format_time", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return rt.ToValue("")
}
format := call.Argument(0).String()
unixTime := call.Argument(1).ToInteger()
t := time.Unix(unixTime, 0)
// Convert Qt-style format to Go format
goFmt := convertQtFormatToGo(format)
return rt.ToValue(t.Format(goFmt))
})
axObj.Set("validate_command", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 2 {
return rt.ToValue(map[string]interface{}{"valid": false, "message": "missing arguments"})
}
agentId := call.Argument(0).String()
cmdline := call.Argument(1).String()
result, err := engine.manager.ValidateCommand(agentId, cmdline)
if err != nil {
return rt.ToValue(map[string]interface{}{"valid": false, "message": err.Error()})
}
return rt.ToValue(result)
})
axObj.Set("service_command", func(call goja.FunctionCall) goja.Value {
// Service calls are handled differently on server; no-op for now
return goja.Undefined()
})
axObj.Set("show_message", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) >= 2 {
logs.Info("", "[%s] Message: %s - %s", engine.name, call.Argument(0).String(), call.Argument(1).String())
}
return goja.Undefined()
})
axObj.Set("get_project", func(call goja.FunctionCall) goja.Value {
return rt.ToValue("")
})
axObj.Set("downloads", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(engine.manager.GetDownloads())
})
axObj.Set("screenshots", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(engine.manager.GetScreenshots())
})
axObj.Set("tunnels", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(engine.manager.GetTunnels())
})
axObj.Set("copy_to_clipboard", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
registerAxUtilities(axObj, rt, engine)
rt.Set("ax", axObj)
}
func exportStringArray(rt *goja.Runtime, val goja.Value) []string {
if val == nil || goja.IsUndefined(val) || goja.IsNull(val) {
return nil
}
exported := val.Export()
if arr, ok := exported.([]interface{}); ok {
var result []string
for _, item := range arr {
if s, ok := item.(string); ok {
result = append(result, s)
}
}
return result
}
return nil
}
func convertQtFormatToGo(qtFmt string) string {
r := strings.NewReplacer(
"yyyy", "2006", "yy", "06",
"MM", "01", "M", "1",
"dd", "02", "d", "2",
"HH", "15", "hh", "03", "h", "3",
"mm", "04", "m", "4",
"ss", "05", "s", "5",
"AP", "PM", "ap", "pm",
)
return r.Replace(qtFmt)
}
func registerAxUtilities(axObj *goja.Object, rt *goja.Runtime, engine *ScriptEngine) {
axObj.Set("base64_encode", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
data := call.Argument(0).String()
return rt.ToValue(base64.StdEncoding.EncodeToString([]byte(data)))
})
axObj.Set("base64_decode", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
data := call.Argument(0).String()
decoded, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return rt.ToValue("")
}
return rt.ToValue(string(decoded))
})
axObj.Set("hex_encode", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
data := call.Argument(0).String()
return rt.ToValue(hex.EncodeToString([]byte(data)))
})
axObj.Set("hex_decode", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("")
}
data := call.Argument(0).String()
decoded, err := hex.DecodeString(data)
if err != nil {
return rt.ToValue("")
}
return rt.ToValue(string(decoded))
})
axObj.Set("hash", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return rt.ToValue("")
}
algorithm := strings.ToLower(call.Argument(0).String())
input := call.Argument(1).String()
var result string
switch algorithm {
case "md5":
h := md5.Sum([]byte(input))
result = hex.EncodeToString(h[:])
case "sha1":
h := sha1.Sum([]byte(input))
result = hex.EncodeToString(h[:])
case "sha256":
h := sha256.Sum256([]byte(input))
result = hex.EncodeToString(h[:])
case "sha512":
h := sha512.Sum512([]byte(input))
result = hex.EncodeToString(h[:])
default:
result = ""
}
length := 0
if len(call.Arguments) > 2 {
length = int(call.Argument(2).ToInteger())
}
if length > 0 && length < len(result) {
result = result[:length]
}
return rt.ToValue(result)
})
axObj.Set("random_string", func(call goja.FunctionCall) goja.Value {
length := 8
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
if len(call.Arguments) > 0 {
length = int(call.Argument(0).ToInteger())
}
if len(call.Arguments) > 1 {
setName := call.Argument(1).String()
switch setName {
case "hex":
charset = "0123456789abcdef"
case "alpha":
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
case "numeric":
charset = "0123456789"
case "lower":
charset = "abcdefghijklmnopqrstuvwxyz"
case "upper":
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
}
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return rt.ToValue(string(b))
})
axObj.Set("random_int", func(call goja.FunctionCall) goja.Value {
min := 0
max := 100
if len(call.Arguments) > 0 {
min = int(call.Argument(0).ToInteger())
}
if len(call.Arguments) > 1 {
max = int(call.Argument(1).ToInteger())
}
if max <= min {
return rt.ToValue(min)
}
return rt.ToValue(min + rand.Intn(max-min))
})
axObj.Set("format_size", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) == 0 {
return rt.ToValue("0 B")
}
size := call.Argument(0).ToInteger()
return rt.ToValue(formatBytes(uint64(size)))
})
axObj.Set("get_commands", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) == 0 {
return rt.ToValue([]interface{}{})
}
agentId := call.Argument(0).String()
names, err := engine.manager.GetCommandNames(agentId)
if err != nil {
return rt.ToValue([]interface{}{})
}
var result []interface{}
for _, n := range names {
result = append(result, n)
}
if result == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(result)
})
axObj.Set("interfaces", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil {
return rt.ToValue([]interface{}{})
}
addrs := engine.manager.GetInterfaces()
var result []interface{}
for _, a := range addrs {
result = append(result, a)
}
if result == nil {
return rt.ToValue([]interface{}{})
}
return rt.ToValue(result)
})
axObj.Set("prompt_confirm", func(call goja.FunctionCall) goja.Value {
return rt.ToValue(false)
})
axObj.Set("prompt_open_file", func(call goja.FunctionCall) goja.Value {
return rt.ToValue("")
})
axObj.Set("prompt_open_dir", func(call goja.FunctionCall) goja.Value {
return rt.ToValue("")
})
axObj.Set("prompt_save_file", func(call goja.FunctionCall) goja.Value {
return rt.ToValue("")
})
axObj.Set("clipboard_set", func(call goja.FunctionCall) goja.Value {
return goja.Undefined()
})
axObj.Set("clipboard_get", func(call goja.FunctionCall) goja.Value {
return rt.ToValue("")
})
axObj.Set("open_agent_console", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("open_access_tunnel", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("open_browser_files", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("open_browser_process", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("open_remote_terminal", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("open_remote_shell", func(call goja.FunctionCall) goja.Value { return goja.Undefined() })
axObj.Set("convert_to_code", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return rt.ToValue("")
}
language := strings.ToLower(call.Argument(0).String())
b64Data := call.Argument(1).String()
varName := "shellcode"
if len(call.Arguments) > 2 {
varName = call.Argument(2).String()
}
data, err := base64.StdEncoding.DecodeString(b64Data)
if err != nil {
return rt.ToValue("")
}
result := bytesToCode(language, data, varName)
return rt.ToValue(result)
})
axObj.Set("encode_data", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return rt.ToValue("")
}
alg := strings.ToLower(call.Argument(0).String())
data := call.Argument(1).String()
key := ""
if len(call.Arguments) > 2 {
key = call.Argument(2).String()
}
return rt.ToValue(encodeData(alg, []byte(data), key))
})
axObj.Set("decode_data", func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return rt.ToValue("")
}
alg := strings.ToLower(call.Argument(0).String())
data := call.Argument(1).String()
key := ""
if len(call.Arguments) > 2 {
key = call.Argument(2).String()
}
return rt.ToValue(decodeData(alg, data, key))
})
axObj.Set("encode_file", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 2 {
return rt.ToValue("")
}
alg := strings.ToLower(call.Argument(0).String())
path := call.Argument(1).String()
key := ""
if len(call.Arguments) > 2 {
key = call.Argument(2).String()
}
data, err := engine.manager.ReadFileSandboxed(engine, path)
if err != nil {
return rt.ToValue("")
}
return rt.ToValue(encodeData(alg, data, key))
})
axObj.Set("decode_file", func(call goja.FunctionCall) goja.Value {
if engine.manager == nil || len(call.Arguments) < 2 {
return rt.ToValue("")
}
alg := strings.ToLower(call.Argument(0).String())
path := call.Argument(1).String()
key := ""
if len(call.Arguments) > 2 {
key = call.Argument(2).String()
}
rawData, err := engine.manager.ReadFileSandboxed(engine, path)
if err != nil {
return rt.ToValue("")
}
decoded := decodeRawData(alg, rawData, key)
return rt.ToValue(base64.StdEncoding.EncodeToString(decoded))
})
}
// /---
func formatBytes(b uint64) string {
const unit = 1024
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := uint64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "KMGTPE"[exp])
}