Advertisement
Guest User

Untitled

a guest
Apr 8th, 2018
541
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.86 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. VERBOSITY=0
  4. TEMP_D=""
  5. DEFAULT_USER="backdoor"
  6.  
  7. error() { echo "$@" 1>&2; }
  8.  
  9. Usage() {
  10.     cat <<EOF
  11. Usage: ${0##*/} [ options ] target
  12.  
  13.    add a 'backdoor' user to a image or filesystem at 'target'
  14.  
  15.    options:
  16.       --import-id U      use 'ssh-import-id' to get ssh public keys
  17.                          may be used more than once.
  18.       --force            required to operate on / filesystem
  19.       --password P       set password P, implies --password-auth
  20.       --password-auth    enable password auth
  21.       --pubkeys  F       add public keys from file 'F'
  22.                          default: ~/.ssh/id_rsa.pub unless --password
  23.                          or --import-id specified
  24.       --user      U      use user 'U' (default: '${DEFAULT_USER}')
  25. EOF
  26. }
  27.  
  28. bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
  29. cleanup() {
  30.     [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
  31. }
  32.  
  33. debug() {
  34.     local level=${1}; shift;
  35.     [ "${level}" -gt "${VERBOSITY}" ] && return
  36.     error "${@}"
  37. }
  38.  
  39. mod_sshd_bool() {
  40.     local cfg="$1" kn="$2" target="$3" dry=${4:-false}
  41.     local ws=$' \t' msg=""
  42.     local match="^\([#]\{0,1\}\)[#$ws]*$kn\([$ws]\+\)\(yes\|no\)"
  43.     local cur="" hsh="#"
  44.     cur=$(sed -n "s/$match/\1\3/p" "$cfg") ||
  45.         { error "failed to read $cfg"; return 1; }
  46.     if [ -n "$cur" ]; then
  47.         case "$cur" in
  48.             "#$target") msg="uncommenting, '$target' line";;
  49.             "#*") msg="uncommenting, changing '${cur#$hsh}' to '$target'";;
  50.             "$target") msg="nochange";;
  51.             "*") msg="changing '$cur' to '$target'";;
  52.         esac
  53.         if [ "$msg" = "nochange" ]; then
  54.             debug 1 "no change to $cfg necessary"
  55.         else
  56.             debug 1 "updating $cfg: $msg"
  57.             $dry && return
  58.             sed -i "s/$match/$kn\2${target}/" "$cfg" ||
  59.                 { error "failed to update $cfg"; return 1; }
  60.         fi
  61.     else
  62.         debug 1 "appending entry for '$kn $target' to $cfg"
  63.         $dry && return
  64.         echo "$kn $target" >> "$cfg" ||
  65.             { error "failed to append entry to $cfg"; return 1; }
  66.     fi
  67.     return 0
  68. }
  69.  
  70. test_mod_sshd_cfg() {
  71.     local kn="PasswordAuthentication"
  72.     echo "#$kn   yes" > f1
  73.     echo "#$kn  no" > f2
  74.     echo "$kn yes" > f3
  75.     echo "$kn no" > f4
  76.     : > f5
  77.     for f in f1 f2 f3 f4 f5; do
  78.         mod_sshd_bool "$f" PasswordAuthentication yes true
  79.     done
  80. }
  81.  
  82. add_group_ent() {
  83.     local group="$1" gid="$2" fgroup="$3" dry="${4:-false}"
  84.     local grent="$group:x:$gid:"
  85.     if grep -q "^$group:" "$fgroup"; then
  86.         debug 1 "remove $group from group file"
  87.         $dry || sed -i "/^$group:/d" "$fgroup" ||
  88.             { error "failed to remove user from group"; return 1; }
  89.     fi
  90.  
  91.     debug 1 "append entry to group: $grent"
  92.     if ! $dry; then
  93.         echo "$grent" >> "$fgroup" ||
  94.             { error "failed to update group file"; return 1; }
  95.     fi
  96.     return 0
  97. }
  98.  
  99. add_passwd_ent() {
  100.     local user="$1" uid="$2" gid="$3" home="$4" fpasswd="$5" dry=${6:-false}
  101.  
  102.     if grep -q "^$user:" "$fpasswd"; then
  103.         debug 1 "remove $user from password file"
  104.         $dry || sed -i "/^$user:/d" "$fpasswd" ||
  105.             { error "failed to remove user from password file"; return 1; }
  106.     fi
  107.  
  108.     local pwent="$user:x:$uid:$gid:backdoor:$home:/bin/bash"
  109.     debug 1 "append entry to passwd: $pwent"
  110.     if ! $dry; then
  111.         echo "$pwent" >> "$fpasswd" ||
  112.             { error "failed to update passwd file"; return 1; }
  113.     fi
  114. }
  115.  
  116. encrypt_pass() {
  117.     local pass="$1" fmt="${2-\$6\$}"
  118.     enc=$(echo "$pass" |
  119.         perl -e '
  120.             $p=<STDIN>; chomp($p);
  121.             $salt = join "", map { (q(a)..q(z))[rand(26)] } 1 .. 8;
  122.             if (${ARGV[0]}) { $salt = "${ARGV[0]}$salt\$"; }
  123.             print crypt($p, "$salt") . "\n";' "$fmt") || return
  124.     [ -n "${enc}" ] && [ -z "${fmt}" -o "${enc#${fmt}}" != "${fmt}" ] &&
  125.     _RET="$enc"
  126. }
  127.  
  128. add_shadow_ent() {
  129.     local user="$1" pass="$2" fshadow="$3" dry="$4"
  130.     local encrypt_pre="\$6\$" shent="" encpass="" pwchange=""
  131.  
  132.     # if input was '$6$' format, just use it verbatum
  133.     if [ "${pass#${encrypt_pre}}" != "${pass}" ]; then
  134.         debug 1 "using encrypted password from cmdline"
  135.         encpass="$pass"
  136.     else
  137.         encrypt_pass "$pass" && encpass="$_RET" ||
  138.             { error "failed to encrypt password"; return 1; }
  139.     fi
  140.  
  141.     # pwchange is number of days since 1970
  142.     pwchange=$(($(date +"(%Y-1970)*365 + 10#%j")))
  143.     shent="$user:$encpass:$pwchange:0:99999:7:::"
  144.  
  145.     if grep -q "^$user:" "$fshadow"; then
  146.         debug 1 "remove $user from shadow file"
  147.         $dry || sed -i "/^$user:/d" "$fshadow" ||
  148.             { error "failed to remove user from shadow"; return 1; }
  149.     fi
  150.  
  151.     debug 1 "append entry to shadow: $shent"
  152.     if ! $dry; then
  153.         echo "$shent" >> "$fshadow" ||
  154.             { error "failed to update shadow file"; return 1; }
  155.     fi
  156.     return 0
  157.  
  158. }
  159.  
  160. add_sudo_ent() {
  161.     local user="$1" mp="$2" dry="$3"
  162.  
  163.     local target="/etc/sudoers.d/99-$user"
  164.  
  165.     local ent="$user ALL=(ALL) NOPASSWD:ALL"
  166.     local start="#BACKDOOR_START_${user}"
  167.     local end="#BACKDOOR_end_${user}"
  168.     local content=$(printf "%s\n%s\n%s\n" "$start" "$ent" "$end")
  169.  
  170.     if [ -f "$mp/etc/lsb-release" ] &&
  171.         grep -i lucid -q "$mp/etc/lsb-release"; then
  172.         target="/etc/sudoers"
  173.         debug 2 "$mp does not seem to support sudoers.d"
  174.         debug 1 "add sudoers ($mp,$target): $ent"
  175.         if grep -q "^$start$" "$mp/$target"; then
  176.             debug 2 "removing $user entry from $target"
  177.             if ! $dry; then
  178.                 sed -i "/^${start}$/,/^${end}$/d" "$target" ||
  179.                     { error "failed update $target"; return 1; }
  180.             fi
  181.         fi
  182.         if ! $dry; then
  183.             ( umask 226 && echo "$content" >> "$mp/$target" ) ||
  184.                 { error "failed to add sudoers entry to $target"; return 1; }
  185.         fi
  186.     else
  187.         debug 1 "add sudoers ($mp,$target): $ent"
  188.         if ! $dry; then
  189.             rm -f "$mp/$target" &&
  190.                 ( umask 226 && echo "$content" > "$mp/$target" ) ||
  191.                 { error "failed to add sudoers entry to $target"; return 1; }
  192.         fi
  193.     fi
  194. }
  195.  
  196. add_user() {
  197.     local user="$1" pass="$2" uid="$3" gid="$4" home="$5"
  198.     local rootd="$6" dry="${7:-false}"
  199.     local fpasswd="$rootd/etc/passwd" fshadow="$rootd/etc/shadow"
  200.     local fgroup="$rootd/etc/group"
  201.  
  202.     [ -f "$fpasswd" ] || { error "no password file"; return 1; }
  203.     [ -f "$fshadow" ] || { error "no shadow file"; return 1; }
  204.     [ -f "$fgroup" ] || { error "no group file"; return 1; }
  205.  
  206.     local group="$user" f="" t=""
  207.    
  208.     add_passwd_ent "$user" "$uid" "$gid" "$home" "$fpasswd" "$dry" || return 1
  209.     add_group_ent "$group" "$gid" "$fgroup" "$dry" || return 1
  210.     add_shadow_ent "$user" "$pass" "$fshadow" "$dry" || return 1
  211.  
  212.     debug 1 "create $rootd/home/$user"
  213.     if ! $dry; then
  214.         mkdir -p "$rootd/home/$user" &&
  215.             chown $uid:$gid "$rootd/home/$user" ||
  216.             { error "failed to make home dir"; return 1; }
  217.         for f in "$rootd/etc/skel/".* "$rootd/etc/skel/"*; do
  218.             [ -e "$f" ] || continue
  219.             t="$rootd/home/$user/${f##*/}"
  220.             [ ! -e "$t" ] || continue
  221.             cp -a "$f" "$t" && chown -R "$uid:$gid" "$t" ||
  222.                 { error "failed to copy $f to $t"; return 1; }
  223.         done
  224.     fi
  225. }
  226.  
  227. add_user_keys() {
  228.     local keys="$1" dir="$2" ownership="$3" dry="${4:-false}"
  229.     debug 1 "add ssh keys to $dir with $ownership"
  230.     $dry && return
  231.     mkdir -p "$dir" &&
  232.         cp "$keys" "$dir/authorized_keys" &&
  233.         chmod 600 "$dir/authorized_keys" &&
  234.         chown "$ownership" "$dir" "$dir/authorized_keys" &&
  235.         chmod 700 "$dir" ||
  236.         { error "failed to add user keys"; return 1; }
  237.     if [ $VERBOSITY -ge 1 ]; then
  238.         debug 1 "added ssh keys:"
  239.         sed "s,^,| ," "$keys"
  240.     fi
  241. }
  242.  
  243. gen_ssh_keys() {
  244.     local mp="$1" types="${2:-rsa}" dry="${3:-false}"
  245.     local ktype="" file="" ftmpl="/etc/ssh/ssh_host_%s_key" out=""
  246.     for ktype in $types; do
  247.         file=${ftmpl//%s/$ktype}
  248.         if [ -f "$mp/$file" ]; then
  249.             debug 2 "existing key for $mp/$file"
  250.             continue
  251.         fi
  252.         debug 1 "ssh-keygen -t $ktype -N '' -f '$file' -C backdoor"
  253.         $dry && continue
  254.         out=$(ssh-keygen -t "$ktype" -N '' -f "$mp/$file" -C backdoor 2>&1) || {
  255.             error "$out"
  256.             error "failed generate keytype $ktype";
  257.             return 1;
  258.         }
  259.         out=$(ssh-keygen -l -f "$mp/$file")
  260.         debug 1 "$out"
  261.     done
  262. }
  263.  
  264. apply_changes() {
  265.     local mp="$1" user="$2" password="$3" pwauth="$4" pubkeys="$5"
  266.     local dry="${6:-false}"
  267.     local home="/home/$user" key=""
  268.     local uid="9999" gid="9999"
  269.  
  270.     local sshcfg="$mp/etc/ssh/sshd_config"
  271.     [ -f "$sshcfg" ] ||
  272.         { error "$sshcfg did no exist"; return 1; }
  273.  
  274.     key="PubkeyAuthentication"
  275.     mod_sshd_bool "$sshcfg" "$key" "yes" "$dry" ||
  276.         { error "failed to set $key to yes"; return 1; }
  277.  
  278.     if $pwauth; then
  279.         key="PasswordAuthentication"
  280.         mod_sshd_bool "$sshcfg" "$key" "yes" "$dry" ||
  281.             { error "failed to set $key to yes"; return 1; }
  282.     fi
  283.  
  284.     gen_ssh_keys "$mp" "rsa" "$dry" || return 1
  285.  
  286.     add_user "$user" "$password" "$uid" "$gid" "$home" "$mp" "$dry" || return 1
  287.  
  288.     [ -z "$pubkeys" ] ||
  289.         add_user_keys "$pubkeys" "$mp/$home/.ssh" "$uid:$gid" || return 1
  290.  
  291.     add_sudo_ent "$user" "$mp" "$dry" || return 1
  292.  
  293. }
  294.  
  295. main() {
  296.     short_opts="hv"
  297.     long_opts="help,dry-run,force,import-id:,password:,password-auth,pubkeys:,user:,verbose"
  298.     getopt_out=$(getopt --name "${0##*/}" \
  299.         --options "${short_opts}" --long "${long_opts}" -- "$@") &&
  300.         eval set -- "${getopt_out}" ||
  301.         bad_Usage
  302.  
  303.     local user="" password="" pwauth=false pubkeys="" import_ids="" dry=false
  304.     local target="" pkfile="" force=false
  305.     user="${DEFAULT_USER}"
  306.  
  307.     local args=""
  308.     args=( "$@" )
  309.     unset args[${#args[@]}-1]
  310.  
  311.     while [ $# -ne 0 ]; do
  312.         cur=${1}; next=${2};
  313.         case "$cur" in
  314.             -h|--help) Usage ; exit 0;;
  315.                --dry-run) dry=true;;
  316.                --force) force=true;;
  317.                --import-id)
  318.                     import_ids="${import_ids:+${import_ids} }$next";
  319.                     shift;;
  320.                --password) password=$next; shift;;
  321.                --password-auth) pwauth=true;;
  322.                --pubkeys) pubkeys=$next; shift;;
  323.                --user) user=$next; shift;;
  324.             -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
  325.             --) shift; break;;
  326.         esac
  327.         shift;
  328.     done
  329.  
  330.     [ $# -ne 0 ] || { bad_Usage "must provide image"; return 1; }
  331.     [ $# -ge 2 ] && { bad_Usage "too many arguments: $*"; return 1; }
  332.  
  333.     [ "$(id -u)" = "0" ] ||
  334.         { error "sorry, must be root"; return 1; }
  335.  
  336.     target="$1"
  337.     if [ -d "$target" ]; then
  338.         if [ "$target" -ef "/" ] && ! $force; then
  339.             error "you must specify --force to operate on /"
  340.             return 1
  341.         fi
  342.     elif [ -f "$target" ]; then
  343.         local vopt="" mcu="mount-callback-umount"
  344.         local mic="mount-image-callback"
  345.         if [ ${VERBOSITY} -ge 2 ]; then
  346.             vopt="-v"
  347.         fi
  348.         if command -v "$mic" >/dev/null 2>&1; then
  349.             :
  350.         elif [ -x "${0%/*}/$mcu" ]; then
  351.             PATH="${0%/*}:$PATH"
  352.             mic="$mcu"
  353.         else
  354.             error "No '$mcu' or '$mic' in PATH"
  355.             return 1
  356.         fi
  357.         exec "$mic" $vopt -- "$target" "$0" "${args[@]}" _MOUNTPOINT_
  358.     else
  359.         [ -f "$target" ] || { error "$target: not a file"; return 1; }
  360.     fi
  361.  
  362.     if [ -n "$password" ] && ! which perl >/dev/null 2>&1; then
  363.         { error "perl required for making password"; return 1; }
  364.         pwauth=true
  365.     fi
  366.  
  367.     { [ -z "$import_ids" ] || which ssh-import-id >/dev/null 2>&1; } ||
  368.         { error "you do not have ssh-import-id"; return 1; }
  369.  
  370.     TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
  371.         { error "failed to make tempdir"; return 1; }
  372.     trap cleanup EXIT
  373.  
  374.     pkfile="${TEMP_D}/pubkeys"
  375.     if [ -z "$password" -a -z "$pubkeys" -a -z "$import_ids" ]; then
  376.         [ -f ~/.ssh/id_rsa.pub ] || {
  377.             error "must specify one of --password, --pubkeys, --import-id"
  378.             error "either pass an argument or create ~/.ssh/id_rsa.pub"
  379.             return 1
  380.         }
  381.         debug 1 "set pubkeys to ~/.ssh/id_rsa.pub"
  382.         pubkeys=$(echo ~/.ssh/id_rsa.pub)
  383.     fi
  384.  
  385.     if [ -n "$pubkeys" ]; then
  386.         cp "$pubkeys" "$pkfile" ||
  387.             { error "failed to copy $pubkeys"; return 1; }
  388.     fi
  389.  
  390.     if [ -n "$import_ids" ]; then
  391.         ssh-import-id --output "$pkfile.i" ${import_ids} &&
  392.             cat "$pkfile.i" >> "$pkfile" ||
  393.             { error "failed to import ssh users: $import_ids"; return 1; }
  394.     fi
  395.  
  396.     [ -f "$pkfile" ] || pkfile=""
  397.  
  398.     apply_changes "$target" "$user" "$password" "$pwauth" "$pkfile"
  399.     [ $? -eq 0 ] || { error "failed to apply changes"; return 1; }
  400.  
  401.     error "added user '$user' to $target"
  402.     [ -n "$password" ] && error "set password to $password."
  403.     $pwauth && error "enabled password auth" ||
  404.         error "did not enable password auth"
  405.     [ -n "$pubkeys" ] && error "added pubkeys from $pubkeys."
  406.     [ -n "$import_ids" ] && error "imported ssh keys for $import_ids"
  407.     return 0
  408. }
  409.  
  410. main "$@"
  411.  
  412. # vi: ts=4 noexpandtab
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement