Overview
Using the existing logon facilities on a *nix host is a popular and straightforward method used by attackers to persist on a system. Once an attacker has compromised a set of credentials, they can log in as if they were an authorized user.
How attackers obtain credentials
Attackers can obtain valid credentials using several methods:
-
Default credentials. A system may come with a valid set of credentials configured out of the box. This practice is common in consumer equipment and virtual machine appliances. A classic example of a default credential is admin/admin.
-
Phishing and credential harvesting sites. Users may be tricked into entering their credentials into malicious sites controlled by the attacker, revealing their password in plain text. Phishing emails may trigger code execution, allowing an attacker to place malware on a system.
-
Brute forcing and password spraying. An attacker may try many passwords against a single account or a known good password against multiple accounts.
-
Credential reuse. Once an attacker verifies that a set of credentials are valid, they may try to figure out where else these credentials will work. Data breaches often expose large sets of valid usernames and passwords. Attackers obtain these breaches then try to use these valid credentials against different sites. People often reuse their passwords, making this a potent form of attack.
-
Sniffing network traffic. Attackers can observe network traffic that contains credentials and harvest this data for later use.
-
Keystroke logging. Since users typically type in their passwords, attackers often deploy keystroke loggers to steal passwords and other sensitive information.
-
Clipboard logging. Often, people will store passwords within a document on their computer. Since strong passwords are long and difficult to type, they copy and paste their credentials into login prompts. Attackers may intercept the contents of their victim’s clipboards to harvest passwords and other sensitive information.
-
Passwords stored in memory. Plain-text passwords and hashes must be placed in memory as they are processed. If an attacker knows where these credentials reside in memory, they may be able to harvest them. A tool that uses these techniques is mimipenguin: https://github.com/huntergregal/mimipenguin
-
Social engineering. Attackers often employ social engineering attacks to obtain credentials. These attacks are sinister and have a high rate of success. The attacker uses a credible pretext and instills confidence in the victim using information they already know about them, tricking them into revealing sensitive information. A basic example is an attacker calling a victim, claiming to be an IT staff member performing maintenance, tricking them into providing their password.
-
Many more.
Attackers may add rogue accounts or modify an existing user once they have achieved the level of access with permissions to do so on a host.
Accounts can be created and manipulated using utilities such as
passwd
, adduser
, usermod
, or chsh
. Since these tools are
expected to be on a system and are legitimate administrative tools,
creating detections based on the usage of these tools can be a
challenge.
Attacking and defending user accounts has been a security problem for decades and I don’t see this going away any time soon.
If you are an incident responder responding to Linux systems, you should be aware of the topics covered here. User account abuse is a recurring theme with computer intrusions.
Below, I have documented information regarding user accounts I’ve found helpful in the context of security. Most of this information also applies to other UNIX-like operating systems. With the knowledge provided here, you will be able to hunt effectively for badness related to user accounts.
login
login
is the process that initiates sessions on a system. login
prompts for a username and password, checking /etc/passwd
and
/etc/shadow
to see if they are valid.
The manual page for login(1) can be found here
login.defs
login.defs
is the shadow suite configuration file. It is usually
found at /etc/login.defs
.
login.defs
provides settings that provide granular control over
sessions, logging, password requirements, and override user-defined
settings.
One interesting setting within login.defs
is the FAKE_SHELL
variable. Setting this will override the user’s shell in /etc/passwd
,
executing FAKE_SHELL
instead.
The manual page for login.defs(5) can be found here
utmp
utmp
stores information about who is currently using the system. It
is typically found at /var/run/utmp
.
The manual page for utmp can be found here
Not all programs utilize utmp logging, so utmp can be an unreliable source of information. Quoted directly from the utmp manual page:
Warning: utmp must not be writable by the user class “other”, because many system programs (foolishly) depend on its integrity. You risk faked system logfiles and modifications of system files if you leave utmp writable to any user other than the owner and group owner of the file.
wtmp(5)
wtmp is much like utmp, however it stores historic login information,
not just currently logged in users. This file usually resides at
/var/log/wtmp
.
btmp
btmp stores information regarding login failures. This file typically
resides at /var/log/btmp
.
lastlog(8)
lastlog
shows recent user logins on a system. This information is
stored at /var/log/lastlog
.
User Management Commands
passwd
passwd
provides an interface to manipulate user’s passwords on a system.
The manual page for passwd
can be found here.
chfn
chfn
allows users to change their full name and other GECOS fields.
chsh
chsh
allows users to change their shell.
chage
chage
allows users to view and manipulate their password expiry
settings.
daniel@wildcat ~ % chage -l daniel
Last password change : Jun 17, 2021
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
useradd
useradd
adds users to the system. Compared to adduser
, useradd
is relatively primitive. useradd
does not create home directories or
transfer the contents of /etc/skel/
to the user’s home directory.
userdel
userdel
removes users from a system.
usermod
usermod
modifies users. You can change settings such as their shell,
home directory, uid, gid, real name, and group membership.
groupadd
groupadd
adds a group to the system.
groupdel
groupdel
deletes a group from the system.
groupmod
groupmod
modifies groups. You
w
w
reports logged in users and what they are doing.
Example w
output:
10:03:04 up 14 days, 13:26, 1 user, load average: 3.13, 2.08, 1.73
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
daniel :0 :0 11Jun21 ?xdm? 20:28m 0.02s /usr/lib/gdm3/gdm
Attackers may alter w
to hide their presence from administrators.
who
who
displays a report of who is logged, where they logged in, where
they logged in from, and when they logged in.
Example who
output:
daniel tty1 2021-06-26 14:38
daniel pts/0 2021-06-26 14:37 (192.168.10.180)
daniel pts/1 2021-06-26 15:47 (192.168.10.180)
lastlog
lastlog
reports recent logins from each user, or a specific user.
This information is gathered from the /var/log/lastlog
file
Example lastlog
output:
Username Port From Latest
root **Never logged in**
daemon **Never logged in**
bin **Never logged in**
sys **Never logged in**
sync **Never logged in**
games **Never logged in**
man **Never logged in**
lp **Never logged in**
mail **Never logged in**
news **Never logged in**
uucp **Never logged in**
proxy **Never logged in**
www-data **Never logged in**
backup **Never logged in**
list **Never logged in**
irc **Never logged in**
gnats **Never logged in**
nobody **Never logged in**
systemd-network **Never logged in**
systemd-resolve **Never logged in**
systemd-timesync **Never logged in**
messagebus **Never logged in**
syslog **Never logged in**
_apt **Never logged in**
tss **Never logged in**
uuidd **Never logged in**
tcpdump **Never logged in**
avahi-autoipd **Never logged in**
usbmux **Never logged in**
rtkit **Never logged in**
dnsmasq **Never logged in**
kernoops **Never logged in**
cups-pk-helper **Never logged in**
whoopsie **Never logged in**
speech-dispatcher **Never logged in**
avahi **Never logged in**
nm-openvpn **Never logged in**
saned **Never logged in**
hplip **Never logged in**
colord **Never logged in**
geoclue **Never logged in**
pulse **Never logged in**
gnome-initial-setup **Never logged in**
gdm **Never logged in**
summerpalace **Never logged in**
systemd-coredump **Never logged in**
daniel pts/3 192.168.1.100 Sat Jun 12 18:59:18 -0700 2021
clamav **Never logged in**
postfix **Never logged in**
sshd **Never logged in**
last
last
shows a similar report to lastlog
, but gathers this data from
/var/log/wtmp
rather than /var/log/lastlog
.
Example last
output:
daniel pts/1 192.168.10.180 Sat Jun 26 15:47 still logged in
daniel tty1 Sat Jun 26 14:38 still logged in
daniel pts/0 192.168.10.180 Sat Jun 26 14:37 still logged in
reboot system boot 4.4.0-186-generi Sat Jun 26 14:37 still running
daniel pts/0 192.168.10.180 Sat Jun 26 14:37 - 14:37 (00:00)
daniel pts/0 192.168.10.180 Sat Jun 26 14:36 - 14:37 (00:00)
daniel pts/1 192.168.10.180 Sat Jun 26 11:49 - 11:55 (00:05)
daniel tty1 Sat Jun 26 11:48 - down (02:48)
daniel pts/0 192.168.10.180 Sat Jun 26 11:47 - 13:53 (02:05)
daniel pts/0 192.168.10.180 Sat Jun 26 10:35 - 11:28 (00:53)
joseph pts/0 192.168.10.180 Fri Jun 25 18:35 - 18:43 (00:08)
daniel pts/0 192.168.10.180 Fri Jun 25 18:35 - 18:35 (00:00)
daniel pts/0 192.168.10.180 Sat Jun 12 21:40 - 21:46 (00:05)
reboot system boot 4.4.0-186-generi Sat Jun 12 21:40 - 14:37 (13+16:56)
daniel pts/0 192.168.10.180 Sat Jun 12 21:38 - 21:40 (00:01)
daniel pts/0 192.168.10.180 Sat Jun 12 18:51 - 18:51 (00:00)
daniel pts/0 192.168.10.180 Sat Jun 12 18:51 - 18:51 (00:00)
wtmp begins Sat Jun 12 18:51:10 2021
lastb
lastb
is like last, but shows failed login attempts.
The data used for this command is at /var/log/btmp
Example lastb
output
UNKNOWN tty1 Sat Jun 26 14:38 - 14:38 (00:00)
UNKNOWN tty1 Sat Jun 26 11:48 - 11:48 (00:00)
joseph ssh:notty 192.168.10.180 Fri Jun 25 18:44 - 18:44 (00:00)
joseph ssh:notty 192.168.10.180 Fri Jun 25 18:43 - 18:43 (00:00)
btmp begins Fri Jun 25 18:43:59 2021
utmpdump
utmpdump
parses arbitrary utmp and wtmp files. This can be useful
for forensics.
Example utmpdump
output:
Utmp dump of /var/log/btmp
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.10.180 ] [192.168.10.180 ] [Fri Jun 25 18:43:59 2021 PDT]
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.10.180 ] [192.168.10.180 ] [Fri Jun 25 18:44:03 2021 PDT]
[7] [01266] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 11:48:12 2021 PDT]
[7] [01254] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 14:38:02 2021 PDT]
utmpdump
can be abused to remove or alter entries from a utmp or
wtmp file. This is demonstrated below by altering this btmp file to
show that the failed logins came from 192.168.2.73
rather than
192.168.10.180
.
root@ubuntu1604:~# utmpdump /var/log/btmp
Utmp dump of /var/log/btmp
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.10.180 ] [192.168.10.180 ] [Fri Jun 25 18:43:59 2021 PDT]
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.10.180 ] [192.168.10.180 ] [Fri Jun 25 18:44:03 2021 PDT]
[7] [01266] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 11:48:12 2021 PDT]
[7] [01254] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 14:38:02 2021 PDT]
root@ubuntu1604:~# utmpdump /var/log/btmp >/tmp/utmpdump
Utmp dump of /var/log/btmp
root@ubuntu1604:~# vi /tmp/utmpdump
root@ubuntu1604:~# utmpdump -r </tmp/utmpdump >/tmp/altered-btmp
Utmp undump of /dev/stdin
root@ubuntu1604:~# utmpdump /tmp/altered-btmp
Utmp dump of /tmp/altered-btmp
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.2.73 ] [192.168.2.73 ] [Fri Jun 25 18:43:59 2021 PDT]
[6] [18454] [ ] [joseph ] [ssh:notty ] [192.168.2.73 ] [192.168.2.73 ] [Fri Jun 25 18:44:03 2021 PDT]
[7] [01266] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 11:48:12 2021 PDT]
[7] [01254] [tty1] [UNKNOWN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Jun 26 14:38:02 2021 PDT]
The attacker can now replace /var/log/btmp
with /tmp/altered-btmp
.
su
su
allows users to run commands as another user. This is often used
to elevate to the root
user to perform systems administration tasks.
Creating a group and setting permissions on su
to deny
non-administrative users the ability to use this command may make a
lot of sense in your environment:
chgrp wheel /usr/bin/su
chmod 4750 /usr/bin/su
This configuration only allows users in the wheel
group and the root
user to utilize su
.
sudo
sudo
is similar to su
, but provides much more granular
configuration options. sudo
is often abused by attackers due to
over-permissive configurations. Often, administrative users are placed
into the sudo
group which has full root access, provided the user
executing commands enters a valid password.
Attackers may abuse sudo
by adding their user to a group with
permissions to use sudo, or adding malicious entries to sudo’s
configuration. This allows an easy method to elevate their privileges
to root.
Configuration for sudo is typically found in the /etc/sudoers
file
and /etc/sudoers.d/
directory.
The manual for sudo
can be found
here.
The manual for sudoers
can be found
here.
Verifying passwd-related binaries
Attackers may manipulate the binaries highlighted in this section. Many of these binaries are suid-root or process sensitive information such as plain-text passwords, making them prime targets for backdoors and information stealing malware.
The deb and rpm package managers allow you to validate an installed package. This will quickly reveal whether or not one of these sensitive binaries has been tampered with.
Here is a quick and dirty script to verify these files on deb-based systems such as Ubuntu or Debian:
#!/bin/sh
packages=""
bins=""
for bin in "login passwd lastlog chfn chsh chage useradd userdel adduser addgroup deluser delgroup usermod w last who su sudo lastb utmpdump"; do
bins="$bins $(which $bin)"
done
for bin in $bins; do
x=$(dpkg -S "$bin" 2>/dev/null)
if [ $? = 1 ]; then
echo "$bin is not provided by a package"
continue
fi
packages="$packages $(echo $x |cut -d : -f 1)"
done
packages=$(echo $packages |tr ' ' '\n' |sort -u)
echo
echo Verifying packages: $packages
for package in $packages; do
dpkg -V $package
done
echo
echo "Done."
/etc/passwd
This file contains a line for each user. Each line is colon-delimited, with seven fields:
- login name
- password
- User ID (UID)
- Group ID (GID)
- Username/Gecos
- Home directory
- Shell
An example user entry looks like this:
root:x:0:0:root:/root:/bin/sh
Example /etc/passwd file from a CentOS 7.9 machine:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
daniel:x:1000:1000:daniel:/home/daniel:/bin/bash
This passwd file is “shadowed.” Most UNIX-like operating systems have shadowed password files nowadays. You can tell that its shadowed because of the ‘x’ character in each user’s password field.
An old-fashioned, unshadowed passwd would look something like this:
root:GGWSv8kfHNYcY:0:0:root:/home/root:/bin/sh
nobody:PKWeKgk73qytM:1:1:nobody:/home/nobody:/bin/sh
bin:vfW/oY6SMPA3U:2:2:bin:/home/bin:/bin/sh
sys:Et8qJNKI/e1SU:3:3:sys:/home/sys:/bin/sh
www:WpocxhyXZqRIY:4:4:www:/home/www:/bin/sh
daniel:qzwur89Gb6JFg:5:5:daniel:/home/daniel:/bin/sh
heather:UXMKaAGFKVwpE:6:6:heather:/home/heather:/bin/sh
zachary:FVCspYztVvRfQ:7:7:zachary:/home/zachary:/bin/sh
nicole:WXcAaQAzW1nsU:8:8:nicole:/home/nicole:/bin/sh
This exposes the user hashes to cracking attacks. To mitigate this type of an attack, the shadow suite was developed.
/etc/shadow
The shadow file stores the hashes on modern Linux systems. This file has restrictive permissions, granting access only by the root user.
If an attacker obtains root on a system, they can steal the shadow file and attempt to crack the hashes held within.
The shadow suite introduces features to passwd such as setting password expiry dates and tracking when the password was last changed.
Here is an example /etc/shadow file, which corresponds with the passwd file shown above:
root:$6$UEQBBViTOmlcFpfM$klq9U1OMGSjCtO4UlMFm3wTUG9U3bJDapDxDJbn66CnLlkYktJhIkhaDBZ9VhgzZnvYdZt9Xz5SHKlm/51kPJ.::0:99999:7:::
bin:*:18353:0:99999:7:::
daemon:*:18353:0:99999:7:::
adm:*:18353:0:99999:7:::
lp:*:18353:0:99999:7:::
sync:*:18353:0:99999:7:::
shutdown:*:18353:0:99999:7:::
halt:*:18353:0:99999:7:::
mail:*:18353:0:99999:7:::
operator:*:18353:0:99999:7:::
games:*:18353:0:99999:7:::
ftp:*:18353:0:99999:7:::
nobody:*:18353:0:99999:7:::
systemd-network:!!:18775::::::
dbus:!!:18775::::::
polkitd:!!:18775::::::
sshd:!!:18775::::::
postfix:!!:18775::::::
chrony:!!:18775::::::
daniel:$6$5JRjKN8Qc2bhKcHf$.Tacg39KOwS1mMWIVuHHPMwSOkIEgvbFZGxPWgfBErAjIVapMbu5Q.B8Hz.zU8vw.nYeqMGHMPTthQLL8mR2i/::0:99999:7:::
The format of the shadow field is as follows:
$id$salt$hash
The id defines which hashing algorithm is used, and may be one of the following:
$1$
MD5$2a$
Blowfish$2y$
Blowfish$5$
SHA256$6$
SHA512
Salts prevent against rainbow table attacks and should be randomly generated.
Hashes can be generated with tools such as mkpasswd
or
Python. Typically, you should use the strongest available algorithm.
mkpasswd -m sha-512 password salt
python -c 'import crypt; print crypt.crypt("password", "$6$saltysalt")'
/etc/group
/etc/group
defines user groups on a system. Its structure is as follows:
groupname:password:GID:comma,separated,users
Attackers may add themselves as members of a group which is able to
execute administrative tools or read sensitive data such as sudo
or
mail
.
/etc/gshadow
/etc/gshadow
contains group passwords. Users can authenticate to
groups with the newgrp
command. Similar to setting a user’s password
with the passwd
command, you can set a group’s password with
gpasswd
.
The structure of a gshadow entry is as follows:
groupname:password:administrators,comma,separated:comma,separated,users
The last 10 lines of my gshadow file look like this. Notice that the
foo
group has a password set.
gdm:!::
sambashare:!::daniel
summerpalace:!::
systemd-coredump:!!::
daniel:!::
clamav:!::
postfix:!::
postdrop:!::
rdma:!::
foo:$6$gPs6k/PqVO./h$gvcjQ0YjiwgS3PmopM8VrtB4MprYrDUrq.EiV31xHj9nHfoNnYfLy2PHyUfMvVz639PiP3ALRU.GE5jvGGV4y0::
Here is an example shell session adding, setting a password for, and authenticating to a group:
daniel@wildcat ~ % sudo addgroup foo
Adding group `foo' (GID 1000) ...
Done.
daniel@wildcat ~ % sudo gpasswd foo
Changing the password for group foo
New Password:
Re-enter new password:
daniel@wildcat ~ % id
uid=1001(daniel) gid=1001(daniel) groups=1001(daniel),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(sambashare)
daniel@wildcat ~ % newgrp foo
Password:
daniel@wildcat ~ % id
uid=1001(daniel) gid=1000(foo) groups=1000(foo),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(sambashare),1001(daniel)
Account Lockouts
Users can be locked out using passwd -l
. Under the hood, account
lockouts work by prepending the existing password with a
string. Typically this is an exclaimation point:
Example unlocked account’s entry in /etc/shadow:
hank:$6$pretendhash:0:99999:7:::
After running passwd -l hank
, this entry will look like this:
hank:!!$6$pretendhash:18796:0:99999:7:::
To unlock this account, use passwd -u hank
.
The status of an account can be viewed with passwd -S USERNAME
. passwd -S
outputs a line with seven space-delimited
fields:
- login name
- lockout status (L = locked, NP = no password, P = password set)
- date of last password change
- minimum age
- maximum age
- warning period
- inactivity period
Example output from passwd -S daniel
:
daniel P 06/17/2021 0 99999 7 -1
The following script will lock all users with passwords set on the system
except for the user running it (typically this will be root), and the
user who initiated this command via sudo
if applicable:
#!/bin/sh
# Lock out all users except $USER and $SUDO_USER
while read -r line; do
user=$(echo "$line" | cut -d : -f 1)
status=$(passwd -S "$user" 2>/dev/null)
if [ $? = 1 ]; then
echo "Unable to check status of user $user (are you root?)"
exit
fi
lostatus=$(echo "$status" | awk {'print $2'})
if [ "$lostatus" != "L" ]; then
case "$user" in
$USER)
echo "Skipping USER $USER"
;;
$SUDO_USER)
echo "Skipping SUDO_USER $SUDO_USER"
;;
*)
echo "Locking $user"
passwd -l "$user"
;;
esac
fi
done </etc/passwd
/etc/skel/ directory
Files within /etc/skel/
will be copied to newly-created users with
the adduser
utility. Attackers may modify existing files or add new
malicious files to this directory to maintain persistence on a system.
One method an attacker might use is to add malicious code to
/etc/skel/.profile
. After adduser
copies this file to the
newly-created user’s home directory, it will be executed each time
they log in.
Each file within /etc/skel/
should be accounted for. Validating the
default files provided by your distribution may help to determine if
these files have been altered.
#!/bin/sh
packages=""
find /etc/skel/ -type f -exec ls -l {} \;
echo
for file in $(find /etc/skel/ -type f); do
x=$(dpkg -S "$file" 2>/dev/null)
if [ $? = 1 ]; then
echo "$file is not provided by a package"
continue
fi
packages="$packages $(echo $x | cut -d : -f 1)"
done
packages=$(echo $packages | tr ' ' '\n' | sort -u)
echo
echo "Verifying packages: $packages"
for package in $packages; do
dpkg -V $package
done
echo
echo "Done."
Running this on a test virtual machine shows that /etc/skel/malware.sh is not provided by a package
SSH authorized_keys files
Attackers may add their own keys to one or more users’ authorized_keys
configuration files. This grants them access to the system even if the
password is changed, provided that publickey authentication is enabled.
authorized_keys
are typically found in the .ssh
directory within a
user’s home directory. They are plaintext files with each line
representing a ssh key that grants access to the user in which it is
configured.
Attackers may also use the more obscure authorized_keys2
file to
avoid detections that only check for authorized_keys
files.
Logs
Successful Console Authentications
Jun 26 11:48:26 ubuntu1604 login[1266]: pam_unix(login:session): session opened for user daniel by LOGIN(uid=0)
Jun 26 11:48:26 ubuntu1604 systemd-logind[906]: New session 3004 of user daniel.
Unsuccessful Console Authentications
Jun 26 11:48:08 ubuntu1604 login[1266]: pam_unix(login:auth): check pass; user unknown
Jun 26 11:48:08 ubuntu1604 login[1266]: pam_unix(login:auth): authentication failure; logname=LOGIN uid=0 euid=0 tty=/dev/tty1 ruser= rhost=
Successful SSH Authentications
Successful login using a password:
Jun 25 18:35:50 ubuntu1604 sshd[18278]: Accepted password for joseph from 192.168.10.180 port 55790 ssh2
Jun 25 18:35:50 ubuntu1604 sshd[18278]: pam_unix(sshd:session): session opened for user joseph by (uid=0)
Jun 25 18:35:50 ubuntu1604 systemd: pam_unix(systemd-user:session): session opened for user joseph by (uid=0)
Jun 25 18:35:50 ubuntu1604 systemd-logind[906]: New session 2840 of user joseph.
Successful login using a key:
Jun 25 18:34:31 ubuntu1604 sshd[18128]: Accepted publickey for daniel from 192.168.10.180 port 55774 ssh2: RSA SHA256:zG4bX5tSoJbac+5NmuNplF68nZ+IDymHHGhsLYnz6rQ
When you are reviewing successful login events, it may be helpful to ask yourself these questions:
- Is this authentication normal?
- Does the time of the login match the user’s typical working hours?
- Is the source IP address associated with this login expected?
- Is the source IP address listed in any bad reputation lists?
- Is the username suspicious?
Unsuccessful SSH authentications
Jun 25 18:43:58 ubuntu1604 sshd[18454]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.10.180 user=joseph
Jun 25 18:43:59 ubuntu1604 sshd[18454]: Failed password for joseph from 192.168.10.180 port 55902 ssh2
Jun 25 18:44:03 ubuntu1604 sshd[18454]: Failed password for joseph from 192.168.10.180 port 55902 ssh2
Jun 25 18:44:05 ubuntu1604 sshd[18454]: PAM 1 more authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.10.180 user=joseph
Authentication logs should be monitored for excessive login failures. Excessive failures usually indicate a brute force attack or misconfigured software.
Fail2Ban
Fail2Ban monitors logs for failed authentications. If a threshold of login failures originating from a remote IP address over a set amount of time is met, Fail2Ban will block these IP addresses temporarily and generate an alert.
Usually, distributions will include Fail2Ban as the fail2ban
package. Default settings may vary between distributions. Default
settings typically block an IP after 5 failures within 10 minutes or
so for 10 minutes. This allows a valid user to wait it out if they
mistakenly type in the wrong password a few times without intervention
from a sysadmin.
Configuration files are typically found in /etc/fail2ban/
Many unsuccessful logins followed by a successful login
The pattern of many unsuccessful logins, followed by a successful login indicates a successful brute force attack. If this pattern is observed, the affected user’s credentials have likely been compromised.
Password changed
Jun 16 19:45:23 wildcat passwd[291039]: pam_unix(passwd:chauthtok): password changed for daniel
Detecting User Account Abuse
Manual review
It helps to have a quick look at the passwd, shadow, and related configurations from time to time. Often, a keen sysadmin will spot misconfigurations that security tooling has missed.
Accounts with passwords
Accounts with a password set will have a hash in their shadow file.
Example:
daniel:$6$5JRjKN8Qc2bhKcHf$.Tacg39KOwS1mMWIVuHHPMwSOkIEgvbFZGxPWgfBErAjIVapMbu5Q.B8Hz.zU8vw.nYeqMGHMPTthQLL8mR2i/::0:99999:7:::
Commonly, accounts will have an asterisk or exclaimation mark in this field. This indicates that these accounts are disabled or locked.
Example:
bin:*:18353:0:99999:7:::
dbus:!!:18775::::::
You should verify if accounts with passwords should have a password
set. Users such as bin
, www-data
, or nobody
having a password
set is suspicious.
This script will list accounts with passwords set
#!/bin/sh
while read -r line
do
user=$(echo "$line" | cut -d : -f 1)
status=$(passwd -S "$user")
lostatus=$(echo "$status" | awk {'print $2'})
if [ "$lostatus" != "L" ]; then
echo $user
fi
done </etc/passwd
Multiple UID=0 Accounts
Usually, root should be the only account with a UID of zero. All additional accounts with a UID of zero should be investigated.
Systems sometimes include a toor
user as a secondary root
user. These two users may be configured so that one or the other only
has access to the console, or for system recovery purposes.
This one-liner will list the passwd entries for accounts with a UID of zero:
while read -r line; do user=$(echo $line |cut -d : -f 1); uid=$(echo $line|cut -d : -f 3); if [ $uid = 0 ]; then echo $line; fi; done </etc/passwd
Passwordless accounts
If an account is lacking data in the password field in /etc/passwd, they will be able to login without a password by simply pressing enter when prompted for a password.
It is common for shellcode and exploit code to add passwordless accounts to the passwd file:
- https://ksdpmx.bitbucket.io/2020/09/03/exploit-db-shellcodes-13461/
- http://shell-storm.org/shellcode/files/shellcode-548.php
Passwords may be deleted using passwd -d USERNAME
Short duration sessions
Attackers may execute one-liners using ssh
rather than using an
interactive shell and remaining logged in. This would make the
attacker’s session not appear in diagnostic tools such as w
or
who
.
Here are some example logs showing a 1 second session, indicating that an attacker is using this method:
Jun 26 11:55:28 ubuntu1604 sshd[23665]: Accepted publickey for daniel from 192.168.10.180 port 34762 ssh2: RSA SHA256:zG4bX5tSoJbac+5NmuNplF68nZ+IDymHHGhsLYnz6rQ
Jun 26 11:55:28 ubuntu1604 sshd[23665]: pam_unix(sshd:session): session opened for user daniel by (uid=0)
Jun 26 11:55:28 ubuntu1604 systemd-logind[906]: New session 3007 of user daniel.
Jun 26 11:55:29 ubuntu1604 sshd[23705]: Received disconnect from 192.168.10.180 port 34762:11: disconnected by user
Jun 26 11:55:29 ubuntu1604 sshd[23705]: Disconnected from 192.168.10.180 port 34762
Jun 26 11:55:29 ubuntu1604 sshd[23665]: pam_unix(sshd:session): session closed for user daniel
Jun 26 11:55:29 ubuntu1604 systemd-logind[906]: Removed session 3007.
Enable CLI logging
Enabling CLI logging provides analysts with the commands entered into a shell session. I feel that this is superior to monitoring shell history files for a number of reasons:
- History files can be altered by the user.
- History files may only be written when the shell exits cleanly. This leaves analysts with limited visibility into what is occurring within a session until the user logs out.
- Attackers may disable history file logging by removing the
HISTFILE
environment variable, or changing it to/dev/null
. - Attackers may set filesystem permissions and attributes which make writing to the history file impossible.
Monitoring these commands creates a lot of opportunity for defenders to fabricate detections.
Observing the cadence of commands ran within a session can help an analyst determine of these commands were part of an automated script, pasted into the session, or hands-on-keyboard activity.
A small a code snippet can be added to /etc/profile
which
effectively logs all commands input in a shell with syslog:
Shells such as bash have syslog functionality, but most distributions package bash with this setting disabled. This blog post shows how to recompile bash with command line logging enabled:
https://dmfrsecurity.com/2019/10/31/building-a-hardened-shell-for-attack-defend-ctf-supremacy/
/etc/shells
/etc/shells
contains a list of valid login shells. A user’s shell
must be listed in this file in order to login.
Often, systems administrators will set a user’s shell to /bin/false
or /bin/nologin
to deny a user from having shell access to a
host.
Attackers may add malicious entries to /etc/shells
, or replace a
valid shell with malicious software. For example, an attacker may
replace bash with a malicious copy with a built in keylogger, or
replace /bin/false
with a shell.
An example /etc/shells
file on Ubuntu 20.04:
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/bin/zsh
/usr/bin/zsh
This script will hash each shell listed within /etc/shells
. This
information can be compared to a known good set of hashes or looked up
on VirusTotal and similar services.
#!/bin/sh
for shell in $(grep -v '^#' /etc/shells); do
sha256sum "$shell"
done
Example output:
wildcat# ./etc-shells-hash-shells.sh
64e48365207d0c19008ba7d53d75c0de3fcd5a1590e4c40fc69c677663fedc20 /bin/sh
04a484f27a4b485b28451923605d9b528453d6c098a5a5112bec859fb5f2eea9 /bin/bash
04a484f27a4b485b28451923605d9b528453d6c098a5a5112bec859fb5f2eea9 /usr/bin/bash
04a484f27a4b485b28451923605d9b528453d6c098a5a5112bec859fb5f2eea9 /bin/rbash
04a484f27a4b485b28451923605d9b528453d6c098a5a5112bec859fb5f2eea9 /usr/bin/rbash
64e48365207d0c19008ba7d53d75c0de3fcd5a1590e4c40fc69c677663fedc20 /bin/dash
64e48365207d0c19008ba7d53d75c0de3fcd5a1590e4c40fc69c677663fedc20 /usr/bin/dash
a6f2177402114fc8b5e7ecf924ffa61a2ac25bd347bc3370fb92e07b76e0b44c /bin/zsh
a6f2177402114fc8b5e7ecf924ffa61a2ac25bd347bc3370fb92e07b76e0b44c /usr/bin/zsh
Another useful trick is verifying that the hashes of each shell
matches what was provided by its respective package. Here is an
example script which verifies these using dpkg
. This will not work
on RPM-based distributions.
#!/bin/sh
packages=""
for shell in $(grep -v '^#' /etc/shells); do
x=$(dpkg -S "$shell" 2>/dev/null |grep -v diversion)
if [ $? = 1 ]; then
echo "$shell is not provided by a package"
continue
fi
packages="$packages $(echo $x |cut -d : -f 1)"
done
echo
echo Verifying packages: $packages
for package in $(echo $packages |tr ' ' '\n' | sort -u); do
dpkg -V $package
done
Example output from this script is shown below. Notice that
/bin/bash
has been tampered with.
wildcat# ./etc-shells-dpkg-verify.sh
/usr/bin/bash is not provided by a package
/usr/bin/rbash is not provided by a package
/usr/bin/dash is not provided by a package
/usr/bin/zsh is not provided by a package
Verifying packages: dash bash bash dash zsh
??5?????? /bin/bash
Backup files
Some tools maintain backup files of the password and group databases before they make changes to them. These backup files have a trailing hyphen:
- /etc/passwd-
- /etc/shadow-
- /etc/group-
- /etc/gshadow-
- /etc/subuid-
- /etc/subgid-
These backups may be useful for recovery or forensic purposes if an attacker has manipulated any of the aforementioned files.
This one-liner will use diff
on each of these backup files,
revealing recent changes.
for x in /etc/*-; do echo $x; diff $(echo $x |tr -d -) $x; echo; done
Anti-Forensics
Deleting logs
Once an attacker has rooted a Linux system, they may simply delete the logs which contain information regarding their malicious logins.
Due to the relative ease of destroying logs once an attacker has gained access to a system, logs should be forwarded to an external server. Shipping logs elsewhere can be done by configuring a centralized syslog server or using a tool such as Splunk.
Recovering deleted log files may be possible with file carving tools such as PhotoRec, extundelete, or testdisk. I have had mixed
Log Cleaners
Deleting log files outright may raise the suspicions of an observant defender or trigger security alerts. As such, attackers have developed tools that selectively alter or delete matching entries to evade detection.
Some example log wiping tools can be found here: https://packetstormsecurity.com/UNIX/penetration/log-wipers/