Jun 062013
 

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

  One Response to “How to monitor a linux users shell in real time”

  1. […] also tried the way to use strace to get the keys pressed here, but I found the result quite difficult to […]

This site uses Akismet to reduce spam. Learn how your comment data is processed.