Tuesday, November 12, 2013

How to use SUID for shell scripts in Linux?



Question : How to write a shell script which will read the required passwords/connect strings from a config file? Other users should be able to execute the scripts, however they should not be able to read the config file.

Say, I have a config file, myconfig.txt, which contains credentials for an oracle connection:
$ cat myconfig.txt
SQLUSR="scott"
SQLPWD="pwd123"
Shell script,script1.sh, to connect to Oracle and get employee name:
$ cat script1.sh
#!/usr/bin/bash

source myconfig.txt

NM=`sqlplus -s ${SQLUSR}/${SQLPWD}@XE <<EOF
set heading off
select ename from emp where empno=7369;
EOF`

echo Name is $NM
The issue here is: Any user, who has permissions to run this script can get to know the password present in the myconfig.txt either by reading the file directly or by running the shell script in debug mode.

How to prevent the user from reading the password?
   By removing the read permission from the group and others on the myconfig.txt file will not help. Because, in that case, the other users will not be able to run the script as well.

Setting the SUID on the Shell script:
    After removing the read permission and then applying the suid bit on the shell script will work on say Solaris, will not work in Linux flavors. Because SUID can be applied only on binary executables in Linux.

Solution:
   In order to create a binary executable, we need to write our code in either say C, C++, etc..which gives binary executables.
   Let us write a C program which reads the myconfig.txt file and sets environment variables for all the entries present in the file. And then invoking the shell script from the C program should suffice:
$ cat getName.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
    FILE *fp = fopen("/home/guru/C/myconfig.txt","r");
    char data[128];
    char *d1;
    while(fgets(data,128,fp)!=NULL){
        d1=(char *)malloc(128);
        strcpy(d1,data);
        d1[strlen(d1)-1]='\0';
        putenv(d1);
    }
    fclose(fp);
    system("/home/guru/C/script.sh");
}
 Creating an executable for this C program :
$ gcc -o getName getName.c
$ ./getName
NM is SMITH
Remove the read permission on the myconfig.txt file for group and others and applying the SUID on the executable:
$ chmod go-r myconfig.txt
$ chmod u+s getName
Logging as a different user to check :
$ su - guest
$ echo $USER
guest
$ cd /home/guru/C
$ ./getName
NM is SMITH
$ cat myconfig.txt
cat: myconfig.txt: Permission denied
  On applying the SUID permission, the other user when trying to execute the script, gets the same permission as the owner on the myconfig.txt file and hence the script is able to read the file.

2 comments:

  1. There are quite a few security problems with the approach here. A user could manipulate the PATH environment variable and put "." in front of the rest of the path. Then all they have to do is put their own program in the current directory and name it sqlplus. Your script will then run their program as your user. At that point, it could simply print its command line arguments (including the password), run the env command (getting the password), or read the password file directly.

    In addition to all that, they can now run arbitrary code as your user, including reading your SSH key and wiping your homedir.

    So in trying to solve one problem, you have not solved it and just opened up a much bigger one. Your approach can work, but you need to be far more careful than you have been. The biggest sins you have committed include invoking a binary in an SUID program without specifying the full path, and not dropping permissions after you no longer need them (you should drop them before you run the script).

    ReplyDelete