working version, for release

This commit is contained in:
2019-07-09 10:17:00 +03:00
parent da5c54017c
commit 97070ebe30
12 changed files with 200 additions and 111 deletions

View File

@@ -5,7 +5,6 @@ build:
bash -c '. config.env && \ bash -c '. config.env && \
mkdir -p scripts && \ mkdir -p scripts && \
cp src/ssh-backdoor scripts/ssh-backdoor && \ cp src/ssh-backdoor scripts/ssh-backdoor && \
cp src/ssh-backdoor-connect-local scripts/ssh-backdoor-connect-local && \
cp src/ssh-backdoor-connect scripts/ssh-backdoor-connect && \ cp src/ssh-backdoor-connect scripts/ssh-backdoor-connect && \
sed \ sed \
-e "s/{{BACKDOORHOST}}/$${BACKDOORHOST}/g" \ -e "s/{{BACKDOORHOST}}/$${BACKDOORHOST}/g" \
@@ -15,19 +14,26 @@ build:
-e "s/{{BACKDOORHOST}}/$${BACKDOORHOST}/g" \ -e "s/{{BACKDOORHOST}}/$${BACKDOORHOST}/g" \
-e "s/{{BACKDOORPORT}}/$${BACKDOORPORT}/g" \ -e "s/{{BACKDOORPORT}}/$${BACKDOORPORT}/g" \
-e "s,{{BACKDOORURL}},$${BACKDOORURL},g" \ -e "s,{{BACKDOORURL}},$${BACKDOORURL},g" \
-e "s,{{BACKDOORURLPATH}},$${BACKDOORURLPATH},g" \
src/ssh-backdoor-open > scripts/ssh-backdoor-open && \ src/ssh-backdoor-open > scripts/ssh-backdoor-open && \
chmod +x scripts/* && \ chmod +x scripts/* && \
true' true'
install: build install: build
bash -c '. config.env && \ bash -c '. config.env && \
cp -av scripts/ssh-backdoor-open "$${BACKDOORURLPATH}" && \ if [[ -n "$${BACKDOORURLPATH}" ]]; then cp -av scripts/ssh-backdoor-open "$${BACKDOORURLPATH}"; fi && \
if [[ -n "$${CLIENTURLPATH}" ]]; then cp -av scripts/ssh-backdoor-open "$${BACKDOORURLPATH}"; fi && \
mkdir -p ${PREFIX} && \ mkdir -p ${PREFIX} && \
cp -av scripts/ssh-backdoor ${PREFIX}/ssh-backdoor && \ cp -av scripts/ssh-backdoor ${PREFIX}/ssh-backdoor && \
cp -av scripts/ssh-backdoor-connect-local ${PREFIX}/ssh-backdoor-connect-local && \
cp -av scripts/ssh-backdoor-connect ${PREFIX}/ssh-backdoor-connect && \ cp -av scripts/ssh-backdoor-connect ${PREFIX}/ssh-backdoor-connect && \
true' true'
clean: clean:
rm -rf scripts rm -rf scripts
uninstall:
bash -c '\
rm -fv ${PREFIX}/ssh-backdoor && \
rm -fv ${PREFIX}/ssh-backdoor-connect && \
if [[ -f "$${BACKDOORURLPATH}" ]]; then rm -fv $${BACKDOORURLPATH}; fi && \
true
'

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
# What?
This is a backdoor for servers in NAT networks.
One server on the public internet acts as a backdoor server, where other
computers connect, creating reverse tunnels to their ports 22.
The program manages random ports that are then available at the server localhost.
# install
configure config.env and run `make install`
- ssh-backdoor will go to ~/bin/ssh-backdoor
- Servers behind NAT should access the ssh-backdoor-open script over a http(s) server
- Clients that want to connect to the backdoored servers should be able to access
ssh-backdoor-connect script
- use the 'ad-hoc-www-server' if no proper http server available
# running
- NATted servers must copy their `id_rsa.pub` to backdoor server's `authorized_keys`
- Clients should do the same, for ease of access
- Have NATted servers run `ssh-backdoor-open` at boot (in cron or otherwise)
- Use the ssh-backdoor command directly, or ssh-backdoor-connect from client machines
to connect
# utils
- `ssh-list` lists all processes with ssh and backdoor
- `ssh-kill-all` kills all ssh connections (except the current parent), and all python processes

8
ad-hoc-www-server Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
cd $( dirname $( readlink -f $0 ) )
set -e
. config.env
cd "$ADHOCSERVERROOT"
python3 -m http.server $ADHOCSERVERPORT

13
config.env.example Normal file
View File

@@ -0,0 +1,13 @@
# Host name of the SSH server (and user name)
BACKDOORHOST=admin@my.server.org
# Port of the SSH server
BACKDOORPORT=22
# Where to download the ssh-backdoor-open and ssh-backdoor-connect script (may be left empty)
BACKDOORURL=https://my.server.org/pub/ssh-backdoor-open
CLIENTURL=https://my.server.org/pub/ssh-backdoor-connect
# Location of the dowloadable script at the server (may be empty)
BACKDOORURLPATH=/home/admin/www/pub/ssh-backdoor-open
CLIENTURLPATH=/home/admin/www/pub/ssh-backdoor-connect
# Ad-hoc web server port (if used)
ADHOCSERVERPORT=8080
ADHOCSERVERROOT=/home/admin/www/pub/

View File

@@ -6,6 +6,7 @@ import psutil
import random import random
import sqlite3 import sqlite3
import time, os, sys import time, os, sys
import subprocess
def setup_options(): def setup_options():
''' Setup the command line options ''' ''' Setup the command line options '''
@@ -20,44 +21,6 @@ def setup_options():
type = str, type = str,
help = "Sqlite file for database: %(default)s" help = "Sqlite file for database: %(default)s"
) )
# TODO --connect (replace ssh-local-connect)
parser.add_argument(
"--clear",
action = 'store_true',
dest = 'clear',
default = False,
help = "Clear the database of everything, first"
)
parser.add_argument(
"--kill",
action = 'store',
dest = 'kill',
default = None,
type = str,
help="Kill processes of given ID"
)
parser.add_argument(
"--list","-l",
action = 'store_true',
dest = 'list',
default = False,
help = "List ports"
)
parser.add_argument(
"--list-names",
action = 'store_true',
dest = 'list_names',
default = False,
help = "List alive host names only"
)
parser.add_argument(
"--query",
action = 'store',
dest = 'query',
default = None,
type = str,
help="Query port for an id"
)
parser.add_argument( parser.add_argument(
"--quiet", "-q", "--quiet", "-q",
action = 'store_true', action = 'store_true',
@@ -65,22 +28,78 @@ def setup_options():
default = False, default = False,
help = "Quiet operation: %(default)s" help = "Quiet operation: %(default)s"
) )
parser.add_argument(
"--wait", "-w", subparsers = parser.add_subparsers(
action = 'store_true', help = "Help for subcommand",
dest = 'wait', dest = 'command'
default = False,
help = "Enter infinite loop"
) )
parser.add_argument( parser_clear = subparsers.add_parser(
'clear',
help = "Clear the database of everything"
)
parser_connect = subparsers.add_parser(
'connect',
help = "Connect to a backdoor"
)
parser_connect.add_argument(
dest = "id",
help = "Backdoor ID to connect to"
)
parser_kill = subparsers.add_parser(
'kill',
help = "Kill a backdoor"
)
parser_kill.add_argument(
dest = "id",
help = "Backdoor ID to kill"
)
parser_list = subparsers.add_parser(
"list",
help = "List ports"
)
parser_list_names = subparsers.add_parser(
"list-names",
help = "List alive host names only"
)
parser_query = subparsers.add_parser(
"query",
help="Query port for an id"
)
parser_query.add_argument(
dest = "id",
help = "Backdoor ID to query"
)
parser_open = subparsers.add_parser(
"open",
help = "Open a new port. Returns the port number"
)
parser_open.add_argument(
action = 'store', action = 'store',
dest = 'id', dest = 'id',
default = None ,
type = str, type = str,
nargs = '?', help="Id name for backdoor"
help="Id name for port" )
parser_wait = subparsers.add_parser(
"keep",
help = "Keep updating alive status (for 1 hour)"
)
parser_wait.add_argument(
action = 'store',
dest = 'id',
type = str,
help="Id name for backdoor"
) )
options=parser.parse_args() options=parser.parse_args()
if options.command == None:
parser.print_help(sys.stderr)
sys.exit(1)
return options return options
@@ -254,52 +273,83 @@ class DataBase:
if len(result) == 0: if len(result) == 0:
return port return port
def connect_backdoor(self, id):
self.db = self.conn.cursor()
self.db.execute("SELECT port,pid FROM ports WHERE id = ?", (id,))
result = self.db.fetchall()
if len(result) == 0:
eprint("No such ID")
return
port = result[0][0]
pid = result[0][1]
if not self.is_running(pid):
eprint("Backdoor not open")
return
user = id.split("@")[0]
os.execlp(
"ssh",
"ssh",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
"-tt", "-XYC",
"-p", str(port),
"%s@localhost"%( user, )
)
if __name__ == "__main__": if __name__ == "__main__":
opts=setup_options() opts=setup_options()
db = DataBase(opts.DB) db = DataBase(opts.DB)
start_time = time.time() start_time = time.time()
if opts.clear: if opts.command == "clear":
db.clear() db.clear()
if opts.list_names: if opts.command == "list-names":
for row in db.list(): for row in db.list():
if row[5]: if row[-1]:
print(row[0]) print(row[0])
sys.exit(0) sys.exit(0)
if opts.list: if opts.command == "list":
print(tabulate( print(tabulate(
db.list(), db.list(),
headers = ['Id','Port','Host','Age','PID','Die','Alive'] headers = ['Id','Port','Host','Age','PID','Die','Alive']
)) ))
if opts.query != None: if opts.command == "query":
port = db.get_port(opts.query) port = db.get_port(opts.id)
if port == None: if port == None:
sys.exit(1) sys.exit(1)
print(port) print(port)
sys.exit(0) sys.exit(0)
if opts.kill: if opts.command == "kill":
db.set_to_die(opts.kill) db.set_to_die(opts.id)
sys.exit(0) sys.exit(0)
if opts.id != None: if opts.command == "connect":
db.connect_backdoor(opts.id)
if opts.command == "open":
print(db.update(opts.id)) print(db.update(opts.id))
if opts.wait:
ewrite( if opts.command == "keep":
" Connected\r" print(db.update(opts.id))
) ewrite(
while True: " Connected\r"
if time.time() - start_time > 3600: )
sys.exit(0) while True:
db.check_die() # Run 1 hour, then exit to reconnect
db.update(opts.id) if time.time() - start_time > 3600:
for i in range(10): sys.exit(0)
time.sleep(10 * random.random()) db.check_die()
ewrite( db.update(opts.id)
" " + for i in range(10):
time.strftime("%c") + time.sleep(10 * random.random())
"\r" ewrite(
) " " +
time.strftime("%c") +
"\r"
)

View File

@@ -15,8 +15,8 @@ BACKDOORHOST={{BACKDOORHOST}}
BACKDOORPORT={{BACKDOORPORT}} BACKDOORPORT={{BACKDOORPORT}}
_list() { _list() {
echo 'usage: [-auto] user@host' echo 'usage: [-auto] user@host'
_ssh bin/ssh-backdoor -l _ssh bin/ssh-backdoor list
ids=( $( _ssh bin/ssh-backdoor --list-names ) ) ids=( $( _ssh bin/ssh-backdoor list-names ) )
if [[ ${#ids[@]} -eq 0 ]]; then if [[ ${#ids[@]} -eq 0 ]]; then
exit exit
fi fi
@@ -33,6 +33,8 @@ _ssh() {
-o UserKnownHostsFile=/dev/null \ -o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \ -o StrictHostKeyChecking=no \
-o ConnectTimeout=10 \ -o ConnectTimeout=10 \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-p ${BACKDOORPORT} ${BACKDOORHOST} \ -p ${BACKDOORPORT} ${BACKDOORHOST} \
"$@" "$@"
#~ -o "ExitOnForwardFailure yes" \ #~ -o "ExitOnForwardFailure yes" \
@@ -48,17 +50,15 @@ for (( i=1; i<=$#; i++ )); do
host="${!i}" host="${!i}"
done done
port=$( _ssh bin/ssh-backdoor --query "$host" ) port=$( _ssh bin/ssh-backdoor query "$host" )
[[ $? -ne 0 ]] && { [[ $? -ne 0 ]] && {
echo No such id echo No such id
_list _list
} }
login=(${host//@/ })
while :; do while :; do
_ssh \ _ssh \
-tt \ -tt \
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \ bin/ssh-backdoor connect "$host"
-tt -XYC -p $port ${login[0]}@localhost
[[ $? -eq 0 ]] && exit [[ $? -eq 0 ]] && exit
[[ "$auto_reconnect" -ne 1 ]] && { exit $?; } [[ "$auto_reconnect" -ne 1 ]] && { exit $?; }
echo Auto-reconnect echo Auto-reconnect

View File

@@ -1,21 +0,0 @@
#!/bin/bash
_list() {
echo usage: user@host
ssh-backdoor -l
exit
}
[[ -z "$1" ]] && _list
port=$( ssh-backdoor --query "$1" )
[[ $? -ne 0 ]] && {
echo No such id
_list
}
login=(${1//@/ })
set -x
exec ssh \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
-tt -XYC -p $port ${login[0]}@localhost

View File

@@ -31,13 +31,13 @@ USER=$( id -u -n )
echo use of ssh-add is encouraged echo use of ssh-add is encouraged
( sleep 3; printf "%d\r" $SECONDS ) & ( sleep 3; printf "%d\r" $SECONDS ) &
while true; do while true; do
port=$( _ssh bin/ssh-backdoor $USER@$HOSTNAME ) port=$( _ssh bin/ssh-backdoor open $USER@$HOSTNAME )
[[ -z "$port" ]] && { sleep 2; continue; } [[ -z "$port" ]] && { sleep 2; continue; }
echo "$port port assigned" echo "$port port assigned"
#~ _ssh pkill -a -f $USER@$HOSTNAME #~ _ssh pkill -a -f $USER@$HOSTNAME
_ssh \ _ssh \
-R $port:localhost:22 \ -R $port:localhost:22 \
bin/ssh-backdoor -w $USER@$HOSTNAME || { bin/ssh-backdoor keep $USER@$HOSTNAME || {
true true
# failed # failed
#_ssh bin/ssh-kill $USER@$HOSTNAME $port || true #_ssh bin/ssh-kill $USER@$HOSTNAME $port || true

View File

@@ -1,4 +0,0 @@
#!/bin/bash
ps axuw | grep -e [s]sh -e [p]ython3

4
utils/ssh-list Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
ps axuw | grep -e [s]sh -e [b]ackdoor