This is a trick I learned a long time ago. I used to teach it in my linux administration, digital forensics, and ethical hacking courses I taught at college. It has been one of the most useful commands I ever learned. So the scenario goes like this: lets assume you have a user you suspect is doing something nefarious…maybe even a hacker has a shell on your server. You would like to be able to see exactly what they are doing. Wouldn’t it be nice to be able to connect to their shell without them knowing so you can watch what they are doing?
Here is how it is done…..
First, a script I have written that does just that…
#!/bin/bash
# By Ed Wiget
# This script is used to attach to a running bash shell of a user
# in order to monitor what they do
##############################
## PROCESS
#############################
# check for process by user
# ps aux | grep pts | grep "\-bash" | grep -v grep
# root 30562 0.0 0.1 4136 1356 pts/0 S 09:59 0:00 -bash
# user 30648 0.0 0.1 4140 1388 pts/2 R 10:45 0:00 -bash
# use strace to attach to process
# strace -f -p 30648 2>&1 | egrep "read|recv|write|send|exec|socket|connect"
#############################
############################
STR=`which strace > /dev/null`
if [ $? -ne "0" ]; then
echo "strace does not exist"
if [ -f /etc/arch-release ]; then
pacman -Sy
pacman -S strace
elif [ -f /etc/redhat-release ]; then
yum update
yum install strace
elif [ -f /etc/debian-release ]; then
apt-get update
apt-get install strace
else
echo "unable to determine distro...please install strace. Exiting..."
exit 1
fi
fi
if [ "$1" == "" ]; then
echo "pass the pid to monitor as an option to this script, i.e. $0 12345"
echo -e "\n\nHere are the running pids\n\n"
ps aux | grep pts | grep "bash" | grep -v grep
else
echo -e "connecting to pid: $1 ... please wait\n\n"
strace -f -p $1 2>&1 | egrep "read|recv|write|send|exec|socket|connect"
fi
So what I do is place this in /bin or /sbin and call it monuser.
Then I create a .bashrc alias to that command like this:
alias mu='monuser'
Now all I need to do is either run the command monuser or mu. Running it without any options basically dumps the help or usage and also all the running bash shells. Here is an example:
$ mu
pass the pid to monitor as an option to this script, i.e. ./monitor-user.sh 12345
or if you have an alias set up
mu 12345
Here are the running pids
user1 20627 0.0 0.0 21320 4120 pts/0 Ss Jun05 0:00 /bin/bash
user2 20676 0.0 0.0 21320 2936 pts/0 S+ Jun05 0:00 /bin/bash
user3 23948 0.0 0.0 21440 4364 pts/1 Ss+ 08:33 0:00 /bin/bash
user4 28269 0.0 0.0 21368 4288 pts/2 Ss 11:26 0:00 /bin/bash
user5 30571 0.0 0.0 21316 4112 pts/3 Ss+ 12:56 0:00 /bin/bash
Lets assume I want to watch user5…..pid 30571
$ mu 30571
connecting to pid: 30571 ... please wait
read(0, "l", 1) = 1
write(2, "l", 1) = 1
read(0, "s", 1) = 1
write(2, "s", 1) = 1
read(0, "\r", 1) = 1
write(2, "\n", 1) = 1
[pid 8970] read(3, <unfinished ...>
[pid 8970] <... read resumed> "", 1) = 0
[pid 8970] execve("/bin/ls", ["ls", "--color=auto"], [/* 50 vars */]) = 0
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260f\0\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220!\0\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\"\0\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\357\1\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\r\0\0\0\0\0\0"..., 832) = 832
[pid 8970] open("tls/x86_64/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8970] open("tls/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8970] open("x86_64/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8970] open("libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8970] open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY) = 3
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\\\0\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\25\0\0\0\0\0\0"..., 832) = 832
[pid 8970] read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tb"..., 1024) = 301
[pid 8970] read(3, "", 1024) = 0
[pid 8970] read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
[pid 8970] read(3, "", 4096) = 0
[pid 8970] write(1, "1 \33[0m\33[01;34mdir1\33[0m file2\n", 31) = 31
write(2, "\33[01;31muser5\33[01;33m@\33[01;36mT"..., 77) = 77
read(0, "c", 1) = 1
write(2, "c", 1) = 1
read(0, "d", 1) = 1
write(2, "d", 1) = 1
read(0, " ", 1) = 1
write(2, " ", 1) = 1
read(0, "d", 1) = 1
write(2, "d", 1) = 1
read(0, "i", 1) = 1
write(2, "i", 1) = 1
read(0, "r", 1) = 1
write(2, "r", 1) = 1
read(0, "1", 1) = 1
write(2, "1", 1) = 1
read(0, "\r", 1) = 1
write(2, "\n", 1) = 1
write(2, "\33[01;31muser5\33[01;33m@\33[01;36mT"..., 82) = 82
read(0, "l", 1) = 1
write(2, "l", 1) = 1
read(0, "s", 1) = 1
write(2, "s", 1) = 1
read(0, "\r", 1) = 1
write(2, "\n", 1) = 1
[pid 8971] read(3, <unfinished ...>
[pid 8971] <... read resumed> "", 1) = 0
[pid 8971] execve("/bin/ls", ["ls", "--color=auto"], [/* 50 vars */]) = 0
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260f\0\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220!\0\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\"\0\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\357\1\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\r\0\0\0\0\0\0"..., 832) = 832
[pid 8971] open("tls/x86_64/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8971] open("tls/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8971] open("x86_64/libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8971] open("libpthread.so.0", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 8971] open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY) = 3
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\\\0\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\25\0\0\0\0\0\0"..., 832) = 832
[pid 8971] read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tb"..., 1024) = 301
[pid 8971] read(3, "", 1024) = 0
[pid 8971] read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
[pid 8971] read(3, "", 4096) = 0
write(2, "\33[01;31muser5\33[01;33m@\33[01;36mT"..., 82) = 82
read(0, "c", 1) = 1
write(2, "c", 1) = 1
read(0, "d", 1) = 1
write(2, "d", 1) = 1
read(0, " ", 1) = 1
write(2, " ", 1) = 1
read(0, ".", 1) = 1
write(2, ".", 1) = 1
read(0, ".", 1) = 1
write(2, ".", 1) = 1
read(0, "\r", 1) = 1
write(2, "\n", 1) = 1
write(2, "\33[01;31muser5\33[01;33m@\33[01;36mT"..., 77) = 77
read(0, "c", 1) = 1
write(2, "c", 1) = 1
read(0, "a", 1) = 1
write(2, "a", 1) = 1
read(0, "t", 1) = 1
write(2, "t", 1) = 1
read(0, " ", 1) = 1
write(2, " ", 1) = 1
read(0, "f", 1) = 1
write(2, "f", 1) = 1
read(0, "i", 1) = 1
write(2, "i", 1) = 1
read(0, "l", 1) = 1
write(2, "l", 1) = 1
read(0, "e", 1) = 1
write(2, "e", 1) = 1
read(0, "2", 1) = 1
write(2, "2", 1) = 1
read(0, "\r", 1) = 1
write(2, "\n", 1) = 1
[pid 8972] read(3, <unfinished ...>
[pid 8972] <... read resumed> "", 1) = 0
[pid 8972] execve("/bin/cat", ["cat", "file2"], [/* 50 vars */]) = 0
[pid 8972] read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\357\1\0\0\0\0\0"..., 832) = 832
[pid 8972] read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
[pid 8972] read(3, "", 4096) = 0
[pid 8972] read(3, "", 32768) = 0
write(2, "\33[01;31muser5\33[01;33m@\33[01;36mT"..., 77) = 77
As the user is typing in real-time, and all return results of any commands they type, are returned back to you. We can tell from the above that user5 ran these commands:
$ ls
1 dir1 file2
$ cd dir1
$ ls
$ cd ..
$ cat file2
[…] also tried the way to use strace to get the keys pressed here, but I found the result quite difficult to […]