diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bba7b53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target/ +/.idea/ diff --git a/SSH-Docker-Library.iml b/SSH-Docker-Library.iml new file mode 100644 index 0000000..9f6a43d --- /dev/null +++ b/SSH-Docker-Library.iml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/certs/.gitignore b/certs/.gitignore new file mode 100644 index 0000000..89f9ac0 --- /dev/null +++ b/certs/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/certs/Dockerfile b/certs/Dockerfile new file mode 100644 index 0000000..d91f319 --- /dev/null +++ b/certs/Dockerfile @@ -0,0 +1,14 @@ +FROM debian:stretch + +RUN apt-get update +RUN apt-get install software-properties-common -y +RUN apt-add-repository 'deb http://security.debian.org/debian-security stretch/updates main' +RUN apt-get update && apt-get install -y \ + openssl \ + libnss3-tools \ + openjdk-8-jdk \ + curl + +WORKDIR /certs +COPY generateCerts.sh cert.cfg /run/ +RUN /run/generateCerts.sh diff --git a/certs/Readme.md b/certs/Readme.md new file mode 100644 index 0000000..735cbfd --- /dev/null +++ b/certs/Readme.md @@ -0,0 +1,16 @@ +After running setup.sh, you have a docker volume with keys and certificate data. +You can add this volume to the container with: +```bash +-v cert-data:/cert/:ro,nocopy +``` + +The volume conains the following files and certificates: +- /cert/ec256key.pem and /cert/ec256cert.pem (alias is cert) +- /cert/rsa2048key.pem and /cert/rsa2048cert.pem (alias is cert) +- /cert/keys.jks (aliases are ec256 and rsa2048) +All passwords are password + +For example, you can run a TLS server with the following command: +```bash +docker run -it -v cert-data:/cert/:ro,nocopy --rm openssl-server -key /cert/ec256key.pem -cert /cert/ec256cert.pem +``` diff --git a/certs/cert.cfg b/certs/cert.cfg new file mode 100644 index 0000000..ec1b934 --- /dev/null +++ b/certs/cert.cfg @@ -0,0 +1,47 @@ +[ca] +default_ca = CA_default + +[CA_default] +dir = ./ca +database = $dir/index.txt +new_certs_dir = $dir/newcerts +serial = $dir/serial +private_key = ./ca_key.pem +certificate = ./ca.pem +default_days = 1024 +default_md = sha256 +policy = policy_anything +copy_extensions = copyall + +[policy_anything] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[req] +prompt = no +distinguished_name = dn +req_extensions = req_ext + +[dn] +C=DE +ST=NRW +L=Bochum +O=RUB +OU=NDS +CN=example.com + +[req_ext] +basicConstraints = CA:FALSE +subjectAltName = @alt_names + +[alt_names] +DNS.1 = example.com +DNS.2 = 192.168.0.20.xip.io +DNS.3 = *.192.168.0.20.xip.io +DNS.4 = 192.168.65.2.xip.io +DNS.5 = *.192.168.65.2.xip.io diff --git a/certs/generateCerts.sh b/certs/generateCerts.sh new file mode 100755 index 0000000..3216b82 --- /dev/null +++ b/certs/generateCerts.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -eu + +WARN='\033[0;31m' +NO_COLOR='\033[0m' + +CFG_PATH=/run/cert.cfg +if [ ! -f "$CFG_PATH" ]; then + echo "\nWARNING: Config file $CFG_PATH does not exist. This script is intended to be run by the Dockerfile.\n" +fi + +mkdir ./ca ./ca/newcerts +touch ./ca/index.txt +echo "unique_subject = no" > ./ca/index.txt.attr +echo "Generating Root CA Key" +openssl genrsa -out ca_key.pem 2048 +echo "Generating Root CA Certificate" +openssl req -new -nodes -x509 -subj "/C=DE/ST=NRW/L=Bochum/O=RUB/OU=NDS/CN=NDS CA" -key ca_key.pem -out ca.pem -days 1024 +echo "Generating RSA keys" +openssl genpkey -algorithm RSA -out rsa2048key.pem -pkeyopt rsa_keygen_bits:2048 +openssl req -new -config $CFG_PATH -nodes -key rsa2048key.pem -out rsa2048cert.csr +openssl ca -config $CFG_PATH -create_serial -batch -in rsa2048cert.csr -out rsa2048cert.pem +cat rsa2048key.pem rsa2048cert.pem > rsa2048combined.pem +echo "Generating EC keys" +openssl genpkey -algorithm EC -out ec256key.pem -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve +openssl req -new -config $CFG_PATH -nodes -key ec256key.pem -out ec256cert.csr +openssl ca -config $CFG_PATH -create_serial -batch -in ec256cert.csr -out ec256cert.pem +cat ec256key.pem ec256cert.pem > ec256combined.pem +echo "Creating DH parameters" +openssl dhparam -out dh.pem 2048 +echo "Creating db" +mkdir db +openssl pkcs12 -export -in rsa2048cert.pem -inkey rsa2048key.pem -out rsa2048.p12 -name cert -passin pass:password -passout pass:password +echo "Importing RSA key" +pk12util -i rsa2048.p12 -d db -K password -W password +openssl pkcs12 -export -in ec256cert.pem -inkey ec256key.pem -out ec256.p12 -name cert -passin pass:password -passout pass:password +echo "Importing EC key" +pk12util -i ec256.p12 -d db -K password -W password +echo "Creating Java keystore" +keytool -importkeystore -srckeystore rsa2048.p12 -srcstoretype pkcs12 -destkeystore keys.jks -deststoretype jks -alias cert -destalias rsa2048 -srcstorepass password -deststorepass password +keytool -importkeystore -srckeystore ec256.p12 -srcstoretype pkcs12 -destkeystore keys.jks -deststoretype jks -alias cert -destalias ec256 -srcstorepass password -deststorepass password +#use test-ca from rustls +curl -L https://github.com/ctz/rustls/tarball/master | tar zx --wildcards --strip-components=1 '*/test-ca/' diff --git a/certs/setup.sh b/certs/setup.sh new file mode 100755 index 0000000..cad4e35 --- /dev/null +++ b/certs/setup.sh @@ -0,0 +1,22 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +set -eu + +docker build -t certs -f Dockerfile . + +docker volume remove cert-data || true +docker volume create cert-data +docker run --rm -v cert-data:/cert/ certs cp -r ./. /cert/ + +echo "Copying Root CA Certificate to relevant image folders" +docker run --rm -d -v cert-data:/cert/ --name tmp-certs certs sleep 10 +docker cp tmp-certs:/cert/ca.pem ../images/baseimage/ +docker cp tmp-certs:/cert/ca.pem ../images/firefox/ + +if [ -d ./out ] +then + rm -r ./out +fi +docker cp tmp-certs:/cert/ ./out +docker cp tmp-certs:/cert/keys.jks ./keys.jks +docker kill tmp-certs diff --git a/clientImages/asyncSSH/Dockerfile b/clientImages/asyncSSH/Dockerfile new file mode 100644 index 0000000..d257995 --- /dev/null +++ b/clientImages/asyncSSH/Dockerfile @@ -0,0 +1,15 @@ +ARG VERSION_PYTHON=3.9 + +FROM python:${VERSION_PYTHON} AS asyncssh-client +ARG VERSION +WORKDIR /usr/local/bin +COPY asyncssh-client-script.py /usr/local/bin +COPY start.sh /usr/local/bin +RUN /usr/local/bin/python -m pip install --upgrade pip +RUN pip install asyncssh==${VERSION} +RUN pip install click +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +LABEL "ssh_implementation"="asyncssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint", "start.sh"] \ No newline at end of file diff --git a/clientImages/asyncSSH/asyncssh-client-script.py b/clientImages/asyncSSH/asyncssh-client-script.py new file mode 100755 index 0000000..c5b3b7d --- /dev/null +++ b/clientImages/asyncSSH/asyncssh-client-script.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +import click +import asyncio +import asyncssh +import sys + + +@click.command() +@click.option('-H', '--host', help='hostname or ip', default='172.17.0.1"') +@click.option('-P', '--port', help='port', default=3022, type=int) +@click.option('-u', '--username', help='username', default='demo') +@click.option('-p', '--password', help='password', default='password') +@click.option('-c', '--command', help='command', default='pwd') +@click.option('-o', '--output', is_flag=True, show_default=True, default=False, help='print output') +@click.option('-e', '--error', is_flag=True, show_default=True, default=False, help='print error') +def client_start(host, port, username, password, command, output, error): + print(f'host {host}\n') + print(f'port {port}\n') + print(f'username {username}\n') + + try: + asyncio.get_event_loop().run_until_complete(run_client(host, port, username, password, command, output, error)) + except (OSError, asyncssh.Error) as exc: + sys.exit('SSH connection failed: ' + str(exc)) + + +async def run_client(host, port, username, password, command, output, error): + async with asyncssh.connect(host=host,port=port, username=username,password=password,known_hosts=None) as conn: + result = await conn.run(command, check=True) + print(result.stdout, end='') + + +if __name__ == '__main__': + client_start() + + diff --git a/clientImages/asyncSSH/asyncssh_build.sh b/clientImages/asyncSSH/asyncssh_build.sh new file mode 100755 index 0000000..4e86f60 --- /dev/null +++ b/clientImages/asyncSSH/asyncssh_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions="2.12.0" "2.11.0" "2.10.0" "2.9.0" "2.8.1" "2.8.0" "2.7.2" "2.7.1" "2.7.0" "2.6.0" "2.5.0" "2.4.2" "2.4.1" "2.4.0" "2.3.0" "2.2.1" "2.2.0" "2.1.0" "2.0.1" "2.0.0" "1.18.0" "1.17.1" "1.17.0" "1.16.1"}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}asyncssh-client:${i} -f Dockerfile --target asyncssh-client . +done + +exit "$EXITCODE" diff --git a/clientImages/asyncSSH/start.sh b/clientImages/asyncSSH/start.sh new file mode 100755 index 0000000..4f0a017 --- /dev/null +++ b/clientImages/asyncSSH/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="172.17.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +echo | python "asyncssh-client-script.py" $parameter + +exit 0 diff --git a/clientImages/base/Dockerfile b/clientImages/base/Dockerfile new file mode 100644 index 0000000..34b0d1e --- /dev/null +++ b/clientImages/base/Dockerfile @@ -0,0 +1,11 @@ +ARG VERSION=latest + +FROM alpine:${VERSION} +RUN apk add --no-cache build-base \ + git \ + curl \ + wget \ + bash \ + openssl-dev \ + zlib-dev +WORKDIR /src \ No newline at end of file diff --git a/clientImages/base/Dockerfile_debian b/clientImages/base/Dockerfile_debian new file mode 100644 index 0000000..660d708 --- /dev/null +++ b/clientImages/base/Dockerfile_debian @@ -0,0 +1,16 @@ +ARG VERSION=latest + +FROM debian:${VERSION} +ARG LIBSSL_VERSION +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + git \ + curl \ + wget \ + bash \ + libssl${LIBSSL_VERSION}-dev \ + zlib1g-dev \ + python3 \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /src diff --git a/clientImages/base/build-base-image.sh b/clientImages/base/build-base-image.sh new file mode 100755 index 0000000..0bd78d7 --- /dev/null +++ b/clientImages/base/build-base-image.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source "../helper-functions.sh" + +#_docker build --build-arg VERSION=3.14 -t alpine-build:3.14 . +# +#_docker build --build-arg VERSION=bullseye -f Dockerfile_debian -t debian-build:bullseye . +_docker build --build-arg VERSION=stretch --build-arg LIBSSL_VERSION=1.0 -f Dockerfile_debian -t debian-build:stretch-libssl1.0 . + +exit "$EXITCODE" diff --git a/clientImages/baseimage/Dockerfile b/clientImages/baseimage/Dockerfile new file mode 100644 index 0000000..d7311b8 --- /dev/null +++ b/clientImages/baseimage/Dockerfile @@ -0,0 +1,30 @@ +ARG VERSION=latest +FROM alpine:$VERSION +RUN apk update &&\ + apk add git \ + mercurial \ + alpine-sdk \ + gettext-dev \ + autoconf \ + automake \ + musl-utils \ + libtool \ + gmp-dev \ + gperf \ + bison \ + zlib-dev \ + linux-headers \ + cmake \ + go \ + strace \ + unzip \ + wget \ + rsync \ + bash \ + coreutils \ + build-base \ + bsd-compat-headers + + +COPY ./ca.pem /etc/ssl/certs/ +WORKDIR /src/ diff --git a/clientImages/baseimage/Dockerfile_debian b/clientImages/baseimage/Dockerfile_debian new file mode 100644 index 0000000..fb6a7d5 --- /dev/null +++ b/clientImages/baseimage/Dockerfile_debian @@ -0,0 +1,25 @@ +ARG VERSION=latest +FROM debian:$VERSION +RUN apt-get update &&\ + apt-get install -y \ + git \ + mercurial \ + autoconf \ + automake \ + autopoint \ + gettext \ + libgettextpo-dev \ + libtool \ + libgmp-dev \ + gperf \ + bison \ + cmake \ + strace \ + unzip \ + wget \ + rsync \ + bash \ + coreutils + +COPY ./ca.pem /etc/ssl/certs/ +WORKDIR /src/ diff --git a/clientImages/baseimage/Dockerfile_entrypoint b/clientImages/baseimage/Dockerfile_entrypoint new file mode 100644 index 0000000..a3b2f1e --- /dev/null +++ b/clientImages/baseimage/Dockerfile_entrypoint @@ -0,0 +1,9 @@ +FROM golang:1.14 AS build +COPY entrypoints /build/ +WORKDIR /build +RUN CGO_ENABLED=0 GOOS=linux go build -o ./ ./... + + +FROM scratch +COPY --from=build /build/client-entrypoint /bin/ +COPY --from=build /build/server-entrypoint /bin/ diff --git a/clientImages/baseimage/build-base-image.sh b/clientImages/baseimage/build-base-image.sh new file mode 100755 index 0000000..39d294a --- /dev/null +++ b/clientImages/baseimage/build-base-image.sh @@ -0,0 +1,20 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +# https://wiki.alpinelinux.org/wiki/Alpine_Linux:Releases +_docker build --build-arg VERSION=3.12 -t alpine-build:3.12 . +#_docker build --build-arg VERSION=3.11 -t alpine-build:3.11 . +#_docker build --build-arg VERSION=3.10 -t alpine-build:3.10 . +#_docker build --build-arg VERSION=3.9 -t alpine-build:3.9 . +#_docker build --build-arg VERSION=3.8 -t alpine-build:3.8 . +#_docker build --build-arg VERSION=3.7 -t alpine-build:3.7 . +_docker build --build-arg VERSION=3.6 -t alpine-build:3.6 . +#_docker build --build-arg VERSION=3.5 -t alpine-build:3.5 . +#_docker build --build-arg VERSION=3.4 -t alpine-build:3.4 . + +_docker build --build-arg VERSION=sid -t debian-build:sid -f Dockerfile_debian . + +_docker build -t entrypoint -f Dockerfile_entrypoint . + +exit "$EXITCODE" diff --git a/clientImages/baseimage/build-base.sh b/clientImages/baseimage/build-base.sh new file mode 100755 index 0000000..68e3613 --- /dev/null +++ b/clientImages/baseimage/build-base.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh +exit_on_error + +track_error ./build-base-image.sh + +exit "$EXITCODE" diff --git a/clientImages/baseimage/entrypoints/cmd/client-entrypoint/main.go b/clientImages/baseimage/entrypoints/cmd/client-entrypoint/main.go new file mode 100644 index 0000000..d856733 --- /dev/null +++ b/clientImages/baseimage/entrypoints/cmd/client-entrypoint/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "entrypoints/lib" + "fmt" + "net/http" + "os" + "time" +) + +func trigger(w http.ResponseWriter, req *http.Request) { + go lib.ExecuteArgs() + fmt.Fprintf(w, "done") +} + +func main() { + if len(os.Args) > 1 { + go lib.ExecuteArgs() + + http.HandleFunc("/trigger", trigger) + http.HandleFunc("/shutdown", lib.Shutdown) + fmt.Println("Listening on :8090...") + _ = http.ListenAndServe(":8090", nil) + } else { + fmt.Println("No args specified; sleeping forever just to keep the container alive") + // Using a fancier sleep (as in https://stackoverflow.com/a/36419222/) causes go to die, + // as all routines are sleeping, which is interpreted as a deadlock + // So we just sleep for an hour forever + for true { + time.Sleep(time.Hour) + } + } +} diff --git a/clientImages/baseimage/entrypoints/cmd/server-entrypoint/main.go b/clientImages/baseimage/entrypoints/cmd/server-entrypoint/main.go new file mode 100644 index 0000000..b4a99d0 --- /dev/null +++ b/clientImages/baseimage/entrypoints/cmd/server-entrypoint/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "entrypoints/lib" + "fmt" + "net/http" + "os" + "strconv" + "time" +) + +func infinite() { + failed := 0 + for { + fmt.Println("Start Server!") + start := time.Now() + exitCode := lib.ExecuteArgs() + elapsed := time.Since(start) + + fmt.Println("Server terminated! (" + strconv.Itoa(int(elapsed.Milliseconds())) + "ms)") + if elapsed < 100*time.Millisecond || exitCode > 0 || exitCode == -1 { + time.Sleep(50 * time.Millisecond) + failed = failed + 1 + } else { + failed = 0 + } + + if failed > 5 { + os.Exit(99) + } + } +} + +func main() { + go infinite() + + http.HandleFunc("/shutdown", lib.Shutdown) + fmt.Println("Listening on :8090...") + _ = http.ListenAndServe(":8090", nil) +} diff --git a/clientImages/baseimage/entrypoints/go.mod b/clientImages/baseimage/entrypoints/go.mod new file mode 100644 index 0000000..d419e94 --- /dev/null +++ b/clientImages/baseimage/entrypoints/go.mod @@ -0,0 +1,3 @@ +module entrypoints + +go 1.14 diff --git a/clientImages/baseimage/entrypoints/lib/lib.go b/clientImages/baseimage/entrypoints/lib/lib.go new file mode 100644 index 0000000..5340fa7 --- /dev/null +++ b/clientImages/baseimage/entrypoints/lib/lib.go @@ -0,0 +1,46 @@ +package lib + +import ( + "fmt" + "net/http" + "os" + "os/exec" +) + +var args = os.Args[1:] + +func ExecuteArgs() int { + var cmd *exec.Cmd + if len(args) > 1 { + program := args[0] + argv := args[1:] + + cmd = exec.Command(program, argv...) + } else if len(args) > 0 { + program := args[0] + + cmd = exec.Command(program) + } else { + fmt.Println("Nothing to do, no args specified") + return -1 + } + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + // keep stdin open + _, err := cmd.StdinPipe() + + err = cmd.Run() + + if err != nil { + fmt.Println(err) + } + return cmd.ProcessState.ExitCode() +} + +func Shutdown(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "shutdown...") + fmt.Println("shutdown...") + + go os.Exit(0) +} diff --git a/clientImages/dropbear/Dockerfile b/clientImages/dropbear/Dockerfile new file mode 100644 index 0000000..e423bde --- /dev/null +++ b/clientImages/dropbear/Dockerfile @@ -0,0 +1,57 @@ +FROM alpine:3.16 as dropbear-client +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] +# linux-headers are required by versions 2013.62 - 2018.76 +RUN apk add --no-cache bzip2 build-base zlib-dev linux-headers +RUN apk add --no-cache --upgrade bash +RUN apk add --no-cache --upgrade expect +ARG VERSION +RUN wget -O - -q "https://matt.ucc.asn.au/dropbear/releases/dropbear-${VERSION}.tar.bz2" | bunzip2 | tar -xf - +WORKDIR "/dropbear-${VERSION}" + +# Apply patches to fix the build for some very old versions. +COPY patches/ . +RUN if ! printf '%s\n%s\n' "0.31" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0001-2.x-0.30-Use-port-22-and-load-host-keys-from-etc-dro.patch ; \ + elif [ "${VERSION}" = "0.31" ]; then \ + patch -p1 < 0002-0.31-Load-host-keys-from-etc-dropbear.patch ; \ + elif [ "${VERSION}" = "0.32" ]; then \ + patch -p1 < 0003-0.32-Load-host-keys-from-etc-dropbear.patch ; \ + elif ! printf '%s\n%s\n' "0.38" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0004-0.33-0.37-Load-host-keys-from-etc-dropbear.patch ; \ + fi && \ + if ! printf '%s\n%s\n' "0.30" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0005-0.2x-Add-rule-to-make-libtomcrypt-to-Makefile.patch ; \ + fi && \ + if ! printf '%s\n%s\n' "0.32" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0006-0.2x-0.31-Add-missing-install-rule-to-Makefile.in.patch ; \ + elif [ "${VERSION}" = "0.32" ]; then \ + patch -p1 < 0007-0.32-Add-missing-install-rule-to-Makefile.in.patch ; \ + elif [ "${VERSION}" = "0.33" ]; then \ + patch -p1 < 0008-0.33-Backport-Makefile-install-rule-from-0.37.patch ; \ + elif ! printf '%s\n%s\n' "0.37" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0009-0.34-0.36-Backport-Makefile-install-rule-from-0.37.patch ; \ + fi && \ + if ! printf '%s\n%s\n' "0.40" "${VERSION}" | sort -c -g; then \ + patch -p1 < 0010-0.3x-Disable-Werror-to-avoid-build-failure-on-recent.patch ; \ + fi && \ + if [ "${VERSION}" = "0.30" ]; then \ + patch -p1 < 0011-0.30-Remove-out-of-place-linebreak-in-string-literal.patch ; \ + fi + +# Configure, build and install dropbear, then add the pre-generated hostkeys. +RUN ./configure && \ + make PROGRAMS="dbclient" -j "$(nproc)" && \ + make PROGRAMS="dbclient" install + + +LABEL "ssh_implementation"="dropbear" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" + +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY start.sh /usr/local/bin/ +COPY login.exp /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] + + diff --git a/clientImages/dropbear/dropbear_build.sh b/clientImages/dropbear/dropbear_build.sh new file mode 100755 index 0000000..a2f0ee3 --- /dev/null +++ b/clientImages/dropbear/dropbear_build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +#versions=(${versions=0.23 0.255 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.49 0.50 0.51 0.52 0.53.1 0.53 2011.54 2012.55 2013.56 2013.57 2013.58 2013.59 2013.60 2013.62 2014.63 2014.64 2014.65 2014.66 2015.67 2015.68 2015.69 2015.70 2015.71 2016.72 2016.73 2016.74 2017.75 2018.76 2019.77 2019.78 2020.79 2020.80 2020.81 2022.82}) +versions=(${versions=0.43 0.44 0.45 0.46 0.47 0.49 0.50}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}dropbear-client:${i} -f Dockerfile --target dropbear-client . +done + +exit "$EXITCODE" diff --git a/clientImages/dropbear/login.exp b/clientImages/dropbear/login.exp new file mode 100644 index 0000000..ebbfd80 --- /dev/null +++ b/clientImages/dropbear/login.exp @@ -0,0 +1,35 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn dbclient "$ip" "-p" "$port" "-l" "$user" + +set timeout 5 +expect { + "y/n" { + send "yes\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof \ No newline at end of file diff --git a/clientImages/dropbear/patches/0001-2.x-0.30-Use-port-22-and-load-host-keys-from-etc-dro.patch b/clientImages/dropbear/patches/0001-2.x-0.30-Use-port-22-and-load-host-keys-from-etc-dro.patch new file mode 100644 index 0000000..5358c43 --- /dev/null +++ b/clientImages/dropbear/patches/0001-2.x-0.30-Use-port-22-and-load-host-keys-from-etc-dro.patch @@ -0,0 +1,35 @@ +From e889237616d06e3afcbffdc55ed7fc2ff82988cd Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 12:01:32 +0200 +Subject: [PATCH] [2.x-0.30] Use port 22 and load host keys from + `/etc/dropbear` + +In dropbear releases up to version 0.30, dropbear used port 2244 by +default and only looked for host keys in the current directory. This +patch makes these versions behave like more recent dropbear releases by +using port 22 and looking for host keys in `/etc/dropbear` instead. +--- + options.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/options.h b/options.h +index c85c2f5..494a578 100644 +--- a/options.h ++++ b/options.h +@@ -10,11 +10,11 @@ + * Define compile-time options below. + ******************************************************************/ + +-#define DROPBEAR_PORT 2244 /* for testing */ ++#define DROPBEAR_PORT 22 /* for testing */ + + /* Hostkey paths */ +-#define DSS_PRIV_FILENAME "dropbear_dss_host_key" +-#define RSA_PRIV_FILENAME "dropbear_rsa_host_key" ++#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" ++#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" + + /* Encryption - at least one required. + * SSH2 RFC Draft requires 3DES and recommends blowfish, aes128 & twofish128 */ +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0002-0.31-Load-host-keys-from-etc-dropbear.patch b/clientImages/dropbear/patches/0002-0.31-Load-host-keys-from-etc-dropbear.patch new file mode 100644 index 0000000..1a1875d --- /dev/null +++ b/clientImages/dropbear/patches/0002-0.31-Load-host-keys-from-etc-dropbear.patch @@ -0,0 +1,28 @@ +From 1b5a5d7e370bb0ff33b698d9453cd245495392ca Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 17:52:39 +0200 +Subject: [PATCH] [0.31] Load host keys from `/etc/dropbear` + +Instead of using the current directory, dropbear should look for the +host keys in `/etc/dropbear`, just like more recent version do. +--- + options.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/options.h b/options.h +index 8207916..95deacb 100644 +--- a/options.h ++++ b/options.h +@@ -38,8 +38,8 @@ + #define DROPBEAR_PORT 22 + + /* Hostkey paths */ +-#define DSS_PRIV_FILENAME "dropbear_dss_host_key" +-#define RSA_PRIV_FILENAME "dropbear_rsa_host_key" ++#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" ++#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" + + /* Encryption - at least one required. + * SSH2 RFC Draft requires 3DES and recommends blowfish, aes128 & twofish128 */ +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0003-0.32-Load-host-keys-from-etc-dropbear.patch b/clientImages/dropbear/patches/0003-0.32-Load-host-keys-from-etc-dropbear.patch new file mode 100644 index 0000000..f4c01f5 --- /dev/null +++ b/clientImages/dropbear/patches/0003-0.32-Load-host-keys-from-etc-dropbear.patch @@ -0,0 +1,28 @@ +From 714690b349235dda5cb78008db8d6dd132d82da7 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 17:55:47 +0200 +Subject: [PATCH] [0.32] Load host keys from `/etc/dropbear` + +Instead of using the current directory, dropbear should look for the +host keys in `/etc/dropbear`, just like more recent version do. +--- + options.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/options.h b/options.h +index 8912e1b..6476e00 100644 +--- a/options.h ++++ b/options.h +@@ -38,8 +38,8 @@ + #define DROPBEAR_PORT 22 + + /* Default hostkey paths - these can be specified on the command line */ +-#define DSS_PRIV_FILENAME "dropbear_dss_host_key" +-#define RSA_PRIV_FILENAME "dropbear_rsa_host_key" ++#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" ++#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" + + /* Disable X11 Forwarding */ + /*#define DISABLE_X11FWD*/ +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0004-0.33-0.37-Load-host-keys-from-etc-dropbear.patch b/clientImages/dropbear/patches/0004-0.33-0.37-Load-host-keys-from-etc-dropbear.patch new file mode 100644 index 0000000..c9c311f --- /dev/null +++ b/clientImages/dropbear/patches/0004-0.33-0.37-Load-host-keys-from-etc-dropbear.patch @@ -0,0 +1,28 @@ +From 9c792299d7dabd12faecaa6db5b967150a33d1e4 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 17:57:44 +0200 +Subject: [PATCH] [0.33-0.37] Load host keys from `/etc/dropbear` + +Instead of using the `/etc`, dropbear should look for the host keys in +`/etc/dropbear`, just like more recent version do. +--- + options.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/options.h b/options.h +index 9254ac3..512b535 100644 +--- a/options.h ++++ b/options.h +@@ -32,8 +32,8 @@ + #define DROPBEAR_PORT 22 + + /* Default hostkey paths - these can be specified on the command line */ +-#define DSS_PRIV_FILENAME "/etc/dropbear_dss_host_key" +-#define RSA_PRIV_FILENAME "/etc/dropbear_rsa_host_key" ++#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" ++#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" + + /* Disable X11 Forwarding */ + /*#define DISABLE_X11FWD*/ +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0005-0.2x-Add-rule-to-make-libtomcrypt-to-Makefile.patch b/clientImages/dropbear/patches/0005-0.2x-Add-rule-to-make-libtomcrypt-to-Makefile.patch new file mode 100644 index 0000000..0872781 --- /dev/null +++ b/clientImages/dropbear/patches/0005-0.2x-Add-rule-to-make-libtomcrypt-to-Makefile.patch @@ -0,0 +1,41 @@ +From 21f2352308dff1eb2e7b013bd56238fe9835ecee Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 13:31:19 +0200 +Subject: [PATCH] [0.2x] Add rule to make libtomcrypt to Makefile + +This avoids having to run make -C libtomcrypt manually. +--- + Makefile.in | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/Makefile.in b/Makefile.in +index b3306e8..503cf29 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -1,7 +1,8 @@ + CC=@CC@ + LD=@CC@ + CFLAGS=-Wall -Ilibtomcrypt @CFLAGS@ +-LIBS=libtomcrypt/libtomcrypt.a @LIBS@ ++LTC=libtomcrypt/libtomcrypt.a ++LIBS=$(LTC) @LIBS@ + LDFLAGS=@LDFLAGS@ + + OBJS=util.o session.o packet.o algo.o buffer.o kex.o dss.o bignum.o \ +@@ -24,7 +25,13 @@ UCLIBC_CC=/home/matt/uclibc/bin/i386-uclibc-gcc + + all: dropbear dropbearkey + +-$(OBJS): $(HEADERS) Makefile ++$(OBJS): $(HEADERS) $(LTC) Makefile ++ ++$(LTC): $(HEADERS) ++ cd libtomcrypt && $(MAKE) clean && $(MAKE) ++ ++ltc-clean: ++ cd libtomcrypt && $(MAKE) clean + + dropbear: $(DROPBEAROBJS) $(HEADERS) Makefile + $(LD) $(LDFLAGS) -o dropbear $(DROPBEAROBJS) $(LIBS) +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0006-0.2x-0.31-Add-missing-install-rule-to-Makefile.in.patch b/clientImages/dropbear/patches/0006-0.2x-0.31-Add-missing-install-rule-to-Makefile.in.patch new file mode 100644 index 0000000..7c36ba5 --- /dev/null +++ b/clientImages/dropbear/patches/0006-0.2x-0.31-Add-missing-install-rule-to-Makefile.in.patch @@ -0,0 +1,32 @@ +From 97d22efe733de9bfdb28cd19a17de293c30e35c2 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 12:26:00 +0200 +Subject: [PATCH] [0.2x-0.31] Add missing `install` rule to `Makefile.in` + +Dropbear versions prior to 0.33 don't have an `install` rule in their +Makefile, so `make install` will fail. This adds such a rule to all +versions <0.32 to make it easier to install the binaries. For version +0.32, a separate patch is necessary. +--- + Makefile.in | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/Makefile.in b/Makefile.in +index b3306e8..5a8ec97 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -49,3 +49,12 @@ clean: + + tidy: + -rm -f *~ *.gcov ++ ++sbindir=/sbin ++bindir=/bin ++ ++install: all ++ install -m 755 dropbear $(DESTDIR)$(sbindir) ++ install -m 755 dropbearkey $(DESTDIR)$(bindir) ++ -chown root.root $(DESTDIR)$(sbindir)/dropbear ++ -chown root.root $(DESTDIR)$(bindir)/dropbearkey +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0007-0.32-Add-missing-install-rule-to-Makefile.in.patch b/clientImages/dropbear/patches/0007-0.32-Add-missing-install-rule-to-Makefile.in.patch new file mode 100644 index 0000000..5e61bb2 --- /dev/null +++ b/clientImages/dropbear/patches/0007-0.32-Add-missing-install-rule-to-Makefile.in.patch @@ -0,0 +1,34 @@ +From 1e87b0399c6fdfa3696bc6529f417cf606a504da Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 14:10:16 +0200 +Subject: [PATCH] [0.32] Add missing `install` rule to `Makefile.in` + +Dropbear 0.32 don't have an `install` rule in their Makefile, so `make +install` will fail. This adds such a rule make it easier to install the +binaries. + +Unfortunately, the patch for previous version won't apply cleanly, which +is why this comes a separate patch. +--- + Makefile.in | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/Makefile.in b/Makefile.in +index be77665..ceabec8 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -69,3 +69,12 @@ clean: ltc-clean ltm-clean + + tidy: + -rm -f *~ *.gcov */*~ ++ ++sbindir=/sbin ++bindir=/bin ++ ++install: all ++ install -m 755 dropbear $(DESTDIR)$(sbindir) ++ install -m 755 dropbearkey $(DESTDIR)$(bindir) ++ -chown root.root $(DESTDIR)$(sbindir)/dropbear ++ -chown root.root $(DESTDIR)$(bindir)/dropbearkey +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0008-0.33-Backport-Makefile-install-rule-from-0.37.patch b/clientImages/dropbear/patches/0008-0.33-Backport-Makefile-install-rule-from-0.37.patch new file mode 100644 index 0000000..4fc88a7 --- /dev/null +++ b/clientImages/dropbear/patches/0008-0.33-Backport-Makefile-install-rule-from-0.37.patch @@ -0,0 +1,37 @@ +From f7b2f1e4a42a700caadfc925875e8fda5f66ad5b Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 17:06:08 +0200 +Subject: [PATCH] [0.33] Backport Makefile `install` rule from 0.37 + +Without these fixes, `make install` does not create the bindir/sbindir +automatically. For example, if sbindir does not exist, dropbear won't be +installed to `sbin/dropbear`. Instead, a file named `sbin` is created. +This fixes the issue by just copying the install rule from version 0.37. +--- + Makefile.in | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/Makefile.in b/Makefile.in +index 19b38eb..ae0239b 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -46,9 +46,14 @@ strip: + -strip dropbearconvert + + install: all +- install -o root -g root dropbear $(sbindir) +- install -o root -g root dropbearkey $(bindir) +- install -o root -g root dropbearconvert $(bindir) ++ install -d -m 755 $(DESTDIR)$(sbindir) ++ install -d -m 755 $(DESTDIR)$(bindir) ++ install -m 755 dropbear $(DESTDIR)$(sbindir) ++ install -m 755 dropbearkey $(DESTDIR)$(bindir) ++ install -m 755 dropbearconvert $(DESTDIR)$(bindir) ++ -chown root.root $(DESTDIR)$(sbindir)/dropbear ++ -chown root.root $(DESTDIR)$(bindir)/dropbearkey ++ -chown root.root $(DESTDIR)$(bindir)/dropbearconvert + + static: dropbear-static dropbearkey-static dropbearconvert-static + +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0009-0.34-0.36-Backport-Makefile-install-rule-from-0.37.patch b/clientImages/dropbear/patches/0009-0.34-0.36-Backport-Makefile-install-rule-from-0.37.patch new file mode 100644 index 0000000..5dfb6ed --- /dev/null +++ b/clientImages/dropbear/patches/0009-0.34-0.36-Backport-Makefile-install-rule-from-0.37.patch @@ -0,0 +1,28 @@ +From dfa5db37cb00519d1a5a0fefcafe7ec6e3ab6394 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 17:20:00 +0200 +Subject: [PATCH] [0.34-0.36] Backport Makefile `install` rule from 0.37 + +Without these fixes, `make install` does not create the bindir/sbindir +automatically. For example, if sbindir does not exist, dropbear won't be +installed to `sbin/dropbear`. Instead, a file named `sbin` is created. +This fixes the issue by just copying the install rule from version 0.37. +--- + Makefile.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/Makefile.in b/Makefile.in +index 064773d..ac0a2bd 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -46,6 +46,8 @@ strip: + -strip dropbearconvert + + install: all ++ install -d -m 755 $(DESTDIR)$(sbindir) ++ install -d -m 755 $(DESTDIR)$(bindir) + install -m 755 dropbear $(DESTDIR)$(sbindir) + install -m 755 dropbearkey $(DESTDIR)$(bindir) + install -m 755 dropbearconvert $(DESTDIR)$(bindir) +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0010-0.3x-Disable-Werror-to-avoid-build-failure-on-recent.patch b/clientImages/dropbear/patches/0010-0.3x-Disable-Werror-to-avoid-build-failure-on-recent.patch new file mode 100644 index 0000000..ec69f1b --- /dev/null +++ b/clientImages/dropbear/patches/0010-0.3x-Disable-Werror-to-avoid-build-failure-on-recent.patch @@ -0,0 +1,29 @@ +From 1b2337ed2098607b6a29fd6282d9b4636e6cc791 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 12:11:31 +0200 +Subject: [PATCH] [0.3x] Disable `-Werror` to avoid build failure on recent + compilers + +Since libtomboy uses `-Werror`, these new compiler refuse to build it. +This just disables the `-Werror` flag to make it build even when +warnings were detected. +--- + libtomcrypt/makefile | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/libtomcrypt/makefile b/libtomcrypt/makefile +index c988ad0..1db2b22 100644 +--- a/libtomcrypt/makefile ++++ b/libtomcrypt/makefile +@@ -34,6 +34,9 @@ CFLAGS += -Os + #CFLAGS += -g3 + #ch1-01-3 + ++# prevent build failure due to some warnings found by recent compiler versions ++CFLAGS += -Wno-error ++ + #These flags control how the library gets built. + + #Output filenames for various targets. +-- +2.37.1 diff --git a/clientImages/dropbear/patches/0011-0.30-Remove-out-of-place-linebreak-in-string-literal.patch b/clientImages/dropbear/patches/0011-0.30-Remove-out-of-place-linebreak-in-string-literal.patch new file mode 100644 index 0000000..c392d40 --- /dev/null +++ b/clientImages/dropbear/patches/0011-0.30-Remove-out-of-place-linebreak-in-string-literal.patch @@ -0,0 +1,26 @@ +From 5bef8423e8a8e341d3147d125a4d5089abfbe013 Mon Sep 17 00:00:00 2001 +From: Jan Holthuis +Date: Fri, 19 Aug 2022 12:36:39 +0200 +Subject: [PATCH] [0.30] Remove out-of-place linebreak in string literal + +--- + chansession.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/chansession.c b/chansession.c +index 8c9578c..ce44ab8 100644 +--- a/chansession.c ++++ b/chansession.c +@@ -731,8 +731,8 @@ static void execchild(struct ChanSess *chansess) { + * usernames with the same uid, but differing groups, then the + * differing groups won't be set (as with initgroups()). The solution + * is for the sysadmin not to give out the UID twice */ +- if (getuid() != ses.authstate.pw->pw_uid) { dropbear_exit("couldn't +- change user as non-root"); ++ if (getuid() != ses.authstate.pw->pw_uid) { ++ dropbear_exit("couldn't change user as non-root"); + } + } + +-- +2.37.1 diff --git a/clientImages/dropbear/start.sh b/clientImages/dropbear/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/clientImages/dropbear/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/clientImages/dropbear/test b/clientImages/dropbear/test new file mode 100644 index 0000000..f0b573e --- /dev/null +++ b/clientImages/dropbear/test @@ -0,0 +1 @@ +0.23 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 0.40 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.49 0.50 0.51 0.52 0.53.1 0.53 0.255 2011.54 2012.55 2013.56 2013.57 2013.58 2013.59 2013.60 2013.62 2014.63 2014.64 2014.65 2014.66 2015.67 2015.68 2015.69 2015.70 2015.71 2016.72 2016.73 2016.74 2017.75 2018.76 2019.77 2019.78 2020.79 2020.80 2020.81 2022.82 diff --git a/clientImages/go/Dockerfile b/clientImages/go/Dockerfile new file mode 100644 index 0000000..f43ab90 --- /dev/null +++ b/clientImages/go/Dockerfile @@ -0,0 +1,22 @@ + +FROM golang:1.19 AS go-build +ARG VERSION +COPY client.go /build/ +WORKDIR /build +RUN go mod init example.com/SSHclient +RUN go mod edit -require golang.org/x/crypto@${VERSION} +RUN go get -v -t ./... +RUN go build +#RUN CGO_ENABLED=0 GOOS=linux + +FROM golang:1.19 AS go-client +ARG VERSION +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=go-build /build/ /usr/local/bin/ +LABEL "ssh_implementation"="go" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin/ +RUN go install +ENTRYPOINT ["client-entrypoint", "SSHclient"] + diff --git a/clientImages/go/client.go b/clientImages/go/client.go new file mode 100644 index 0000000..a256ddd --- /dev/null +++ b/clientImages/go/client.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "log" + "os" + + "golang.org/x/crypto/ssh" +) + +func main() { + if len(os.Args) != 5 { + log.Fatalf("Usage: %s ", os.Args[0]) + } + + client, session, err := connectToHost(os.Args[1], os.Args[2], os.Args[3]) + if err != nil { + panic(err) + } + out, err := session.CombinedOutput(os.Args[4]) + if err != nil { + panic(err) + } + fmt.Println(string(out)) + client.Close() +} + +func connectToHost(user, host string, pass string) (*ssh.Client, *ssh.Session, error) { + + sshConfig := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ssh.Password(pass)}, + } + sshConfig.HostKeyCallback = ssh.InsecureIgnoreHostKey() + + client, err := ssh.Dial("tcp", host, sshConfig) + if err != nil { + return nil, nil, err + } + + session, err := client.NewSession() + if err != nil { + client.Close() + return nil, nil, err + } + + return client, session, nil +} diff --git a/clientImages/go/go_build.sh b/clientImages/go/go_build.sh new file mode 100755 index 0000000..213a394 --- /dev/null +++ b/clientImages/go/go_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions= v0.0.0-20220924013350-4ba4fb4dd9e7 v0.0.0-20220919173607-35f4265a4bc0 v0.0.0-20220829220503-c86fa9a7ed90 v0.0.0-20220722155217-630584e8d5aa v0.0.0-20220622213112-05595931fe9d v0.0.0-20220517005047-85d78b3ac167 v0.0.0-20220511200225-c6db032c6c88 v0.0.0-20220427172511-eb4f295cb31f v0.0.0-20220331220935-ae2d96664a29 v0.0.0-20220315160706-3147a52a75dd}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}go-client:${i} -f Dockerfile --target go-client . +done + +exit "$EXITCODE" diff --git a/clientImages/go/start.sh b/clientImages/go/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/clientImages/go/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/clientImages/go/test_go_build.sh b/clientImages/go/test_go_build.sh new file mode 100755 index 0000000..26fcde5 --- /dev/null +++ b/clientImages/go/test_go_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions=8.8p1 9.0p1}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}openssh-server:${i} -f Dockerfile --target openssh-server . +done + +exit "$EXITCODE" diff --git a/clientImages/helper-functions.sh b/clientImages/helper-functions.sh new file mode 100644 index 0000000..0cedaef --- /dev/null +++ b/clientImages/helper-functions.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Imported from TLS-Docker-Library +# https://github.com/tls-attacker/TLS-Docker-Library/blob/master/images/helper-functions.sh + +set -uo pipefail + +FOLDER="$(realpath "$(dirname "$BASH_SOURCE")")" +DOCKER_REPOSITORY="${DOCKER_REPOSITORY:-}" + +# if set to 1, docker commands are recorded and written to cmds.sh +# required by build-everythin.py +CMD_GENERATION_MODE="${CMD_GENERATION_MODE:-0}" + +EXITCODE=0 +function _docker { + tag=$(python - "$@" << E +import sys +s = ['-t', '--tag'] +for i in s: + if i in sys.argv: + print(sys.argv[sys.argv.index(i)+1]) + sys.exit(0) +E +) + + if [[ $CMD_GENERATION_MODE -eq 0 ]]; then + echo -e "\033[1;33mBuilding $tag...\033[0m" + if ! outp=$(docker "$@" 2>&1); then + echo -e "[-]\033[1;31m Failed to build $tag!\033[0m\n$outp" + EXITCODE=$((EXITCODE + 1)) + return $EXITCODE + else + echo -e "[+]\033[1;32m Successfully built $tag!\033[0m" + fi + else + echo "cd '$(pwd)'" >> "$FOLDER/cmds.sh" + # see https://stackoverflow.com/a/8723305 + C='' + for arg in "$@"; do + arg="${arg//\\/\\\\}" + C="$C \"${arg//\"/\\\"}\"" + done + echo docker "$C" >> "$FOLDER/cmds.sh" + fi + + return $EXITCODE +} + + +function exit_on_error { + trap 'echo >&2 "Error - exited with status $? at line $LINENO:"; + pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR + set -euo pipefail +} + + +function track_error { + # does not result in an exit, even if set -e is enabled + if ! "$@"; then + EXITCODE=$((EXITCODE + 1)) + fi +} \ No newline at end of file diff --git a/clientImages/libssh/Dockerfile b/clientImages/libssh/Dockerfile new file mode 100644 index 0000000..e655463 --- /dev/null +++ b/clientImages/libssh/Dockerfile @@ -0,0 +1,26 @@ +FROM debian-build:stretch-libssl1.0 AS libssh-builder +ARG VERSION +RUN git clone https://gitlab.com/libssh/libssh-mirror.git libssh +RUN cd libssh \ + && git checkout tags/libssh-${VERSION} +WORKDIR /src/libssh +RUN mkdir -p build +RUN cd build \ + && cmake -DCMAKE_INSTALL_PREFIX=/install -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DWITH_STATIC_LIB=ON .. \ + && make +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static + +FROM debian:bullseye AS libssh-client +ARG VERSION=0.9.6 +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=libssh-builder /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] diff --git a/clientImages/libssh/DockerfileClient b/clientImages/libssh/DockerfileClient new file mode 100644 index 0000000..e655463 --- /dev/null +++ b/clientImages/libssh/DockerfileClient @@ -0,0 +1,26 @@ +FROM debian-build:stretch-libssl1.0 AS libssh-builder +ARG VERSION +RUN git clone https://gitlab.com/libssh/libssh-mirror.git libssh +RUN cd libssh \ + && git checkout tags/libssh-${VERSION} +WORKDIR /src/libssh +RUN mkdir -p build +RUN cd build \ + && cmake -DCMAKE_INSTALL_PREFIX=/install -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DWITH_STATIC_LIB=ON .. \ + && make +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static + +FROM debian:bullseye AS libssh-client +ARG VERSION=0.9.6 +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=libssh-builder /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] diff --git a/clientImages/libssh/build.sh b/clientImages/libssh/build.sh new file mode 100755 index 0000000..f2cc0cd --- /dev/null +++ b/clientImages/libssh/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh +exit_on_error + +track_error ./libssh-0_7_0_to_0_8_9.sh +track_error ./libssh-0_9_x.sh + +exit "$EXITCODE" diff --git a/clientImages/libssh/libssh-build.sh b/clientImages/libssh/libssh-build.sh new file mode 100755 index 0000000..a2f3ccd --- /dev/null +++ b/clientImages/libssh/libssh-build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(0.6.5 0.7.0 0.7.1 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.7.7 0.8.0 0.8.1 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.8.8 0.8.9 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.10.0 0.10.2 0.10.3 0.10.4) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}libssh-client:${i} -f Dockerfile --target libssh-client . +done + +exit "$EXITCODE" diff --git a/clientImages/libssh/libsshTest.c b/clientImages/libssh/libsshTest.c new file mode 100644 index 0000000..a9dc0f7 --- /dev/null +++ b/clientImages/libssh/libsshTest.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +int verify_knownhost(ssh_session session) +{ + int state, hlen; + unsigned char *hash = NULL; + char *hexa; + char buf[10]; + + state = ssh_is_server_known(session); + + hlen = ssh_get_pubkey_hash(session, &hash); + if (hlen < 0) + return -1; + + switch (state) + { + case SSH_SERVER_KNOWN_OK: + break; + + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr, "Host key for server changed: it is now:\n"); + ssh_print_hexa("Public key hash", hash, hlen); + break; + + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr, "The host key for this server was not found but an other" + "type of key exists.\n"); + break; + + case SSH_SERVER_FILE_NOT_FOUND: + fprintf(stderr, "Could not find known host file.\n"); + + case SSH_SERVER_NOT_KNOWN: + hexa = ssh_get_hexa(hash, hlen); + fprintf(stderr, "Public key hash: %s\n", hexa); + free(hexa); + break; + + case SSH_SERVER_ERROR: + fprintf(stderr, "Error %s", ssh_get_error(session)); + free(hash); + return -1; + } + + free(hash); + return 0; +} + +int show_remote_processes(ssh_session session, char *command) +{ + ssh_channel channel; + int rc; + char buffer[256]; + int nbytes; + + channel = ssh_channel_new(session); + if (channel == NULL) + return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + + rc = ssh_channel_request_exec(channel, command); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != (unsigned int) nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return SSH_OK; +} + +int main(int argc, char **argv) +{ + ssh_session ssh_session; + int rc; + + char *password; + int port = 22; + char *host; + char *user; + char *command; + + if(argc != 6){ + fprintf(stderr, "wrong arg count"); + exit(-1); + } + + + host = argv[1]; + port = atoi(argv[2]); + user = argv[3]; + password = argv[4]; + command = argv[5]; + + fprintf(stderr, "PARAMETER OK\n"); + // Open session and set options + ssh_session = ssh_new(); + if (ssh_session == NULL){ + exit(-1); + } + + fprintf(stderr, "SSH SESSION CREATED\n"); + + fprintf(stderr, "host: %s\n", host); + + if(ssh_options_set(ssh_session, SSH_OPTIONS_HOST, host) < 0){ + fprintf(stderr, "OPTION HOST ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_PORT, &port) < 0){ + fprintf(stderr, "OPTION PORT ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_USER, user) < 0){ + fprintf(stderr, "OPTION USER ERROR\n"); + } + + fprintf(stderr, "SSH OPTIONS OK\n"); + // Connect to server + rc = ssh_connect(ssh_session); + fprintf(stderr, "SSH CONNECTION START OK\n"); + if (rc != SSH_OK) + { + fprintf(stderr, "Error connecting to host: %s\n", + ssh_get_error(ssh_session)); + ssh_free(ssh_session); + exit(-1); + } + fprintf(stderr, "SSH CONNECT OK\n"); + + + // Verify the server's identity + if (verify_knownhost(ssh_session) < 0) + { + fprintf(stderr, "Error verify"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH VERIFY HOST OK\n"); + // Authenticate ourselves + rc = ssh_userauth_password(ssh_session, NULL, password); + if (rc != SSH_AUTH_SUCCESS) + { + fprintf(stderr, "Error authenticating with password: %s\n", + ssh_get_error(ssh_session)); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH AUTH OK\n"); + + rc = show_remote_processes(ssh_session,command); + if(rc != SSH_OK){ + fprintf(stderr, "Error on exec command\n"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH COMMAND EXEC OK\n"); + + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(0); +} \ No newline at end of file diff --git a/clientImages/metasploit/Dockerfile b/clientImages/metasploit/Dockerfile new file mode 100644 index 0000000..d1cf753 --- /dev/null +++ b/clientImages/metasploit/Dockerfile @@ -0,0 +1,10 @@ +FROM metasploitframework/metasploit-framework AS metasploit-scanner +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY start.sh /usr/local/bin/ +COPY msfScript.rc /usr/local/bin/ +LABEL "ssh_implementation"="metasploit" +LABEL "ssh_implementation_version"="latest" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin +ENTRYPOINT ["client-entrypoint", "start.sh"] + diff --git a/clientImages/metasploit/metasploit_build.sh b/clientImages/metasploit/metasploit_build.sh new file mode 100755 index 0000000..349c3d0 --- /dev/null +++ b/clientImages/metasploit/metasploit_build.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +_docker build -t ${DOCKER_REPOSITORY}metasploit -f Dockerfile --target metasploit-scanner . + +exit "$EXITCODE" diff --git a/clientImages/metasploit/msfScript.rc b/clientImages/metasploit/msfScript.rc new file mode 100644 index 0000000..4b6babb --- /dev/null +++ b/clientImages/metasploit/msfScript.rc @@ -0,0 +1,6 @@ +use auxiliary/scanner/ssh/ssh_login +set RHOSTS HOSTPLACEHOLDER +set RPORT PORTPLACEHOLDER +set PASSWORD PASSPLACEHOLDER +set USERNAME USERPLACEHOLDER +run \ No newline at end of file diff --git a/clientImages/metasploit/start.sh b/clientImages/metasploit/start.sh new file mode 100755 index 0000000..a78562b --- /dev/null +++ b/clientImages/metasploit/start.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +sed -i "s/HOSTPLACEHOLDER/$host/g" msfScript.rc +sed -i "s/PORTPLACEHOLDER/$port/g" msfScript.rc +sed -i "s/USERPLACEHOLDER/$user/g" msfScript.rc +sed -i "s/PASSPLACEHOLDER/$password/g" msfScript.rc + +/usr/src/metasploit-framework/msfconsole -r msfScript.rc + +exit 0 diff --git a/clientImages/openSSH/Dockerfile b/clientImages/openSSH/Dockerfile new file mode 100644 index 0000000..d95ab37 --- /dev/null +++ b/clientImages/openSSH/Dockerfile @@ -0,0 +1,32 @@ +FROM debian-build:bullseye AS openssh-downloader +ARG VERSION +RUN echo ${VERSION} +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +FROM debian-build:stretch-libssl1.0 AS openssh-builder +ARG VERSION +COPY --from=openssh-downloader /src/openssh-${VERSION} /src/openssh-${VERSION} +WORKDIR /src/openssh-${VERSION} +RUN ./configure CFLAGS="-DDEBUG_KEX=1 -DDEBUG_KEXDH=1 -DDEBUG_KEXECDH=1" --prefix /install && \ + make -j ${NUMCPUS:-8} && \ + make install + +FROM debian:stretch AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl1.0-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/clientImages/openSSH/Dockerfile2 b/clientImages/openSSH/Dockerfile2 new file mode 100644 index 0000000..edda7c0 --- /dev/null +++ b/clientImages/openSSH/Dockerfile2 @@ -0,0 +1,28 @@ +FROM debian-build:bullseye AS openssh-builder +ARG VERSION +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +WORKDIR /src/openssh-${VERSION} +RUN ./configure --prefix /install +RUN make +RUN make install + +FROM debian:bullseye AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/clientImages/openSSH/Dockerfile3 b/clientImages/openSSH/Dockerfile3 new file mode 100644 index 0000000..01031f3 --- /dev/null +++ b/clientImages/openSSH/Dockerfile3 @@ -0,0 +1,31 @@ +FROM debian-build:bullseye AS openssh-downloader +ARG VERSION +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +FROM debian-build:stretch-libssl1.0 AS openssh-builder +ARG VERSION +COPY --from=openssh-downloader /src/openssh-${VERSION} /src/openssh-${VERSION} +WORKDIR /src/openssh-${VERSION} +RUN ./configure --prefix /install && \ + make && \ + make install + +FROM debian:stretch AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl1.0-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/clientImages/openSSH/login.exp b/clientImages/openSSH/login.exp new file mode 100644 index 0000000..a56e00e --- /dev/null +++ b/clientImages/openSSH/login.exp @@ -0,0 +1,35 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn /install/bin/ssh "$ip" "-p" "$port" "-l" "$user" + +set timeout 5 +expect { + "yes/no" { + send "yes\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof diff --git a/clientImages/openSSH/openssh_build.sh b/clientImages/openSSH/openssh_build.sh new file mode 100755 index 0000000..5672fef --- /dev/null +++ b/clientImages/openSSH/openssh_build.sh @@ -0,0 +1,20 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions=7.0p1 7.1p1 7.2p1 7.2p2 7.3p1 7.4p1 7.5p1 7.6p1 7.7p1 8.4p1 8.5p1 8.6p1 8.7p1 8.8p1 9.0p1}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}openssh-client:${i} -f Dockerfile --target openssh-client . +done + +versions2=(${versions2=8.0p1 8.1p1 8.2p1 8.3p1}) +for j in "${versions2[@]}"; do + _docker build --build-arg VERSION=${j} -t ${DOCKER_REPOSITORY}openssh-client:${j} -f Dockerfile2 --target openssh-client . +done + +versions3=(${versions3=7.8p1 7.9p1}) +for k in "${versions3[@]}"; do + _docker build --build-arg VERSION=${k} -t ${DOCKER_REPOSITORY}openssh-client:${k} -f Dockerfile3 --target openssh-client . +done + +exit "$EXITCODE" diff --git a/clientImages/openSSH/start.sh b/clientImages/openSSH/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/clientImages/openSSH/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/clientImages/paramiko/Dockerfile b/clientImages/paramiko/Dockerfile new file mode 100644 index 0000000..54a2b5a --- /dev/null +++ b/clientImages/paramiko/Dockerfile @@ -0,0 +1,15 @@ +ARG VERSION_PYTHON=3.9 + +FROM python:${VERSION_PYTHON} AS paramiko-client +ARG VERSION +WORKDIR /usr/local/bin +COPY paramiko-client-script.py /usr/local/bin +COPY start.sh /usr/local/bin +RUN /usr/local/bin/python -m pip install --upgrade pip +RUN pip install paramiko==${VERSION} +RUN pip install click +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +LABEL "ssh_implementation"="paramiko" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint", "start.sh"] \ No newline at end of file diff --git a/clientImages/paramiko/paramiko-client-script.py b/clientImages/paramiko/paramiko-client-script.py new file mode 100755 index 0000000..5e7d081 --- /dev/null +++ b/clientImages/paramiko/paramiko-client-script.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import paramiko +import click + +@click.command() +@click.option('-H', '--host', help='hostname or ip', default='172.17.0.1"') +@click.option('-P', '--port', help='prot', default=3022, type=int) +@click.option('-u', '--username', help='username', default='demo') +@click.option('-p', '--password', help='password', default='password') +@click.option('-c', '--command', help='command', default='pwd') +@click.option('-o', '--output', is_flag=True, show_default=True, default=False, help='print output') +@click.option('-e', '--error', is_flag=True, show_default=True, default=False, help='print error') +def client_start(host, port, username, password, command, output, error): + print(f'host {host}\n') + print(f'port {port}\n') + print(f'username {username}\n') + if output: + print("init Client") + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + if output: + print("start connection") + client.connect(host, port=port, username=username, password=password) + + + if output: + print("start command") + stdin, stdout, stderr = client.exec_command(command) + + if output: + print("finish") + out = stdout.readlines() + err = stderr.readlines() + if output: + print("output: ", "\n".join(out)) + if error: + print("error: ", "\n".join(err)) + + stdin.close() + stdout.close() + stderr.close() + +if __name__ == '__main__': + client_start() + + diff --git a/clientImages/paramiko/paramiko.sh b/clientImages/paramiko/paramiko.sh new file mode 100755 index 0000000..b401879 --- /dev/null +++ b/clientImages/paramiko/paramiko.sh @@ -0,0 +1,9 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source "../../helper-functions.sh" + +#_docker build --build-arg VERSION=3.14 -t alpine-build:3.14 . +# +_docker build --build-arg VERSION=latest -f Dockerfile -t parmiko-client:1.0 . + +exit "$EXITCODE" \ No newline at end of file diff --git a/clientImages/paramiko/paramiko_build.sh b/clientImages/paramiko/paramiko_build.sh new file mode 100755 index 0000000..936fff4 --- /dev/null +++ b/clientImages/paramiko/paramiko_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions="2.11.0" "2.10.5" "2.10.4" "2.10.3" "2.10.2" "2.10.1" "2.10.0"}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}paramiko-client:${i} -f Dockerfile --target paramiko-client . +done + +exit "$EXITCODE" diff --git a/clientImages/paramiko/start.sh b/clientImages/paramiko/start.sh new file mode 100755 index 0000000..6d3971e --- /dev/null +++ b/clientImages/paramiko/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="172.17.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +echo | python "paramiko-client-script.py" $parameter + +exit 0 diff --git a/clientImages/puttyLinux/Dockerfile b/clientImages/puttyLinux/Dockerfile new file mode 100644 index 0000000..e93f343 --- /dev/null +++ b/clientImages/puttyLinux/Dockerfile @@ -0,0 +1,16 @@ +ARG U_VERSION +FROM ubuntu:${U_VERSION} AS putty-client +ARG VERSION +WORKDIR /usr/local/bin +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +RUN apt-get update -y +RUN apt-cache madison putty-tools +RUN apt list -a putty-tools +RUN apt-get install -y putty-tools=${VERSION} +RUN apt-get install expect -y +LABEL "ssh_implementation"="putty" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "start.sh"] diff --git a/clientImages/puttyLinux/login.exp b/clientImages/puttyLinux/login.exp new file mode 100644 index 0000000..a692f77 --- /dev/null +++ b/clientImages/puttyLinux/login.exp @@ -0,0 +1,40 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn plink -ssh "-P" "$port" "-l" "$user" "$ip" + +set timeout 2 +expect { + "y/n" { + send "y\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + "Press Return to begin session" { + send "\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof diff --git a/clientImages/puttyLinux/putty_linux_build.sh b/clientImages/puttyLinux/putty_linux_build.sh new file mode 100755 index 0000000..f79a159 --- /dev/null +++ b/clientImages/puttyLinux/putty_linux_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + + +_docker build --build-arg VERSION=0.76-2 --build-arg U_VERSION=22.04 -t ${DOCKER_REPOSITORY}putty-client:0.76-2 -f Dockerfile --target putty-client . +_docker build --build-arg VERSION=0.73-2 --build-arg U_VERSION=20.04 -t ${DOCKER_REPOSITORY}putty-client:0.73-2 -f Dockerfile --target putty-client . +_docker build --build-arg VERSION=0.70-4 --build-arg U_VERSION=18.04 -t ${DOCKER_REPOSITORY}putty-client:0.70-4 -f Dockerfile --target putty-client . + +exit "$EXITCODE" diff --git a/clientImages/puttyLinux/start.sh b/clientImages/puttyLinux/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/clientImages/puttyLinux/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/clientImages/wolfssh/Dockerfile b/clientImages/wolfssh/Dockerfile new file mode 100644 index 0000000..7ddb00c --- /dev/null +++ b/clientImages/wolfssh/Dockerfile @@ -0,0 +1,26 @@ +FROM debian-build:bullseye AS wolfssh-downloader +ARG VERSION +WORKDIR /src +RUN wget -q https://github.com/wolfSSL/wolfssh/archive/refs/tags/v${VERSION}.tar.gz +RUN tar -xf v${VERSION}.tar.gz + +FROM debian-build:bullseye AS wolfssh-client +ARG VERSION +COPY --from=wolfssh-downloader /src/wolfssh-${VERSION} /src/wolfssh-${VERSION} +WORKDIR /src/wolfssh-${VERSION} +RUN apt-get update +RUN apt-get install -y libtool +RUN apt-get install -y libwolfssl-dev +RUN apt-get install -y autoconf +RUN ./autogen.sh +RUN ./configure --enable-ssh +RUN make +RUN make check +RUN make install +LABEL "ssh_implementation"="wolfssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +WORKDIR /src/wolfssh-${VERSION}/examples/client +RUN ./client -h +ENTRYPOINT ["client-entrypoint", "./client"] \ No newline at end of file diff --git a/clientImages/wolfssh/wolfssh_build.sh b/clientImages/wolfssh/wolfssh_build.sh new file mode 100755 index 0000000..cebf466 --- /dev/null +++ b/clientImages/wolfssh/wolfssh_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh +#1.4.3-stable 1.4.2-stable 1.4.0-stable 1.3.0-stable 1.2.0-stable 1.1.0-stable 1.0.0-stable +versions=(${versions=1.4.11-stable 1.4.10-stable 1.4.8-stable 1.4.7-stable 1.4.6-stable 1.4.5-stable 1.4.4-stable 1.4.3-stable }) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}wolfssh-client:${i} -f Dockerfile --target wolfssh-client . +done + +exit "$EXITCODE" diff --git a/clientImages/zGrab2/Dockerfile b/clientImages/zGrab2/Dockerfile new file mode 100644 index 0000000..8f92fa2 --- /dev/null +++ b/clientImages/zGrab2/Dockerfile @@ -0,0 +1,14 @@ +FROM bberastegui/zgrab2:latest AS zgrab2-scanner +RUN apk update +RUN apk upgrade +RUN apk add bash +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY input.txt /usr/local/bin/ +COPY multiple.ini /usr/local/bin/ +COPY start.sh /usr/local/bin/ +LABEL "ssh_implementation"="zgrab2" +LABEL "ssh_implementation_version"="latest" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "bash /usr/local/bin/start.sh"] + diff --git a/clientImages/zGrab2/input.txt b/clientImages/zGrab2/input.txt new file mode 100644 index 0000000..9aabd88 --- /dev/null +++ b/clientImages/zGrab2/input.txt @@ -0,0 +1 @@ +HOSTPLACEHOLDER, , server \ No newline at end of file diff --git a/clientImages/zGrab2/multiple.ini b/clientImages/zGrab2/multiple.ini new file mode 100644 index 0000000..a396e75 --- /dev/null +++ b/clientImages/zGrab2/multiple.ini @@ -0,0 +1,6 @@ +[Application Options] +output-file="output.txt" +input-file="input.txt" +[ssh] +trigger="server" +port=PORTPLACEHOLDER \ No newline at end of file diff --git a/clientImages/zGrab2/start.sh b/clientImages/zGrab2/start.sh new file mode 100755 index 0000000..6899bf3 --- /dev/null +++ b/clientImages/zGrab2/start.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +sed -i "s/HOSTPLACEHOLDER/$host/g" input.txt +sed -i "s/PORTPLACEHOLDER/$port/g" multiple.ini + +cat input.txt | zgrab2 multiple -c multiple.ini -o output.txt + +exit 0 diff --git a/clientImages/zGrab2/zgrab2_build.sh b/clientImages/zGrab2/zgrab2_build.sh new file mode 100755 index 0000000..750bbf4 --- /dev/null +++ b/clientImages/zGrab2/zgrab2_build.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +_docker build -t ${DOCKER_REPOSITORY}zgrab2 -f Dockerfile --target zgrab2-scanner . + +exit "$EXITCODE" diff --git a/hostkey.ser_ecdsa_sha2_nistp521 b/hostkey.ser_ecdsa_sha2_nistp521 new file mode 100644 index 0000000..d66b1b0 Binary files /dev/null and b/hostkey.ser_ecdsa_sha2_nistp521 differ diff --git a/images/asyncssh/Dockerfile b/images/asyncssh/Dockerfile index f96b1e2..2bf2b4b 100644 --- a/images/asyncssh/Dockerfile +++ b/images/asyncssh/Dockerfile @@ -1,4 +1,6 @@ -FROM python:3 +ARG VERSION_PYTHON=3.9 + +FROM python:${VERSION_PYTHON} AS asyncssh-server WORKDIR /app COPY requirements.txt simple_server.py ./ @@ -24,3 +26,17 @@ ENV USERNAME="${USERNAME}" PASSWORD="${PASSWORD}" # hadolint ignore=DL3025 CMD python simple_server.py "${USERNAME}" --password "${PASSWORD}" --authorized-keys "authorized_keys" EXPOSE 22 + +FROM python:${VERSION_PYTHON} AS asyncssh-client +ARG VERSION +WORKDIR /usr/local/bin +COPY asyncssh-client-script.py /usr/local/bin +COPY start.sh /usr/local/bin +RUN /usr/local/bin/python -m pip install --upgrade pip +RUN pip install click +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +RUN pip install asyncssh==${VERSION} +LABEL "ssh_implementation"="asyncssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint", "start.sh"] diff --git a/images/asyncssh/compose.yml b/images/asyncssh/compose.yml index 5875af3..3011926 100644 --- a/images/asyncssh/compose.yml +++ b/images/asyncssh/compose.yml @@ -4,125 +4,366 @@ services: image: rub-nds/asyncssh-server:2.0.0 build: context: . + target: asyncssh-server args: VERSION: 2.0.0 + profiles: [ server ] asyncssh-server-2.0.1: image: rub-nds/asyncssh-server:2.0.1 build: context: . + target: asyncssh-server args: VERSION: 2.0.1 + profiles: [ server ] asyncssh-server-2.1.0: image: rub-nds/asyncssh-server:2.1.0 build: context: . + target: asyncssh-server args: VERSION: 2.1.0 + profiles: [ server ] asyncssh-server-2.2.0: image: rub-nds/asyncssh-server:2.2.0 build: context: . + target: asyncssh-server args: VERSION: 2.2.0 + profiles: [ server ] asyncssh-server-2.2.1: image: rub-nds/asyncssh-server:2.2.1 build: context: . + target: asyncssh-server args: VERSION: 2.2.1 + profiles: [ server ] asyncssh-server-2.3.0: image: rub-nds/asyncssh-server:2.3.0 build: context: . + target: asyncssh-server args: VERSION: 2.3.0 + profiles: [ server ] asyncssh-server-2.4.0: image: rub-nds/asyncssh-server:2.4.0 build: context: . + target: asyncssh-server args: VERSION: 2.4.0 + profiles: [ server ] asyncssh-server-2.4.1: image: rub-nds/asyncssh-server:2.4.1 build: context: . + target: asyncssh-server args: VERSION: 2.4.1 + profiles: [ server ] asyncssh-server-2.4.2: image: rub-nds/asyncssh-server:2.4.2 build: context: . + target: asyncssh-server args: VERSION: 2.4.2 + profiles: [ server ] asyncssh-server-2.5.0: image: rub-nds/asyncssh-server:2.5.0 build: context: . + target: asyncssh-server args: VERSION: 2.5.0 + profiles: [ server ] asyncssh-server-2.6.0: image: rub-nds/asyncssh-server:2.6.0 build: context: . + target: asyncssh-server args: VERSION: 2.6.0 + profiles: [ server ] asyncssh-server-2.7.0: image: rub-nds/asyncssh-server:2.7.0 build: context: . + target: asyncssh-server args: VERSION: 2.7.0 + profiles: [ server ] asyncssh-server-2.7.1: image: rub-nds/asyncssh-server:2.7.1 build: context: . + target: asyncssh-server args: VERSION: 2.7.1 + profiles: [ server ] asyncssh-server-2.7.2: image: rub-nds/asyncssh-server:2.7.2 build: context: . + target: asyncssh-server args: VERSION: 2.7.2 + profiles: [ server ] asyncssh-server-2.8.0: image: rub-nds/asyncssh-server:2.8.0 build: context: . + target: asyncssh-server args: VERSION: 2.8.0 + profiles: [ server ] asyncssh-server-2.8.1: image: rub-nds/asyncssh-server:2.8.1 build: context: . + target: asyncssh-server args: VERSION: 2.8.1 + profiles: [ server ] asyncssh-server-2.9.0: image: rub-nds/asyncssh-server:2.9.0 build: context: . + target: asyncssh-server args: VERSION: 2.9.0 + profiles: [ server ] asyncssh-server-2.10.0: image: rub-nds/asyncssh-server:2.10.0 build: context: . + target: asyncssh-server args: VERSION: 2.10.0 + profiles: [ server ] asyncssh-server-2.10.1: image: rub-nds/asyncssh-server:2.10.1 build: context: . + target: asyncssh-server args: VERSION: 2.10.1 + profiles: [ server ] asyncssh-server-2.11.0: image: rub-nds/asyncssh-server:2.11.0 build: context: . + target: asyncssh-server args: VERSION: 2.11.0 + profiles: [ server ] asyncssh-server-2.12.0: image: rub-nds/asyncssh-server:2.12.0 build: context: . + target: asyncssh-server args: VERSION: 2.12.0 + profiles: [ server ] + +# client + asyncssh-client-2.12.0: + image: rub-nds/asyncssh-client:2.12.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.12.0 + profiles: [ client ] + asyncssh-client-2.11.0: + image: rub-nds/asyncssh-client:2.11.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.11.0 + profiles: [ client ] + asyncssh-client-2.10.0: + image: rub-nds/asyncssh-client:2.10.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.10.0 + profiles: [ client ] + asyncssh-client-2.9.0: + image: rub-nds/asyncssh-client:2.9.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.9.0 + profiles: [ client ] + asyncssh-client-2.8.1: + image: rub-nds/asyncssh-client:2.8.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.8.1 + profiles: [ client ] + asyncssh-client-2.8.0: + image: rub-nds/asyncssh-client:2.8.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.8.0 + profiles: [ client ] + asyncssh-client-2.7.2: + image: rub-nds/asyncssh-client:2.7.2 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.7.2 + profiles: [ client ] + asyncssh-client-2.7.1: + image: rub-nds/asyncssh-client:2.7.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.7.1 + profiles: [ client ] + asyncssh-client-2.7.0: + image: rub-nds/asyncssh-client:2.7.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.7.0 + profiles: [ client ] + asyncssh-client-2.6.0: + image: rub-nds/asyncssh-client:2.6.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.6.0 + profiles: [ client ] + asyncssh-client-2.5.0: + image: rub-nds/asyncssh-client:2.5.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.5.0 + profiles: [ client ] + asyncssh-client-2.4.2: + image: rub-nds/asyncssh-client:2.4.2 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.4.2 + profiles: [ client ] + asyncssh-client-2.4.1: + image: rub-nds/asyncssh-client:2.4.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.4.1 + profiles: [ client ] + asyncssh-client-2.4.0: + image: rub-nds/asyncssh-client:2.4.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.4.0 + profiles: [ client ] + asyncssh-client-2.3.0: + image: rub-nds/asyncssh-client:2.3.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.3.0 + profiles: [ client ] + asyncssh-client-2.2.1: + image: rub-nds/asyncssh-client:2.2.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.2.1 + profiles: [ client ] + asyncssh-client-2.2.0: + image: rub-nds/asyncssh-client:2.2.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.2.0 + profiles: [ client ] + asyncssh-client-2.1.0: + image: rub-nds/asyncssh-client:2.1.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.1.0 + profiles: [ client ] + asyncssh-client-2.0.1: + image: rub-nds/asyncssh-client:2.0.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.0.1 + profiles: [ client ] + asyncssh-client-2.0.0: + image: rub-nds/asyncssh-client:2.0.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 2.0.0 + profiles: [ client ] + asyncssh-client-1.18.0: + image: rub-nds/asyncssh-client:1.18.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 1.18.0 + profiles: [ client ] + asyncssh-client-1.17.1: + image: rub-nds/asyncssh-client:1.17.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 1.17.1 + profiles: [ client ] + asyncssh-client-1.17.0: + image: rub-nds/asyncssh-client:1.17.0 + build: + context: . + target: asyncssh-client + args: + VERSION: 1.17.0 + profiles: [ client ] + asyncssh-client-1.16.1: + image: rub-nds/asyncssh-client:1.16.1 + build: + context: . + target: asyncssh-client + args: + VERSION: 1.16.1 + profiles: [ client ] + + + + + diff --git a/images/asyncssh/start.sh b/images/asyncssh/start.sh new file mode 100755 index 0000000..4f0a017 --- /dev/null +++ b/images/asyncssh/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="172.17.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +echo | python "asyncssh-client-script.py" $parameter + +exit 0 diff --git a/images/baseimage/Dockerfile b/images/baseimage/Dockerfile new file mode 100644 index 0000000..34b0d1e --- /dev/null +++ b/images/baseimage/Dockerfile @@ -0,0 +1,11 @@ +ARG VERSION=latest + +FROM alpine:${VERSION} +RUN apk add --no-cache build-base \ + git \ + curl \ + wget \ + bash \ + openssl-dev \ + zlib-dev +WORKDIR /src \ No newline at end of file diff --git a/images/baseimage/Dockerfile_debian b/images/baseimage/Dockerfile_debian new file mode 100644 index 0000000..04bb9d7 --- /dev/null +++ b/images/baseimage/Dockerfile_debian @@ -0,0 +1,16 @@ +ARG VERSION=latest + +FROM debian:${VERSION} as stretch-libssl +ARG LIBSSL_VERSION +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + git \ + curl \ + wget \ + bash \ + libssl${LIBSSL_VERSION}-dev \ + zlib1g-dev \ + python3 \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /src diff --git a/images/baseimage/Dockerfile_entrypoint b/images/baseimage/Dockerfile_entrypoint new file mode 100644 index 0000000..a3b2f1e --- /dev/null +++ b/images/baseimage/Dockerfile_entrypoint @@ -0,0 +1,9 @@ +FROM golang:1.14 AS build +COPY entrypoints /build/ +WORKDIR /build +RUN CGO_ENABLED=0 GOOS=linux go build -o ./ ./... + + +FROM scratch +COPY --from=build /build/client-entrypoint /bin/ +COPY --from=build /build/server-entrypoint /bin/ diff --git a/images/baseimage/build-base-image.sh b/images/baseimage/build-base-image.sh new file mode 100755 index 0000000..0bd78d7 --- /dev/null +++ b/images/baseimage/build-base-image.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source "../helper-functions.sh" + +#_docker build --build-arg VERSION=3.14 -t alpine-build:3.14 . +# +#_docker build --build-arg VERSION=bullseye -f Dockerfile_debian -t debian-build:bullseye . +_docker build --build-arg VERSION=stretch --build-arg LIBSSL_VERSION=1.0 -f Dockerfile_debian -t debian-build:stretch-libssl1.0 . + +exit "$EXITCODE" diff --git a/images/baseimage/build-base.sh b/images/baseimage/build-base.sh new file mode 100644 index 0000000..68e3613 --- /dev/null +++ b/images/baseimage/build-base.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh +exit_on_error + +track_error ./build-base-image.sh + +exit "$EXITCODE" diff --git a/images/baseimage/entrypoints/cmd/client-entrypoint/main.go b/images/baseimage/entrypoints/cmd/client-entrypoint/main.go new file mode 100644 index 0000000..d856733 --- /dev/null +++ b/images/baseimage/entrypoints/cmd/client-entrypoint/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "entrypoints/lib" + "fmt" + "net/http" + "os" + "time" +) + +func trigger(w http.ResponseWriter, req *http.Request) { + go lib.ExecuteArgs() + fmt.Fprintf(w, "done") +} + +func main() { + if len(os.Args) > 1 { + go lib.ExecuteArgs() + + http.HandleFunc("/trigger", trigger) + http.HandleFunc("/shutdown", lib.Shutdown) + fmt.Println("Listening on :8090...") + _ = http.ListenAndServe(":8090", nil) + } else { + fmt.Println("No args specified; sleeping forever just to keep the container alive") + // Using a fancier sleep (as in https://stackoverflow.com/a/36419222/) causes go to die, + // as all routines are sleeping, which is interpreted as a deadlock + // So we just sleep for an hour forever + for true { + time.Sleep(time.Hour) + } + } +} diff --git a/images/baseimage/entrypoints/cmd/server-entrypoint/main.go b/images/baseimage/entrypoints/cmd/server-entrypoint/main.go new file mode 100644 index 0000000..b4a99d0 --- /dev/null +++ b/images/baseimage/entrypoints/cmd/server-entrypoint/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "entrypoints/lib" + "fmt" + "net/http" + "os" + "strconv" + "time" +) + +func infinite() { + failed := 0 + for { + fmt.Println("Start Server!") + start := time.Now() + exitCode := lib.ExecuteArgs() + elapsed := time.Since(start) + + fmt.Println("Server terminated! (" + strconv.Itoa(int(elapsed.Milliseconds())) + "ms)") + if elapsed < 100*time.Millisecond || exitCode > 0 || exitCode == -1 { + time.Sleep(50 * time.Millisecond) + failed = failed + 1 + } else { + failed = 0 + } + + if failed > 5 { + os.Exit(99) + } + } +} + +func main() { + go infinite() + + http.HandleFunc("/shutdown", lib.Shutdown) + fmt.Println("Listening on :8090...") + _ = http.ListenAndServe(":8090", nil) +} diff --git a/images/baseimage/entrypoints/go.mod b/images/baseimage/entrypoints/go.mod new file mode 100644 index 0000000..d419e94 --- /dev/null +++ b/images/baseimage/entrypoints/go.mod @@ -0,0 +1,3 @@ +module entrypoints + +go 1.14 diff --git a/images/baseimage/entrypoints/lib/lib.go b/images/baseimage/entrypoints/lib/lib.go new file mode 100644 index 0000000..5340fa7 --- /dev/null +++ b/images/baseimage/entrypoints/lib/lib.go @@ -0,0 +1,46 @@ +package lib + +import ( + "fmt" + "net/http" + "os" + "os/exec" +) + +var args = os.Args[1:] + +func ExecuteArgs() int { + var cmd *exec.Cmd + if len(args) > 1 { + program := args[0] + argv := args[1:] + + cmd = exec.Command(program, argv...) + } else if len(args) > 0 { + program := args[0] + + cmd = exec.Command(program) + } else { + fmt.Println("Nothing to do, no args specified") + return -1 + } + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + // keep stdin open + _, err := cmd.StdinPipe() + + err = cmd.Run() + + if err != nil { + fmt.Println(err) + } + return cmd.ProcessState.ExitCode() +} + +func Shutdown(w http.ResponseWriter, req *http.Request) { + fmt.Fprintf(w, "shutdown...") + fmt.Println("shutdown...") + + go os.Exit(0) +} diff --git a/images/dropbear/Dockerfile b/images/dropbear/Dockerfile index 8586c7d..b4fb207 100644 --- a/images/dropbear/Dockerfile +++ b/images/dropbear/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 +FROM alpine:3.16 as dropbear-downloader SHELL ["/bin/ash", "-eo", "pipefail", "-c"] # linux-headers are required by versions 2013.62 - 2018.76 RUN apk add --no-cache bzip2 build-base zlib-dev linux-headers @@ -38,7 +38,14 @@ RUN if ! printf '%s\n%s\n' "0.31" "${VERSION}" | sort -c -g; then \ patch -p1 < 0011-0.30-Remove-out-of-place-linebreak-in-string-literal.patch ; \ fi + +FROM alpine:3.16 as dropbear-server +RUN apk add --no-cache bzip2 build-base zlib-dev linux-headers +ARG VERSION # Configure, build and install dropbear, then add the pre-generated hostkeys. +COPY --from=dropbear-downloader /dropbear-${VERSION} /dropbear-${VERSION} +WORKDIR "/dropbear-${VERSION}" + RUN ./configure && \ make PROGRAMS="dropbear" -j "$(nproc)" && \ make PROGRAMS="dropbear" install @@ -61,3 +68,31 @@ LABEL ssh.implementation.name="dropbear" \ ssh.implementation.type="server" CMD ["dropbear", "-FEB"] EXPOSE 22 + + +FROM alpine:3.16 as dropbear-client +RUN apk add --no-cache bzip2 build-base zlib-dev linux-headers +RUN apk add --no-cache --upgrade bash +RUN apk add --no-cache --upgrade expect + +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY start.sh /usr/local/bin/ +COPY login.exp /usr/local/bin/ + +ARG VERSION +# Configure, build and install dropbear, then add the pre-generated hostkeys. +COPY --from=dropbear-downloader /dropbear-${VERSION} /dropbear-${VERSION} +WORKDIR "/dropbear-${VERSION}" + +# Configure, build and install bdclient +RUN ./configure && \ + make PROGRAMS="dbclient" -j "$(nproc)" && \ + make PROGRAMS="dbclient" install + + +LABEL "ssh_implementation"="dropbear" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" + +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] diff --git a/images/dropbear/compose.yml b/images/dropbear/compose.yml index fea76c3..9d5b465 100644 --- a/images/dropbear/compose.yml +++ b/images/dropbear/compose.yml @@ -4,335 +4,901 @@ services: image: rub-nds/dropbear-server:0.23 build: context: . + target: dropbear-server args: VERSION: '0.23' + profiles: [ server ] dropbear-server-0.28: image: rub-nds/dropbear-server:0.28 build: context: . + target: dropbear-server args: VERSION: '0.28' + profiles: [ server ] dropbear-server-0.29: image: rub-nds/dropbear-server:0.29 build: context: . + target: dropbear-server args: VERSION: '0.29' + profiles: [ server ] dropbear-server-0.30: image: rub-nds/dropbear-server:0.30 build: context: . + target: dropbear-server args: VERSION: '0.30' + profiles: [ server ] dropbear-server-0.31: image: rub-nds/dropbear-server:0.31 build: context: . + target: dropbear-server args: VERSION: '0.31' + profiles: [ server ] dropbear-server-0.32: image: rub-nds/dropbear-server:0.32 build: context: . + target: dropbear-server args: VERSION: '0.32' + profiles: [ server ] dropbear-server-0.33: image: rub-nds/dropbear-server:0.33 build: context: . + target: dropbear-server args: VERSION: '0.33' + profiles: [ server ] dropbear-server-0.34: image: rub-nds/dropbear-server:0.34 build: context: . + target: dropbear-server args: VERSION: '0.34' + profiles: [ server ] dropbear-server-0.35: image: rub-nds/dropbear-server:0.35 build: context: . + target: dropbear-server args: VERSION: '0.35' + profiles: [ server ] dropbear-server-0.36: image: rub-nds/dropbear-server:0.36 build: context: . + target: dropbear-server args: VERSION: '0.36' + profiles: [ server ] dropbear-server-0.37: image: rub-nds/dropbear-server:0.37 build: context: . + target: dropbear-server args: VERSION: '0.37' + profiles: [ server ] dropbear-server-0.38: image: rub-nds/dropbear-server:0.38 build: context: . + target: dropbear-server args: VERSION: '0.38' + profiles: [ server ] dropbear-server-0.39: image: rub-nds/dropbear-server:0.39 build: context: . + target: dropbear-server args: VERSION: '0.39' + profiles: [ server ] dropbear-server-0.40: image: rub-nds/dropbear-server:0.40 build: context: . + target: dropbear-server args: VERSION: '0.40' + profiles: [ server ] dropbear-server-0.41: image: rub-nds/dropbear-server:0.41 build: context: . + target: dropbear-server args: VERSION: '0.41' + profiles: [ server ] dropbear-server-0.42: image: rub-nds/dropbear-server:0.42 build: context: . + target: dropbear-server args: VERSION: '0.42' + profiles: [ server ] dropbear-server-0.43: image: rub-nds/dropbear-server:0.43 build: context: . + target: dropbear-server args: VERSION: '0.43' + profiles: [ server ] dropbear-server-0.44: image: rub-nds/dropbear-server:0.44 build: context: . + target: dropbear-server args: VERSION: '0.44' + profiles: [ server ] dropbear-server-0.45: image: rub-nds/dropbear-server:0.45 build: context: . + target: dropbear-server args: VERSION: '0.45' + profiles: [ server ] dropbear-server-0.46: image: rub-nds/dropbear-server:0.46 build: context: . + target: dropbear-server args: VERSION: '0.46' + profiles: [ server ] dropbear-server-0.47: image: rub-nds/dropbear-server:0.47 build: context: . + target: dropbear-server args: VERSION: '0.47' + profiles: [ server ] dropbear-server-0.49: image: rub-nds/dropbear-server:0.49 build: context: . + target: dropbear-server args: VERSION: '0.49' + profiles: [ server ] dropbear-server-0.50: image: rub-nds/dropbear-server:0.50 build: context: . + target: dropbear-server args: VERSION: '0.50' + profiles: [ server ] dropbear-server-0.51: image: rub-nds/dropbear-server:0.51 build: context: . + target: dropbear-server args: VERSION: '0.51' + profiles: [ server ] dropbear-server-0.52: image: rub-nds/dropbear-server:0.52 build: context: . + target: dropbear-server args: VERSION: '0.52' + profiles: [ server ] dropbear-server-0.53.1: image: rub-nds/dropbear-server:0.53.1 build: context: . + target: dropbear-server args: - VERSION: 0.53.1 + VERSION: '0.53.1' + profiles: [ server ] dropbear-server-0.53: image: rub-nds/dropbear-server:0.53 build: context: . + target: dropbear-server args: VERSION: '0.53' + profiles: [ server ] dropbear-server-0.255: image: rub-nds/dropbear-server:0.255 build: context: . + target: dropbear-server args: VERSION: '0.255' + profiles: [ server ] dropbear-server-2011.54: image: rub-nds/dropbear-server:2011.54 build: context: . + target: dropbear-server args: VERSION: '2011.54' + profiles: [ server ] dropbear-server-2012.55: image: rub-nds/dropbear-server:2012.55 build: context: . + target: dropbear-server args: VERSION: '2012.55' + profiles: [ server ] dropbear-server-2013.56: image: rub-nds/dropbear-server:2013.56 build: context: . + target: dropbear-server args: VERSION: '2013.56' + profiles: [ server ] dropbear-server-2013.57: image: rub-nds/dropbear-server:2013.57 build: context: . + target: dropbear-server args: VERSION: '2013.57' + profiles: [ server ] dropbear-server-2013.58: image: rub-nds/dropbear-server:2013.58 build: context: . + target: dropbear-server args: VERSION: '2013.58' + profiles: [ server ] dropbear-server-2013.59: image: rub-nds/dropbear-server:2013.59 build: context: . + target: dropbear-server args: VERSION: '2013.59' + profiles: [ server ] dropbear-server-2013.60: image: rub-nds/dropbear-server:2013.60 build: context: . + target: dropbear-server args: VERSION: '2013.60' + profiles: [ server ] dropbear-server-2013.62: image: rub-nds/dropbear-server:2013.62 build: context: . + target: dropbear-server args: VERSION: '2013.62' + profiles: [ server ] dropbear-server-2014.63: image: rub-nds/dropbear-server:2014.63 build: context: . + target: dropbear-server args: VERSION: '2014.63' + profiles: [ server ] dropbear-server-2014.64: image: rub-nds/dropbear-server:2014.64 build: context: . + target: dropbear-server args: VERSION: '2014.64' + profiles: [ server ] dropbear-server-2014.65: image: rub-nds/dropbear-server:2014.65 build: context: . + target: dropbear-server args: VERSION: '2014.65' + profiles: [ server ] dropbear-server-2014.66: image: rub-nds/dropbear-server:2014.66 build: context: . + target: dropbear-server args: VERSION: '2014.66' + profiles: [ server ] dropbear-server-2015.67: image: rub-nds/dropbear-server:2015.67 build: context: . + target: dropbear-server args: VERSION: '2015.67' + profiles: [ server ] dropbear-server-2015.68: image: rub-nds/dropbear-server:2015.68 build: context: . + target: dropbear-server args: VERSION: '2015.68' + profiles: [ server ] dropbear-server-2015.69: image: rub-nds/dropbear-server:2015.69 build: context: . + target: dropbear-server args: VERSION: '2015.69' + profiles: [ server ] dropbear-server-2015.70: image: rub-nds/dropbear-server:2015.70 build: context: . + target: dropbear-server args: VERSION: '2015.70' + profiles: [ server ] dropbear-server-2015.71: image: rub-nds/dropbear-server:2015.71 build: context: . + target: dropbear-server args: VERSION: '2015.71' + profiles: [ server ] dropbear-server-2016.72: image: rub-nds/dropbear-server:2016.72 build: context: . + target: dropbear-server args: VERSION: '2016.72' + profiles: [ server ] dropbear-server-2016.73: image: rub-nds/dropbear-server:2016.73 build: context: . + target: dropbear-server args: VERSION: '2016.73' + profiles: [ server ] dropbear-server-2016.74: image: rub-nds/dropbear-server:2016.74 build: context: . + target: dropbear-server args: VERSION: '2016.74' + profiles: [ server ] dropbear-server-2017.75: image: rub-nds/dropbear-server:2017.75 build: context: . + target: dropbear-server args: VERSION: '2017.75' + profiles: [ server ] dropbear-server-2018.76: image: rub-nds/dropbear-server:2018.76 build: context: . + target: dropbear-server args: VERSION: '2018.76' + profiles: [ server ] dropbear-server-2019.77: image: rub-nds/dropbear-server:2019.77 build: context: . + target: dropbear-server args: VERSION: '2019.77' + profiles: [ server ] dropbear-server-2019.78: image: rub-nds/dropbear-server:2019.78 build: context: . + target: dropbear-server args: VERSION: '2019.78' + profiles: [ server ] dropbear-server-2020.79: image: rub-nds/dropbear-server:2020.79 build: context: . + target: dropbear-server args: VERSION: '2020.79' + profiles: [ server ] dropbear-server-2020.80: image: rub-nds/dropbear-server:2020.80 build: context: . + target: dropbear-server args: VERSION: '2020.80' + profiles: [ server ] dropbear-server-2020.81: image: rub-nds/dropbear-server:2020.81 build: context: . + target: dropbear-server args: VERSION: '2020.81' + profiles: [ server ] dropbear-server-2022.82: image: rub-nds/dropbear-server:2022.82 build: context: . + target: dropbear-server args: VERSION: '2022.82' + profiles: [ server ] + + + #client + +# +# dropbear-client-0.23: +# image: rub-nds/dropbear-client:0.23 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.23' +# profiles: [ client ] +# dropbear-client-0.255: +# image: rub-nds/dropbear-client:0.255 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.255' +# profiles: [ client ] +# dropbear-client-0.28: +# image: rub-nds/dropbear-client:0.28 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.28' +# profiles: [ client ] +# dropbear-client-0.29: +# image: rub-nds/dropbear-client:0.29 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.29' +# profiles: [ client ] +# dropbear-client-0.30: +# image: rub-nds/dropbear-client:0.30 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.30' +# profiles: [ client ] +# dropbear-client-0.31: +# image: rub-nds/dropbear-client:0.31 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.31' +# profiles: [ client ] +# dropbear-client-0.32: +# image: rub-nds/dropbear-client:0.32 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.32' +# profiles: [ client ] +# dropbear-client-0.33: +# image: rub-nds/dropbear-client:0.33 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.33' +# profiles: [ client ] +# dropbear-client-0.34: +# image: rub-nds/dropbear-client:0.34 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.34' +# profiles: [ client ] +# dropbear-client-0.35: +# image: rub-nds/dropbear-client:0.35 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.35' +# profiles: [ client ] +# dropbear-client-0.36: +# image: rub-nds/dropbear-client:0.36 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.36' +# profiles: [ client ] +# dropbear-client-0.37: +# image: rub-nds/dropbear-client:0.37 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.37' +# profiles: [ client ] +# dropbear-client-0.38: +# image: rub-nds/dropbear-client:0.38 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.38' +# profiles: [ client ] +# dropbear-client-0.39: +# image: rub-nds/dropbear-client:0.39 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.39' +# profiles: [ client ] +# dropbear-client-0.40: +# image: rub-nds/dropbear-client:0.40 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.40' +# profiles: [ client ] +# dropbear-client-0.41: +# image: rub-nds/dropbear-client:0.41 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.41' +# profiles: [ client ] +# dropbear-client-0.42: +# image: rub-nds/dropbear-client:0.42 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.42' +# profiles: [ client ] +# dropbear-client-0.43: +# image: rub-nds/dropbear-client:0.43 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.43' +# profiles: [ client ] + +# dropbear-client-0.44: +# image: rub-nds/dropbear-client:0.44 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.44' +# profiles: [ client ] +# dropbear-client-0.45: +# image: rub-nds/dropbear-client:0.45 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.45' +# profiles: [ client ] +# dropbear-client-0.46: +# image: rub-nds/dropbear-client:0.46 +# build: +# context: . +# target: dropbear-client +# args: +# VERSION: '0.46' +# profiles: [ client ] + dropbear-client-0.47: + image: rub-nds/dropbear-client:0.47 + build: + context: . + target: dropbear-client + args: + VERSION: '0.47' + profiles: [ client ] + dropbear-client-0.49: + image: rub-nds/dropbear-client:0.49 + build: + context: . + target: dropbear-client + args: + VERSION: '0.49' + profiles: [ client ] + dropbear-client-0.50: + image: rub-nds/dropbear-client:0.50 + build: + context: . + target: dropbear-client + args: + VERSION: '0.50' + profiles: [ client ] + dropbear-client-0.51: + image: rub-nds/dropbear-client:0.51 + build: + context: . + target: dropbear-client + args: + VERSION: '0.51' + profiles: [ client ] + dropbear-client-0.52: + image: rub-nds/dropbear-client:0.52 + build: + context: . + target: dropbear-client + args: + VERSION: '0.52' + profiles: [ client ] + dropbear-client-0.53.1: + image: rub-nds/dropbear-client:0.53.1 + build: + context: . + target: dropbear-client + args: + VERSION: '0.53.1' + profiles: [ client ] + dropbear-client-0.53: + image: rub-nds/dropbear-client:0.53 + build: + context: . + target: dropbear-client + args: + VERSION: '0.53' + profiles: [ client ] + dropbear-client-2011.54: + image: rub-nds/dropbear-client:2011.54 + build: + context: . + target: dropbear-client + args: + VERSION: '2011.54' + profiles: [ client ] + dropbear-client-2012.55: + image: rub-nds/dropbear-client:2012.55 + build: + context: . + target: dropbear-client + args: + VERSION: '2012.55' + profiles: [ client ] + dropbear-client-2013.56: + image: rub-nds/dropbear-client:2013.56 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.56' + profiles: [ client ] + dropbear-client-2013.57: + image: rub-nds/dropbear-client:2013.57 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.57' + profiles: [ client ] + dropbear-client-2013.58: + image: rub-nds/dropbear-client:2013.58 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.58' + profiles: [ client ] + dropbear-client-2013.59: + image: rub-nds/dropbear-client:2013.59 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.59' + profiles: [ client ] + dropbear-client-2013.60: + image: rub-nds/dropbear-client:2013.60 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.60' + profiles: [ client ] + dropbear-client-2013.62: + image: rub-nds/dropbear-client:2013.62 + build: + context: . + target: dropbear-client + args: + VERSION: '2013.62' + profiles: [ client ] + dropbear-client-2014.63: + image: rub-nds/dropbear-client:2014.63 + build: + context: . + target: dropbear-client + args: + VERSION: '2014.63' + profiles: [ client ] + dropbear-client-2014.64: + image: rub-nds/dropbear-client:2014.64 + build: + context: . + target: dropbear-client + args: + VERSION: '2014.64' + profiles: [ client ] + dropbear-client-2014.65: + image: rub-nds/dropbear-client:2014.65 + build: + context: . + target: dropbear-client + args: + VERSION: '2014.65' + profiles: [ client ] + dropbear-client-2014.66: + image: rub-nds/dropbear-client:2014.66 + build: + context: . + target: dropbear-client + args: + VERSION: '2014.66' + profiles: [ client ] + dropbear-client-2015.67: + image: rub-nds/dropbear-client:2015.67 + build: + context: . + target: dropbear-client + args: + VERSION: '2015.67' + profiles: [ client ] + dropbear-client-2015.68: + image: rub-nds/dropbear-client:2015.68 + build: + context: . + target: dropbear-client + args: + VERSION: '2015.68' + profiles: [ client ] + dropbear-client-2015.69: + image: rub-nds/dropbear-client:2015.69 + build: + context: . + target: dropbear-client + args: + VERSION: '2015.69' + profiles: [ client ] + dropbear-client-2015.70: + image: rub-nds/dropbear-client:2015.70 + build: + context: . + target: dropbear-client + args: + VERSION: '2015.70' + profiles: [ client ] + dropbear-client-2015.71: + image: rub-nds/dropbear-client:2015.71 + build: + context: . + target: dropbear-client + args: + VERSION: '2015.71' + profiles: [ client ] + dropbear-client-2016.72: + image: rub-nds/dropbear-client:2016.72 + build: + context: . + target: dropbear-client + args: + VERSION: '2016.72' + profiles: [ client ] + dropbear-client-2016.73: + image: rub-nds/dropbear-client:2016.73 + build: + context: . + target: dropbear-client + args: + VERSION: '2016.73' + profiles: [ client ] + dropbear-client-2016.74: + image: rub-nds/dropbear-client:2016.74 + build: + context: . + target: dropbear-client + args: + VERSION: '2016.74' + profiles: [ client ] + dropbear-client-2017.75: + image: rub-nds/dropbear-client:2017.75 + build: + context: . + target: dropbear-client + args: + VERSION: '2017.75' + profiles: [ client ] + dropbear-client-2018.76: + image: rub-nds/dropbear-client:2018.76 + build: + context: . + target: dropbear-client + args: + VERSION: '2018.76' + profiles: [ client ] + dropbear-client-2019.77: + image: rub-nds/dropbear-client:2019.77 + build: + context: . + target: dropbear-client + args: + VERSION: '2019.77' + profiles: [ client ] + dropbear-client-2019.78: + image: rub-nds/dropbear-client:2019.78 + build: + context: . + target: dropbear-client + args: + VERSION: '2019.78' + profiles: [ client ] + dropbear-client-2020.79: + image: rub-nds/dropbear-client:2020.79 + build: + context: . + target: dropbear-client + args: + VERSION: '2020.79' + profiles: [ client ] + dropbear-client-2020.80: + image: rub-nds/dropbear-client:2020.80 + build: + context: . + target: dropbear-client + args: + VERSION: '2020.80' + profiles: [ client ] + dropbear-client-2020.81: + image: rub-nds/dropbear-client:2020.81 + build: + context: . + target: dropbear-client + args: + VERSION: '2020.81' + profiles: [ client ] + dropbear-client-2022.82: + image: rub-nds/dropbear-client:2022.82 + build: + context: . + target: dropbear-client + args: + VERSION: '2022.82' + profiles: [ client ] \ No newline at end of file diff --git a/images/dropbear/login.exp b/images/dropbear/login.exp new file mode 100644 index 0000000..ebbfd80 --- /dev/null +++ b/images/dropbear/login.exp @@ -0,0 +1,35 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn dbclient "$ip" "-p" "$port" "-l" "$user" + +set timeout 5 +expect { + "y/n" { + send "yes\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof \ No newline at end of file diff --git a/images/dropbear/start.sh b/images/dropbear/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/images/dropbear/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/images/go/Dockerfile b/images/go/Dockerfile new file mode 100644 index 0000000..f43ab90 --- /dev/null +++ b/images/go/Dockerfile @@ -0,0 +1,22 @@ + +FROM golang:1.19 AS go-build +ARG VERSION +COPY client.go /build/ +WORKDIR /build +RUN go mod init example.com/SSHclient +RUN go mod edit -require golang.org/x/crypto@${VERSION} +RUN go get -v -t ./... +RUN go build +#RUN CGO_ENABLED=0 GOOS=linux + +FROM golang:1.19 AS go-client +ARG VERSION +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=go-build /build/ /usr/local/bin/ +LABEL "ssh_implementation"="go" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin/ +RUN go install +ENTRYPOINT ["client-entrypoint", "SSHclient"] + diff --git a/images/go/client.go b/images/go/client.go new file mode 100644 index 0000000..a256ddd --- /dev/null +++ b/images/go/client.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "log" + "os" + + "golang.org/x/crypto/ssh" +) + +func main() { + if len(os.Args) != 5 { + log.Fatalf("Usage: %s ", os.Args[0]) + } + + client, session, err := connectToHost(os.Args[1], os.Args[2], os.Args[3]) + if err != nil { + panic(err) + } + out, err := session.CombinedOutput(os.Args[4]) + if err != nil { + panic(err) + } + fmt.Println(string(out)) + client.Close() +} + +func connectToHost(user, host string, pass string) (*ssh.Client, *ssh.Session, error) { + + sshConfig := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ssh.Password(pass)}, + } + sshConfig.HostKeyCallback = ssh.InsecureIgnoreHostKey() + + client, err := ssh.Dial("tcp", host, sshConfig) + if err != nil { + return nil, nil, err + } + + session, err := client.NewSession() + if err != nil { + client.Close() + return nil, nil, err + } + + return client, session, nil +} diff --git a/images/go/compose.yml b/images/go/compose.yml new file mode 100644 index 0000000..09747c7 --- /dev/null +++ b/images/go/compose.yml @@ -0,0 +1,86 @@ +version: '3.2' +services: + go-client-v0.0.0-20220924013350-4ba4fb4dd9e7: + image: rub-nds/go-client:0.0.0-20220924013350-4ba4fb4dd9e7 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220924013350-4ba4fb4dd9e7 + profiles: [client] + go-client-v0.0.0-20220919173607-35f4265a4bc0: + image: rub-nds/go-client:v0.0.0-20220919173607-35f4265a4bc0 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220919173607-35f4265a4bc0 + profiles: [client] + go-client-v0.0.0-20220829220503-c86fa9a7ed90: + image: rub-nds/go-client:v0.0.0-20220829220503-c86fa9a7ed90 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220829220503-c86fa9a7ed90 + profiles: [client] + go-client-v0.0.0-20220722155217-630584e8d5aa: + image: rub-nds/go-client:v0.0.0-20220722155217-630584e8d5aa + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220722155217-630584e8d5aa + profiles: [client] + go-client-v0.0.0-20220622213112-05595931fe9d: + image: rub-nds/go-client:v0.0.0-20220622213112-05595931fe9d + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220622213112-05595931fe9d + profiles: [client] + go-client-v0.0.0-20220517005047-85d78b3ac167: + image: rub-nds/go-client:v0.0.0-20220517005047-85d78b3ac167 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220517005047-85d78b3ac167 + profiles: [client] + go-client-v0.0.0-20220511200225-c6db032c6c88: + image: rub-nds/go-client:v0.0.0-20220511200225-c6db032c6c88 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220511200225-c6db032c6c88 + profiles: [client] + go-client-v0.0.0-20220427172511-eb4f295cb31f: + image: rub-nds/go-client:v0.0.0-20220427172511-eb4f295cb31f + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220427172511-eb4f295cb31f + profiles: [client] + go-client-v0.0.0-20220331220935-ae2d96664a29: + image: rub-nds/go-client:v0.0.0-20220331220935-ae2d96664a29 + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220331220935-ae2d96664a29 + profiles: [client] + go-client-v0.0.0-20220315160706-3147a52a75dd: + image: rub-nds/go-client:v0.0.0-20220315160706-3147a52a75dd + build: + context: . + target: go-client + args: + VERSION: v0.0.0-20220315160706-3147a52a75dd + profiles: [client] + + + + diff --git a/images/go/start.sh b/images/go/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/images/go/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/images/go/test_go_build.sh b/images/go/test_go_build.sh new file mode 100755 index 0000000..26fcde5 --- /dev/null +++ b/images/go/test_go_build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit 1 +source ../helper-functions.sh + +versions=(${versions=8.8p1 9.0p1}) +for i in "${versions[@]}"; do + _docker build --build-arg VERSION=${i} -t ${DOCKER_REPOSITORY}openssh-server:${i} -f Dockerfile --target openssh-server . +done + +exit "$EXITCODE" diff --git a/images/helper-functions.sh b/images/helper-functions.sh new file mode 100644 index 0000000..0cedaef --- /dev/null +++ b/images/helper-functions.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Imported from TLS-Docker-Library +# https://github.com/tls-attacker/TLS-Docker-Library/blob/master/images/helper-functions.sh + +set -uo pipefail + +FOLDER="$(realpath "$(dirname "$BASH_SOURCE")")" +DOCKER_REPOSITORY="${DOCKER_REPOSITORY:-}" + +# if set to 1, docker commands are recorded and written to cmds.sh +# required by build-everythin.py +CMD_GENERATION_MODE="${CMD_GENERATION_MODE:-0}" + +EXITCODE=0 +function _docker { + tag=$(python - "$@" << E +import sys +s = ['-t', '--tag'] +for i in s: + if i in sys.argv: + print(sys.argv[sys.argv.index(i)+1]) + sys.exit(0) +E +) + + if [[ $CMD_GENERATION_MODE -eq 0 ]]; then + echo -e "\033[1;33mBuilding $tag...\033[0m" + if ! outp=$(docker "$@" 2>&1); then + echo -e "[-]\033[1;31m Failed to build $tag!\033[0m\n$outp" + EXITCODE=$((EXITCODE + 1)) + return $EXITCODE + else + echo -e "[+]\033[1;32m Successfully built $tag!\033[0m" + fi + else + echo "cd '$(pwd)'" >> "$FOLDER/cmds.sh" + # see https://stackoverflow.com/a/8723305 + C='' + for arg in "$@"; do + arg="${arg//\\/\\\\}" + C="$C \"${arg//\"/\\\"}\"" + done + echo docker "$C" >> "$FOLDER/cmds.sh" + fi + + return $EXITCODE +} + + +function exit_on_error { + trap 'echo >&2 "Error - exited with status $? at line $LINENO:"; + pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR + set -euo pipefail +} + + +function track_error { + # does not result in an exit, even if set -e is enabled + if ! "$@"; then + EXITCODE=$((EXITCODE + 1)) + fi +} \ No newline at end of file diff --git a/images/libssh-0.7.x-0.8.x/Dockerfile b/images/libssh-0.7.x-0.8.x/Dockerfile index 58114a3..56f2303 100644 --- a/images/libssh-0.7.x-0.8.x/Dockerfile +++ b/images/libssh-0.7.x-0.8.x/Dockerfile @@ -43,10 +43,29 @@ RUN if printf '%s\n%s\n%s\n' "0.8.2" "${VERSION}" "0.8.6" | sort -c -g; then \ fi WORKDIR /src/libssh/build -RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/install .. && \ +RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/install -DWITH_STATIC_LIB=ON .. && \ make -j "$(nproc)" && \ make install + +FROM debian:bullseye AS libssh-client +COPY --from=libssh-builder /src/libssh /src/libssh +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static +ARG VERSION +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +#COPY --from=libssh-builder-client /build/libsshTest /usr/local/bin/ +RUN cp /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] + FROM debian:stretch AS libssh-server ARG VERSION RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/images/libssh-0.7.x-0.8.x/DockerfileClient b/images/libssh-0.7.x-0.8.x/DockerfileClient new file mode 100644 index 0000000..e655463 --- /dev/null +++ b/images/libssh-0.7.x-0.8.x/DockerfileClient @@ -0,0 +1,26 @@ +FROM debian-build:stretch-libssl1.0 AS libssh-builder +ARG VERSION +RUN git clone https://gitlab.com/libssh/libssh-mirror.git libssh +RUN cd libssh \ + && git checkout tags/libssh-${VERSION} +WORKDIR /src/libssh +RUN mkdir -p build +RUN cd build \ + && cmake -DCMAKE_INSTALL_PREFIX=/install -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DWITH_STATIC_LIB=ON .. \ + && make +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static + +FROM debian:bullseye AS libssh-client +ARG VERSION=0.9.6 +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=libssh-builder /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] diff --git a/images/libssh-0.7.x-0.8.x/compose.yml b/images/libssh-0.7.x-0.8.x/compose.yml index 49b2e72..86cdea1 100644 --- a/images/libssh-0.7.x-0.8.x/compose.yml +++ b/images/libssh-0.7.x-0.8.x/compose.yml @@ -6,105 +6,296 @@ services: context: . args: VERSION: 0.7.0 + profiles: [ server ] libssh-server-0.7.1: image: rub-nds/libssh-server:0.7.1 build: context: . args: VERSION: 0.7.1 + profiles: [ server ] libssh-server-0.7.2: image: rub-nds/libssh-server:0.7.2 build: context: . args: VERSION: 0.7.2 + profiles: [ server ] libssh-server-0.7.3: image: rub-nds/libssh-server:0.7.3 build: context: . args: VERSION: 0.7.3 + profiles: [ server ] libssh-server-0.7.4: image: rub-nds/libssh-server:0.7.4 build: context: . args: VERSION: 0.7.4 + profiles: [ server ] libssh-server-0.7.5: image: rub-nds/libssh-server:0.7.5 build: context: . args: VERSION: 0.7.5 + profiles: [ server ] libssh-server-0.7.6: image: rub-nds/libssh-server:0.7.6 build: context: . args: VERSION: 0.7.6 + profiles: [ server ] libssh-server-0.7.7: image: rub-nds/libssh-server:0.7.7 build: context: . args: VERSION: 0.7.7 + profiles: [ server ] libssh-server-0.8.0: image: rub-nds/libssh-server:0.8.0 build: context: . args: VERSION: 0.8.0 + profiles: [ server ] libssh-server-0.8.1: image: rub-nds/libssh-server:0.8.1 build: context: . args: VERSION: 0.8.1 + profiles: [ server ] libssh-server-0.8.2: image: rub-nds/libssh-server:0.8.2 build: context: . args: VERSION: 0.8.2 + profiles: [ server ] libssh-server-0.8.3: image: rub-nds/libssh-server:0.8.3 build: context: . args: VERSION: 0.8.3 + profiles: [ server ] libssh-server-0.8.4: image: rub-nds/libssh-server:0.8.4 build: context: . args: VERSION: 0.8.4 + profiles: [ server ] libssh-server-0.8.5: image: rub-nds/libssh-server:0.8.5 build: context: . args: VERSION: 0.8.5 + profiles: [ server ] libssh-server-0.8.6: image: rub-nds/libssh-server:0.8.6 build: context: . args: VERSION: 0.8.6 + profiles: [ server ] libssh-server-0.8.7: image: rub-nds/libssh-server:0.8.7 build: context: . args: VERSION: 0.8.7 + profiles: [ server ] libssh-server-0.8.8: image: rub-nds/libssh-server:0.8.8 build: context: . args: VERSION: 0.8.8 + profiles: [ server ] libssh-server-0.8.9: image: rub-nds/libssh-server:0.8.9 build: context: . args: VERSION: 0.8.9 + profiles: [ server ] + + #client + libssh-client-0.6.5: + image: rub-nds/libssh-client:0.6.5 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.6.5 + profiles: [ client ] + libssh-client-0.7.0: + image: rub-nds/libssh-client:0.7.0 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.0 + profiles: [ client ] + libssh-client-0.7.1: + image: rub-nds/libssh-client:0.7.1 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.1 + profiles: [ client ] + libssh-client-0.7.2: + image: rub-nds/libssh-client:0.7.2 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.2 + profiles: [ client ] + libssh-client-0.7.3: + image: rub-nds/libssh-client:0.7.3 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.3 + profiles: [ client ] + libssh-client-0.7.4: + image: rub-nds/libssh-client:0.7.4 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.4 + profiles: [ client ] + libssh-client-0.7.5: + image: rub-nds/libssh-client:0.7.5 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.5 + profiles: [ client ] + libssh-client-0.7.6: + image: rub-nds/libssh-client:0.7.6 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.6 + profiles: [ client ] + libssh-client-0.7.7: + image: rub-nds/libssh-client:0.7.7 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.7.7 + profiles: [ client ] + libssh-client-0.8.0: + image: rub-nds/libssh-client:0.8.0 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.0 + profiles: [ client ] + libssh-client-0.8.1: + image: rub-nds/libssh-client:0.8.1 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.1 + profiles: [ client ] + libssh-client-0.8.2: + image: rub-nds/libssh-client:0.8.2 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.2 + profiles: [ client ] + libssh-client-0.8.3: + image: rub-nds/libssh-client:0.8.3 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.3 + profiles: [ client ] + libssh-client-0.8.4: + image: rub-nds/libssh-client:0.8.4 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.4 + profiles: [ client ] + libssh-client-0.8.5: + image: rub-nds/libssh-client:0.8.5 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.5 + profiles: [ client ] + libssh-client-0.8.6: + image: rub-nds/libssh-client:0.8.6 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.6 + profiles: [ client ] + libssh-client-0.8.7: + image: rub-nds/libssh-client:0.8.7 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.7 + profiles: [ client ] + libssh-client-0.8.8: + image: rub-nds/libssh-client:0.8.8 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.8 + profiles: [ client ] + libssh-client-0.8.9: + image: rub-nds/libssh-client:0.8.9 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.8.9 + profiles: [ client ] diff --git a/images/libssh-0.7.x-0.8.x/libsshTest.c b/images/libssh-0.7.x-0.8.x/libsshTest.c new file mode 100644 index 0000000..a9dc0f7 --- /dev/null +++ b/images/libssh-0.7.x-0.8.x/libsshTest.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +int verify_knownhost(ssh_session session) +{ + int state, hlen; + unsigned char *hash = NULL; + char *hexa; + char buf[10]; + + state = ssh_is_server_known(session); + + hlen = ssh_get_pubkey_hash(session, &hash); + if (hlen < 0) + return -1; + + switch (state) + { + case SSH_SERVER_KNOWN_OK: + break; + + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr, "Host key for server changed: it is now:\n"); + ssh_print_hexa("Public key hash", hash, hlen); + break; + + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr, "The host key for this server was not found but an other" + "type of key exists.\n"); + break; + + case SSH_SERVER_FILE_NOT_FOUND: + fprintf(stderr, "Could not find known host file.\n"); + + case SSH_SERVER_NOT_KNOWN: + hexa = ssh_get_hexa(hash, hlen); + fprintf(stderr, "Public key hash: %s\n", hexa); + free(hexa); + break; + + case SSH_SERVER_ERROR: + fprintf(stderr, "Error %s", ssh_get_error(session)); + free(hash); + return -1; + } + + free(hash); + return 0; +} + +int show_remote_processes(ssh_session session, char *command) +{ + ssh_channel channel; + int rc; + char buffer[256]; + int nbytes; + + channel = ssh_channel_new(session); + if (channel == NULL) + return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + + rc = ssh_channel_request_exec(channel, command); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != (unsigned int) nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return SSH_OK; +} + +int main(int argc, char **argv) +{ + ssh_session ssh_session; + int rc; + + char *password; + int port = 22; + char *host; + char *user; + char *command; + + if(argc != 6){ + fprintf(stderr, "wrong arg count"); + exit(-1); + } + + + host = argv[1]; + port = atoi(argv[2]); + user = argv[3]; + password = argv[4]; + command = argv[5]; + + fprintf(stderr, "PARAMETER OK\n"); + // Open session and set options + ssh_session = ssh_new(); + if (ssh_session == NULL){ + exit(-1); + } + + fprintf(stderr, "SSH SESSION CREATED\n"); + + fprintf(stderr, "host: %s\n", host); + + if(ssh_options_set(ssh_session, SSH_OPTIONS_HOST, host) < 0){ + fprintf(stderr, "OPTION HOST ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_PORT, &port) < 0){ + fprintf(stderr, "OPTION PORT ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_USER, user) < 0){ + fprintf(stderr, "OPTION USER ERROR\n"); + } + + fprintf(stderr, "SSH OPTIONS OK\n"); + // Connect to server + rc = ssh_connect(ssh_session); + fprintf(stderr, "SSH CONNECTION START OK\n"); + if (rc != SSH_OK) + { + fprintf(stderr, "Error connecting to host: %s\n", + ssh_get_error(ssh_session)); + ssh_free(ssh_session); + exit(-1); + } + fprintf(stderr, "SSH CONNECT OK\n"); + + + // Verify the server's identity + if (verify_knownhost(ssh_session) < 0) + { + fprintf(stderr, "Error verify"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH VERIFY HOST OK\n"); + // Authenticate ourselves + rc = ssh_userauth_password(ssh_session, NULL, password); + if (rc != SSH_AUTH_SUCCESS) + { + fprintf(stderr, "Error authenticating with password: %s\n", + ssh_get_error(ssh_session)); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH AUTH OK\n"); + + rc = show_remote_processes(ssh_session,command); + if(rc != SSH_OK){ + fprintf(stderr, "Error on exec command\n"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH COMMAND EXEC OK\n"); + + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(0); +} \ No newline at end of file diff --git a/images/libssh/Dockerfile b/images/libssh/Dockerfile index cd47c02..f61b178 100644 --- a/images/libssh/Dockerfile +++ b/images/libssh/Dockerfile @@ -18,16 +18,33 @@ RUN mkdir /src && \ curl -s "https://gitlab.com/libssh/libssh-mirror/-/archive/libssh-${VERSION}/libssh-mirror-libssh-${VERSION}.tar.gz" | tar xzf - && \ mv "libssh-mirror-libssh-${VERSION}" /src/libssh WORKDIR /src/libssh - ARG USERNAME=sshattacker ARG PASSWORD=secret RUN sed -i "s/myuser/${USERNAME}/g" examples/ssh_server_fork.c && \ sed -i "s/mypassword/${PASSWORD}/g" examples/ssh_server_fork.c WORKDIR /src/libssh/build -RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/install .. && \ +RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/install -DWITH_STATIC_LIB=ON .. && \ make -j "$(nproc)" && \ make install +FROM debian:bullseye AS libssh-client +COPY --from=libssh-builder /src/libssh /src/libssh +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static +ARG VERSION +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +#COPY --from=libssh-builder-client /build/libsshTest /usr/local/bin/ +RUN cp /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] + FROM debian:bullseye AS libssh-server ARG VERSION RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/images/libssh/DockerfileClient b/images/libssh/DockerfileClient new file mode 100644 index 0000000..e655463 --- /dev/null +++ b/images/libssh/DockerfileClient @@ -0,0 +1,26 @@ +FROM debian-build:stretch-libssl1.0 AS libssh-builder +ARG VERSION +RUN git clone https://gitlab.com/libssh/libssh-mirror.git libssh +RUN cd libssh \ + && git checkout tags/libssh-${VERSION} +WORKDIR /src/libssh +RUN mkdir -p build +RUN cd build \ + && cmake -DCMAKE_INSTALL_PREFIX=/install -DWITH_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DWITH_STATIC_LIB=ON .. \ + && make +COPY libsshTest.c /build/ +RUN find /src/libssh/build/include/libssh -name libssh_version.h -exec cp {} /src/libssh/include/libssh \; +RUN gcc -I/src/libssh/include /build/libsshTest.c /src/libssh/build/src/libssh.a -lrt -lcrypto -lz -lpthread -ldl -o /build/libsshTest -static + +FROM debian:bullseye AS libssh-client +ARG VERSION=0.9.6 +RUN apt-get update +RUN apt-get install -y --fix-missing libssl-dev zlib1g-dev +RUN rm -rf /var/lib/apt/lists/* +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY --from=libssh-builder /build/libsshTest /usr/local/bin/ +WORKDIR /usr/local/bin/ +LABEL "ssh_implementation"="libssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint","/usr/local/bin/libsshTest"] diff --git a/images/libssh/compose.yml b/images/libssh/compose.yml index 7c004a3..2b5c5f0 100644 --- a/images/libssh/compose.yml +++ b/images/libssh/compose.yml @@ -4,41 +4,158 @@ services: image: rub-nds/libssh-server:0.9.0 build: context: . + target: libssh-server args: VERSION: 0.9.0 + profiles: [ server ] libssh-server-0.9.1: image: rub-nds/libssh-server:0.9.1 build: context: . + target: libssh-server args: VERSION: 0.9.1 + profiles: [ server ] libssh-server-0.9.2: image: rub-nds/libssh-server:0.9.2 build: context: . + target: libssh-server args: VERSION: 0.9.2 + profiles: [ server ] libssh-server-0.9.3: image: rub-nds/libssh-server:0.9.3 build: context: . + target: libssh-server args: VERSION: 0.9.3 + profiles: [ server ] libssh-server-0.9.4: image: rub-nds/libssh-server:0.9.4 build: context: . + target: libssh-server args: VERSION: 0.9.4 + profiles: [ server ] libssh-server-0.9.5: image: rub-nds/libssh-server:0.9.5 build: context: . + target: libssh-server args: VERSION: 0.9.5 + profiles: [ server ] libssh-server-0.9.6: image: rub-nds/libssh-server:0.9.6 build: context: . + target: libssh-server args: VERSION: 0.9.6 + profiles: [ server ] + + #Clients + + libssh-client-0.9.0: + image: rub-nds/libssh-client:0.9.0 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.9.0 + profiles: [ client ] + libssh-client-0.9.1: + image: rub-nds/libssh-client:0.9.1 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.9.1 + profiles: [ client ] + libssh-client-0.9.2: + image: rub-nds/libssh-client:0.9.2 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.9.2 + profiles: [ client ] + libssh-client-0.9.3: + image: rub-nds/libssh-client:0.9.3 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.9.3 + profiles: [ client ] + libssh-client-0.9.4: + image: rub-nds/libssh-client:0.9.4 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.9.4 + profiles: [ client ] +# libssh-client-0.9.5: dont work +# image: rub-nds/libssh-client:0.9.5 +# build: +# context: . +# dockerfile: DockerfileClient +# target: libssh-client +# args: +# VERSION: 0.9.5 +# profiles: [ client ] +# libssh-client-0.9.6: dont work +# image: rub-nds/libssh-client:0.9.6 +# build: +# context: . +# dockerfile: DockerfileClient +# target: libssh-client +# args: +# VERSION: 0.9.6 +# profiles: [ client ] +# libssh-client-0.10.0: # dont work +# image: rub-nds/libssh-client:0.10.0 +# build: +# context: . +# dockerfile: DockerfileClient +# target: libssh-client +# args: +# VERSION: 0.10.0 +# profiles: [ client ] + libssh-client-0.10.2: + image: rub-nds/libssh-client:0.10.2 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.10.2 + profiles: [ client ] + libssh-client-0.10.3: + image: rub-nds/libssh-client:0.10.3 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.10.3 + profiles: [ client ] + libssh-client-0.10.4: + image: rub-nds/libssh-client:0.10.4 + build: + context: . + dockerfile: DockerfileClient + target: libssh-client + args: + VERSION: 0.10.4 + profiles: [ client ] + diff --git a/images/libssh/libsshTest.c b/images/libssh/libsshTest.c new file mode 100644 index 0000000..a9dc0f7 --- /dev/null +++ b/images/libssh/libsshTest.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +int verify_knownhost(ssh_session session) +{ + int state, hlen; + unsigned char *hash = NULL; + char *hexa; + char buf[10]; + + state = ssh_is_server_known(session); + + hlen = ssh_get_pubkey_hash(session, &hash); + if (hlen < 0) + return -1; + + switch (state) + { + case SSH_SERVER_KNOWN_OK: + break; + + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr, "Host key for server changed: it is now:\n"); + ssh_print_hexa("Public key hash", hash, hlen); + break; + + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr, "The host key for this server was not found but an other" + "type of key exists.\n"); + break; + + case SSH_SERVER_FILE_NOT_FOUND: + fprintf(stderr, "Could not find known host file.\n"); + + case SSH_SERVER_NOT_KNOWN: + hexa = ssh_get_hexa(hash, hlen); + fprintf(stderr, "Public key hash: %s\n", hexa); + free(hexa); + break; + + case SSH_SERVER_ERROR: + fprintf(stderr, "Error %s", ssh_get_error(session)); + free(hash); + return -1; + } + + free(hash); + return 0; +} + +int show_remote_processes(ssh_session session, char *command) +{ + ssh_channel channel; + int rc; + char buffer[256]; + int nbytes; + + channel = ssh_channel_new(session); + if (channel == NULL) + return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + + rc = ssh_channel_request_exec(channel, command); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != (unsigned int) nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return SSH_OK; +} + +int main(int argc, char **argv) +{ + ssh_session ssh_session; + int rc; + + char *password; + int port = 22; + char *host; + char *user; + char *command; + + if(argc != 6){ + fprintf(stderr, "wrong arg count"); + exit(-1); + } + + + host = argv[1]; + port = atoi(argv[2]); + user = argv[3]; + password = argv[4]; + command = argv[5]; + + fprintf(stderr, "PARAMETER OK\n"); + // Open session and set options + ssh_session = ssh_new(); + if (ssh_session == NULL){ + exit(-1); + } + + fprintf(stderr, "SSH SESSION CREATED\n"); + + fprintf(stderr, "host: %s\n", host); + + if(ssh_options_set(ssh_session, SSH_OPTIONS_HOST, host) < 0){ + fprintf(stderr, "OPTION HOST ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_PORT, &port) < 0){ + fprintf(stderr, "OPTION PORT ERROR\n"); + } + + if(ssh_options_set(ssh_session, SSH_OPTIONS_USER, user) < 0){ + fprintf(stderr, "OPTION USER ERROR\n"); + } + + fprintf(stderr, "SSH OPTIONS OK\n"); + // Connect to server + rc = ssh_connect(ssh_session); + fprintf(stderr, "SSH CONNECTION START OK\n"); + if (rc != SSH_OK) + { + fprintf(stderr, "Error connecting to host: %s\n", + ssh_get_error(ssh_session)); + ssh_free(ssh_session); + exit(-1); + } + fprintf(stderr, "SSH CONNECT OK\n"); + + + // Verify the server's identity + if (verify_knownhost(ssh_session) < 0) + { + fprintf(stderr, "Error verify"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH VERIFY HOST OK\n"); + // Authenticate ourselves + rc = ssh_userauth_password(ssh_session, NULL, password); + if (rc != SSH_AUTH_SUCCESS) + { + fprintf(stderr, "Error authenticating with password: %s\n", + ssh_get_error(ssh_session)); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH AUTH OK\n"); + + rc = show_remote_processes(ssh_session,command); + if(rc != SSH_OK){ + fprintf(stderr, "Error on exec command\n"); + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(-1); + } + + fprintf(stderr, "SSH COMMAND EXEC OK\n"); + + ssh_disconnect(ssh_session); + ssh_free(ssh_session); + exit(0); +} \ No newline at end of file diff --git a/images/metasploit/Dockerfile b/images/metasploit/Dockerfile new file mode 100644 index 0000000..870df34 --- /dev/null +++ b/images/metasploit/Dockerfile @@ -0,0 +1,10 @@ +FROM metasploitframework/metasploit-framework AS metasploit-client +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY start.sh /usr/local/bin/ +COPY msfScript.rc /usr/local/bin/ +LABEL "ssh_implementation"="metasploit" +LABEL "ssh_implementation_version"="latest" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin +ENTRYPOINT ["client-entrypoint", "start.sh"] + diff --git a/images/metasploit/compose.yml b/images/metasploit/compose.yml new file mode 100644 index 0000000..9c412e2 --- /dev/null +++ b/images/metasploit/compose.yml @@ -0,0 +1,10 @@ +version: '3.2' +services: + metasploit-client: + image: rub-nds/metasploit-client + build: + context: . + target: metasploit-client + args: + VERSON: 0.7.2 + profiles: [ client ] diff --git a/images/metasploit/msfScript.rc b/images/metasploit/msfScript.rc new file mode 100644 index 0000000..4b6babb --- /dev/null +++ b/images/metasploit/msfScript.rc @@ -0,0 +1,6 @@ +use auxiliary/scanner/ssh/ssh_login +set RHOSTS HOSTPLACEHOLDER +set RPORT PORTPLACEHOLDER +set PASSWORD PASSPLACEHOLDER +set USERNAME USERPLACEHOLDER +run \ No newline at end of file diff --git a/images/metasploit/start.sh b/images/metasploit/start.sh new file mode 100755 index 0000000..a78562b --- /dev/null +++ b/images/metasploit/start.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +sed -i "s/HOSTPLACEHOLDER/$host/g" msfScript.rc +sed -i "s/PORTPLACEHOLDER/$port/g" msfScript.rc +sed -i "s/USERPLACEHOLDER/$user/g" msfScript.rc +sed -i "s/PASSPLACEHOLDER/$password/g" msfScript.rc + +/usr/src/metasploit-framework/msfconsole -r msfScript.rc + +exit 0 diff --git a/images/openssh/Dockerfile b/images/openssh/Dockerfile index 0f5bc2f..5db2fed 100644 --- a/images/openssh/Dockerfile +++ b/images/openssh/Dockerfile @@ -65,8 +65,8 @@ ARG DOTSSH=dotssh-server COPY --chown="${USERNAME}:${USERNAME}" "${DOTSSH}" "/home/${USERNAME}/.ssh/" RUN chmod -R g=,o= "/home/${USERNAME}/.ssh" -ARG VERSION -COPY --from=openssh-builder /install /install + ARG VERSION + COPY --from=openssh-builder /install /install LABEL ssh.implementation.name="openssh" \ ssh.implementation.version="${VERSION}" \ ssh.implementation.type="server" diff --git a/images/openssh/Dockerfile-8_0_to_9_0 b/images/openssh/Dockerfile-8_0_to_9_0 new file mode 100644 index 0000000..e69de29 diff --git a/images/openssh/compose.yml b/images/openssh/compose.yml index e3bac1a..feb4b75 100644 --- a/images/openssh/compose.yml +++ b/images/openssh/compose.yml @@ -7,6 +7,7 @@ services: target: openssh-server args: VERSION: 8.0p1 + profiles: [ server ] openssh-client-8.0p1: image: rub-nds/openssh-client:8.0p1 build: @@ -14,7 +15,7 @@ services: target: openssh-client args: VERSION: 8.0p1 - profiles: [client] + profiles: [ client ] openssh-server-8.1p1: image: rub-nds/openssh-server:8.1p1 build: @@ -22,6 +23,7 @@ services: target: openssh-server args: VERSION: 8.1p1 + profiles: [ server ] openssh-client-8.1p1: image: rub-nds/openssh-client:8.1p1 build: @@ -29,7 +31,7 @@ services: target: openssh-client args: VERSION: 8.1p1 - profiles: [client] + profiles: [ client ] openssh-server-8.2p1: image: rub-nds/openssh-server:8.2p1 build: @@ -37,6 +39,7 @@ services: target: openssh-server args: VERSION: 8.2p1 + profiles: [ server ] openssh-client-8.2p1: image: rub-nds/openssh-client:8.2p1 build: @@ -44,7 +47,7 @@ services: target: openssh-client args: VERSION: 8.2p1 - profiles: [client] + profiles: [ client ] openssh-server-8.3p1: image: rub-nds/openssh-server:8.3p1 build: @@ -52,6 +55,7 @@ services: target: openssh-server args: VERSION: 8.3p1 + profiles: [ server ] openssh-client-8.3p1: image: rub-nds/openssh-client:8.3p1 build: @@ -59,7 +63,7 @@ services: target: openssh-client args: VERSION: 8.3p1 - profiles: [client] + profiles: [ client ] openssh-server-8.4p1: image: rub-nds/openssh-server:8.4p1 build: @@ -67,6 +71,7 @@ services: target: openssh-server args: VERSION: 8.4p1 + profiles: [ server ] openssh-client-8.4p1: image: rub-nds/openssh-client:8.4p1 build: @@ -74,7 +79,7 @@ services: target: openssh-client args: VERSION: 8.4p1 - profiles: [client] + profiles: [ client ] openssh-server-8.5p1: image: rub-nds/openssh-server:8.5p1 build: @@ -82,6 +87,7 @@ services: target: openssh-server args: VERSION: 8.5p1 + profiles: [ server ] openssh-client-8.5p1: image: rub-nds/openssh-client:8.5p1 build: @@ -89,7 +95,7 @@ services: target: openssh-client args: VERSION: 8.5p1 - profiles: [client] + profiles: [ client ] openssh-server-8.6p1: image: rub-nds/openssh-server:8.6p1 build: @@ -97,6 +103,7 @@ services: target: openssh-server args: VERSION: 8.6p1 + profiles: [ server ] openssh-client-8.6p1: image: rub-nds/openssh-client:8.6p1 build: @@ -104,7 +111,7 @@ services: target: openssh-client args: VERSION: 8.6p1 - profiles: [client] + profiles: [ client ] openssh-server-8.7p1: image: rub-nds/openssh-server:8.7p1 build: @@ -112,6 +119,7 @@ services: target: openssh-server args: VERSION: 8.7p1 + profiles: [ server ] openssh-client-8.7p1: image: rub-nds/openssh-client:8.7p1 build: @@ -119,7 +127,7 @@ services: target: openssh-client args: VERSION: 8.7p1 - profiles: [client] + profiles: [ client ] openssh-server-8.8p1: image: rub-nds/openssh-server:8.8p1 build: @@ -127,6 +135,7 @@ services: target: openssh-server args: VERSION: 8.8p1 + profiles: [ server ] openssh-client-8.8p1: image: rub-nds/openssh-client:8.8p1 build: @@ -134,7 +143,7 @@ services: target: openssh-client args: VERSION: 8.8p1 - profiles: [client] + profiles: [ client ] openssh-server-8.9p1: image: rub-nds/openssh-server:8.9p1 build: @@ -142,6 +151,7 @@ services: target: openssh-server args: VERSION: 8.9p1 + profiles: [ server ] openssh-client-8.9p1: image: rub-nds/openssh-client:8.9p1 build: @@ -149,7 +159,7 @@ services: target: openssh-client args: VERSION: 8.9p1 - profiles: [client] + profiles: [ client ] openssh-server-9.0p1: image: rub-nds/openssh-server:9.0p1 build: @@ -157,6 +167,7 @@ services: target: openssh-server args: VERSION: 9.0p1 + profiles: [ server ] openssh-client-9.0p1: image: rub-nds/openssh-client:9.0p1 build: @@ -164,4 +175,4 @@ services: target: openssh-client args: VERSION: 9.0p1 - profiles: [client] + profiles: [ client ] \ No newline at end of file diff --git a/images/openssh_client/Dockerfile b/images/openssh_client/Dockerfile new file mode 100644 index 0000000..6fb1e02 --- /dev/null +++ b/images/openssh_client/Dockerfile @@ -0,0 +1,31 @@ +FROM debian-build:bullseye AS openssh-downloader +ARG VERSION +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +FROM debian-build:stretch-libssl1.0 AS openssh-builder +ARG VERSION +COPY --from=openssh-downloader /src/openssh-${VERSION} /src/openssh-${VERSION} +WORKDIR /src/openssh-${VERSION} +RUN ./configure CFLAGS="-DDEBUG_KEX=1 -DDEBUG_KEXDH=1 -DDEBUG_KEXECDH=1" --prefix /install && \ + make -j ${NUMCPUS:-8} && \ + make install + +FROM debian:stretch AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl1.0-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/images/openssh_client/Dockerfile2 b/images/openssh_client/Dockerfile2 new file mode 100644 index 0000000..edda7c0 --- /dev/null +++ b/images/openssh_client/Dockerfile2 @@ -0,0 +1,28 @@ +FROM debian-build:bullseye AS openssh-builder +ARG VERSION +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +WORKDIR /src/openssh-${VERSION} +RUN ./configure --prefix /install +RUN make +RUN make install + +FROM debian:bullseye AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/images/openssh_client/Dockerfile3 b/images/openssh_client/Dockerfile3 new file mode 100644 index 0000000..01031f3 --- /dev/null +++ b/images/openssh_client/Dockerfile3 @@ -0,0 +1,31 @@ +FROM debian-build:bullseye AS openssh-downloader +ARG VERSION +RUN curl -s https://cloudflare.cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${VERSION}.tar.gz | tar xzf - + +FROM debian-build:stretch-libssl1.0 AS openssh-builder +ARG VERSION +COPY --from=openssh-downloader /src/openssh-${VERSION} /src/openssh-${VERSION} +WORKDIR /src/openssh-${VERSION} +RUN ./configure --prefix /install && \ + make && \ + make install + +FROM debian:stretch AS openssh-client +ARG VERSION +RUN adduser --system --no-create-home sshd \ + && mkdir -p /var/empty \ + && (echo bydahirsch; echo bydahirsch) | adduser sshattacker --ingroup users +RUN apt-get update && apt-get install -y \ + libssl1.0-dev \ + zlib1g-dev \ + expect \ + && rm -rf /var/lib/apt/lists/* +COPY --from=openssh-builder /install /install +LABEL "ssh_implementation"="openssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "./start.sh"] \ No newline at end of file diff --git a/images/openssh_client/compose.yml b/images/openssh_client/compose.yml new file mode 100644 index 0000000..6342c0f --- /dev/null +++ b/images/openssh_client/compose.yml @@ -0,0 +1,191 @@ +version: '3.2' +services: + openssh-client-7.0p1: + image: rub-nds/openssh-client:7.0p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.0p1 + profiles: [ client ] + openssh-client-7.1p1: + image: rub-nds/openssh-client:7.1p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.1p1 + profiles: [ client ] + openssh-client-7.2p1: + image: rub-nds/openssh-client:7.2p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.2p1 + profiles: [ client ] + openssh-client-7.2p2: + image: rub-nds/openssh-client:7.2p2 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.2p2 + profiles: [ client ] + openssh-client-7.3p1: + image: rub-nds/openssh-client:7.3p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.3p1 + profiles: [ client ] + openssh-client-7.4p1: + image: rub-nds/openssh-client:7.4p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.4p1 + profiles: [ client ] + openssh-client-7.5p1: + image: rub-nds/openssh-client:7.5p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.5p1 + profiles: [ client ] + openssh-client-7.6p1: + image: rub-nds/openssh-client:7.6p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.6p1 + profiles: [ client ] + openssh-client-7.7p1: + image: rub-nds/openssh-client:7.7p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 7.7p1 + profiles: [ client ] + openssh-client-8.4p1: + image: rub-nds/openssh-client:8.4p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 8.4p1 + profiles: [ client ] + openssh-client-8.5p1: + image: rub-nds/openssh-client:8.5p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 8.5p1 + profiles: [ client ] + openssh-client-8.6p1: + image: rub-nds/openssh-client:8.6p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 8.6p1 + profiles: [ client ] + openssh-client-8.7p1: + image: rub-nds/openssh-client:8.7p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 8.7p1 + profiles: [ client ] + openssh-client-8.8p1: + image: rub-nds/openssh-client:8.8p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 8.8p1 + profiles: [ client ] + openssh-client-9.0p1: + image: rub-nds/openssh-client:9.0p1 + build: + context: . + dockerfile: Dockerfile + target: openssh-client + args: + VERSION: 9.0p1 + profiles: [ client ] + openssh-client-8.0p1: + image: rub-nds/openssh-client:8.0p1 + build: + context: . + dockerfile: Dockerfile2 + target: openssh-client + args: + VERSION: 8.0p1 + profiles: [ client ] + openssh-client-8.1p1: + image: rub-nds/openssh-client:8.1p1 + build: + context: . + dockerfile: Dockerfile2 + target: openssh-client + args: + VERSION: 8.1p1 + profiles: [ client ] + openssh-client-8.2p1: + image: rub-nds/openssh-client:8.2p1 + build: + context: . + dockerfile: Dockerfile2 + target: openssh-client + args: + VERSION: 8.2p1 + profiles: [ client ] + openssh-client-8.3p1: + image: rub-nds/openssh-client:8.3p1 + build: + context: . + dockerfile: Dockerfile2 + target: openssh-client + args: + VERSION: 8.3p1 + profiles: [ client ] + openssh-client-7.8p1: + image: rub-nds/openssh-client:7.8p1 + build: + context: . + dockerfile: Dockerfile3 + target: openssh-client + args: + VERSION: 7.8p1 + profiles: [ client ] + openssh-client-7.9p1: + image: rub-nds/openssh-client:7.9p1 + build: + context: . + dockerfile: Dockerfile3 + target: openssh-client + args: + VERSION: 7.9p1 + profiles: [ client ] diff --git a/images/openssh_client/login.exp b/images/openssh_client/login.exp new file mode 100644 index 0000000..a56e00e --- /dev/null +++ b/images/openssh_client/login.exp @@ -0,0 +1,35 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn /install/bin/ssh "$ip" "-p" "$port" "-l" "$user" + +set timeout 5 +expect { + "yes/no" { + send "yes\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof diff --git a/images/openssh_client/start.sh b/images/openssh_client/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/images/openssh_client/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/images/paramiko/Dockerfile b/images/paramiko/Dockerfile new file mode 100644 index 0000000..fea62d9 --- /dev/null +++ b/images/paramiko/Dockerfile @@ -0,0 +1,17 @@ +ARG VERSION_PYTHON=3.5 + +FROM python:${VERSION_PYTHON} AS paramiko-client +ARG VERSION +WORKDIR /usr/local/bin +RUN /usr/local/bin/python -m pip install --upgrade pip +#RUN pip install transport +RUN pip install six +RUN pip install paramiko==${VERSION} +RUN pip install click +COPY paramiko-client-script.py /usr/local/bin +COPY start.sh /usr/local/bin +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +LABEL "ssh_implementation"="paramiko" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +ENTRYPOINT ["client-entrypoint", "start.sh"] \ No newline at end of file diff --git a/images/paramiko/compose.yml b/images/paramiko/compose.yml new file mode 100644 index 0000000..6368b72 --- /dev/null +++ b/images/paramiko/compose.yml @@ -0,0 +1,602 @@ +version: '3.2' +services: + paramiko-client-2.11.0: + image: rub-nds/paramiko-client:2.11.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.11.0 + profiles: [client] + paramiko-client-2.10.5: + image: rub-nds/paramiko-client:2.10.5 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.5 + profiles: [ client ] + paramiko-client-2.10.4: + image: rub-nds/paramiko-client:2.10.4 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.4 + profiles: [ client ] + paramiko-client-2.10.3: + image: rub-nds/paramiko-client:2.10.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.3 + profiles: [ client ] + paramiko-client-2.10.2: + image: rub-nds/paramiko-client:2.10.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.2 + profiles: [ client ] + paramiko-client-2.10.1: + image: rub-nds/paramiko-client:2.10.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.1 + profiles: [ client ] + paramiko-client-2.10.0: + image: rub-nds/paramiko-client:2.10.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.10.0 + profiles: [ client ] + paramiko-client-2.9.5: + image: rub-nds/paramiko-client:2.9.5 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.5 + profiles: [ client ] + paramiko-client-2.9.4: + image: rub-nds/paramiko-client:2.9.4 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.4 + profiles: [ client ] + paramiko-client-2.9.3: + image: rub-nds/paramiko-client:2.9.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.3 + profiles: [ client ] + paramiko-client-2.9.2: + image: rub-nds/paramiko-client:2.9.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.2 + profiles: [ client ] + paramiko-client-2.9.1: + image: rub-nds/paramiko-client:2.9.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.1 + profiles: [ client ] + paramiko-client-2.9.0: + image: rub-nds/paramiko-client:2.9.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.9.0 + profiles: [ client ] + paramiko-client-2.8.1: + image: rub-nds/paramiko-client:2.8.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.8.1 + profiles: [ client ] + paramiko-client-2.8.0: + image: rub-nds/paramiko-client:2.8.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.8.0 + profiles: [ client ] + paramiko-client-2.7.2: + image: rub-nds/paramiko-client:2.7.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.7.2 + profiles: [ client ] + paramiko-client-2.7.1: + image: rub-nds/paramiko-client:2.7.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.7.1 + profiles: [ client ] + paramiko-client-2.7.0: + image: rub-nds/paramiko-client:2.7.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.7.0 + profiles: [ client ] + paramiko-client-2.6.0: + image: rub-nds/paramiko-client:2.6.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.6.0 + profiles: [ client ] + paramiko-client-2.5.1: + image: rub-nds/paramiko-client:2.5.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.5.1 + profiles: [ client ] + paramiko-client-2.5.0: + image: rub-nds/paramiko-client:2.5.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.5.0 + profiles: [ client ] + paramiko-client-2.4.2: + image: rub-nds/paramiko-client:2.4.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.4.2 + profiles: [ client ] + paramiko-client-2.4.3: + image: rub-nds/paramiko-client:2.4.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.4.3 + profiles: [ client ] + paramiko-client-2.4.1: + image: rub-nds/paramiko-client:2.4.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.4.1 + profiles: [ client ] + paramiko-client-2.4.0: + image: rub-nds/paramiko-client:2.4.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.4.0 + profiles: [ client ] + paramiko-client-2.3.3: + image: rub-nds/paramiko-client:2.3.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.3.3 + profiles: [ client ] + paramiko-client-2.3.2: + image: rub-nds/paramiko-client:2.3.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.3.2 + profiles: [ client ] + paramiko-client-2.3.1: + image: rub-nds/paramiko-client:2.3.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.3.1 + profiles: [ client ] + paramiko-client-2.3.0: + image: rub-nds/paramiko-client:2.3.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.3.0 + profiles: [ client ] + paramiko-client-2.2.4: + image: rub-nds/paramiko-client:2.2.4 + build: + context: . + target: paramiko-client + args: + VERSION: 2.2.4 + profiles: [ client ] + paramiko-client-2.2.3: + image: rub-nds/paramiko-client:2.2.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.2.3 + profiles: [ client ] + paramiko-client-2.2.2: + image: rub-nds/paramiko-client:2.2.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.2.2 + profiles: [ client ] + paramiko-client-2.2.1: + image: rub-nds/paramiko-client:2.2.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.2.1 + profiles: [ client ] + paramiko-client-2.2.0: + image: rub-nds/paramiko-client:2.2.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.2.0 + profiles: [ client ] + paramiko-client-2.1.6: + image: rub-nds/paramiko-client:2.1.6 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.6 + profiles: [ client ] + paramiko-client-2.1.5: + image: rub-nds/paramiko-client:2.1.5 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.5 + profiles: [ client ] + paramiko-client-2.1.4: + image: rub-nds/paramiko-client:2.1.4 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.4 + profiles: [ client ] + paramiko-client-2.1.3: + image: rub-nds/paramiko-client:2.1.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.3 + profiles: [ client ] + paramiko-client-2.1.2: + image: rub-nds/paramiko-client:2.1.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.2 + profiles: [ client ] + paramiko-client-2.1.1: + image: rub-nds/paramiko-client:2.1.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.1 + profiles: [ client ] + paramiko-client-2.1.0: + image: rub-nds/paramiko-client:2.1.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.1.0 + profiles: [ client ] + paramiko-client-2.0.9: + image: rub-nds/paramiko-client:2.0.9 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.9 + profiles: [ client ] + paramiko-client-2.0.8: + image: rub-nds/paramiko-client:2.0.8 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.8 + profiles: [ client ] + paramiko-client-2.0.7: + image: rub-nds/paramiko-client:2.0.7 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.7 + profiles: [ client ] + paramiko-client-2.0.6: + image: rub-nds/paramiko-client:2.0.6 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.6 + profiles: [ client ] + paramiko-client-2.0.5: + image: rub-nds/paramiko-client:2.0.5 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.5 + profiles: [ client ] + paramiko-client-2.0.4: + image: rub-nds/paramiko-client:2.0.4 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.4 + profiles: [ client ] + paramiko-client-2.0.3: + image: rub-nds/paramiko-client:2.0.3 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.3 + profiles: [ client ] + paramiko-client-2.0.2: + image: rub-nds/paramiko-client:2.0.2 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.2 + profiles: [ client ] + paramiko-client-2.0.1: + image: rub-nds/paramiko-client:2.0.1 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.1 + profiles: [ client ] + paramiko-client-2.0.0: + image: rub-nds/paramiko-client:2.0.0 + build: + context: . + target: paramiko-client + args: + VERSION: 2.0.0 + profiles: [ client ] + paramiko-client-1.18.5: + image: rub-nds/paramiko-client:1.18.5 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.5 + profiles: [ client ] + paramiko-client-1.18.4: + image: rub-nds/paramiko-client:1.18.4 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.4 + profiles: [ client ] + paramiko-client-1.18.3: + image: rub-nds/paramiko-client:1.18.3 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.3 + profiles: [ client ] + paramiko-client-1.18.2: + image: rub-nds/paramiko-client:1.18.2 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.2 + profiles: [ client ] + paramiko-client-1.18.1: + image: rub-nds/paramiko-client:1.18.1 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.1 + profiles: [ client ] + paramiko-client-1.18.0: + image: rub-nds/paramiko-client:1.18.0 + build: + context: . + target: paramiko-client + args: + VERSION: 1.18.0 + profiles: [ client ] + paramiko-client-1.17.6: + image: rub-nds/paramiko-client:1.17.6 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.6 + profiles: [ client ] + paramiko-client-1.17.5: + image: rub-nds/paramiko-client:1.17.5 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.5 + profiles: [ client ] + paramiko-client-1.17.4: + image: rub-nds/paramiko-client:1.17.4 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.4 + profiles: [ client ] + paramiko-client-1.17.3: + image: rub-nds/paramiko-client:1.17.3 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.3 + profiles: [ client ] + paramiko-client-1.17.2: + image: rub-nds/paramiko-client:1.17.2 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.2 + profiles: [ client ] + paramiko-client-1.17.1: + image: rub-nds/paramiko-client:1.17.1 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.1 + profiles: [ client ] + paramiko-client-1.17.0: + image: rub-nds/paramiko-client:1.17.0 + build: + context: . + target: paramiko-client + args: + VERSION: 1.17.0 + profiles: [ client ] + paramiko-client-1.16.3: + image: rub-nds/paramiko-client:1.16.3 + build: + context: . + target: paramiko-client + args: + VERSION: 1.16.3 + profiles: [ client ] + paramiko-client-1.16.2: + image: rub-nds/paramiko-client:1.16.2 + build: + context: . + target: paramiko-client + args: + VERSION: 1.16.2 + profiles: [ client ] + paramiko-client-1.16.1: + image: rub-nds/paramiko-client:1.16.1 + build: + context: . + target: paramiko-client + args: + VERSION: 1.16.1 + profiles: [ client ] + paramiko-client-1.16.0: + image: rub-nds/paramiko-client:1.16.0 + build: + context: . + target: paramiko-client + args: + VERSION: 1.16.0 + profiles: [ client ] + paramiko-client-1.15.5: + image: rub-nds/paramiko-client:1.15.5 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.5 + profiles: [ client ] + paramiko-client-1.15.5: + image: rub-nds/paramiko-client:1.15.5 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.5 + profiles: [ client ] + paramiko-client-1.15.4: + image: rub-nds/paramiko-client:1.15.4 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.4 + profiles: [ client ] + paramiko-client-1.15.3: + image: rub-nds/paramiko-client:1.15.3 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.3 + profiles: [ client ] + paramiko-client-1.15.2: + image: rub-nds/paramiko-client:1.15.2 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.2 + profiles: [ client ] + paramiko-client-1.15.1: + image: rub-nds/paramiko-client:1.15.1 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.1 + profiles: [ client ] + paramiko-client-1.15.0: + image: rub-nds/paramiko-client:1.15.0 + build: + context: . + target: paramiko-client + args: + VERSION: 1.15.0 + profiles: [ client ] diff --git a/images/paramiko/paramiko-client-script.py b/images/paramiko/paramiko-client-script.py new file mode 100755 index 0000000..607a209 --- /dev/null +++ b/images/paramiko/paramiko-client-script.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import paramiko +import click + +@click.command() +@click.option('-H', '--host', help='hostname or ip', default='172.17.0.1"') +@click.option('-P', '--port', help='prot', default=3022, type=int) +@click.option('-u', '--username', help='username', default='demo') +@click.option('-p', '--password', help='password', default='password') +@click.option('-c', '--command', help='command', default='pwd') +@click.option('-o', '--output', is_flag=True, show_default=True, default=False, help='print output') +@click.option('-e', '--error', is_flag=True, show_default=True, default=False, help='print error') +def client_start(host, port, username, password, command, output, error): + print("host {host}\n") + print("port {port}\n") + print("username {username}\n") + if output: + print("init Client") + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + if output: + print("start connection") + client.connect(host, port=port, username=username, password=password) + + + if output: + print("start command") + stdin, stdout, stderr = client.exec_command(command) + + if output: + print("finish") + out = stdout.readlines() + err = stderr.readlines() + if output: + print("output: ", "\n".join(out)) + if error: + print("error: ", "\n".join(err)) + + stdin.close() + stdout.close() + stderr.close() + +if __name__ == '__main__': + client_start() + + diff --git a/images/paramiko/start.sh b/images/paramiko/start.sh new file mode 100755 index 0000000..6d3971e --- /dev/null +++ b/images/paramiko/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="172.17.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +echo | python "paramiko-client-script.py" $parameter + +exit 0 diff --git a/images/puttyLinux/Dockerfile b/images/puttyLinux/Dockerfile new file mode 100644 index 0000000..bcdd82e --- /dev/null +++ b/images/puttyLinux/Dockerfile @@ -0,0 +1,16 @@ +ARG U_VERSION +FROM ubuntu:${U_VERSION} AS puttyLinux-client +ARG VERSION +WORKDIR /usr/local/bin +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +RUN apt-get update -y +RUN apt-cache madison putty-tools +RUN apt list -a putty-tools +RUN apt-get install -y putty-tools=${VERSION} +RUN apt-get install expect -y +LABEL "ssh_implementation"="putty" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY login.exp /usr/local/bin/ +COPY start.sh /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "start.sh"] diff --git a/images/puttyLinux/compose.yml b/images/puttyLinux/compose.yml new file mode 100644 index 0000000..ef2ef87 --- /dev/null +++ b/images/puttyLinux/compose.yml @@ -0,0 +1,29 @@ +version: '3.2' +services: + putty-linux-client-0.76-2: + image: rub-nds/putty-linux-client:0.76-2 + build: + context: . + target: puttyLinux-client + args: + VERSION: 0.76-2 + U_VERSION: 22.04 + profiles: [client] + putty-linux-client-0.73-2: + image: rub-nds/putty-linux-client:0.73-2 + build: + context: . + target: puttyLinux-client + args: + VERSION: 0.73-2 + U_VERSION: 20.04 + profiles: [ client ] + putty-linux-client-0.70-4: + image: rub-nds/putty-linux-client:0.70-4 + build: + context: . + target: puttyLinux-client + args: + VERSION: 0.70-4 + U_VERSION: 18.04 + profiles: [ client ] diff --git a/images/puttyLinux/login.exp b/images/puttyLinux/login.exp new file mode 100644 index 0000000..a692f77 --- /dev/null +++ b/images/puttyLinux/login.exp @@ -0,0 +1,40 @@ +#!/usr/bin/expect -f + +set ip [lindex $argv 0] +set port [lindex $argv 1] +set user [lindex $argv 2] +set password [lindex $argv 3] +set command [lindex $argv 4] + +spawn plink -ssh "-P" "$port" "-l" "$user" "$ip" + +set timeout 2 +expect { + "y/n" { + send "y\r" + } +} +expect { + "assword:" { + send "$password\r" + } +} +expect { + "Press Return to begin session" { + send "\r" + } +} +expect { + ":" { + send "$command\r" + } +} + +expect { + ":" { + send "\r" + } +} + + +expect eof diff --git a/images/puttyLinux/start.sh b/images/puttyLinux/start.sh new file mode 100755 index 0000000..e28d25d --- /dev/null +++ b/images/puttyLinux/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +parameter="--host $host -P $port -u $user -p $password -c $command" + +if [ "$print_output" == "true" ] +then + parameter+=" -o" +fi + +if [ "$print_error" == "true" ] +then + parameter+=" -e" +fi + +expect login.exp $host $ip $port $user $password $command + +exit 0 diff --git a/images/wolfssh/Dockerfile b/images/wolfssh/Dockerfile new file mode 100644 index 0000000..7ddb00c --- /dev/null +++ b/images/wolfssh/Dockerfile @@ -0,0 +1,26 @@ +FROM debian-build:bullseye AS wolfssh-downloader +ARG VERSION +WORKDIR /src +RUN wget -q https://github.com/wolfSSL/wolfssh/archive/refs/tags/v${VERSION}.tar.gz +RUN tar -xf v${VERSION}.tar.gz + +FROM debian-build:bullseye AS wolfssh-client +ARG VERSION +COPY --from=wolfssh-downloader /src/wolfssh-${VERSION} /src/wolfssh-${VERSION} +WORKDIR /src/wolfssh-${VERSION} +RUN apt-get update +RUN apt-get install -y libtool +RUN apt-get install -y libwolfssl-dev +RUN apt-get install -y autoconf +RUN ./autogen.sh +RUN ./configure --enable-ssh +RUN make +RUN make check +RUN make install +LABEL "ssh_implementation"="wolfssh" +LABEL "ssh_implementation_version"="${VERSION}" +LABEL "ssh_implementation_connectionRole"="client" +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +WORKDIR /src/wolfssh-${VERSION}/examples/client +RUN ./client -h +ENTRYPOINT ["client-entrypoint", "./client"] \ No newline at end of file diff --git a/images/wolfssh/compose.yml b/images/wolfssh/compose.yml new file mode 100644 index 0000000..4fe14a6 --- /dev/null +++ b/images/wolfssh/compose.yml @@ -0,0 +1,74 @@ +version: '3.2' +services: + wolfssh-client-1.4.11-stable: + image: rub-nds/wolfssh-client:1.4.11-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.11-stable + profiles: [ client ] + wolfssh-client-1.4.10-stable: + image: rub-nds/wolfssh-client:1.4.10-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.10-stable + profiles: [ client ] + wolfssh-client-1.4.8-stable: + image: rub-nds/wolfssh-client:1.4.8-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.8-stable + profiles: [ client ] + wolfssh-client-1.4.7-stable: + image: rub-nds/wolfssh-client:1.4.7-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.7-stable + profiles: [ client ] + wolfssh-client-1.4.6-stable: + image: rub-nds/wolfssh-client:1.4.6-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.6-stable + profiles: [ client ] + wolfssh-client-1.4.5-stable: + image: rub-nds/wolfssh-client:1.4.5-stable + build: + context: . + target: wolfssh-client + args: + VERSION: 1.4.5-stable + profiles: [ client ] +# wolfssh-client-1.4.4-stable: # tests fail +# image: rub-nds/wolfssh-client:1.4.4-stable +# build: +# context: . +# target: wolfssh-client +# args: +# VERSION: 1.4.4-stable +# profiles: [ client ] +# wolfssh-client-1.4.3-stable: # dont build +# image: rub-nds/wolfssh-client:1.4.3-stable +# build: +# context: . +# target: wolfssh-client +# args: +# VERSION: 1.4.3-stable +# profiles: [ client ] +# wolfssh-client-1.4.2-stable: # dont build +# image: rub-nds/wolfssh-client:1.4.2-stable +# build: +# context: . +# target: wolfssh-client +# args: +# VERSION: 1.4.2-stable +# profiles: [ client ] diff --git a/images/zGrab2/Dockerfile b/images/zGrab2/Dockerfile new file mode 100644 index 0000000..b3e4fb0 --- /dev/null +++ b/images/zGrab2/Dockerfile @@ -0,0 +1,14 @@ +FROM bberastegui/zgrab2:latest AS zgrab2-client +RUN apk update +RUN apk upgrade +RUN apk add bash +COPY --from=entrypoint /bin/client-entrypoint /usr/local/bin/ +COPY input.txt /usr/local/bin/ +COPY multiple.ini /usr/local/bin/ +COPY start.sh /usr/local/bin/ +LABEL "ssh_implementation"="zgrab2" +LABEL "ssh_implementation_version"="latest" +LABEL "ssh_implementation_connectionRole"="client" +WORKDIR /usr/local/bin/ +ENTRYPOINT ["client-entrypoint", "start.sh"] + diff --git a/images/zGrab2/compose.yml b/images/zGrab2/compose.yml new file mode 100644 index 0000000..a1e22ff --- /dev/null +++ b/images/zGrab2/compose.yml @@ -0,0 +1,8 @@ +version: '3.2' +services: + zgrap2-client: + image: rub-nds/zgrap2-client + build: + context: . + target: zgrab2-client + profiles: [ client ] diff --git a/images/zGrab2/input.txt b/images/zGrab2/input.txt new file mode 100644 index 0000000..9aabd88 --- /dev/null +++ b/images/zGrab2/input.txt @@ -0,0 +1 @@ +HOSTPLACEHOLDER, , server \ No newline at end of file diff --git a/images/zGrab2/multiple.ini b/images/zGrab2/multiple.ini new file mode 100644 index 0000000..a396e75 --- /dev/null +++ b/images/zGrab2/multiple.ini @@ -0,0 +1,6 @@ +[Application Options] +output-file="output.txt" +input-file="input.txt" +[ssh] +trigger="server" +port=PORTPLACEHOLDER \ No newline at end of file diff --git a/images/zGrab2/start.sh b/images/zGrab2/start.sh new file mode 100755 index 0000000..6899bf3 --- /dev/null +++ b/images/zGrab2/start.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +host=$1 +port=$2 +user=$3 +password=$4 +command=$5 +print_output=$6 +print_error=$7 + +if [ -z "$host" ] +then + host="127.0.0.1" +fi + +if [ -z "$port" ] +then + port=3022 +fi + +if [ -z "$user" ] +then + user="demo" +fi + +if [ -z "$passtord" ] +then + password="none" +fi + + +if [ -z "$command" ] +then + command="pwd" +fi + +sed -i "s/HOSTPLACEHOLDER/$host/g" input.txt +sed -i "s/PORTPLACEHOLDER/$port/g" multiple.ini + +cat input.txt | zgrab2 multiple -c multiple.ini -o output.txt + +exit 0 diff --git a/license_header_plain.txt b/license_header_plain.txt new file mode 100644 index 0000000..33b403d --- /dev/null +++ b/license_header_plain.txt @@ -0,0 +1,6 @@ +${projectName} - ${description} + +Copyright ${startYear}-${year} ${owner} + +Licensed under ${licenseName} +${licenseURL} \ No newline at end of file diff --git a/maven-eclipse-codestyle.xml b/maven-eclipse-codestyle.xml new file mode 100755 index 0000000..f9f51fd --- /dev/null +++ b/maven-eclipse-codestyle.xml @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5a826ae --- /dev/null +++ b/pom.xml @@ -0,0 +1,384 @@ + + + 4.0.0 + SSH-Docker-Library + de.rub.nds.sshdockerlib + 2.1.2-SNAPSHOT + jar + + + + jsomorovsky + Juraj Somorovsky + Juraj.Somorovsky@rub.de + https://www.nds.rub.de/ + NDS + https://www.nds.rub.de + + Architect + Developer + + + + ic0ns + Robert Merget + robert.merget@rub.de + http://www.nds.rub.de/ + NDS + https://www.nds.rub.de + + Team lead + + + + mmaehren + Marcel Maehren + marcel.maehren@rub.de + https://www.nds.rub.de/ + NDS + https://www.nds.rub.de + + Developer + + + + NErinola + Nurullah Erinola + nurullah.erinola@rub.de + https://www.nds.rub.de/ + NDS + https://www.nds.rub.de + + Developer + + + + MMakulski + Maxim Makulski + maxim.makulski@rub.de + https://www.nds.rub.de/ + NDS + https://www.nds.rub.de + + Developer + + + + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + rub-nexus + SSH-Attacker Internal Repository + https://hydrogen.cloud.nds.rub.de/nexus/repository/maven-snapshots/ + + + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + scm:git:git://github.com/tls-attacker/TLS-Docker-Library.git + scm:git:ssh://github.com/tls-attacker/TLS-Docker-Library.git + https://github.com/tls-attacker/TLS-Docker-Library/tree/master + + + + + org.apache.logging.log4j + log4j-iostreams + 2.11.2 + + + org.apache.sshd + sshd-core + 2.9.1 + + + org.pcap4j + pcap4j-core + [1.0, 2.0) + + + ch.qos.logback + logback-classic + + + + + org.apache.logging.log4j + log4j-api + 2.9.0 + + + org.apache.logging.log4j + log4j-core + 2.9.0 + + + com.github.docker-java + docker-java-core + 3.2.5 + + + com.github.docker-java + docker-java-transport-httpclient5 + 3.2.5 + + + com.codepoetics + protonpack + 1.15 + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.11.2 + + + junit + junit + 4.12 + test + + + com.google.guava + guava + 27.1-jre + + + org.apache.httpcomponents + httpcore + 4.4.11 + + + org.reflections + reflections + 0.10.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.3 + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.3 + jar + + + + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.14.0 + + + + com.mycila + license-maven-plugin + 3.0 + + + + SSH-Docker-Library + + + maven-dependency-plugin + 3.1.1 + + + copy + package + + copy + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + ${project.packaging} + ${project.build.finalName}.${project.packaging} + + + ${basedir}/apps + + + + copy-dependencies + package + + copy-dependencies + + + ${basedir}/apps/lib + + compile + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + + true + lib/ + de.rub.nds.ssh.extractor.Main + + + + + + com.spotify + dockerfile-maven-plugin + 1.4.13 + + + build-docker-base-image + package + + build + + + alpine-build + ${project.basedir}/clientImages/baseimage/ + + + + + + com.mycila + 3.0 + license-maven-plugin + +
${main.basedir}/license_header_plain.txt
+ true + + src/**/*.java + + + 2014 + ${buildYear} + A Modular Penetration Testing Framework for SSH + Ruhr University Bochum, Paderborn University, Hackmanit GmbH + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + SSH-Attacker + + + JAVADOC_STYLE + +
+ + + process-sources + + format + + + +
+ + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M3 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + -Dfile.encoding=${project.build.sourceEncoding} + + ${slowTests} + + + + net.revelc.code.formatter + formatter-maven-plugin + + ${main.basedir}/maven-eclipse-codestyle.xml + + + + process-sources + + format + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.1 + + + com.puppycrawl.tools + checkstyle + 8.37 + + + + checkstyle.xml + info + false + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0-M1 + + +
+
+ + UTF-8 + yyyy + ${maven.build.timestamp} + ${project.basedir} + 11 + 11 + de.rub.nds.ssh.subject.SlowTests + +
diff --git a/src/main/java/de/rub/nds/ssh/subject/ConnectionRole.java b/src/main/java/de/rub/nds/ssh/subject/ConnectionRole.java new file mode 100644 index 0000000..7456916 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/ConnectionRole.java @@ -0,0 +1,15 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject; + +public enum ConnectionRole { + CLIENT, + SERVER +} diff --git a/src/main/java/de/rub/nds/ssh/subject/HostInfo.java b/src/main/java/de/rub/nds/ssh/subject/HostInfo.java new file mode 100644 index 0000000..996fa78 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/HostInfo.java @@ -0,0 +1,79 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject; + +import de.rub.nds.ssh.subject.constants.TransportType; + +public class HostInfo { + private String ip; + private String hostname; + private Integer port; + private TransportType type; + + public HostInfo(String ip, String hostname, int port, TransportType type) { + // called for SSH Clients + // specifies where the client connects to + this.ip = ip; + this.hostname = hostname; + this.port = port; + this.type = type; + } + + public HostInfo(String hostname, int port, TransportType transportType) { + // called for SSH Servers + // specifies where the server is available + this.port = port; + if (hostname == null) { + this.hostname = "127.0.0.42"; + this.ip = "127.0.0.42"; + } else { + this.hostname = hostname; + this.ip = hostname; + } + + this.type = transportType; + } + + public TransportType getType() { + return type; + } + + public String getIp() { + return ip; + } + + public String getHostname() { + return hostname; + } + + public void updatePort(int port) { + this.port = port; + } + + public Integer getPort() { + return port; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public void setPort(Integer port) { + this.port = port; + } + + public void setType(TransportType type) { + this.type = type; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/ServerUtil.java b/src/main/java/de/rub/nds/ssh/subject/ServerUtil.java new file mode 100644 index 0000000..0d3b3dd --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/ServerUtil.java @@ -0,0 +1,55 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject; + +import java.io.IOException; +import java.net.Socket; + +import de.rub.nds.ssh.subject.exceptions.ImplementationDidNotStartException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ServerUtil { + private static final Logger LOGGER = LogManager.getLogger(); + + private static final int SERVER_POLL_INTERVAL_MILLISECONDS = 50; + + private static final int TIMEOUT_WAIT_FOR_SERVER_SPINUP_MILLISECONDS = 10000; + + public void waitUntilServerIsOnline(String host, int port) { + long startTime = System.currentTimeMillis(); + while (!isServerOnline(host, port)) { + if (startTime + TIMEOUT_WAIT_FOR_SERVER_SPINUP_MILLISECONDS < System.currentTimeMillis()) { + throw new ImplementationDidNotStartException("Could not start Server: Timeout"); + } + try { + Thread.sleep(SERVER_POLL_INTERVAL_MILLISECONDS); + } catch (InterruptedException ex) { + throw new ImplementationDidNotStartException("Interrupted while waiting for Server", ex); + } + } + } + + public boolean isServerOnline(String address, int port) { + try { + Socket ss = new Socket(address, port); + if (ss.isConnected()) { + ss.close(); + return true; + } else { + return false; + } + } catch (IOException e) { + LOGGER.debug("Server is not online yet", e); + return false; + } + } + +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/SlowTests.java b/src/main/java/de/rub/nds/ssh/subject/SlowTests.java new file mode 100644 index 0000000..e83e4c8 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/SlowTests.java @@ -0,0 +1,16 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject; + +/** + * JUnit test category. Slow tests that should not be included in the default packaging process. + */ +public interface SlowTests { +} diff --git a/src/main/java/de/rub/nds/ssh/subject/SshImplementationType.java b/src/main/java/de/rub/nds/ssh/subject/SshImplementationType.java new file mode 100644 index 0000000..f0cb0ca --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/SshImplementationType.java @@ -0,0 +1,33 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject; + +public enum SshImplementationType { + OPENSSH, + PARAMIKO, + DROPBEAR, + GO, + PUTTY, + ASYNCSSH, + LIBSSH, + WOLFSSH, + ZGRAB2, + METASPLOIT; + + public static SshImplementationType fromString(String type) { + for (SshImplementationType i : SshImplementationType.values()) { + if (i.name().toLowerCase().equals(type.toLowerCase())) { + return i; + } + } + return null; + } + +} diff --git a/src/main/java/de/rub/nds/ssh/subject/constants/SshImageLabels.java b/src/main/java/de/rub/nds/ssh/subject/constants/SshImageLabels.java new file mode 100644 index 0000000..191f3b2 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/constants/SshImageLabels.java @@ -0,0 +1,26 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.constants; + +public enum SshImageLabels { + IMPLEMENTATION("ssh_implementation"), + VERSION("ssh_implementation_version"), + CONNECTION_ROLE("ssh_implementation_connectionRole"); + + private String labelName; + + SshImageLabels(String label) { + this.labelName = label; + } + + public String getLabelName() { + return this.labelName; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/constants/TransportType.java b/src/main/java/de/rub/nds/ssh/subject/constants/TransportType.java new file mode 100644 index 0000000..519ce4c --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/constants/TransportType.java @@ -0,0 +1,36 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.constants; + +import com.github.dockerjava.api.model.InternetProtocol; + +/** + * + * @author robert + */ +public enum TransportType { + UDP, + TCP; + + public InternetProtocol toInternetProtocol() { + switch (this) { + case UDP: + return InternetProtocol.UDP; + case TCP: + return InternetProtocol.TCP; + } + return null; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerClientManager.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerClientManager.java new file mode 100644 index 0000000..613afae --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerClientManager.java @@ -0,0 +1,61 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; +import com.github.dockerjava.transport.DockerHttpClient; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DockerClientManager { + private static final Logger LOGGER = LogManager.getLogger(); + private static DockerClient DOCKER = null; + private static DockerClientConfig DCONFIG = null; + private static DockerHttpClient DHTTPCLIENT = null; + + public static DockerClient getDockerClient() { + if (DOCKER == null) { + DOCKER = getNewDockerClient(); + } + return DOCKER; + } + + private static void ensureConfigExists() { + if (DCONFIG == null) { + DefaultDockerClientConfig.Builder cfgBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder(); + if (System.getenv("DOCKER_HOST") == null && System.getProperty("os.name").startsWith("Windows")) { + cfgBuilder = cfgBuilder.withDockerHost("npipe:////./pipe/docker_engine"); + } + DCONFIG = cfgBuilder.build(); + } + } + + public static DockerClient getNewDockerClient() { + ensureConfigExists(); + if (DHTTPCLIENT == null) { + DHTTPCLIENT = new ApacheDockerHttpClient.Builder().dockerHost(DCONFIG.getDockerHost()) + .sslConfig(DCONFIG.getSSLConfig()).build(); + } + return DockerClientImpl.getInstance(DCONFIG, DHTTPCLIENT); + } + + private DockerClientManager() { + throw new IllegalStateException("Utility class"); + } +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerExecInstance.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerExecInstance.java new file mode 100644 index 0000000..c1596c2 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerExecInstance.java @@ -0,0 +1,51 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.io.IOException; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; + +import de.rub.nds.ssh.subject.instance.ExecInstance; + +public class DockerExecInstance implements ExecInstance { + private final DockerClient DOCKER; + public final ExecCreateCmdResponse execCreation; + public final FrameHandler frameHandler; + + public DockerExecInstance(ExecCreateCmdResponse execCreation) { + // if we are not using detach in execStart we must use our own docker client (as + // we otherwise block other execStarts) + DOCKER = DockerClientManager.getDockerClient(); + this.execCreation = execCreation; + this.frameHandler = new FrameHandler(); + DOCKER.execStartCmd(execCreation.getId()).exec(frameHandler); + } + + @Override + public void close() { + try { + frameHandler.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public boolean isRunning() { + return DOCKER.inspectExecCmd(execCreation.getId()).exec().isRunning(); + } + + public Long getExitCode() { + return DOCKER.inspectExecCmd(execCreation.getId()).exec().getExitCodeLong(); + } +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshClientInstance.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshClientInstance.java new file mode 100644 index 0000000..6c22ca3 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshClientInstance.java @@ -0,0 +1,135 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.function.UnaryOperator; + +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.ExecCreateCmdResponse; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.ContainerConfig; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Volume; + +import de.rub.nds.ssh.subject.HostInfo; +import de.rub.nds.ssh.subject.params.ParameterProfile; +import de.rub.nds.ssh.subject.properties.ImageProperties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.rub.nds.ssh.subject.ConnectionRole; + +public class DockerSshClientInstance extends DockerSshInstance { + private static final String[] EMPTY_STR_ARR = {}; + private static final int EXEC_POLL_INTERVAL_MILLISECONDS = 50; + private static final Logger LOGGER = LogManager.getLogger(); + + private final HostInfo hostInfo; + private final String additionalParameters; + private final boolean parallelize; + private final boolean insecureConnection; + private final boolean connectOnStartup; + + // TODO move away from HostInfo for client... + public DockerSshClientInstance(String containerName, ParameterProfile profile, ImageProperties imageProperties, + String version, boolean autoRemove, HostInfo hostInfo, String additionalParameters, boolean parallelize, + boolean insecureConnection, boolean connectOnStartup, UnaryOperator hostConfigHook) { + super(containerName, profile, imageProperties, version, ConnectionRole.CLIENT, autoRemove, hostConfigHook); + this.hostInfo = hostInfo; + this.additionalParameters = additionalParameters; + this.parallelize = parallelize; + this.insecureConnection = insecureConnection; + this.connectOnStartup = connectOnStartup; + } + + @Override + protected HostConfig prepareHostConfig(HostConfig cfg) { + String extraHost = "test:127.0.0.27"; + if (hostInfo.getHostname() != null) { + extraHost = hostInfo.getHostname() + ":" + hostInfo.getIp(); + } + cfg = super.prepareHostConfig(cfg).withExtraHosts(extraHost); + + List binds = new ArrayList<>(Arrays.asList(cfg.getBinds())); + // TODO: Bind of X11 Settings does not work as expected + binds.add(new Bind("/tmp/.X11-unix", new Volume("/tmp/.X11-unix"))); + cfg = cfg.withBinds(binds); + + return cfg; + } + + @Override + protected CreateContainerCmd prepareCreateContainerCmd(CreateContainerCmd cmd) { + cmd = super.prepareCreateContainerCmd(cmd); + + String host; + if (hostInfo.getHostname() == null || imageProperties.isUseIP()) { + host = hostInfo.getIp(); + } else { + host = hostInfo.getHostname(); + } + if (connectOnStartup) { + cmd = cmd.withCmd(parameterProfile.toParameters(host, hostInfo.getPort(), imageProperties, + additionalParameters, parallelize, insecureConnection)); + } else { + cmd = cmd.withEntrypoint("client-entrypoint"); + } + return cmd; + } + + public DockerExecInstance connect() { + String host; + if (hostInfo.getHostname() == null || imageProperties.isUseIP()) { + host = hostInfo.getIp(); + } else { + host = hostInfo.getHostname(); + } + return connect(host, hostInfo.getPort()); + } + + public DockerExecInstance connect(String host, int targetPort) { + return connect(host, targetPort, additionalParameters, parallelize, insecureConnection); + } + + public DockerExecInstance connect(String host, int targetPort, String additionalParameters) { + return connect(host, targetPort, additionalParameters, parallelize, insecureConnection); + } + + public DockerExecInstance connect(String host, int targetPort, String additionalParameters, Boolean parallelize, + Boolean insecureConnection) { + ContainerConfig imageCfg = DOCKER.inspectImageCmd(image.getId()).exec().getConfig(); + if (imageCfg == null) { + throw new IllegalStateException("Could not get config for image " + image.getId()); + } + String[] entrypoint = imageCfg.getEntrypoint(); + if (entrypoint == null) { + throw new IllegalStateException("Could not get entrypoint for image " + image.getId()); + } + List cmd_lst = new LinkedList(Arrays.asList(entrypoint)); + if (cmd_lst.get(0).equals("client-entrypoint")) { + cmd_lst.remove(0); + } else { + LOGGER.warn("Image {} did not have client-entrypoint as entrypoint", image.getId()); + } + String[] params = parameterProfile.toParameters(host, targetPort, imageProperties, additionalParameters, + parallelize, insecureConnection); + cmd_lst.addAll(Arrays.asList(params)); + ExecCreateCmdResponse exec = DOCKER.execCreateCmd(getId()).withCmd(cmd_lst.toArray(EMPTY_STR_ARR)) + .withAttachStdin(false).withAttachStdout(true).withAttachStderr(true).withTty(true).exec(); + DockerExecInstance ret = new DockerExecInstance(exec); + childExecs.add(ret); + return ret; + } + +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshInstance.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshInstance.java new file mode 100644 index 0000000..2e5b286 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshInstance.java @@ -0,0 +1,252 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.UnaryOperator; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectContainerResponse.ContainerState; +import com.github.dockerjava.api.command.InspectVolumeResponse; +import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.model.AccessMode; +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.api.model.SELContext; +import com.github.dockerjava.api.model.Volume; + +import de.rub.nds.ssh.subject.constants.SshImageLabels; +import de.rub.nds.ssh.subject.exceptions.CertVolumeNotFoundException; +import de.rub.nds.ssh.subject.exceptions.SshVersionNotFoundException; +import de.rub.nds.ssh.subject.params.ParameterProfile; +import de.rub.nds.ssh.subject.properties.ImageProperties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import de.rub.nds.ssh.subject.ConnectionRole; + +public abstract class DockerSshInstance { + protected static final DockerClient DOCKER = DockerClientManager.getDockerClient(); + private static final Logger LOGGER = LogManager.getLogger(); + + private final String containerName; + private String containerId; + protected final Image image; + private Optional exitCode = Optional.empty(); + private boolean autoRemove; + private int logReadOffset = 0; + protected final ParameterProfile parameterProfile; + protected final ImageProperties imageProperties; + protected List childExecs = new LinkedList<>(); + private final UnaryOperator hostConfigHook; + + public DockerSshInstance(String containerName, ParameterProfile profile, ImageProperties imageProperties, + String version, ConnectionRole role, boolean autoRemove, UnaryOperator hostConfigHook) { + if (profile == null) { + throw new NullPointerException("profile may not be null"); + } + if (imageProperties == null) { + throw new NullPointerException("imageProperties may not be null"); + } + this.autoRemove = autoRemove; + this.parameterProfile = profile; + this.imageProperties = imageProperties; + this.hostConfigHook = hostConfigHook; + this.containerName = containerName; + Map labels = new HashMap<>(); + labels.put(SshImageLabels.IMPLEMENTATION.getLabelName(), profile.getType().name().toLowerCase()); + labels.put(SshImageLabels.VERSION.getLabelName(), version); + labels.put(SshImageLabels.CONNECTION_ROLE.getLabelName(), role.toString().toLowerCase()); + this.image = DOCKER.listImagesCmd().withLabelFilter(labels).exec().stream().findFirst() + .orElseThrow(SshVersionNotFoundException::new); + } + + protected HostConfig prepareHostConfig(HostConfig cfg) { + // Check if volume exists; Without this check, the container would be started + // without any problems, swallowing the error and making it harder to identify + InspectVolumeResponse vol = DOCKER.listVolumesCmd().withFilter("name", Arrays.asList("cert-data")).exec() + .getVolumes().stream().findFirst().orElseThrow(CertVolumeNotFoundException::new); + + // hook is handled in prepareCreateContainerCmd; this ensures it is called last + return cfg.withBinds(new Bind(vol.getName(), new Volume("/cert/"), AccessMode.ro, SELContext.DEFAULT, true)); + } + + protected CreateContainerCmd prepareCreateContainerCmd(CreateContainerCmd cmd) { + HostConfig hcfg = prepareHostConfig(HostConfig.newHostConfig()); + if (hostConfigHook != null) { + hcfg = hostConfigHook.apply(hcfg); + } + return cmd.withAttachStderr(true).withAttachStdout(true).withAttachStdin(true).withTty(true).withStdInOnce(true) + .withStdinOpen(true).withHostConfig(hcfg); + // missing: hostConfig, exposedPorts, cmd + } + + protected String createContainer() { + if (this.image == null) { + throw new IllegalStateException("Container could not be created, image is missing"); + } + @SuppressWarnings("squid:S2095") // sonarlint: Resources should be closed + // Create container does not need to be closed + CreateContainerCmd containerCmd = DOCKER.createContainerCmd(image.getId()); + if (containerName != null) { + containerCmd.withName(containerName); + } + containerCmd = prepareCreateContainerCmd(containerCmd); + CreateContainerResponse container = containerCmd.exec(); + String[] warnings = container.getWarnings(); + if (warnings != null && warnings.length != 0 && LOGGER.isWarnEnabled()) { + LOGGER.warn("During container creation the following warnings were raised:"); + for (String warning : warnings) { + LOGGER.warn(warning); + } + } + return container.getId(); + } + + public void ensureContainerExists() { + // TODO check if container already exists + if (containerId != null) { + // check if still exists + // TODO + } + if (containerId == null) { + // create new container + containerId = createContainer(); + } + } + + public void start() { + ensureContainerExists(); + DOCKER.startContainerCmd(getId()).exec(); + } + + public void remove() { + String id = getId(); + if (id != null) { + DOCKER.removeContainerCmd(id).exec(); + } + closeChildren(); + containerId = null; + } + + private void autoRemove() { + if (autoRemove) { + remove(); + } + } + + private void storeExitCode() { + this.exitCode = Optional.of(DOCKER.inspectContainerCmd(getId()).exec().getState().getExitCodeLong()); + } + + private void closeChildren() { + for (DockerExecInstance exec : childExecs) { + try { + exec.close(); + } catch (Exception e) { + LOGGER.warn("Error while closing exec instance", e); + } + } + childExecs.clear(); + } + + public void stop(int secondsToWaitBeforeKilling) { + DOCKER.stopContainerCmd(getId()).withTimeout(secondsToWaitBeforeKilling).exec(); + closeChildren(); + storeExitCode(); + autoRemove(); + } + + public void stop() { + stop(2); + } + + public void kill() { + DOCKER.killContainerCmd(getId()).exec(); + closeChildren(); + storeExitCode(); + autoRemove(); + } + + public Image getImage() { + return image; + } + + @SuppressWarnings("squid:S2142") // sonarlint: "InterruptedException" should not be ignored + // we rethrow the interrupted exception a bit later + public void close() { + closeChildren(); + if (autoRemove) { + try { + String id = getId(); + if (id != null) { + DOCKER.killContainerCmd(id).exec(); + } + } catch (DockerException e) { + LOGGER.warn("Failed to kill container on close()"); + } + try { + remove(); + } catch (DockerException e) { + // we did our best + LOGGER.warn("Failed to remove container on close()", e); + } + } + } + + public void restart() { + DOCKER.restartContainerCmd(getId()).exec(); + } + + public String getId() { + return containerId; + } + + public String getLogs() throws InterruptedException { + FrameHandler fh = new FrameHandler(); + DOCKER.logContainerCmd(getId()).exec(fh); + fh.awaitCompletion(); + String[] lines = fh.getLines(); + // TODO optimize the following into the frame handler itself + String logs = + Arrays.stream(lines).skip(logReadOffset).map(s -> s.concat("\n")).reduce(String::concat).orElse("-"); + logReadOffset = lines.length; + return logs; + } + + @SuppressWarnings("squid:S3655") // sonarlint: Optional value should only be accessed after calling isPresent() + // this is fixed as if there is no value we either throw an exception or store a + // new value + public long getExitCode() { + if (!exitCode.isPresent()) { + // check if still running + ContainerState state = DOCKER.inspectContainerCmd(getId()).exec().getState(); + if (Boolean.TRUE.equals(state.getRunning())) { + throw new IllegalStateException("Container is still running"); + } else { + storeExitCode(); + autoRemove(); + } + } + return exitCode.get(); + } + + public String getContainerName() { + return containerName; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshManagerFactory.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshManagerFactory.java new file mode 100644 index 0000000..558b56f --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshManagerFactory.java @@ -0,0 +1,227 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; + +import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Image; + +import de.rub.nds.ssh.subject.HostInfo; +import de.rub.nds.ssh.subject.constants.SshImageLabels; +import de.rub.nds.ssh.subject.constants.TransportType; +import de.rub.nds.ssh.subject.exceptions.DefaultProfileNotFoundException; +import de.rub.nds.ssh.subject.exceptions.PropertyNotFoundException; +import de.rub.nds.ssh.subject.params.ParameterProfile; +import de.rub.nds.ssh.subject.params.ParameterProfileManager; +import de.rub.nds.ssh.subject.properties.ImageProperties; +import de.rub.nds.ssh.subject.properties.PropertyManager; +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; + +/** + * Creates SSH-Server or SSH-Client Instances as Docker Container Holds the Config for each SSH-Server or SSH-Client + */ +public class DockerSshManagerFactory { + private DockerSshManagerFactory() { + throw new UnsupportedOperationException("Utility class"); + } + + private static final com.github.dockerjava.api.DockerClient DOCKER = DockerClientManager.getDockerClient(); + + private static final int DEFAULT_PORT = 4433; + + @SuppressWarnings("unchecked") + public abstract static class SshInstanceBuilder> { + protected final ParameterProfile profile; + protected final ImageProperties imageProperties; + protected final String version; + protected boolean autoRemove = true; + // shared constructor parameters + // Host Info: + protected final TransportType transportType; + protected String ip = null; + protected String hostname = null; + protected int port = DEFAULT_PORT; + protected UnaryOperator hostConfigHook; + // remaining shared params + protected String additionalParameters = null; + protected boolean parallelize = false; + protected boolean insecureConnection = false; + protected String containerName; + + public SshInstanceBuilder(SshImplementationType type, String version, ConnectionRole role, + TransportType transportType) { + this.profile = retrieveParameterProfile(type, version, role); + this.imageProperties = retrieveImageProperties(role, type); + this.version = version; + this.transportType = transportType; + } + + public T autoRemove(boolean value) { + autoRemove = value; + return (T) this; + } + + public T ip(String value) { + ip = value; + return (T) this; + } + + public T hostname(String value) { + hostname = value; + return (T) this; + } + + public T containerName(String value) { + containerName = value; + return (T) this; + } + + public T port(int value) { + port = value; + return (T) this; + } + + public T additionalParameters(String value) { + additionalParameters = value; + return (T) this; + } + + public T parallelize(boolean value) { + parallelize = value; + return (T) this; + } + + public T insecureConnection(boolean value) { + insecureConnection = value; + return (T) this; + } + + public T hostConfigHook(UnaryOperator value) { + hostConfigHook = value; + return (T) this; + } + + public abstract DockerSshInstance build() throws DockerException, InterruptedException; + } + + public static class SshClientInstanceBuilder extends SshInstanceBuilder { + + protected boolean connectOnStartup = true; + + public SshClientInstanceBuilder(SshImplementationType type, String version, TransportType transportType) { + super(type, version, ConnectionRole.CLIENT, transportType); + } + + @Override + public DockerSshClientInstance build() throws DockerException, InterruptedException { + return new DockerSshClientInstance(containerName, profile, imageProperties, version, autoRemove, + new HostInfo(ip, hostname, port, transportType), additionalParameters, parallelize, insecureConnection, + connectOnStartup, hostConfigHook); + } + + public SshClientInstanceBuilder connectOnStartup(boolean value) { + connectOnStartup = value; + return this; + } + + } + + public static class SshServerInstanceBuilder extends SshInstanceBuilder { + + public SshServerInstanceBuilder(SshImplementationType type, String version, TransportType transportType) { + super(type, version, ConnectionRole.SERVER, transportType); + } + + @Override + public DockerSshServerInstance build() throws DockerException, InterruptedException { + return new DockerSshServerInstance(containerName, profile, imageProperties, version, autoRemove, + new HostInfo(ip, hostname, port, transportType), additionalParameters, parallelize, insecureConnection, + hostConfigHook); + } + + } + + public static SshClientInstanceBuilder getSshClientBuilder(SshImplementationType type, String version) { + return new SshClientInstanceBuilder(type, version, TransportType.TCP); + } + + public static SshClientInstanceBuilder getDSshClientBuilder(SshImplementationType type, String version) { + return new SshClientInstanceBuilder(type, version, TransportType.UDP); + } + + public static SshServerInstanceBuilder getSshServerBuilder(SshImplementationType type, String version) { + return new SshServerInstanceBuilder(type, version, TransportType.TCP); + } + + public static SshServerInstanceBuilder getDSshServerBuilder(SshImplementationType type, String version) { + return new SshServerInstanceBuilder(type, version, TransportType.UDP); + } + + public static boolean clientExists(SshImplementationType type, String version) { + return checkExists(type, version, ConnectionRole.CLIENT); + } + + public static boolean serverExists(SshImplementationType type, String version) { + return checkExists(type, version, ConnectionRole.SERVER); + } + + private static boolean checkExists(SshImplementationType type, String version, ConnectionRole role) { + return PropertyManager.instance().getProperties(role, type) != null + && ParameterProfileManager.instance().getProfile(type, version, role) != null; + } + + public static ImageProperties retrieveImageProperties(ConnectionRole role, SshImplementationType type) + throws PropertyNotFoundException { + ImageProperties properties = PropertyManager.instance().getProperties(role, type); + if (properties == null) { + throw new PropertyNotFoundException("Could not find a Property for " + role.name() + ": " + type.name()); + } + return properties; + } + + public static ParameterProfile retrieveParameterProfile(SshImplementationType type, String version, + ConnectionRole role) throws DefaultProfileNotFoundException { + ParameterProfile profile = ParameterProfileManager.instance().getProfile(type, version, role); + if (profile == null) { + throw new DefaultProfileNotFoundException( + "Could not find a Profile for " + role.name() + ": " + type.name() + ":" + version); + } + return profile; + } + + public static List getAvailableVersions(ConnectionRole role, SshImplementationType type) { + List versionList = new LinkedList<>(); + Map labels = new HashMap<>(); + labels.put(SshImageLabels.IMPLEMENTATION.getLabelName(), type.name().toLowerCase()); + labels.put(SshImageLabels.CONNECTION_ROLE.getLabelName(), role.toString().toLowerCase()); + List serverImageList = DOCKER.listImagesCmd().withLabelFilter(labels).withDanglingFilter(false).exec(); + for (Image image : serverImageList) { + if (image.getLabels() != null) { + String version = image.getLabels().get(SshImageLabels.VERSION.getLabelName()); + if (version != null) { + versionList.add(version); + } + } + } + return versionList; + } + + public static List getAllImages() { + return DOCKER.listImagesCmd().withLabelFilter(SshImageLabels.IMPLEMENTATION.getLabelName()) + .withDanglingFilter(false).exec(); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshServerInstance.java b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshServerInstance.java new file mode 100644 index 0000000..159d103 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/DockerSshServerInstance.java @@ -0,0 +1,107 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.util.function.UnaryOperator; + +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.NetworkSettings; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports.Binding; + +import de.rub.nds.ssh.subject.HostInfo; +import de.rub.nds.ssh.subject.params.ParameterProfile; +import de.rub.nds.ssh.subject.properties.ImageProperties; +import de.rub.nds.ssh.subject.ConnectionRole; + +public class DockerSshServerInstance extends DockerSshInstance { + + private int port; + private final HostInfo hostInfo; + private final String additionalParameters; + private final boolean parallelize; + private final boolean insecureConnection; + private ExposedPort exposedImplementationPort; + + public DockerSshServerInstance(String containerName, ParameterProfile profile, ImageProperties imageProperties, + String version, boolean autoRemove, HostInfo hostInfo, String additionalParameters, boolean parallelize, + boolean insecureConnection, UnaryOperator hostConfigHook) { + super(containerName, profile, imageProperties, version, ConnectionRole.SERVER, autoRemove, hostConfigHook); + this.port = hostInfo.getPort(); // fill with default port + this.hostInfo = hostInfo; + this.additionalParameters = additionalParameters; + this.parallelize = parallelize; + this.insecureConnection = insecureConnection; + } + + @Override + protected HostConfig prepareHostConfig(HostConfig cfg) { + return super.prepareHostConfig(cfg) + .withPortBindings(new PortBinding(Binding.empty(), + new ExposedPort(imageProperties.getInternalPort(), hostInfo.getType().toInternetProtocol()))) + .withReadonlyRootfs(true); + } + + @Override + protected CreateContainerCmd prepareCreateContainerCmd(CreateContainerCmd cmd) { + String host; + if (hostInfo.getHostname() == null || imageProperties.isUseIP()) { + host = hostInfo.getIp(); + } else { + host = hostInfo.getHostname(); + } + exposedImplementationPort = new ExposedPort(hostInfo.getPort(), hostInfo.getType().toInternetProtocol()); + return super.prepareCreateContainerCmd(cmd).withCmd(parameterProfile.toParameters(host, hostInfo.getPort(), + imageProperties, additionalParameters, parallelize, insecureConnection)) + .withExposedPorts(exposedImplementationPort); + } + + @Override + public void start() { + super.start(); + updateInstancePort(); + } + + /** + * Update port to match actually exposed port. + */ + public void updateInstancePort() { + InspectContainerResponse containerInfo = DOCKER.inspectContainerCmd(getId()).exec(); + if (containerInfo == null) { + throw new IllegalStateException("Could not find container with ID:" + getId()); + } + NetworkSettings networkSettings = containerInfo.getNetworkSettings(); + if (networkSettings == null) { + throw new IllegalStateException( + "Cannot retrieve InstacePort, Network not properly configured for container with ID:" + getId()); + } + if (exposedImplementationPort == null) { + throw new IllegalStateException( + "Unable to update port - no exposed port set for container with ID:" + getId()); + } + + Binding[] binding = networkSettings.getPorts().getBindings().get(exposedImplementationPort); + if (binding != null) { + // only update if port mapping was necessary + port = Integer.valueOf(binding[0].getHostPortSpec()); + } + } + + public int getPort() { + return port; + } + + public HostInfo getHostInfo() { + return hostInfo; + } +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/docker/FrameHandler.java b/src/main/java/de/rub/nds/ssh/subject/docker/FrameHandler.java new file mode 100644 index 0000000..73332e4 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/docker/FrameHandler.java @@ -0,0 +1,61 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.docker; + +import java.util.Collection; +import java.util.LinkedList; + +import com.github.dockerjava.api.async.ResultCallbackTemplate; +import com.github.dockerjava.api.model.Frame; + +public class FrameHandler extends ResultCallbackTemplate { + private static String[] EMPTY_STR_ARR = new String[] {}; + + private Collection frames; + + public FrameHandler() { + frames = new LinkedList<>(); + } + + @Override + public void onNext(Frame object) { + synchronized (frames) { + frames.add(object); + } + } + + public String[] getLines() { + Collection ret = new LinkedList<>(); + boolean pending_newline = false; + StringBuilder current_line = new StringBuilder(); + for (Frame frame : frames) { + for (byte b : frame.getPayload()) { + if (b == '\r') { + pending_newline = true; + } else if (b == '\n' || pending_newline) { + // handle newline + pending_newline = false; + ret.add(current_line.toString()); + current_line = new StringBuilder(); + if (b != '\n') { + current_line.append((char) b); + } + } else { + current_line.append((char) b); + } + } + } + if (current_line.length() > 0) { + ret.add(current_line.toString()); + } + return ret.toArray(EMPTY_STR_ARR); + } + +} diff --git a/src/main/java/de/rub/nds/ssh/subject/exceptions/CertVolumeNotFoundException.java b/src/main/java/de/rub/nds/ssh/subject/exceptions/CertVolumeNotFoundException.java new file mode 100644 index 0000000..77c66cb --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/exceptions/CertVolumeNotFoundException.java @@ -0,0 +1,33 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.exceptions; + +public class CertVolumeNotFoundException extends RuntimeException { + + public CertVolumeNotFoundException() { + } + + public CertVolumeNotFoundException(String message) { + super(message); + } + + public CertVolumeNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public CertVolumeNotFoundException(Throwable cause) { + super(cause); + } + + public CertVolumeNotFoundException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/exceptions/DefaultProfileNotFoundException.java b/src/main/java/de/rub/nds/ssh/subject/exceptions/DefaultProfileNotFoundException.java new file mode 100644 index 0000000..b5d0d47 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/exceptions/DefaultProfileNotFoundException.java @@ -0,0 +1,39 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.exceptions; + +public class DefaultProfileNotFoundException extends RuntimeException { + + public DefaultProfileNotFoundException() { + } + + public DefaultProfileNotFoundException(String message) { + super(message); + } + + public DefaultProfileNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public DefaultProfileNotFoundException(Throwable cause) { + super(cause); + } + + public DefaultProfileNotFoundException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/de/rub/nds/ssh/subject/exceptions/ImplementationDidNotStartException.java b/src/main/java/de/rub/nds/ssh/subject/exceptions/ImplementationDidNotStartException.java new file mode 100644 index 0000000..b829e38 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/exceptions/ImplementationDidNotStartException.java @@ -0,0 +1,33 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.exceptions; + +public class ImplementationDidNotStartException extends RuntimeException { + + public ImplementationDidNotStartException() { + } + + public ImplementationDidNotStartException(String message) { + super(message); + } + + public ImplementationDidNotStartException(String message, Throwable cause) { + super(message, cause); + } + + public ImplementationDidNotStartException(Throwable cause) { + super(cause); + } + + public ImplementationDidNotStartException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/exceptions/PropertyNotFoundException.java b/src/main/java/de/rub/nds/ssh/subject/exceptions/PropertyNotFoundException.java new file mode 100644 index 0000000..e2ef945 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/exceptions/PropertyNotFoundException.java @@ -0,0 +1,33 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.exceptions; + +public class PropertyNotFoundException extends RuntimeException { + + public PropertyNotFoundException() { + } + + public PropertyNotFoundException(String message) { + super(message); + } + + public PropertyNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public PropertyNotFoundException(Throwable cause) { + super(cause); + } + + public PropertyNotFoundException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/exceptions/SshVersionNotFoundException.java b/src/main/java/de/rub/nds/ssh/subject/exceptions/SshVersionNotFoundException.java new file mode 100644 index 0000000..98f9d50 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/exceptions/SshVersionNotFoundException.java @@ -0,0 +1,33 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.exceptions; + +public class SshVersionNotFoundException extends RuntimeException { + + public SshVersionNotFoundException() { + } + + public SshVersionNotFoundException(String message) { + super(message); + } + + public SshVersionNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public SshVersionNotFoundException(Throwable cause) { + super(cause); + } + + public SshVersionNotFoundException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/instance/ExecInstance.java b/src/main/java/de/rub/nds/ssh/subject/instance/ExecInstance.java new file mode 100644 index 0000000..1c8d077 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/instance/ExecInstance.java @@ -0,0 +1,16 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.instance; + +public interface ExecInstance { + boolean isRunning(); + + void close(); +} \ No newline at end of file diff --git a/src/main/java/de/rub/nds/ssh/subject/params/Parameter.java b/src/main/java/de/rub/nds/ssh/subject/params/Parameter.java new file mode 100644 index 0000000..cf9a71b --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/Parameter.java @@ -0,0 +1,48 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import java.io.Serializable; + +public class Parameter implements Serializable { + + private String cmdParameter; + + private ParameterType type; + + public Parameter() { + } + + public Parameter(String cmdParameter, ParameterType type) { + this.cmdParameter = cmdParameter; + this.type = type; + } + + public String getCmdParameter() { + return cmdParameter; + } + + public void setCmdParameter(String cmdParameter) { + this.cmdParameter = cmdParameter; + } + + public ParameterType getType() { + return type; + } + + public void setType(ParameterType type) { + this.type = type; + } + + @Override + public String toString() { + return "Parameter{" + "cmdParameter=" + cmdParameter + ", type=" + type + '}'; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfile.java b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfile.java new file mode 100644 index 0000000..3c73fa4 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfile.java @@ -0,0 +1,142 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import de.rub.nds.ssh.subject.properties.ImageProperties; +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; + +import java.io.Serializable; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ParameterProfile implements Serializable { + private static final Logger LOGGER = LogManager.getLogger(); + + private String name; + + private String description; + + private SshImplementationType type; + + private ConnectionRole role; + + @XmlElements(value = { @XmlElement(type = String.class, name = "Version") }) + private List versionList; + + @XmlElements(value = { @XmlElement(type = Parameter.class, name = "Parameter") }) + private List parameterList; + + public ParameterProfile() { + } + + public ParameterProfile(String name, String description, SshImplementationType type, ConnectionRole role, + List versionList, List parameterList) { + this.name = name; + this.description = description; + this.type = type; + this.role = role; + this.versionList = versionList; + this.parameterList = parameterList; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public SshImplementationType getType() { + return type; + } + + public ConnectionRole getRole() { + return role; + } + + public List getVersionList() { + return versionList; + } + + public List getParameterList() { + return parameterList; + } + + public boolean supportsInsecure() { + return parameterList.stream().anyMatch(i -> i.getType() == ParameterType.INSECURE); + } + + @Override + public String toString() { + return "ParameterProfile{" + "name=" + name + ", description=" + description + ", type=" + type + ", role=" + + role + ", versionList=" + versionList + ", parameterList=" + parameterList + '}'; + } + + @SuppressWarnings("squid:S3776") // sonarlint: Cognitive Complexity of methods should not be too high + // at some point this should be refactored (especially if more params are added) + public String[] toParameters(String host, Integer targetPort, ImageProperties imageProperties, + String additionalParameters, boolean parallelize, boolean insecureConnection) { + StringBuilder finalParams = new StringBuilder(); + for (Parameter param : parameterList) { + if (supportsInsecure()) { + if ((insecureConnection && param.getType() == ParameterType.CA_CERTIFICATE) + || (!insecureConnection && param.getType() == ParameterType.INSECURE)) { + // do not add CA param if we use insecure, do not add insecure + // if not wanted + continue; + } + } + + if (!parallelize && param.getType() == ParameterType.PARALLELIZE) { + // do not add parallelize if not wanted + continue; + } + if (param.getCmdParameter().equals("")) { + // do not add empty commands that cause a blank space + continue; + } + finalParams.append(param.getCmdParameter()); + finalParams.append(" "); + } + if (additionalParameters != null) { + finalParams.append(additionalParameters); + } + String afterReplace = finalParams.toString(); + + if (host != null) { + afterReplace = afterReplace.replace("[host]", host); + } + if (targetPort != null) { + afterReplace = afterReplace.replace("[port]", "" + targetPort); + } + if (imageProperties.getDefaultCertPath() != null) { + afterReplace = afterReplace.replace("[cert]", imageProperties.getDefaultCertPath()); + } + if (imageProperties.getDefaultKeyPath() != null) { + afterReplace = afterReplace.replace("[key]", imageProperties.getDefaultKeyPath()); + } + if (imageProperties.getDefaultCertKeyCombinedPath() != null) { + afterReplace = afterReplace.replace("[combined]", imageProperties.getDefaultCertKeyCombinedPath()); + } + afterReplace = afterReplace.trim(); + LOGGER.debug("Final parameters: {}", afterReplace); + return afterReplace.split(" "); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileManager.java b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileManager.java new file mode 100644 index 0000000..f9a0911 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileManager.java @@ -0,0 +1,140 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.reflections.Reflections; + +import javax.xml.bind.JAXBException; +import javax.xml.stream.XMLStreamException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.reflections.scanners.Scanners; + +public class ParameterProfileManager { + + static final Logger LOGGER = LogManager.getLogger(ParameterProfileSerializer.class.getName()); + + private static ParameterProfileManager instance; + + public static ParameterProfileManager instance() { + if (instance == null) { + instance = new ParameterProfileManager(); + } + return instance; + } + + private static final String RESOURCE_PATH = "/profiles/"; + + private final List defaultClientProfileList; + + private final List allProfileList; + + private final List defaultServerProfileList; + + protected ParameterProfileManager() { + defaultServerProfileList = new LinkedList<>(); + defaultClientProfileList = new LinkedList<>(); + allProfileList = new LinkedList<>(); + + for (ConnectionRole role : ConnectionRole.values()) { + try { + for (String filename : getResourceFiles(RESOURCE_PATH + role.name().toLowerCase() + "/")) { + ParameterProfile profile = tryLoadProfile(role, filename); + if (profile != null) { + LOGGER.debug("Loaded:" + profile.getName() + " : " + profile.getRole().name() + " - " + + profile.getDescription()); + allProfileList.add(profile); + } + } + } catch (IOException ex) { + LOGGER.warn("Problem reading profiles", ex); + ex.printStackTrace(); + } + } + + for (SshImplementationType type : SshImplementationType.values()) { + ParameterProfile profile = + tryLoadProfile(ConnectionRole.SERVER, "" + type.name().toLowerCase() + ".profile"); + if (profile != null) { + LOGGER.debug("Loaded:" + profile.getName() + " : " + profile.getRole().name()); + defaultServerProfileList.add(profile); + } + profile = tryLoadProfile(ConnectionRole.CLIENT, "" + type.name().toLowerCase() + ".profile"); + if (profile != null) { + LOGGER.debug("Loaded:" + profile.getName() + " : " + profile.getRole().name()); + defaultClientProfileList.add(profile); + } + } + } + + private List getResourceFiles(String path) throws IOException { + Reflections reflections = new Reflections("profiles", Scanners.Resources); + Set resourceList = reflections.getResources(Pattern.compile(".*\\.profile")).parallelStream() + .map(x -> new File(x).getName()).collect(Collectors.toSet()); + return new ArrayList<>(resourceList); + } + + private ParameterProfile tryLoadProfile(ConnectionRole role, String filename) { + try { + InputStream stream = ParameterProfileManager.class + .getResourceAsStream(RESOURCE_PATH + role.name().toLowerCase() + "/" + filename); + return ParameterProfileSerializer.read(stream); + } catch (IOException | JAXBException | XMLStreamException E) { + LOGGER.debug("Could not find other ParameterProfile for: " + RESOURCE_PATH + role.name().toLowerCase() + "/" + + filename + ": " + role.name()); + LOGGER.trace(E); + return null; + } + } + + public ParameterProfile getProfile(SshImplementationType type, String version, ConnectionRole role) { + for (ParameterProfile profile : allProfileList) { + if (profile.getRole() == role && profile.getType() == type) { + if (profile.getVersionList() != null && !profile.getVersionList().isEmpty()) { + for (String versionRegex : profile.getVersionList()) { + if (version.matches(versionRegex)) { + return profile; + } + } + } + } + } + return getDefaultProfile(type, role); + } + + public ParameterProfile getDefaultProfile(SshImplementationType type, ConnectionRole role) { + List profileList; + if (role == ConnectionRole.CLIENT) { + profileList = defaultClientProfileList; + } else if (role == ConnectionRole.SERVER) { + profileList = defaultServerProfileList; + } else { + throw new IllegalArgumentException("Unknown ConnectionRole: " + role); + } + + for (ParameterProfile profile : profileList) { + if (profile.getType() == type) { + return profile; + } + } + return null; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializer.java b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializer.java new file mode 100644 index 0000000..66aa9f7 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializer.java @@ -0,0 +1,106 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ParameterProfileSerializer { + + static final Logger LOGGER = LogManager.getLogger(ParameterProfileSerializer.class.getName()); + + private static JAXBContext context; + + private static synchronized JAXBContext getJAXBContext() throws JAXBException, IOException { + if (context == null) { + context = JAXBContext.newInstance(ParameterProfile.class, Parameter.class); + } + return context; + } + + public static void write(File file, ParameterProfile profile) + throws FileNotFoundException, JAXBException, IOException { + FileOutputStream fos = new FileOutputStream(file); + ParameterProfileSerializer.write(fos, profile); + } + + public static String write(ParameterProfile profile) throws JAXBException, IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ParameterProfileSerializer.write(bos, profile); + return new String(bos.toByteArray(), "UTF-8"); + } + + public static void write(OutputStream outputStream, ParameterProfile profile) throws JAXBException, IOException { + context = getJAXBContext(); + Marshaller m = context.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.marshal(profile, outputStream); + outputStream.close(); + } + + public static ParameterProfile read(InputStream inputStream) throws JAXBException, IOException, XMLStreamException { + context = getJAXBContext(); + Unmarshaller m = context.createUnmarshaller(); + + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLStreamReader xsr = xif.createXMLStreamReader(inputStream); + + ParameterProfile profile = (ParameterProfile) m.unmarshal(xsr); + inputStream.close(); + return profile; + } + + public static List readFolder(File f) { + if (f.isDirectory()) { + ArrayList list = new ArrayList<>(); + for (File file : f.listFiles()) { + if (file.getName().startsWith(".")) { + // We ignore the .gitignore File + continue; + } + ParameterProfile profile; + try { + profile = ParameterProfileSerializer.read(new FileInputStream(file)); + list.add(profile); + } catch (JAXBException | IOException | XMLStreamException ex) { + LOGGER.warn("Could not read " + file.getAbsolutePath() + " from Folder."); + LOGGER.debug(ex.getLocalizedMessage(), ex); + } + } + return list; + } else { + throw new IllegalArgumentException("Cannot read Folder, because its not a Folder"); + } + + } + + private ParameterProfileSerializer() { + + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileTemplate.java b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileTemplate.java new file mode 100644 index 0000000..58c04e4 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/ParameterProfileTemplate.java @@ -0,0 +1,14 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +public class ParameterProfileTemplate { + +} diff --git a/src/main/java/de/rub/nds/ssh/subject/params/ParameterType.java b/src/main/java/de/rub/nds/ssh/subject/params/ParameterType.java new file mode 100644 index 0000000..916ae10 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/params/ParameterType.java @@ -0,0 +1,23 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +public enum ParameterType { + NONE, // Undefined Parameterfunction + HOST_PORT, // To who we shall connect to / where we should open our port + VERIFY_DEPTH, // How deep certificates should be validated + CERTIFICATE_KEY, // The cert and key which should be used_//PEM + JKS_CERTIFICATE_KEY, // The cert and key which should be used_//JKS + LOOP, // Keeps the Server from closing after a single connection + NO_CLIENT_AUTHENTICATION, // Some servers/clients have client authentication active by default + CA_CERTIFICATE, // The ca cert which should be used_//CRT + INSECURE, // Disables server certificate validation + PARALLELIZE // multi threaded server +} diff --git a/src/main/java/de/rub/nds/ssh/subject/properties/ImageProperties.java b/src/main/java/de/rub/nds/ssh/subject/properties/ImageProperties.java new file mode 100644 index 0000000..b4a4579 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/properties/ImageProperties.java @@ -0,0 +1,99 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.properties; + +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; + +public class ImageProperties { + + private final ConnectionRole role; + private final SshImplementationType type; + private final String defaultVersion; + + private Integer internalPort; + private String defaultKeyPath; + private String defaultCertPath; + private String defaultCertKeyCombinedPath; + private boolean useIP; + + public ImageProperties(ConnectionRole role, SshImplementationType type, String defaultVersion, + String defaultCertPath, boolean useIP) { + // Called for SSH client images + this.role = role; + this.type = type; + this.defaultVersion = defaultVersion; + this.internalPort = null; + this.defaultKeyPath = null; + this.defaultCertPath = defaultCertPath; + this.useIP = useIP; + } + + public ImageProperties(ConnectionRole role, SshImplementationType type, String defaultVersion, Integer internalPort, + String defaultKeyPath, String defaultCertPath, String defaultCertKeyCombindPath) { + // Called for SSH server images + this.role = role; + this.type = type; + this.defaultVersion = defaultVersion; + this.internalPort = internalPort; + this.defaultKeyPath = defaultKeyPath; + this.defaultCertPath = defaultCertPath; + this.defaultCertKeyCombinedPath = defaultCertKeyCombindPath; + this.useIP = true; + } + + public ConnectionRole getRole() { + return role; + } + + public SshImplementationType getType() { + return type; + } + + public String getDefaultVersion() { + return defaultVersion; + } + + public Integer getInternalPort() { + return internalPort; + } + + public String getDefaultKeyPath() { + return defaultKeyPath; + } + + public String getDefaultCertPath() { + return defaultCertPath; + } + + public boolean isUseIP() { + return useIP; + } + + public void setInternalPort(Integer internalPort) { + this.internalPort = internalPort; + } + + public void setDefaultKeyPath(String defaultKeyPath) { + this.defaultKeyPath = defaultKeyPath; + } + + public void setDefaultCertPath(String defaultCertPath) { + this.defaultCertPath = defaultCertPath; + } + + public void setUseIP(boolean useIP) { + this.useIP = useIP; + } + + public String getDefaultCertKeyCombinedPath() { + return defaultCertKeyCombinedPath; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/properties/PropertyManager.java b/src/main/java/de/rub/nds/ssh/subject/properties/PropertyManager.java new file mode 100644 index 0000000..0578517 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/properties/PropertyManager.java @@ -0,0 +1,86 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.properties; + +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; +import de.rub.nds.ssh.subject.exceptions.PropertyNotFoundException; +import java.util.LinkedList; +import java.util.List; + +public class PropertyManager { + + private static PropertyManager instance; + + public static PropertyManager instance() { + if (instance == null) { + instance = new PropertyManager(); + } + return instance; + } + + private final List imagePropertyList; + + private static class Const { + + static final String CERT_KEY_PEM = "/cert/rsa2048key.pem"; + static final String CERT_CERT_PEM = "/cert/rsa2048cert.pem"; + static final String CERT_COMBINED_PEM = "/cert/rsa2048combined.pem"; + static final String RUST_TEST_CA_KEY = "/cert/test-ca/rsa/end.rsa"; + static final String RUST_TEST_CA_FULLCHAIN = "/cert/test-ca/rsa/end.fullchain"; + static final String CA_CERT = "/cert/ca.pem"; + } + + protected PropertyManager() { + imagePropertyList = new LinkedList<>(); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.OPENSSH, "9.0p1", Const.CA_CERT, false)); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.PARAMIKO, "2.11.0", Const.CA_CERT, false)); + + imagePropertyList.add(new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.DROPBEAR, "2022.82", + Const.CA_CERT, false)); + + imagePropertyList.add(new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.GO, + "v0.0.0-20220924013350-4ba4fb4dd9e7", Const.CA_CERT, false)); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.PUTTY, "0.76-2", Const.CA_CERT, false)); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.ASYNCSSH, "2.12.0", Const.CA_CERT, false)); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.LIBSSH, "0.9.4", Const.CA_CERT, false)); + + imagePropertyList.add(new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.WOLFSSH, "1.4.11-stable", + Const.CA_CERT, false)); + + imagePropertyList.add( + new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.ZGRAB2, "latest", Const.CA_CERT, false)); + + imagePropertyList.add(new ImageProperties(ConnectionRole.CLIENT, SshImplementationType.METASPLOIT, "latest", + Const.CA_CERT, false)); + + } + + public ImageProperties getProperties(ConnectionRole role, SshImplementationType type) { + for (ImageProperties properties : imagePropertyList) { + if (properties.getRole().equals(role)) { + if (properties.getType().equals(type)) { + return properties; + } + } + } + throw new PropertyNotFoundException("No " + role.name() + " properties found for: " + type.name()); + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/report/ContainerReport.java b/src/main/java/de/rub/nds/ssh/subject/report/ContainerReport.java new file mode 100644 index 0000000..e00cd83 --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/report/ContainerReport.java @@ -0,0 +1,116 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + */ + +package de.rub.nds.ssh.subject.report; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ContainerReport implements Serializable { + + static final Logger LOGGER = LogManager.getLogger(); + + private static JAXBContext context; + + private static synchronized JAXBContext getJAXBContext() throws JAXBException, IOException { + if (context == null) { + context = JAXBContext.newInstance(ContainerReport.class, InstanceContainer.class); + } + return context; + } + + public static void write(File file, ContainerReport report) + throws FileNotFoundException, JAXBException, IOException { + FileOutputStream fos = new FileOutputStream(file); + write(fos, report); + } + + public static void write(OutputStream outputStream, ContainerReport report) throws JAXBException, IOException { + context = getJAXBContext(); + Marshaller m = context.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.marshal(report, outputStream); + outputStream.close(); + } + + public static ContainerReport read(InputStream inputStream) throws JAXBException, IOException, XMLStreamException { + context = getJAXBContext(); + Unmarshaller m = context.createUnmarshaller(); + + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLStreamReader xsr = xif.createXMLStreamReader(inputStream); + + ContainerReport report = (ContainerReport) m.unmarshal(xsr); + inputStream.close(); + return report; + } + + @XmlElements(value = { @XmlElement(type = InstanceContainer.class, name = "Container") }) + private final List functionalContainerList; + @XmlElements(value = { @XmlElement(type = InstanceContainer.class, name = "Container") }) + private final List nonFunctionalContainerList; + @XmlElements(value = { @XmlElement(type = InstanceContainer.class, name = "Container") }) + + private final List totalContainerList; + + public ContainerReport() { + functionalContainerList = new LinkedList<>(); + nonFunctionalContainerList = new LinkedList<>(); + totalContainerList = new LinkedList<>(); + + } + + public void addInstanceContainer(InstanceContainer container) { + if (container.isFunctional()) { + functionalContainerList.add(container); + } else { + nonFunctionalContainerList.add(container); + } + totalContainerList.add(container); + } + + public List getFunctionalImplementationList() { + return functionalContainerList; + } + + public List getNonFunctionalImplementationList() { + return nonFunctionalContainerList; + } + + public List getTotalImplementationList() { + return totalContainerList; + } +} diff --git a/src/main/java/de/rub/nds/ssh/subject/report/InstanceContainer.java b/src/main/java/de/rub/nds/ssh/subject/report/InstanceContainer.java new file mode 100644 index 0000000..a069cfb --- /dev/null +++ b/src/main/java/de/rub/nds/ssh/subject/report/InstanceContainer.java @@ -0,0 +1,75 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + */ + +package de.rub.nds.ssh.subject.report; + +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; + +import java.io.Serializable; + +/** + * + * @author robert + */ +public class InstanceContainer implements Serializable { + + private ConnectionRole role; + + private SshImplementationType implementationType; + + private String version; + + private boolean functional; + + private InstanceContainer() { + } + + public InstanceContainer(ConnectionRole role, SshImplementationType implementationType, String version, + boolean functional) { + this.role = role; + this.implementationType = implementationType; + this.version = version; + this.functional = functional; + } + + public ConnectionRole getRole() { + return role; + } + + public void setRole(ConnectionRole role) { + this.role = role; + } + + public SshImplementationType getImplementationType() { + return implementationType; + } + + public void setImplementationType(SshImplementationType implementationType) { + this.implementationType = implementationType; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public boolean isFunctional() { + return functional; + } + + public void setFunctional(boolean functional) { + this.functional = functional; + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..131d991 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/profiles/client/asyncssh.profile b/src/main/resources/profiles/client/asyncssh.profile new file mode 100644 index 0000000..8aba9b1 --- /dev/null +++ b/src/main/resources/profiles/client/asyncssh.profile @@ -0,0 +1,15 @@ + + + asyncssh + Default Profile for AsyncSSH + ASYNCSSH + CLIENT + + [host] + HOST + + + [port] + PORT + + diff --git a/src/main/resources/profiles/client/dropbear.profile b/src/main/resources/profiles/client/dropbear.profile new file mode 100644 index 0000000..b03c6e9 --- /dev/null +++ b/src/main/resources/profiles/client/dropbear.profile @@ -0,0 +1,15 @@ + + + dropbear + Default Profile for Dropbear + DROPBEAR + CLIENT + + [host] + HOST + + + [port] + PORT + + diff --git a/src/main/resources/profiles/client/go.profile b/src/main/resources/profiles/client/go.profile new file mode 100644 index 0000000..5014976 --- /dev/null +++ b/src/main/resources/profiles/client/go.profile @@ -0,0 +1,23 @@ + + + go + Default Profile for GO + GO + CLIENT + + demo + User + + + [host]:[port] + HOST_PORT + + + password + PASSWORD + + + pwd + Command + + diff --git a/src/main/resources/profiles/client/libssh.profile b/src/main/resources/profiles/client/libssh.profile new file mode 100644 index 0000000..f120e8d --- /dev/null +++ b/src/main/resources/profiles/client/libssh.profile @@ -0,0 +1,27 @@ + + + libssh + Default Profile for LIBSSH + LIBSSH + CLIENT + + [host] + HOST + + + [port] + PORT + + + demo + User + + + password + PASSWORD + + + pwd + Command + + diff --git a/src/main/resources/profiles/client/metasploit.profile b/src/main/resources/profiles/client/metasploit.profile new file mode 100644 index 0000000..e10a589 --- /dev/null +++ b/src/main/resources/profiles/client/metasploit.profile @@ -0,0 +1,27 @@ + + + metasploit + Default Profile for Metasploit + METASPLOIT + CLIENT + + [host] + HOST + + + [port] + PORT + + + demo + User + + + password + PASSWORD + + + pwd + Command + + diff --git a/src/main/resources/profiles/client/openssh.profile b/src/main/resources/profiles/client/openssh.profile new file mode 100644 index 0000000..984b080 --- /dev/null +++ b/src/main/resources/profiles/client/openssh.profile @@ -0,0 +1,27 @@ + + + openSSH + Default Profile for OpenSSH + OPENSSH + CLIENT + + [host] + HOST + + + [port] + PORT + + + demo + USER + + + pw + PASSWORD + + + pwd + COMMAND + + diff --git a/src/main/resources/profiles/client/paramiko.profile b/src/main/resources/profiles/client/paramiko.profile new file mode 100644 index 0000000..b85ec62 --- /dev/null +++ b/src/main/resources/profiles/client/paramiko.profile @@ -0,0 +1,15 @@ + + + paramiko + Default Profile for Paramiko + PARAMIKO + CLIENT + + [host] + HOST + + + [port] + PORT + + diff --git a/src/main/resources/profiles/client/putty.profile b/src/main/resources/profiles/client/putty.profile new file mode 100644 index 0000000..51a8c4c --- /dev/null +++ b/src/main/resources/profiles/client/putty.profile @@ -0,0 +1,27 @@ + + + putty-linux + Default Profile for Putty for linux + PUTTY + CLIENT + + [host] + HOST + + + [port] + PORT + + + demo + USER + + + pw + PASSWORD + + + pwd + COMMAND + + diff --git a/src/main/resources/profiles/client/wolfssh.profile b/src/main/resources/profiles/client/wolfssh.profile new file mode 100644 index 0000000..ce3a64b --- /dev/null +++ b/src/main/resources/profiles/client/wolfssh.profile @@ -0,0 +1,28 @@ + + + wolfssh + Default Profile for WolfSSH + WOLFSSH + CLIENT + + -h [host] + HOST + + + -p [port] + PROT + + + + -u demo + User + + + -P password + PASSWORD + + + -c pwd + COMMAND + + diff --git a/src/main/resources/profiles/client/zgrab2.profile b/src/main/resources/profiles/client/zgrab2.profile new file mode 100644 index 0000000..3c6f64b --- /dev/null +++ b/src/main/resources/profiles/client/zgrab2.profile @@ -0,0 +1,27 @@ + + + zgrab2 + Default Profile for zGrab2 + ZGRAB2 + CLIENT + + [host] + HOST + + + [port] + PORT + + + demo + User + + + password + PASSWORD + + + pwd + Command + + diff --git a/src/test/java/de/rub/nds/ssh/subject/params/AvailableClientVersionsTest.java b/src/test/java/de/rub/nds/ssh/subject/params/AvailableClientVersionsTest.java new file mode 100644 index 0000000..8892e3b --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/AvailableClientVersionsTest.java @@ -0,0 +1,401 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.params; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.xml.bind.JAXBException; +import de.rub.nds.ssh.subject.SshImplementationType; +import de.rub.nds.ssh.subject.docker.DockerSshManagerFactory; +import org.junit.Test; +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.docker.DockerExecInstance; +import de.rub.nds.ssh.subject.docker.DockerSshClientInstance; +import de.rub.nds.ssh.subject.report.ContainerReport; +import de.rub.nds.ssh.subject.report.InstanceContainer; + +import static junit.framework.TestCase.assertTrue; + +public class AvailableClientVersionsTest { + + // private static final String HOSTNAME = "nds.tls-docker-library-test.de"; + // when running the tests on another os than linux you might need to change the + // ip use `docker run --rm -it alpine-build:3.12 ping -c1 host.docker.internal` + // to find the correct IP + private static final String IP = "172.26.0.1"; + private static final int PORT = 3022; + private static final int CONNECTION_TIMEOUT = 10; + + @Test + public void listAllClients() { + System.out.println("Available Clients: "); + for (SshImplementationType type : SshImplementationType.values()) { + + List availableVersions = DockerSshManagerFactory.getAvailableVersions(ConnectionRole.CLIENT, type); + System.out.println("Client version: " + type); + for (String version : availableVersions) { + System.out.println(version); + } + } + } + + @Test + public void serverTest() { + + System.out.println("Start Test Server \n"); + + SshTestServer testServer = new SshTestServer(PORT, true); + testServer.start(); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + System.out.println("Server is running .... "); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + boolean waiting = true; + int timeout = 0; + while (waiting && timeout < CONNECTION_TIMEOUT) { + if (testServer.isConnectionSuccessful()) { + waiting = false; + } + try { + TimeUnit.SECONDS.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + timeout++; + } + + try { + testServer.stop(""); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + public void functionalSingleClient(SshImplementationType clientType, String version) { + functionalSingleClient(clientType, version, false); + } + + public void functionalSingleClient(SshImplementationType clientType, String version, Boolean debug) { + System.out.println("Start Test Server \n"); + + SshTestServer testServer = new SshTestServer(PORT, debug); + testServer.start(); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + System.out.println("Functional Clients: "); + SshImplementationType type = clientType; + + try { + boolean isFunctional = isFunctional(testServer, type, version); + System.out.println(type.name() + ":" + version + " - " + isFunctional); + } catch (Exception E) { + E.printStackTrace(); + System.out.println(type.name() + ":" + version + " ERROR"); + } + + try { + testServer.stop(""); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Test + public void functionalClientParamiko() { + functionalSingleClient(SshImplementationType.PARAMIKO, "2.11.0"); + } + + @Test + public void functionalClientAllParamiko() { + functionalClientsAllOfType(SshImplementationType.PARAMIKO); + } + + @Test + public void functionalClientOpenssh() { + functionalSingleClient(SshImplementationType.OPENSSH, "8.8p1"); + } + + @Test + public void functionalClientsAllOpenssh() { + functionalClientsAllOfType(SshImplementationType.OPENSSH); + } + + @Test + public void functionalClientDropbear() { + functionalSingleClient(SshImplementationType.DROPBEAR, "2013.60", true); + } + + @Test + public void functionalClientsAllDropbear() { + functionalClientsAllOfType(SshImplementationType.DROPBEAR); + } + + public void functionalClientsAllOfType(SshImplementationType type) { + System.out.println("Start Test Server \n"); + SshTestServer testServer = new SshTestServer(PORT); + testServer.start(); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + System.out.println("Functional Clients: "); + + ContainerReport report = new ContainerReport(); + + List availableVersions = DockerSshManagerFactory.getAvailableVersions(ConnectionRole.CLIENT, type); + for (String version : availableVersions) { + try { + boolean isFunctional = isFunctional(testServer, type, version); + System.out.println(type.name() + ":" + version + " - " + isFunctional); + report.addInstanceContainer(new InstanceContainer(ConnectionRole.CLIENT, type, version, isFunctional)); + } catch (Exception E) { + E.printStackTrace(); + System.out.println(type.name() + ":" + version + " ERROR"); + } + } + + try { + ContainerReport.write(new File("client_report_" + type + ".xml"), report); + testServer.stop(""); + } catch (JAXBException | IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void functionalClientPuttyLinux() { + functionalSingleClient(SshImplementationType.PUTTY, "0.76-2"); + } + + @Test + public void functionalClientsAllPuttyLinux() { + functionalClientsAllOfType(SshImplementationType.PUTTY); + } + + @Test + public void functionalClientGO() { + functionalSingleClient(SshImplementationType.GO, "v0.0.0-20220924013350-4ba4fb4dd9e7"); + } + + @Test + public void functionalClientAllGO() { + functionalClientsAllOfType(SshImplementationType.GO); + } + + @Test + public void functionalClientAsyncssh() { + functionalSingleClient(SshImplementationType.ASYNCSSH, "2.12.0"); + } + + @Test + public void functionalClientAllAsyncssh() { + functionalClientsAllOfType(SshImplementationType.ASYNCSSH); + } + + @Test + public void functionalClientZgrab2() { + // work but not fire a command + functionalSingleClient(SshImplementationType.ZGRAB2, "latest", true); + } + + @Test + public void functionalClientWolfSSH() { + functionalSingleClient(SshImplementationType.WOLFSSH, "1.4.11-stable"); + } + + @Test + public void functionalClientAllWolfSSH() { + functionalClientsAllOfType(SshImplementationType.WOLFSSH); + } + + @Test + public void functionalClientMetasploit() { + functionalSingleClient(SshImplementationType.METASPLOIT, "latest", false); + } + + @Test + public void functionalClientLibssh() { + functionalSingleClient(SshImplementationType.LIBSSH, "0.9.5"); + } + + @Test + public void functionalClientAllLibssh() { + functionalClientsAllOfType(SshImplementationType.LIBSSH); + } + + @Test + public void functionalClientsTest() { + + System.out.println("Start Test Server \n"); + SshTestServer testServer = new SshTestServer(PORT); + testServer.start(); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + System.out.println("Functional Clients: "); + + ContainerReport report = new ContainerReport(); + for (SshImplementationType type : SshImplementationType.values()) { + List availableVersions = DockerSshManagerFactory.getAvailableVersions(ConnectionRole.CLIENT, type); + + for (String version : availableVersions) { + try { + boolean isFunctional = isFunctional(testServer, type, version); + System.out.println(type.name() + ":" + version + " - " + isFunctional); + report.addInstanceContainer( + new InstanceContainer(ConnectionRole.CLIENT, type, version, isFunctional)); + } catch (Exception E) { + E.printStackTrace(); + System.out.println(type.name() + ":" + version + " ERROR"); + } + } + } + + try { + ContainerReport.write(new File("client_report.xml"), report); + testServer.stop(""); + } catch (JAXBException | IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void functionalClientsTestBreakOnFalse() { + + System.out.println("Start Test Server \n"); + SshTestServer testServer = new SshTestServer(PORT); + testServer.start(); + + while (testServer.isStarted() != true) { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + System.out.println("Functional Clients: "); + + ContainerReport report = new ContainerReport(); + for (SshImplementationType type : SshImplementationType.values()) { + List availableVersions = DockerSshManagerFactory.getAvailableVersions(ConnectionRole.CLIENT, type); + + for (String version : availableVersions) { + try { + boolean isFunctional = isFunctional(testServer, type, version); + System.out.println(type.name() + ":" + version + " - " + isFunctional); + report.addInstanceContainer( + new InstanceContainer(ConnectionRole.CLIENT, type, version, isFunctional)); + if (!isFunctional) { + ContainerReport.write(new File("client_report.xml"), report); + testServer.stop(""); + } + assertTrue(isFunctional); + + } catch (Exception E) { + E.printStackTrace(); + System.out.println(type.name() + ":" + version + " ERROR"); + } + } + } + + try { + ContainerReport.write(new File("client_report.xml"), report); + testServer.stop(""); + } catch (JAXBException | IOException e) { + throw new RuntimeException(e); + } + } + + public boolean isFunctional(SshTestServer testServer, SshImplementationType type, String version) { + DockerSshClientInstance client = null; + DockerExecInstance ei = null; + + try { + if (version == null || type == null) { + System.out.println("Null: " + version); + return false; + } + client = DockerSshManagerFactory.getSshClientBuilder(type, version).ip(IP).port(PORT) + .connectOnStartup(false).insecureConnection(false).build(); + client.start(); + ei = (DockerExecInstance) client.connect(); + boolean waiting = true; + int timeout = 0; + while (waiting && timeout < CONNECTION_TIMEOUT) { + if (testServer.isConnectionSuccessful()) { + waiting = false; + } + TimeUnit.SECONDS.sleep(3); + timeout++; + } + + boolean res = testServer.isConnectionSuccessful(); + testServer.setIsConnectionSuccessful(false); + + if (!res) { + System.out.println("-Failed- Log:"); + for (String ln : ei.frameHandler.getLines()) { + System.out.println(ln); + } + } + return res; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } finally { + if (client != null) { + client.close(); + } + } + } + +} diff --git a/src/test/java/de/rub/nds/ssh/subject/params/AvailableServerVersionsTest.java b/src/test/java/de/rub/nds/ssh/subject/params/AvailableServerVersionsTest.java new file mode 100644 index 0000000..de7495c --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/AvailableServerVersionsTest.java @@ -0,0 +1,87 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import de.rub.nds.ssh.subject.SshImplementationType; +import de.rub.nds.ssh.subject.docker.DockerSshManagerFactory; + +public class AvailableServerVersionsTest { + +// private Logger LOGGER = LogManager.getLogger(); +// +// public AvailableServerVersionsTest() { +// } +// +// @Test +// public void listAllServers() { +// for (TlsImplementationType type : TlsImplementationType.values()) { +// +// List availableVersions = DockerTlsManagerFactory.getAvailableVersions(ConnectionRole.SERVER, type); +// System.out.println("Server version: " + type); +// for (String version : availableVersions) { +// System.out.println(version); +// } +// } +// } +// +// @Test +// @Category(SlowTests.class) +// public void testAllVersionsFunctional() throws JAXBException, IOException { +// Configurator.setRootLevel(org.apache.logging.log4j.Level.OFF); +// ContainerReport report = new ContainerReport(); +// for (TlsImplementationType type : TlsImplementationType.values()) { +// List availableVersions = DockerTlsManagerFactory.getAvailableVersions(ConnectionRole.SERVER, type); +// for (String version : availableVersions) { +// try { +// boolean isFunctional = isFunctional(type, version); +// System.out.println(type.name() + ":" + version + " - " + isFunctional); +// report.addInstanceContainer( +// new InstanceContainer(ConnectionRole.SERVER, type, version, isFunctional)); +// } catch (Exception E) { +// E.printStackTrace(); +// System.out.println(type.name() + ":" + version + " ERROR"); +// } +// +// } +// } +// ContainerReport.write(new File("server_report.xml"), report); +// } +// +// public boolean isFunctional(TlsImplementationType type, String version) { +// DockerTlsServerInstance server = null; +// try { +// if (version == null || type == null) { +// System.out.println("Null: " + version); +// return false; +// } +// try { +// server = DockerTlsManagerFactory.getTlsServerBuilder(type, version).build(); +// server.start(); +// } catch (Exception E) { +// LOGGER.warn("Instance seems not runnable", E); +// return false; +// } +// the hostname part might need some fixing. +// On windows(wsl2) I needed the IP of the wsl vm here +// Socket socket = new Socket(server.getHostInfo().getHostname(), server.getPort()); +// if (socket.isConnected()) { +// return true; +// } else { +// return false; +// } +// } catch (IOException ex) { +// return false; +// } finally { +// if (server != null) { +// server.close(); +// } +// } +// } +} diff --git a/src/test/java/de/rub/nds/ssh/subject/params/DockerWorkingTest.java b/src/test/java/de/rub/nds/ssh/subject/params/DockerWorkingTest.java new file mode 100644 index 0000000..2364482 --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/DockerWorkingTest.java @@ -0,0 +1,27 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ + +package de.rub.nds.ssh.subject.params; + +import static org.junit.Assert.assertTrue; + +import com.github.dockerjava.api.DockerClient; + +import org.junit.Test; + +import de.rub.nds.ssh.subject.docker.DockerClientManager; + +public class DockerWorkingTest { + @Test + public void isDockerOK() { + DockerClient docker = DockerClientManager.getDockerClient(); + docker.pingCmd().exec(); + assertTrue(true); // assert that no exception occured + } +} \ No newline at end of file diff --git a/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileManagerTest.java b/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileManagerTest.java new file mode 100644 index 0000000..a71a151 --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileManagerTest.java @@ -0,0 +1,55 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.params; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.config.Configurator; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author robert + */ +public class ParameterProfileManagerTest { + + public ParameterProfileManagerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testGetDefaultProfile() { + Configurator.setRootLevel(Level.DEBUG); + ParameterProfileManager manager = new ParameterProfileManager(); + } +} diff --git a/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializerTest.java b/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializerTest.java new file mode 100644 index 0000000..93de28b --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/ParameterProfileSerializerTest.java @@ -0,0 +1,64 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.params; + +import de.rub.nds.ssh.subject.ConnectionRole; +import de.rub.nds.ssh.subject.SshImplementationType; +import java.util.LinkedList; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author robert + */ +public class ParameterProfileSerializerTest { + + public ParameterProfileSerializerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testWrite() throws Exception { + List versionList = new LinkedList<>(); + versionList.add("1.1.0f"); + versionList.add("1.1.0g"); + List parameterList = new LinkedList<>(); + parameterList.add(new Parameter("-port [port]", ParameterType.HOST_PORT)); + parameterList.add(new Parameter("-cert [cert] -key [key]", ParameterType.CERTIFICATE_KEY)); + ParameterProfile profile = new ParameterProfile("openssl_default", "Default Profile for OpenSSH", + SshImplementationType.OPENSSH, ConnectionRole.CLIENT, versionList, parameterList); + System.out.println(ParameterProfileSerializer.write(profile)); + } +} diff --git a/src/test/java/de/rub/nds/ssh/subject/params/SshTestServer.java b/src/test/java/de/rub/nds/ssh/subject/params/SshTestServer.java new file mode 100644 index 0000000..47e0e7c --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/SshTestServer.java @@ -0,0 +1,335 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.params; + +import org.apache.sshd.common.kex.BuiltinDHFactories; +import org.apache.sshd.common.kex.KexProposalOption; +import org.apache.sshd.common.kex.KeyExchangeFactory; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.keyprovider.MappedKeyPairProvider; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.session.SessionListener; +import org.apache.sshd.common.util.logging.AbstractLoggingBean; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.auth.password.PasswordAuthenticator; +import org.apache.sshd.server.auth.pubkey.StaticPublickeyAuthenticator; +import org.apache.sshd.server.channel.ChannelSession; +import org.apache.sshd.server.command.AbstractCommandSupport; +import org.apache.sshd.server.command.Command; +import org.apache.sshd.server.command.CommandFactory; +import org.apache.sshd.server.kex.DHGServer; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.shell.ShellFactory; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.List; +import java.util.Map; + +public class SshTestServer extends Thread { + + private final int port; + private Boolean debug = false; + private boolean isServerDone = false; + private boolean isConnectionSuccessful = false; + private static final String PATH_TO_KEYSTORE = "./certs/keys.jks"; + private static final String KEYSTORE_PASSWORD = "password"; + private SshServer server = null; + + SshTestServer(int port) { + this.port = port; + } + + SshTestServer(int port, Boolean debug) { + this.debug = debug; + this.port = port; + } + + private class AcceptAllPasswordAuthenticator extends AbstractLoggingBean implements PasswordAuthenticator { + + public AcceptAllPasswordAuthenticator() { + super(); + } + + @Override + public boolean authenticate(String username, String password, ServerSession session) { + return true; + } + } + + private class AcceptAllPublickeyAuthenticator extends StaticPublickeyAuthenticator { + private AcceptAllPublickeyAuthenticator() { + super(true); + } + } + + private class EchoShell extends CommandExecutionHelper { + public EchoShell(String command) { + super(command); + } + + public EchoShell() { + super(); + } + + @Override + protected boolean handleCommandLine(String command) throws Exception { + OutputStream out = getOutputStream(); + out.write((command + "\n").getBytes(StandardCharsets.UTF_8)); + out.write(("# : \n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + isConnectionSuccessful = true; + return true; + } + + @Override + public void destroy(ChannelSession channel) throws Exception { + super.destroy(channel); + } + } + + private abstract class CommandExecutionHelper extends AbstractCommandSupport { + protected CommandExecutionHelper() { + this(null); + } + + protected CommandExecutionHelper(String command) { + super(command, null); + } + + @Override + public void run() { + ; + String command = getCommand(); + try { + if (command == null) { + BufferedWriter w = + new BufferedWriter(new OutputStreamWriter(getOutputStream(), StandardCharsets.UTF_8)); + w.write("Hallo Test server here! \n"); + w.write("# : "); + w.flush(); + + try (BufferedReader r = + new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8))) { + for (;;) { + command = r.readLine(); + if (!handleCommandLine(command)) { + return; + } + } + } + } else { + handleCommandLine(command); + } + } catch (InterruptedIOException e) { + // Ignore - signaled end + } catch (Exception e) { + String message = + "Failed (" + e.getClass().getSimpleName() + ") to handle '" + command + "': " + e.getMessage(); + try { + OutputStream stderr = getErrorStream(); + stderr.write(message.getBytes(StandardCharsets.US_ASCII)); + } catch (IOException ioe) { + log.warn("Failed ({}) to write error message={}: {}", e.getClass().getSimpleName(), message, + ioe.getMessage()); + } finally { + onExit(-1, message); + } + } finally { + onExit(0); + } + } + + /** + * @param command + * The command line + * @return {@code true} if continue accepting command + * @throws Exception + * If failed to handle the command line + */ + protected abstract boolean handleCommandLine(String command) throws Exception; + } + + private SshServer createSshServer() { + + SshServer server = SshServer.setUpDefaultServer(); + if (debug) { + server.addSessionListener(new SessionListener() { + @Override + public void sessionEstablished(Session session) { + System.out.println("SESSION ESTABLISHED"); + } + + @Override + public void sessionCreated(Session session) { + System.out.println("SESSION CREATED"); + } + + @Override + public void sessionPeerIdentificationSend(Session session, String version, List extraLines) { + System.out.println("SESSION PeerIdentificationSend version:" + version + " extraLines"); + } + + @Override + public void sessionPeerIdentificationLine(Session session, String line, List extraLines) { + System.out + .println("SESSION sessionPeerIdentificationLine line:" + line + " extralines:" + extraLines); + } + + @Override + public void sessionPeerIdentificationReceived(Session session, String version, + List extraLines) { + System.out.println( + "SESSION sessionPeerIdentificationReceived version:" + version + " extralines:" + extraLines); + } + + @Override + public void sessionNegotiationOptionsCreated(Session session, Map proposal) { + System.out.println("SESSION sessionNegotiationOptionsCreated KexProposalOption" + proposal); + } + + @Override + public void sessionNegotiationStart(Session session, Map clientProposal, + Map serverProposal) { + System.out.println("SESSION sessionNegotiationStart clientProposal" + clientProposal + + " serverProposal " + serverProposal); + } + + @Override + public void sessionNegotiationEnd(Session session, Map clientProposal, + Map serverProposal, Map negotiatedOptions, + Throwable reason) { + System.out.println("SESSION sessionNegotiationEnd clientProposal:" + clientProposal + + " serverProposal:" + serverProposal + " negotiatedOptions:" + negotiatedOptions); + } + + @Override + public void sessionEvent(Session session, Event event) { + System.out.println("SESSION sessionEvent event:" + event); + } + + @Override + public void sessionException(Session session, Throwable t) { + System.out.println("SESSION sessionException"); + } + + @Override + public void sessionDisconnect(Session session, int reason, String msg, String language, + boolean initiator) { + System.out + .println("SESSION sessionDisconnect reason" + reason + " msg:" + msg + " language:" + language); + } + + @Override + public void sessionClosed(Session session) { + System.out.println("SESSION sessionClosed"); + } + }); + } + server.setPort(port); +// SimpleGeneratorHostKeyProvider hostKeyProvider_ssh_rsa = +// new SimpleGeneratorHostKeyProvider(new File("hostkey_ssh_rsa.ser").toPath()); + SimpleGeneratorHostKeyProvider hostKeyProvider_ecdsa_sha2_nistp521 = + new SimpleGeneratorHostKeyProvider(new File("hostkey.ser_ecdsa_sha2_nistp521").toPath()); +// SimpleGeneratorHostKeyProvider hostKeyProvider_ssh_ed25519 = +// new SimpleGeneratorHostKeyProvider(new File("hostkey.ssh_ed25519").toPath()); +// SimpleGeneratorHostKeyProvider hostKeyProvider_ecdsa_sha2_nistp256 = +// new SimpleGeneratorHostKeyProvider(new File("hostkey.").toPath()); + + MappedKeyPairProvider provider; + try { + KeyPair ec521 = hostKeyProvider_ecdsa_sha2_nistp521.loadKey(null, KeyPairProvider.ECDSA_SHA2_NISTP521); +// KeyPair ed25519 = hostKeyProvider_ssh_ed25519.loadKey(null,KeyPairProvider.SSH_ED25519); +// KeyPair rsa = hostKeyProvider_ssh_rsa.loadKey(null,KeyPairProvider.SSH_RSA); +// provider = new MappedKeyPairProvider(ec521,rsa); + + provider = new MappedKeyPairProvider(SecurityUtils.getKeyPairGenerator("RSA").generateKeyPair(), ec521); + + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + + server.setKeyPairProvider(provider); + server.setPublickeyAuthenticator(new AcceptAllPublickeyAuthenticator()); + server.setPasswordAuthenticator(new AcceptAllPasswordAuthenticator()); + server.setShellFactory(new ShellFactory() { + @Override + public Command createShell(ChannelSession channel) throws IOException { + return new EchoShell(); + } + }); + server.setCommandFactory(new CommandFactory() { + + @Override + public Command createCommand(ChannelSession channel, String command) throws IOException { + return new EchoShell(command); + } + }); + + List kexExchangeFactories = server.getKeyExchangeFactories(); + kexExchangeFactories.add(DHGServer.newFactory(BuiltinDHFactories.dhg1)); + server.setKeyExchangeFactories(kexExchangeFactories); + + return server; + } + + @Override + public void run() { + server = createSshServer(); + try { + server.start(); + while (!isServerDone) { + } + server.stop(); + + } catch (IOException | RuntimeException e) { + + } + } + + public int getPort() { + return port; + } + + public boolean isServerDone() { + return isServerDone; + } + + public boolean isStarted() { + if (server != null) { + return server.isStarted(); + } else { + return false; + } + } + + public void stop(String host) throws IOException { + this.isServerDone = true; + } + + public void setIsConnectionSuccessful(boolean isConnectionSuccessful) { + this.isConnectionSuccessful = isConnectionSuccessful; + } + + public boolean isConnectionSuccessful() { + return isConnectionSuccessful; + } +} diff --git a/src/test/java/de/rub/nds/ssh/subject/params/TlsTestServer.java b/src/test/java/de/rub/nds/ssh/subject/params/TlsTestServer.java new file mode 100644 index 0000000..369f7c0 --- /dev/null +++ b/src/test/java/de/rub/nds/ssh/subject/params/TlsTestServer.java @@ -0,0 +1,136 @@ +/** + * SSH-Attacker - A Modular Penetration Testing Framework for SSH + * + * Copyright 2014-2023 Ruhr University Bochum, Paderborn University, Hackmanit GmbH + * + * Licensed under Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + */ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package de.rub.nds.ssh.subject.params; + +import java.io.FileInputStream; +import java.io.IOException; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +public class TlsTestServer extends Thread { + + private final int port; + private boolean isServerDone = false; + private boolean isConnectionSuccessful = false; + private static final String PATH_TO_KEYSTORE = "./certs/keys.jks"; + private static final String KEYSTORE_PASSWORD = "password"; + + TlsTestServer(int port) { + this.port = port; + } + + private SSLContext createSSLContext() { + try { + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(new FileInputStream(PATH_TO_KEYSTORE), KEYSTORE_PASSWORD.toCharArray()); + // Create key manager + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); + keyManagerFactory.init(keyStore, KEYSTORE_PASSWORD.toCharArray()); + KeyManager[] km = keyManagerFactory.getKeyManagers(); + // Create trust manager + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); + trustManagerFactory.init(keyStore); + TrustManager[] tm = trustManagerFactory.getTrustManagers(); + // Initialize SSLContext + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(km, tm, null); + return sslContext; + } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException + | UnrecoverableKeyException | KeyManagementException ex) { + ex.printStackTrace(); + } + return null; + } + + @Override + public void run() { + SSLServerSocket sslServerSocket = null; + SSLSocket sslSocket = null; + SSLContext sslContext = this.createSSLContext(); + try { + SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); + sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port); + while (!isServerDone) { + sslSocket = (SSLSocket) sslServerSocket.accept(); + new ServerConnectionThread(sslSocket).start(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + if (sslServerSocket != null) { + try { + sslServerSocket.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + + private class ServerConnectionThread extends Thread { + + private SSLSocket sslSocket = null; + + ServerConnectionThread(SSLSocket sslSocket) { + this.sslSocket = sslSocket; + } + + @Override + public void run() { + sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); + try { + if (sslSocket.isConnected()) { + isConnectionSuccessful = true; + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + public int getPort() { + return port; + } + + public boolean isServerDone() { + return isServerDone; + } + + public void stop(String host, int port) throws IOException { + this.isServerDone = true; + Socket socket = new Socket(host, port); + } + + public void setIsConnectionSuccessful(boolean isConnectionSuccessful) { + this.isConnectionSuccessful = isConnectionSuccessful; + } + + public boolean isConnectionSuccessful() { + return isConnectionSuccessful; + } +}