193 lines
7.4 KiB
Python
193 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) 2018, salesforce.com, inc.
|
|
# All rights reserved.
|
|
# Licensed under the BSD 3-Clause license.
|
|
# For full license text, see the LICENSE file in the repo root
|
|
# or https://opensource.org/licenses/BSD-3-Clause
|
|
|
|
import argparse
|
|
import json
|
|
import docker
|
|
import time
|
|
from subprocess import Popen
|
|
|
|
__author__ = "Adel '0x4D31' Karimi"
|
|
__email__ = "akarimishiraz@salesforce.com"
|
|
__version__ = "1.0"
|
|
__copyright__ = "Copyright (c) 2018, salesforce.com, inc."
|
|
__license__ = "BSD 3-Clause License"
|
|
|
|
|
|
# Default command for running hassh.py
|
|
HASSH_COMMAND = 'python3 ../hassh.py -i en1 -l json -o fingerprint.json'
|
|
# Dockerfiles directory
|
|
DOCKERFILE_DIR = 'dockerfiles'
|
|
|
|
|
|
def parse_cmd_args():
|
|
"""parse command line arguments"""
|
|
desc = "A python script to automate building docker images with different\
|
|
SSH clients/versions."
|
|
parser = argparse.ArgumentParser(description=(desc))
|
|
helptxt = 'Docker image name. e.g. alpine, ubuntu'
|
|
parser.add_argument('-i', '--image', type=str, help=helptxt)
|
|
helptxt = 'Docker image version. e.g. 18.04, latest'
|
|
parser.add_argument('-iV', '--image_ver', type=str, help=helptxt)
|
|
helptxt = 'SSH client name'
|
|
parser.add_argument('-c', '--sshclient', type=str, help=helptxt)
|
|
helptxt = 'SSH client version'
|
|
parser.add_argument('-cV', '--sshclient_ver', type=str, help=helptxt)
|
|
helptxt = 'Specify the Dockerfile'
|
|
parser.add_argument('-d', '--docker_file', type=str, help=helptxt)
|
|
helptxt = 'Specify the server address to test the SSH connection'
|
|
parser.add_argument(
|
|
'-s', '--server', type=str, required=True, help=helptxt)
|
|
helptxt = 'Bulk mode; Specify an input file containing a list of docker\
|
|
image, image version, sshclient and sshclient version'
|
|
parser.add_argument('-iF', '--input_file', type=str, help=helptxt)
|
|
helptxt = 'Set this option to automatically run hassh.py for capturing SSH\
|
|
client fingerprints (HASSH). Specify the command for running hassh.py\
|
|
using --cmd arg.'
|
|
parser.add_argument(
|
|
'-f', '--fingerprint', action="store_true", help=helptxt)
|
|
helptxt = 'Enter the command for running hassh.py. Use with\
|
|
-f/--fingerprint arg'
|
|
parser.add_argument(
|
|
'--cmd', type=str, default=HASSH_COMMAND, help=helptxt)
|
|
return parser.parse_args()
|
|
|
|
|
|
def command_exec(container, server, ssh_client, rm):
|
|
"""Runs the container and exec SSH command"""
|
|
client = docker.DockerClient(
|
|
base_url='unix://var/run/docker.sock',
|
|
version='auto')
|
|
if 'openssh' in ssh_client:
|
|
cmd = ('ssh -o UserKnownHostsFile=/dev/null '
|
|
'-o StrictHostKeyChecking=no {}').format(server)
|
|
elif 'dropbear' in ssh_client:
|
|
cmd = 'dbclient -y {}'.format(server)
|
|
elif 'paramiko' in ssh_client:
|
|
cmd = 'python /tmp/paramiko_conn.py {}'.format(server)
|
|
try:
|
|
client.containers.run(container, command=cmd)
|
|
except Exception as e:
|
|
errorMsg = str(e)
|
|
pass
|
|
if ('Permission denied' in errorMsg or 'paramiko.ssh_exception' in errorMsg
|
|
or 'dbclient: Connection' in errorMsg):
|
|
out = "[+] Command successfully executed!"
|
|
else:
|
|
out = "[-] Error: {}".format(errorMsg)
|
|
# Delete the image
|
|
if rm:
|
|
client.images.remove(image=container, force=True, noprune=True)
|
|
|
|
return out
|
|
|
|
|
|
def main():
|
|
"""Intake arguments from the user to build docker images and initiate SSH
|
|
connections for generating client HASSHs"""
|
|
args = parse_cmd_args()
|
|
tag_id = 0
|
|
tag_name = "hasshgen:{img}{id}"
|
|
client = docker.DockerClient(
|
|
base_url='unix://var/run/docker.sock',
|
|
version='auto')
|
|
|
|
if args.input_file:
|
|
with open(args.input_file) as file:
|
|
input_list = json.load(file)
|
|
# Run hassh.py
|
|
proc = None
|
|
if args.fingerprint and not proc:
|
|
proc = Popen(args.cmd, shell=True)
|
|
time.sleep(1)
|
|
for record in input_list:
|
|
# Find the Dockerfile
|
|
if record['image'] in ('debian', 'ubuntu'):
|
|
docker_file = "{}/Dockerfile.debian".format(DOCKERFILE_DIR)
|
|
elif record['image'] in ('centos', 'fedora'):
|
|
docker_file = "{}/Dockerfile.centos".format(DOCKERFILE_DIR)
|
|
else:
|
|
docker_file = "{}/Dockerfile.{}".format(
|
|
DOCKERFILE_DIR,
|
|
record['image'])
|
|
# Build the docker images
|
|
tag_id += 1
|
|
container = tag_name.format(img=record['image'], id=tag_id)
|
|
try:
|
|
output = client.images.build(
|
|
path='.',
|
|
dockerfile=docker_file,
|
|
tag=container,
|
|
nocache=True,
|
|
rm=True,
|
|
forcerm=True,
|
|
buildargs={"IMAGE": record['image'],
|
|
"IMAGE_VER": record['image_ver'],
|
|
"SSHCLIENT": record['sshclient'],
|
|
"SSHCLIENT_VER": record['sshclient_ver']})
|
|
# Docker image successfully built
|
|
print("[+]", output[0], "successfully built")
|
|
print(" - image: {}:{}, ssh client: {} {}".format(
|
|
record['image'], record['image_ver'], record['sshclient'],
|
|
record['sshclient_ver']))
|
|
# Run the container and exec SSH command
|
|
out = command_exec(
|
|
container, args.server, record['sshclient'], rm=False)
|
|
if out:
|
|
print(out)
|
|
except Exception as e:
|
|
print("[-] Error:", e)
|
|
# One more command_exec to make sure all captured (bug)
|
|
command_exec(container, args.server, record['sshclient'], rm=True)
|
|
# Kill hassh.py
|
|
if proc:
|
|
proc.kill()
|
|
|
|
elif (args.image and args.image_ver and args.sshclient_ver
|
|
and args.sshclient):
|
|
container = tag_name.format(img=args.image, id=tag_id)
|
|
try:
|
|
output = client.images.build(
|
|
path='.',
|
|
dockerfile=args.docker_file,
|
|
tag=container,
|
|
nocache=True,
|
|
rm=True,
|
|
forcerm=True,
|
|
buildargs={"IMAGE": args.image,
|
|
"IMAGE_VER": args.image_ver,
|
|
"SSHCLIENT": args.sshclient,
|
|
"SSHCLIENT_VER": args.sshclient_ver})
|
|
# Docker image successfully built
|
|
print("[+]", output[0], "successfully built")
|
|
print(" - image: {}:{}, ssh client: {} {}".format(
|
|
args.image, args.image_ver, args.sshclient, args.sshclient_ver))
|
|
# Run hassh.py
|
|
proc = None
|
|
if args.fingerprint and not proc:
|
|
proc = Popen(args.cmd, shell=True)
|
|
# Run the container and exec SSH command
|
|
time.sleep(1)
|
|
out = command_exec(
|
|
container, args.server, args.sshclient, rm=False)
|
|
time.sleep(1)
|
|
# One more command_exec to make sure all captured (bug)
|
|
out = command_exec(
|
|
container, args.server, args.sshclient, rm=True)
|
|
time.sleep(1)
|
|
if out:
|
|
print(out)
|
|
# Kill hassh.py
|
|
if proc:
|
|
proc.kill()
|
|
except Exception as e:
|
|
print("[-] Error:", e)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|