Dtracing Passwords for Fun
Going through an old hardware stash, I've noticed a harddisk that was part of a Solaris test system of mine I disassembled 10 years ago. The disk contains an interesting artifact I implemented at that time: a proof-of-concept for capturing user entered passwords with DTrace.
Proof of Concept¶
The objective of this POC is to capture all passwords of all users that try to login to a Solaris system via password authentication. That means logins via ssh or even via rlogin.
With Solaris 10 and later, the natural and very convenient tool for this job is DTrace. DTrace comes with several providers, e.g. you can install a probe for specific system calls or even specific userspace functions.
The challenge with this is that in contrast to - say - syscall
tracing, userspace tracing always has a significant overhead.
Thus, the pid
provider requires a concrete process
id (PID) to trace, one cannot directly trace all userspace
processes of an executable with that provider.
Fortunately, one can work around this: write two DTrace scripts.
The first installs a probe that is executed for each exec of a -
say - ssh process. In the probe's action the just started process
is stopped (to avoid a race condition) and the second DTrace
script with some pid provider userspace probes is started for
the new PID. The first action of this script is to restart the
stopped process (see also Destructive Actions, Dtrace
Manual). By default, dtrace only allows safe
actions, but it also supports enabling unsafe ones. Clearly,
running external commands from a dtrace script and recursively
calling dtrace
isn't safe and can escalate quickly.
The remainder of the challenge is then to identify the interesting functions that are called by the SSH and rlogin daemon for obtaining the user entered password.
The wrapper script (part 1):
1 2 3 4 5 6 7 8 9 10 |
|
The actual script for ssh (part 2):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Note that prun
is a Solaris command line utility for restarting
a stopped process (think: kill -CONT
). There is also room for
improvement: we don't really need to trace this ssh process until
it exits. For our purposes, it is sufficient to trace it until -
say - it returns from ssh_login
. Thus, we can add another probe
for just that event that executes the exit-Action, such that this
dtrace process stops all tracing and exits. This doesn't
interrupts the traced process.
Example output:
ssh 1234: sehrgeheimespassword
ssh`read_passphrase
ssh`input_userauth_info_req+0xef
ssh`dispatch_run+0x49
ssh`ssh_userauth2+0x19e
ssh`ssh_login+0xa6
ssh`main+0xbd2
ssh`0x80586ba
Similar script for rlogin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Example output:
CPU ID FUNCTION:NAME
0 1 :BEGIN
0 42222 getpassphrase:return sehrgeheimespassword
0xd0ea86ec
libnsl.so.1`S_tab+0x2e
libnsl.so.1`S_tab+0x282
0xd0c10bd6
libnsl.so.1`_C0095A10+0xa3
libnsl.so.1`_C0095A14+0x50
bash`0x8052829
bash`0x8053b59
bash`0x8052137
bash`0x8051e3a
How to read the disk¶
The old Solaris system was built with left-over hardware that was
old even 10 years ago. That means the disk is a 80 GB
parallel-ATA one. Thus, I've used a parallel-ATA to USB adaptor
to connect the old disk to a modern Fedora 27 Linux system. A
quick inspection shows that the disk contains some BSD style
partition slices that fdisk
doesn't understand but the kernel
does such that some additional /dev/sdXY
files are created. The
kernel even correctly detects the UFS magic - but fails to mount
the filesystem:
# mount -o noatime /dev/sdf5 /mnt/old
mount: /mnt/old: unknown filesystem type 'ufs'.
This is due to the ufs kernel module missing - Fedora 27 doesn't install it, by default. Thus:
# dnf -y install kernel-modules-extra
With the ufs kernel module available the mount succeeds:
# mount -t ufs -o noatime /dev/sdf5 /mnt/old
mount: /mnt/old: WARNING: device write-protected, mounted read-only.
The module also prints some warnings to the kernel log:
kernel: ufs: ufs was compiled with read-only support, can't be mounted as read-write
kernel: ufs: You didn't specify the type of your ufs filesystem
mount -t ufs -o ufstype=sun|sunx86|44bsd|ufs2|5xbsd|old|hp|nextstep|nextstep-cd|openstep ...
>>>WARNING<<< Wrong ufstype may corrupt your filesystem, default is ufstype=old
The warning might look scary, but the filesystem is mounted read-only, thus a wrong fs-type can't really damage the filesystem. At least for OpenSolaris post 10-ish/Solaris 11 pre-release the default ufs type is good enough and all files can be accessed.